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


Posted in Javascript onApril 18, 2019

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的数据劫持以及操作数组的坑详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
RGB颜色值转HTML十六进制(HEX)代码的JS函数
Apr 25 Javascript
javascript 利用Image对象实现的埋点(某处的点击数)统计
Dec 28 Javascript
javascript通过navigator.userAgent识别各种浏览器
Oct 25 Javascript
javascript实现删除前弹出确认框
Jun 04 Javascript
基于jQuery实现仿百度首页换肤背景图片切换代码
Aug 25 Javascript
jQuery移动web开发中的页面初始化与加载事件
Dec 03 Javascript
JS中的eval 为什么加括号
Apr 13 Javascript
jQuery 检查某个元素在页面上是否存在实例代码
Oct 27 Javascript
Angular2.js实现表单验证详解
Jun 23 Javascript
vue+iview写个弹框的示例代码
Dec 05 Javascript
vue draggable resizable gorkys与v-chart使用与总结
Sep 05 Javascript
Vue基础配置讲解
Nov 29 Javascript
微信小程序 setData 对 data数据影响问题
Apr 18 #Javascript
详解JavaScript中关于this指向的4种情况
Apr 18 #Javascript
vue.js高德地图实现热点图代码实例
Apr 18 #Javascript
基于vue实现滚动条滚动到指定位置对应位置数字进行tween特效
Apr 18 #Javascript
Vue的H5页面唤起支付宝支付功能
Apr 18 #Javascript
详解Vue中的scoped及穿透方法
Apr 18 #Javascript
Node.js对MongoDB进行增删改查操作的实例代码
Apr 18 #Javascript
You might like
PHP实现一维数组转二维数组的方法
2015/02/25 PHP
Yii实现显示静态页的方法
2016/04/25 PHP
PHP将字符串首字母大小写转换的实例
2017/01/21 PHP
PHP 断点续传实例详解
2017/11/11 PHP
thinkphp5.1框架实现格式化mysql时间戳为日期的方式小结
2019/10/10 PHP
贴一个在Mozilla中常用的Javascript代码
2007/01/09 Javascript
简单实用的js调试logger组件实现代码
2010/11/20 Javascript
javascript实现数字验证码的简单实例
2014/02/10 Javascript
IE中图片的onload事件无效问题和解决方法
2014/06/06 Javascript
JSP中使用JavaScript动态插入删除输入框实现代码
2014/06/13 Javascript
node.js开发中使用Node Supervisor实现监测文件修改并自动重启应用
2014/11/04 Javascript
jQuery实现tab选项卡效果的方法
2015/07/08 Javascript
Javascript仿新浪游戏频道鼠标悬停显示子菜单效果
2015/08/21 Javascript
原生JavaScript实现Ajax的方法
2016/04/07 Javascript
Bootstrap企业网站实战项目4
2016/10/14 Javascript
使用JS批量选中功能实现更改数据库中的status状态值(批量展示)
2016/11/22 Javascript
利用select实现年月日三级联动的日期选择效果【推荐】
2016/12/13 Javascript
vue使用axios跨域请求数据问题详解
2017/10/18 Javascript
如何自动化部署项目?折腾服务器之旅~
2019/04/16 Javascript
[03:17]2016完美“圣”典风云人物:冷冷专访
2016/12/08 DOTA
Python将多个excel表格合并为一个表格
2021/02/22 Python
对pycharm代码整体左移和右移缩进快捷键的介绍
2018/07/16 Python
Django中使用Celery的教程详解
2018/08/24 Python
Python+Selenium随机生成手机验证码并检查页面上是否弹出重复手机号码提示框
2020/09/21 Python
全球领先的中国制造商品在线批发平台:DHgate
2020/01/28 全球购物
全球最大运动品牌的男装、女装和童装官方库存商:A&A Sports
2021/01/17 全球购物
Linux管理员面试题 Linux admin interview questions
2014/11/01 面试题
解释i节点在文件系统中的作用
2013/11/26 面试题
心理健康教育制度
2014/01/27 职场文书
亲子拓展活动方案
2014/02/20 职场文书
社区精神文明建设汇报材料
2014/08/17 职场文书
战略性融资合作协议书范本
2014/10/17 职场文书
钢琴师观后感
2015/06/12 职场文书
vue实现无缝轮播效果(跑马灯)
2021/05/14 Vue.js
Java中CyclicBarrier和CountDownLatch的用法与区别
2021/08/23 Java/Android
如何解决php-fpm启动不了问题
2021/11/17 PHP