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图片平滑连续滚动插件
Apr 27 Javascript
基于javascript实现泡泡大冒险网页版小游戏
Mar 23 Javascript
JSON与String互转的实现方法(Javascript)
Sep 27 Javascript
js拖拽功能实现代码解析
Nov 28 Javascript
遍历json获得数据的几种方法小结
Jan 21 Javascript
使用jQuery的load方法设计动态加载及解决被加载页面js失效问题
Mar 01 Javascript
vue.js路由跳转详解
Aug 28 Javascript
js原生日历的实例(推荐)
Oct 31 Javascript
JS中this的指向以及call、apply的作用
May 06 Javascript
原生JS+HTML5实现的可调节写字板功能示例
Aug 30 Javascript
es6中使用map简化复杂条件判断操作实例详解
Feb 19 Javascript
Vue-cli4 配置 element-ui 按需引入操作
Sep 11 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提取数据库内容中的图片地址并循环输出
2010/03/21 PHP
PHP缓存机制Output Control详解
2014/07/14 PHP
50个优秀经典PHP算法大集合 附源码
2020/08/26 PHP
js下弹出窗口的变通
2007/04/18 Javascript
基于JQuery实现异步刷新的代码(转载)
2011/03/29 Javascript
通过jquery的$.getJSON做一个跨域ajax请求试验
2011/05/03 Javascript
DOM和XMLHttpRequest对象的属性和方法整理
2012/01/04 Javascript
window.onerror()的用法与实例分析
2016/01/27 Javascript
Node.js中JavaScript操作MySQL的常用方法整理
2016/03/01 Javascript
JavaScript设计模式之单体模式全面解析
2016/09/09 Javascript
Vue.js添加组件操作示例
2018/06/13 Javascript
Angular8基础应用之表单及其验证
2019/08/11 Javascript
Vue为什么要谨慎使用$attrs与$listeners
2020/08/27 Javascript
Python通过解析网页实现看报程序的方法
2014/08/04 Python
Python实现对字符串的加密解密方法示例
2017/04/29 Python
Python网络编程之TCP与UDP协议套接字用法示例
2018/02/02 Python
python基础教程项目四之新闻聚合
2018/04/02 Python
Python实现确认字符串是否包含指定字符串的实例
2018/05/02 Python
Python高斯消除矩阵
2019/01/02 Python
python 计算数据偏差和峰度的方法
2019/06/29 Python
使用python制作游戏下载进度条的代码(程序说明见注释)
2019/10/24 Python
python科学计算之scipy——optimize用法
2019/11/25 Python
浅谈tensorflow中张量的提取值和赋值
2020/01/19 Python
在django中使用apscheduler 执行计划任务的实现方法
2020/02/11 Python
python爬取”顶点小说网“《纯阳剑尊》的示例代码
2020/10/16 Python
浅析几个CSS3常用功能的写法
2014/06/05 HTML / CSS
css3实现平移效果(transfrom:translate)的示例
2020/11/13 HTML / CSS
用HTML5制作视频拼图的教程
2015/05/13 HTML / CSS
银行授权委托书样本
2014/10/13 职场文书
党的群众路线教育实践活动制度建设计划方案
2014/10/31 职场文书
珍爱生命主题班会
2015/08/13 职场文书
《赵州桥》教学反思
2016/02/17 职场文书
幽默口才训练经典句子(48句)
2019/08/19 职场文书
CSS中妙用 drop-shadow 实现线条光影效果
2021/11/11 HTML / CSS
继承Win10缺点!教你关闭Win11烦人的网络搜索
2021/11/23 数码科技
mysql sock 文件解析及作用讲解
2022/07/15 MySQL