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 工具库 Cloudgamer JavaScript Library v0.1 发布
Oct 29 Javascript
jquery 淡入淡出效果的简单实现
Feb 07 Javascript
javascript和jQuery中的AJAX技术详解【包含AJAX各种跨域技术】
Dec 15 Javascript
基于JavaScript实现五子棋游戏
Aug 26 Javascript
微信小程序中吸底按钮适配iPhone X方案
Nov 29 Javascript
angularJs利用$scope处理升降序的方法
Oct 08 Javascript
小程序转发探索示例
Feb 19 Javascript
微信小程序实现的一键复制功能示例
Apr 24 Javascript
微信小程序自定义可滑动顶部TabBar选项卡实现页面切换功能示例
May 14 Javascript
Node.js 实现简单的无侵入式缓存框架的方法
Jul 21 Javascript
详解js中的几种常用设计模式
Jul 16 Javascript
原生js实现俄罗斯方块
Oct 20 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获取用户IPv4或IPv6地址的代码
2012/11/15 PHP
thinkphp中连接oracle时封装方法无法用的解决办法
2013/06/17 PHP
WordPress中获取所使用的模板的页面ID的简单方法
2015/12/31 PHP
PHP封装的数据库保存session功能类
2016/07/11 PHP
PHP中call_user_func_array回调函数的用法示例
2016/11/26 PHP
PHP+mysql实现的三级联动菜单功能示例
2019/02/15 PHP
浅谈PHP各环境下的伪静态配置
2019/03/13 PHP
学习ExtJS table布局
2009/10/08 Javascript
jqGrid随窗口大小变化自适应大小的示例代码
2013/12/28 Javascript
jQuery之DOM对象和jQuery对象的转换与区别分析
2015/01/08 Javascript
JavaScript中的值是按值传递还是按引用传递问题探讨
2015/01/30 Javascript
简单了解JavaScript操作XPath的一些基本方法
2016/06/03 Javascript
AngularJs 禁止模板缓存的方法
2017/11/28 Javascript
详解如何构建Promise队列实现异步函数顺序执行
2018/10/23 Javascript
小程序云开发初探(小结)
2018/10/24 Javascript
ES6 Promise对象的应用实例分析
2019/06/27 Javascript
JavaScript浅层克隆与深度克隆示例详解
2020/09/01 Javascript
openlayers实现地图测距测面
2020/09/25 Javascript
Python中Django框架下的staticfiles使用简介
2015/05/30 Python
Django框架中render_to_response()函数的使用方法
2015/07/16 Python
Python中使用haystack实现django全文检索搜索引擎功能
2017/08/26 Python
Python常见字典内建函数用法示例
2018/05/14 Python
pytorch使用指定GPU训练的实例
2019/08/19 Python
python检测服务器端口代码实例
2019/08/31 Python
对python中return与yield的区别详解
2020/03/12 Python
python 对xml解析的示例
2021/02/27 Python
移动端Html5中百度地图的点击事件
2019/01/31 HTML / CSS
共产党员承诺书
2014/03/25 职场文书
电钳工人个人求职信
2014/05/10 职场文书
2014年数学教研组工作总结
2014/12/06 职场文书
美术教师个人工作总结
2015/02/06 职场文书
观后感的写法
2015/06/19 职场文书
2016高校自主招生自荐信范文
2016/01/28 职场文书
win10+anaconda安装yolov5的方法及问题解决方案
2021/04/29 Python
MySQL时间盲注的五种延时方法实现
2021/05/18 MySQL
js判断两个数组相等的5种方法
2022/05/06 Javascript