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


Posted in Javascript onOctober 12, 2016

前言

最近在尝试用Vue.js重构公司的现有业务代码,组件化的设计思路和MVVM的思想让我深深沉迷于其中。但是是踩到了不少坑,就比如这篇文章介绍的数组绑定后的更新检测。

相信大家都知道ObserverWatchervm 可谓 Vue 中比较重要的部分,检测数据变动后视图更新的重要环节。在 vue.js中$watch的用法示例 中,我们讨论了如何实现基本的 watch 。

接下来,我们来看看如何实现数组变动检测。

例子:

// 创建 vm
let vm = new Vue({
 data: {
 a: [{}, {}, {}]
 }
})

// 键路径
vm.$watch('a', function () {
 // 做点什么
})

思路

在 js 中, 很容易实现 hook, 比如:

// hook 一个 console。log
let _log = console.log
console.log = function (data) {
 // do someting
 _log.call(this, data)
}

我们只要实现自定义的函数,就能观测到数组变动。

Vue.js 中使用Object.create() 这个函数来实现继承, 从而实现自定义函数,以观测数组。

// 简单介绍
var a = new Object(); // 创建一个对象,没有父类

var b = Object.create(a.prototype); // b 继承了a的原型

继承

array.js定义如下:

// 获取原型
const arrayProto = Array.prototype
// 创建新原型对象
export const arrayMethods = Object.create(arrayProto)
// 给新原型实现这些函数
[
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
]
.forEach(function (method) {
// 获取新原型函数 (此时未实现 undefined)
 const original = arrayProto[method]
 // 给新原型添加函数实现
 Object.defineProperty(arrayMethods, method, {
 value: function mutator() {
  let i = arguments.length
  // 获取参数
  const args = new Array(i)
  while (i--) {
  args[i] = arguments[i]
  }
  // 实现函数
  const result = original.apply(this, args)
  // 获取观察者
  const ob = this.__ob__
  // 是否更改数组本身
  let inserted
  switch (method) {
  case 'push':
   inserted = args
   break
  case 'unshift':
   inserted = args
   break
  case 'splice':
   inserted = args.slice(2)
   break
  }
  // 观察新数组
  inserted && ob.observeArray(inserted)
  // 触发更新
  ob.dep.notify()
  return result
 },
 enumerable: true,
 writable: true,
 configurable: true
 })
})

ok, 我们定义完了 array.js, 并作为模块导出,修改 Observer 的实现:

export function Observer (value) {
 this.dep = new Dep()
 this.value = value

 // 如果是数组就更改其原型指向
 if (Array.isArray(value)) {
 value.__proto__ = arrayMethods
 this.observeArray(value)
 } else {
 this.walk(value)
 }
}

// 观测数据元素
Observer.prototype.observeArray = function (items) {
 for (let i = 0, l = items.length; i < l; i++) {
 observe(items[i])
 }
}

Observer 修改完毕后,我们再看看 Watcher , 只需要改动其 update 函数,

Watcher.prototype.update = function (dep) {
 console.log('2.update')
 const value = this.get()
 const oldValue = this.value
 this.value = value
 if (value !== this.value || value !== null) {
 this.cb.call(this.vm, value, oldValue)
 // 如果没有此函数, 会导致重复调用 $watch 回调函数。
 // 原因:数组变异过了,并对新数组也进行了观察,应该移除旧的观察者
 dep.subs.shift()
 }
}

结果:

const vm = new Vue({
 data: {
 b: [{a: 'a'}, {b: 'b'}]
 }
})

vm.$watch('b', (val) => {
 console.log('------我看到你们了-----')
})

vm.b.push({c: 'c'})
vm.b.pop({c: 'c'})
vm.b.push({c: 'c'})

// 结果:
// console.log('------我看到你们了-----')
// console.log('------我看到你们了-----')
// console.log('------我看到你们了-----')

总结

