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学习笔记之jQuery选择器的使用
Dec 22 Javascript
js代码实现的加入收藏效果并兼容主流浏览器
Jun 23 Javascript
JavaScript使用指针操作实现约瑟夫问题实例
Apr 07 Javascript
jquery选择器简述
Aug 31 Javascript
JS实现超精简响应鼠标显示二级菜单代码
Sep 12 Javascript
jQuery Timelinr实现垂直水平时间轴插件(附源码下载)
Feb 16 Javascript
AngularJS基础 ng-repeat 指令简单示例
Aug 03 Javascript
最新Javascript程序员面试试题和解题方法
Nov 23 Javascript
React key值的作用和使用详解
Aug 23 Javascript
JavaScript中filter的用法实例分析
Feb 27 Javascript
浅析vue cli3 封装Svgicon组件正确姿势(推荐)
Apr 27 Javascript
Vue中使用Echarts仪表盘展示实时数据的实现
Nov 01 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
ip签名探针
2006/10/09 PHP
有关PHP中MVC的开发经验分享
2012/05/17 PHP
深入php list()函数的详解
2013/06/05 PHP
jquery遍历筛选数组的几种方法和遍历解析json对象
2013/12/13 Javascript
document.addEventListener使用介绍
2014/03/07 Javascript
javascript检查浏览器是否支持flash的实现代码
2014/08/14 Javascript
Javascript冒泡排序算法详解
2014/12/03 Javascript
深入理解JS实现快速排序和去重
2016/10/17 Javascript
深入了解JavaScript的逻辑运算符(与、或)
2016/12/20 Javascript
JavaScript利用Date实现简单的倒计时实例
2017/01/12 Javascript
Nodejs实现多房间简易聊天室功能
2017/06/20 NodeJs
vue实现文章内容过长点击阅读全文功能的实例
2017/12/28 Javascript
在vue中v-bind使用三目运算符绑定class的实例
2018/09/29 Javascript
解决Angularjs异步操作后台请求用$q.all排列先后顺序问题
2019/11/29 Javascript
JavaScript实现无限轮播效果
2020/11/19 Javascript
[47:39]2018DOTA2亚洲邀请赛 3.31 小组赛 A组 LGD vs OPTIC
2018/03/31 DOTA
python调用shell的方法
2013/11/20 Python
Python中用startswith()函数判断字符串开头的教程
2015/04/07 Python
在Python的Django框架中包装视图函数
2015/07/20 Python
详解MySQL数据类型int(M)中M的含义
2016/11/20 Python
Django开发中的日志输出的方法
2018/07/02 Python
python 扩展print打印文件路径和当前时间信息的实例代码
2019/10/11 Python
使用python写一个自动浏览文章的脚本实例
2019/12/05 Python
python 实现PIL模块在图片画线写字
2020/05/16 Python
Python selenium爬取微博数据代码实例
2020/05/22 Python
基于Python实现粒子滤波效果
2020/12/01 Python
Kipling意大利官网:世界著名的时尚休闲包袋品牌
2019/06/05 全球购物
大学考试作弊检讨书
2014/01/30 职场文书
安全教育感言
2014/03/04 职场文书
购房意向书范本
2014/04/01 职场文书
2014银行领导班子四风对照检查材料思想汇报
2014/09/25 职场文书
2014年社区民政工作总结
2014/12/02 职场文书
2015质检员个人年终工作总结
2015/10/23 职场文书
《好妈妈胜过好老师》:每个孩子的优秀都是有源头的
2020/01/03 职场文书
SpringBoot中HttpSessionListener的简单使用方式
2022/03/17 Java/Android
InterProcessMutex实现zookeeper分布式锁原理
2022/03/21 Java/Android