详解vue的数据劫持以及操作数组的坑

TL;DR

给data添加新属性的时候vm.$set(vm.info,'newKey','newValue')
data上面属性值是数组的时候,需要用数组的方法操作数组,而不能通过index或者length属性去操作数组,因为监听不到属性操作的动作。

安装和初使用vue

vue是构建用户界面的渐进式框架。所谓的渐进式:vue + components + vue-router + vuex + vue-cli可以根据需要选择相应的功能。
来串命令mkdir vue-apply;cd vue-apply;npm init -y;npm i vue。
来一个html文件如下,浏览器瞄下~,浏览器控制台vm.msg=0再看下

 <div id="app">{{msg}}</div>
  <script src="node_modules/vue/dist/vue.js"></script>
  <script>
  let vm = new Vue({
    el:'#app',
    // template加上之后会替换掉#app这个标签
    // template:'<h1>en</h1>',
    data:{msg:'msg'}
  })
  vm.msg = 'msg'
  </script>

说说mvvm mvc

mvc其实是model view Model传统所有逻辑在controller,难以维护。用户输入 => 控制器 => 数据改变,如果数据变了需要获取dom,操作属性,再渲染到视图上。

mvvm其实是model view viewModel数据变化驱动视图。数据变了,不需要你获取dom,然后改变dom的内容。这边数据变了,vm负责监听,视图那边自动发生变化。最明显的是不需要document.querySelector之类的了。

vm的实质

上面说了vm负责让数据变了,视图能自动发生变化。这么神奇魔术背后的原理是Object.defineProperty。其实就是属性的读取和设置操作都进行了监听,当有这样的操作的时候,进行某种动作。来一个demo玩下。

// 对obj上面的属性进行读取和设置监听
let obj = {
    name:'huahua',
    age:18
  }
  function observer(obj){
    if(typeof obj === 'object'){
      for (const key in obj) {
        defineReactive(obj,key,obj[key])
      }
    }
  }
  // get的return的值才是最终你读取到的值。所以设的值是为读取准备的。
  // set传的参数是设置的值,注意这里不要有obj.name = newVal 这样又触发set监听,会死循环的。
  function defineReactive(obj,key,value){
    Object.defineProperty(obj,key,{
      get:function(){
        console.log('你在读取')
        // happy的话这边可以value++,这样你发现读取的值始终比设置的大一个,因为return就是读取到的值
        return value
      },
      set:function(newVal){
        console.log('数据更新了')
        value = newVal
      }

    })
  }
  observer(obj)
  obj.age = 2
  console.log(obj.age)

在浏览器执行的时候,控制台随手也可以obj.name="hua1"类似的操作,发现都监听到了。但是如果更深一步,obj.name={firstname:'hua',lastname:'piaoliang'};obj.name.lastname='o'就不能监听到属性修改了。因为并没有将新的赋值对象监听其属性。所以函数需要改进。

需要在defineReactive的第一行加上observer(value)。设置值的时候如果是对象的话,也需要将这个对象数据劫持。同理,set那边也需要加这行。

 function defineReactive(obj,key,value){
    // 注意这里!!!!!!!
    observer(value)
    Object.defineProperty(obj,key,{
      get:function(){
        return value
      },
      set:function(newVal){
        // 注意这里!!!!!!!
        observer(newVal)
        console.log('数据更新了')
        value = newVal
      }

    })
  }

继续,如果obj.name=[1,2,3];obj.name.push(4)发现又没有通知了,这是因为Object.defineProperty不支持监听数组变化。所以需要重写数组上面的方法。话说,最近看了个文章,理论上也可以监听数组,但是性能消耗和收益不成正比,所以,vue就没去实现了。

// 把数组上大部分方法重写了,这里不一一列举。但是如果你 [1,2].length--,这是捕捉不到的
  let arr = ['push','slice','split']
  arr.forEach(method=>{
    let oldPush = Array.property[method]
    Array.property[method] = function(value){
      console.log('数据更新')
      oldPush.call(this,value)
    }
  })

vue使用的时候注意的坑

正如上面的解释,vue2.0的底层约莫是这个逻辑,所以使用需要注意的点:

因为是一开始就数据劫持了。所以后来如果新绑定属性,是没有数据劫持的。如果需要调用 vm.$set(vm.info,'newKey','newValue'),vm是vue的实例。

当属性值是数组,数组变化的时候,跟踪不到变化。因为数组虽然是对象,但是Object.defineProperty不支持数组,所以vue改写了数组的所有方法,当调用数组方法的时候,就调动变动事件。但是不能通过属性或者索引控制数组,比如length,index。

总结:data上,绑定所有属性避免后期加新属性。如果是数组,只能通过数组方法修改数组。如下例子,控制台vm.arr--发现视图并不会变化,vm.arr.push(4)就能变化

 <div id="app">{{msg}}{{arr}}</div>
  <script src="node_modules/vue/dist/vue.js"></script>
  <script>
  let vm = new Vue({
    el:'#app',
    // template加上之后会替换掉#app这个标签
    // template:'<h1>en</h1>',
    data:{msg:'msg',arr:[1,2,3]}
  })
  vm.msg = 'msg'
  </script>

