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 相关文章推荐
jquery 弹出登录窗口实现代码
Dec 24 Javascript
jquery加载页面的方法(页面加载完成就执行)
Jun 21 Javascript
使用js在页面中绘制表格核心代码
Sep 16 Javascript
浅析js中的浮点型运算问题
Jan 06 Javascript
Javascript中的Array数组对象详谈
Mar 03 Javascript
javascript中的3种继承实现方法
Jan 27 Javascript
jQuery选择器之表单元素选择器详解
Sep 19 jQuery
简单理解Vue中的nextTick方法
Jan 30 Javascript
vuex直接赋值的三种方法总结
Sep 16 Javascript
详解vue-router的Import异步加载模块问题的解决方案
May 13 Javascript
jquery绑定事件 bind和on的用法与区别分析
May 22 jQuery
基于angular实现树形二级表格
Oct 16 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电台频率大全 - 24 贵州省
2020/03/11 无线电
SESSION信息保存在哪个文件目录下以及能够用来保存什么类型的数据
2012/06/17 PHP
php的zip解压缩类pclzip使用示例
2014/03/14 PHP
PHP处理会话函数大总结
2015/08/05 PHP
最新的10款jQuery内容滑块插件分享
2011/09/18 Javascript
Jquery多选框互相内容交换的实例代码
2013/07/04 Javascript
浅析jQuery1.8的几个小变化
2013/12/10 Javascript
js自定义鼠标右键的实现原理及源码
2014/06/23 Javascript
javascript通过获取html标签属性class实现多选项卡的方法
2015/07/27 Javascript
jquery实现的3D旋转木马特效代码分享
2015/08/25 Javascript
常见JS验证脚本汇总
2015/12/01 Javascript
JavaScript String 对象常用方法详解
2016/05/13 Javascript
基于JavaScript实现无限加载瀑布流
2017/07/21 Javascript
vue-router实现tab标签页(单页面)详解
2017/10/17 Javascript
微信小程序项目实践之验证码倒计时功能
2018/07/18 Javascript
详解小程序rich-text对富文本支持方案
2018/11/28 Javascript
Vue Router的手写实现方法实现
2020/03/02 Javascript
[08:42]DOTA2每周TOP10 精彩击杀集锦vol.2
2014/06/25 DOTA
python使用pil生成缩略图的方法
2015/03/26 Python
python装饰器-限制函数调用次数的方法(10s调用一次)
2018/04/21 Python
使用pandas将numpy中的数组数据保存到csv文件的方法
2018/06/14 Python
PyQt 实现使窗口中的元素跟随窗口大小的变化而变化
2019/06/18 Python
python中with用法讲解
2020/02/07 Python
用 Django 开发一个 Python Web API的方法步骤
2020/12/03 Python
HTML5对比HTML4的主要改变和改进总结
2016/05/27 HTML / CSS
使用canvas实现黑客帝国数字雨效果
2020/01/02 HTML / CSS
伦敦剧院及景点门票:Encore Tickets
2018/07/01 全球购物
美术专业个人自我评价
2014/01/18 职场文书
小学生学习雷锋倡议书
2014/05/15 职场文书
竞选宣传委员演讲稿
2014/05/24 职场文书
超搞笑婚前保证书
2015/05/08 职场文书
小学生教师节广播稿
2015/08/19 职场文书
如何使用PyCharm及常用配置详解
2021/06/03 Python
Golang解析JSON对象
2022/04/30 Golang
Python TypeError: ‘float‘ object is not subscriptable错误解决
2022/12/24 Python
关于MySQL中explain工具的使用
2023/05/08 MySQL