至此,我们已经实现对数组变动的检测。主要使用了Object.create()函数。希望这篇文章的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
Javascript常考语句107条收集
Mar 09 Javascript
JQUBAR1.1 jQuery 柱状图插件发布
Nov 28 Javascript
如何学习Javascript入门指导
Nov 01 Javascript
关于Javascript 对象(object)的prototype
May 09 Javascript
html文本框提示效果的示例代码
Jun 28 Javascript
jquery操作对象数组元素方法详解
Nov 26 Javascript
javascript实现3D变换的立体圆圈实例
Aug 06 Javascript
js操作数据库实现注册和登陆的简单实例
May 26 Javascript
JS 动态加载js文件和css文件 同步/异步的两种简单方式
Sep 23 Javascript
Node.js利用Net模块实现多人命令行聊天室的方法
Dec 23 Javascript
vue 中自定义指令改变data中的值
Jun 02 Javascript
JS处理数据四舍五入(tofixed与round的区别详解)
Oct 26 Javascript
有关文件上传 非ajax提交 得到后台数据问题
Oct 12 #Javascript
jquery动态创建div与input的实例代码
Oct 12 #Javascript
javascript加载xml 并解析各节点的值(实现方法)
Oct 12 #Javascript
jquery动态添加文本并获取值的方法
Oct 12 #Javascript
jquery 动态增加,减少input表单的简单方法(必看)
Oct 12 #Javascript
微信小程序 生命周期详解
Oct 12 #Javascript
让html元素随浏览器的大小自适应垂直居中的实现方法
Oct 12 #Javascript
You might like
PHP中使用FFMPEG获取视频缩略图和视频总时长实例
2014/05/04 PHP
PHP的fsockopen、pfsockopen函数被主机商禁用的解决办法
2014/07/08 PHP
对比PHP对MySQL的缓冲查询和无缓冲查询
2016/07/01 PHP
php判断str字符串是否是xml格式数据的方法示例
2017/07/26 PHP
PHPExcel 修改已存在Excel的方法
2018/05/03 PHP
php实现等比例压缩图片
2018/07/26 PHP
tp5(thinkPHP5框架)时间查询操作实例分析
2019/05/29 PHP
javascript 多种搜索引擎集成的页面实现代码
2010/01/02 Javascript
关于IE浏览器以及Firefox下的javascript冒泡事件的响应层级
2010/10/14 Javascript
jQuery实现购物车计算价格功能的方法
2015/03/25 Javascript
jQuery中prepend()方法使用详解
2015/08/11 Javascript
JavaScript学习笔记之DOM基础 2.4
2015/08/14 Javascript
jQuery中ajax的load()与post()方法实例详解
2016/01/05 Javascript
JS组件Bootstrap Table布局详解
2016/05/27 Javascript
深入理解JS正则表达式---分组
2016/07/18 Javascript
vue制作加载更多功能的正确打开方式
2016/10/12 Javascript
angularjs实现多张图片上传并预览功能
2017/02/24 Javascript
微信小程序日历组件calendar详解及实例
2017/06/08 Javascript
详解webpack2+React 实例demo
2017/09/11 Javascript
vue Element-ui input 远程搜索与修改建议显示模版的示例代码
2017/10/19 Javascript
vue 界面刷新数据被清除 localStorage的使用详解
2018/09/16 Javascript
手把手教你写一个微信小程序(推荐)
2018/10/17 Javascript
vue项目打包上传github并制作预览链接(pages)
2019/04/19 Javascript
js实现简单的贪吃蛇游戏
2020/04/23 Javascript
[01:07:13]TNC vs Pain 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
读写json中文ASCII乱码问题的解决方法
2016/11/05 Python
Python中Django发送带图片和附件的邮件
2017/03/31 Python
解决Matplotlib图表不能在Pycharm中显示的问题
2018/05/24 Python
Python3中详解fabfile的编写
2018/06/24 Python
python实现linux下抓包并存库功能
2018/07/18 Python
对Python3.x版本print函数左右对齐详解
2018/12/22 Python
goodhealth官方海外旗舰店:新西兰国民营养师
2017/12/15 全球购物
What's the difference between deep copy and shallow copy? (深拷贝与浅拷贝有什么区别)
2015/11/10 面试题
企业节能减排实施方案
2014/03/19 职场文书
《称象》教学反思
2014/04/25 职场文书
2014年医院后勤工作总结
2014/12/06 职场文书