以上所述是小编给大家介绍的vue的数据劫持以及操作数组的坑详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Vue.js中数组变动的检测详解

    前言 最近在尝试用Vue.js重构公司的现有业务代码,组件化的设计思路和MVVM的思想让我深深沉迷于其中.但是是踩到了不少坑,就比如这篇文章介绍的数组绑定后的更新检测. 相信大家都知道Observer,Watcher,vm 可谓 Vue 中比较重要的部分,检测数据变动后视图更新的重要环节.在 vue.js中$watch的用法示例 中,我们讨论了如何实现基本的 watch . 接下来,我们来看看如何实现数组变动检测. 例子: // 创建 vm let vm = new Vue({ data: {

  • Vue响应式添加、修改数组和对象的值

    有些时候,不得不想添加.修改数组和对象的值,但是直接添加.修改后又失去了getter.setter. 由于 JavaScript 的限制, Vue 不能检测以下变动的数组: 1. 利用索引直接设置一个项时,例如: vm.items[indexOfItem] = newValue 2. 修改数组的长度时,例如: vm.items.length = newLength 为了避免第一种情况,以下两种方式将达到像 vm.items[indexOfItem] = newValue 的效果, 同时也将触发状

  • 浅谈vue的踩坑路

    ------>axios模拟get json一直拿不到文件,先把data放到根目录,再去dev-server.js(就是npm执行的那个文件)里面设置静态资源访问路径app.use('/data',express.static('./data')) ... app.use(hotMiddleware) // serve pure static assets var staticPath = path.posix.join(config.dev.assetsPublicPath, config.d

  • 详解VUE 数组更新

    1.数据方法分类: (1)原数组改变 push  pop  unshift  shift  reverse  sort  splice (2)原数组未变,生成新数组 slice  concat  filter 对于使原数组变化的方法,可以直接更新视图. 对于原数组未变的方法,可以使用新数组替换原来的数组,以使视图发生变化. 示例代码: <!DOCTYPE html> <html lang="zh"> <head> <meta charset=&

  • Vue不能观察到数组length的变化

    由于 JavaScript 的限制,Vue 不能检测以下变动的数组: 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue 当你修改数组的长度时,例如:vm.items.length = newLength 因为vue的响应式是通过 Object.defineProperty 来实现的,但是数组的length属性是不能添加getter和setter,所有无法通过观察length来判断. 为什么Vue不能观察到数组length的变化 如下代码,虽然看

  • vue踩坑记录之数组定义和赋值问题

    问题场景:vue页面初始化展示请求后台返回的数据失败,没有报错 <el-form-item label="有效日期"> <div class="block"> <el-date-picker v-model="effective_date" type="daterange" range-separator="至" start-placeholder="开始日期&quo

  • Vue数组更新及过滤排序功能

    前面的话 Vue为了增加列表渲染的功能,增加了一组观察数组的方法,而且可以显示一个数组的过滤或排序的副本.本文将详细介绍Vue数组更新及过滤排序 变异方法 Vue 包含一组观察数组的变异方法,它们将会触发视图更新,包含以下方法 push() 接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度 pop() 从数组末尾移除最后一项,减少数组的length值,然后返回移除的项 shift() 移除数组中的第一个项并返回该项,同时数组的长度减1 unshift() 在数组前端添加任意个

  • vue 数组和对象不能直接赋值情况和解决方法(推荐)

    Vue 不能检测以下变动的数组: 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue 当你修改数组的长度时,例如:vm.items.length = newLength 当第一种情况需求时,可以使用this.$set(this.arr,index,newVal) Vue 不能检测对象属性的添加或删除: 可以使用this.$set(this.person,'age',12) 当需要添加多个对象时,Object.assign({},this.pers

  • 详解vantUI框架在vue项目中的应用踩坑

    1.订单提交地址等组件的应用. 使用的组件有如下: import {Card,button,Toast,AddressList,Popup,AddressEdit,Area} from 'vant' 主要是配货地址编辑这块: <van-address-edit :area-list="areaList" :address-info="addressInfo" show-postal show-delete show-set-default show-searc

  • 详解Vue微信公众号开发踩坑全记录

    本文介绍了Vue微信公众号开发踩坑全记录,分享给大家,也给自己留个笔记. 需求 微信授权登录(基于公众号的登录方案) 接入JS-SDK实现图片上传,分享等功能 现状及难点 采用的Vue框架,前后端分离模式(vue工程仅作为客户端),用户通过域名访问的是客户端,但是微信授权中涉及签名和token校验依赖服务端 JS-SDK需要向服务端获取签名,且获取签名中需要的参数包括所在页面的url,但由于单页应用的路由特殊,其中涉及到IOS和android微信客户端浏览器内核的差异性导致的兼容问题 解决方案

随机推荐