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 相关文章推荐
js AspxButton的客户端操作
Jun 26 Javascript
判断滚动条到底部的JS代码
Nov 04 Javascript
javascript版2048小游戏
Mar 18 Javascript
Bootstrap CSS组件之输入框组
Dec 17 Javascript
基于jQuery实现选项卡效果
Jan 04 Javascript
详解vue-resource promise兼容性问题
Jun 20 Javascript
JS库 Highlightjs 添加代码行号的实现代码
Sep 13 Javascript
Vue项目webpack打包部署到Tomcat刷新报404错误问题的解决方案
May 15 Javascript
vue底部加载更多的实例代码
Jun 29 Javascript
vue.js 图片上传并预览及图片更换功能的实现代码
Aug 27 Javascript
JS实现数组的增删改查操作示例
Aug 29 Javascript
js实现验证码干扰(动态)
Feb 23 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
Zend的AutoLoad机制介绍
2012/09/27 PHP
php中用加号与用array_merge合并数组的区别深入分析
2013/06/03 PHP
PHP中CheckBox多选框上传失败的代码写法
2017/02/13 PHP
function, new function, new Function之间的区别
2007/03/08 Javascript
检测是否已安装 .NET Framework 3.5的js脚本
2009/02/14 Javascript
jquery下操作HTML控件的实现代码
2010/01/12 Javascript
用js解决数字不能换行问题
2010/08/10 Javascript
浅析JQuery获取和设置Select选项的常用方法总结
2013/07/04 Javascript
ie下$.getJSON出现问题的解决方法
2014/02/12 Javascript
浅谈window.onbeforeunload() 事件调用ajax
2016/06/29 Javascript
轮播图组件js代码
2016/08/08 Javascript
JavaScript的继承实现小结
2017/05/07 Javascript
vue router路由嵌套不显示问题的解决方法
2017/06/17 Javascript
微信小程序MUI导航栏透明渐变功能示例(通过改变rgba的a值实现)
2019/01/24 Javascript
[01:11:02]Secret vs Newbee 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
Python 字符串与数字输出方法
2018/07/16 Python
pytorch中tensor的合并与截取方法
2018/07/26 Python
python 多线程串行和并行的实例
2019/02/22 Python
浅谈pyqt5在QMainWindow中布局的问题
2019/06/21 Python
详解Python二维数组与三维数组切片的方法
2019/07/18 Python
python自动结束mysql慢查询会话的实例代码
2019/10/27 Python
python如何发送带有附件、正文为HTML的邮件
2021/02/27 Python
Snapfish英国:在线照片打印和个性化照片礼品
2017/01/13 全球购物
医疗保健专业人士购物网站:Scrubs & Beyond
2017/02/08 全球购物
Vita Fede官网:在意大利手工制作,在纽约市设计
2019/10/25 全球购物
美国电子产品购物网站:BuyDig.com
2020/06/17 全球购物
劳动工资科岗位职责范本
2014/03/02 职场文书
年度考核自我鉴定
2014/03/19 职场文书
赔偿协议书范本
2014/04/15 职场文书
计划生育宣传标语
2014/06/21 职场文书
小学向国旗敬礼活动方案
2014/09/27 职场文书
公司员工辞职信范文
2015/05/12 职场文书
学校元旦晚会开场白
2015/05/29 职场文书
压缩Redis里的字符串大对象操作
2021/06/23 Redis
Oracle配置dblink访问PostgreSQL的操作方法
2022/03/21 PostgreSQL
讨论nginx location 顺序问题
2022/05/30 Servers