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 验证浏览器是否支持javascript的方法小结
May 17 Javascript
jquery 学习笔记 传智博客佟老师附详细注释
Sep 12 Javascript
文本框根据输入内容自适应高度的代码
Oct 24 Javascript
图片在浏览器中底部对齐 解决方法之一
Nov 30 Javascript
给Flash加一个超链接(推荐使用透明层)兼容主流浏览器
Jun 09 Javascript
jQuery中parents()和parent()的区别分析
Oct 28 Javascript
深入理解JavaScript中Ajax
Aug 02 Javascript
微信小程序 加载 app-service.js 错误解决方法
Oct 12 Javascript
用最少的JS代码写出贪吃蛇游戏
Jan 12 Javascript
javascript匿名函数中的'return function()'作用
Oct 15 Javascript
Javascript通过控制类名更改样式
May 24 Javascript
js中位数不足自动补位扩展padLeft、padRight实现代码
Apr 06 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
使用TinyButStrong模板引擎来做WEB开发
2007/03/16 PHP
PHP使用CURL获取302跳转后的地址实例
2014/05/04 PHP
使用phpstorm和xdebug实现远程调试的方法
2015/12/29 PHP
jQuery对象和DOM对象相互转化
2009/04/24 Javascript
jquery增加和删除元素的方法
2015/01/14 Javascript
谈一谈js中的执行环境及作用域
2016/03/30 Javascript
深入学习jQuery中的data()
2016/12/22 Javascript
微信小程序教程系列之视图层的条件渲染(10)
2017/04/19 Javascript
360doc网站不登录就无法复制内容的解决方法
2018/01/27 Javascript
vue中设置height:100%无效的问题及解决方法
2018/07/27 Javascript
原生JS封装_new函数实现new关键字的功能
2018/08/12 Javascript
微信小程序websocket实现即时聊天功能
2019/05/21 Javascript
JavaScript模块管理的简单实现方式详解
2019/06/15 Javascript
JavaScript经典案例之简易计算器
2020/08/24 Javascript
在vue中使用jsonp进行跨域请求接口操作
2020/10/29 Javascript
[52:08]DOTA2上海特级锦标赛主赛事日 - 3 败者组第三轮#2Fnatic VS OG第一局
2016/03/05 DOTA
python网络爬虫采集联想词示例
2014/02/11 Python
Python中使用HTMLParser解析html实例
2015/02/08 Python
Python实现基于权重的随机数2种方法
2015/04/28 Python
Python下rrdtool模块的基本使用方法
2015/11/13 Python
深入理解Python 关于supper 的 用法和原理
2018/02/28 Python
Python Series从0开始索引的方法
2018/11/06 Python
详解python中init方法和随机数方法
2019/03/13 Python
python实现点击按钮修改数据的方法
2019/07/17 Python
python scipy卷积运算的实现方法
2019/09/16 Python
AUC计算方法与Python实现代码
2020/02/28 Python
一款利用html5和css3实现的3D滚动特效的教程
2015/01/04 HTML / CSS
英国网上超市:Ocado
2020/03/05 全球购物
运动会通讯稿100字
2014/01/31 职场文书
DIY手工制作经营店创业计划书
2014/02/01 职场文书
2014年五四青年节演讲比赛方案
2014/04/22 职场文书
自主招生学校推荐信范文
2015/03/26 职场文书
2015年“我们的节日·重阳节”活动总结
2015/07/29 职场文书
JUnit5常用注解的使用
2021/07/02 Java/Android
《雀魂PONG☆》4月1日播出 PV角色设定情报
2022/03/20 日漫
Mysql调整优化之四种分区方式以及组合分区
2022/04/13 MySQL