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 相关文章推荐
解放web程序员的输入验证
Oct 06 Javascript
JavaScript的事件绑定(方便不支持js的时候)
Oct 01 Javascript
js实现用户注册协议倒计时的方法
Jan 21 Javascript
js实现文本框只允许输入数字并限制数字大小的方法
Aug 19 Javascript
基于jQuery实现仿搜狐辩论投票动画代码(附源码下载)
Feb 18 Javascript
jQuery插件ajaxfileupload.js实现上传文件
Oct 23 Javascript
JS使用cookie设置样式的方法
Jun 30 Javascript
使用JS中的Replace()方法遇到的问题小结
Oct 20 Javascript
JavaScript实现为事件句柄绑定监听函数的方法分析
Nov 14 Javascript
通过vue提供的keep-alive减少对服务器的请求次数
Apr 01 Javascript
React精髓!一篇全概括小结(急速)
May 23 Javascript
js实现点击选项置顶动画效果
Aug 25 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
全国FM电台频率大全 - 25 云南省
2020/03/11 无线电
PHP最常用的ini函数分析 针对PHP.ini配置文件
2010/04/22 PHP
preg_match_all使用心得分享
2014/01/31 PHP
解决php表单重复提交实现方法
2015/09/29 PHP
php处理抢购类功能的高并发请求
2018/02/08 PHP
php和nginx交互实例讲解
2019/09/24 PHP
js loading加载效果实现代码
2009/11/24 Javascript
DIV菜单层实现代码
2010/11/19 Javascript
动态创建script在IE中缓存js文件时导致编码的解决方法
2014/05/04 Javascript
详解JavaScript逻辑And运算符
2015/12/04 Javascript
仿百度换肤功能的简单实例代码
2016/07/11 Javascript
真正好用的js验证上传文件大小的简单方法
2016/10/27 Javascript
jQuery实现的简单排序功能示例【冒泡排序】
2017/01/13 Javascript
封装运动框架实战左右与上下滑动的焦点轮播图(实例)
2017/10/17 Javascript
Puppeteer环境搭建的详细步骤
2018/09/21 Javascript
微信端调取相册和摄像头功能,实现图片上传,并上传到服务器
2019/05/16 Javascript
浅谈Express.js解析Post数据类型的正确姿势
2019/05/30 Javascript
python三元运算符实现方法
2013/12/17 Python
Python多继承原理与用法示例
2018/08/23 Python
Python os.rename() 重命名目录和文件的示例
2018/10/25 Python
django的ORM模型的实现原理
2019/03/04 Python
python pygame实现方向键控制小球
2019/05/17 Python
pycharm配置当鼠标悬停时快速提示方法参数
2019/07/31 Python
python递归函数求n的阶乘,优缺点及递归次数设置方式
2020/04/02 Python
python实现手势识别的示例(入门)
2020/04/15 Python
详解Pymongo常用查询方法总结
2021/01/29 Python
关于解决iframe标签嵌套问题的解决方法
2020/03/04 HTML / CSS
Shopee菲律宾:在线购买和出售
2019/11/25 全球购物
阿里巴巴英国:Alibaba英国
2019/12/11 全球购物
Footshop法国:购买运动鞋
2020/01/19 全球购物
屈臣氏越南官网:Watsons越南
2021/01/14 全球购物
Java里面StringBuilder和StringBuffer有什么区别
2016/06/06 面试题
党员领导干部廉洁从政承诺书
2014/03/27 职场文书
redis 存储对象的方法对比分析
2021/08/02 Redis
苹果可能正在打击不进行更新的 App
2022/04/24 数码科技
windows server 2016 域环境搭建的方法步骤(图文)
2022/06/25 Servers