浅谈Vue响应式(数组变异方法)


Posted in Javascript onMay 07, 2018

前言

很多初使用Vue的同学会发现,在改变数组的值的时候,值确实是改变了,但是视图却无动于衷,果然是因为数组太高冷了吗?

查看官方文档才发现,不是女神太高冷,而是你没用对方法。

浅谈Vue响应式(数组变异方法)

浅谈Vue响应式(数组变异方法)

看来想让女神自己动,关键得用对方法。虽然在官方文档中已经给出了方法,但是在下实在好奇的紧,想要解锁更多姿势的话,那就必须先要深入女神的心,于是乎才有了去探索Vue响应式原理的想法。(如果你愿意一层一层地剥开我的心。你会发现,你会讶异…… 沉迷于鬼哭狼嚎 无法自拔QAQ)。

前排提示,Vue的响应式原理主要是使用了ES5的Object.defineProperty,毫不知情的同学可以查看相关资料。

为啥数组不响应?

仔细一想,Vue的响应是基于Object.definePropery的,这个方法主要是对对象属性的描述进行修改。数组其实也是对象,通过定义数组的属性应该也能产生响应的效果呀。先验证一下自己的想法,撸起袖子就开干。

const arr = [1,2,3];

let val = arr[0];

Object.defineProperty(arr,'0',{
  enumerable: true,
  configurable: true,
  get(){
    doSomething();
    return val;
  },
  set(a){
    val = a;
    doSomething();
  }
});

function doSomething() {

}

然后在控制台中分别输入arr、arr[0] = 2、arr,可以看到如下图的结果。

浅谈Vue响应式(数组变异方法)

咦,一切居然都如预想猜想的一样。

接下来,看到这段代码,有的同学可能会有所疑问,为啥在get()方法里不直接返回this[0]呢?而是要借助val来返回值呢?仔细一想,卧槽!!!差点特么的死循环了,你想呀,get()本身就是获取当前属性的值,在get()里调用this[0]不是等同于再次调用了get()方法吗? 好可怕好可怕,简直吓死劳资了。

虽然你想象中的女神可能会这种姿势,但是你眼前的这个女神确实不是这种姿势的,像我这种?潘渴粜员┞段抟傻娜嗽趺纯赡懿峦概?竦男乃迹课?裁床徽庋?煊κ?菽兀炕蛐硎且蛭??楹投韵蠡故怯兴?畋穑?ㄒ迨?榈氖粜钥赡芑岵??恍┞榉秤?ug。又或许是因为在交互的过程中可能会产生大量的数据,导致整体的性能下降。也有可能是作者权衡利弊之后用其他方法也可以达到数据响应的效果。反正我是猜不透啦。

为啥调用数组原生方法就可以响应了?

为什么使用了这些数组的方法就就能让数据响应了呢?先看看数组部分的源码吧。

简单的来讲,def的作用就是重新定义对象属性的value值。

//array.js
import { def } from '../util/index'

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
//arrayMethods是对数组的原型对象的拷贝,
//在之后会将该对象里的特定方法进行变异后替换正常的数组原型对象
/**
 * Intercept mutating methods and emit events
 */
[
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
]
.forEach(function (method) {
 // cache original method
 //将上面的方法保存到original中
 const original = arrayProto[method]
 def(arrayMethods, method, function mutator (...args) {
  const result = original.apply(this, args)
  const ob = this.__ob__
  let inserted
  switch (method) {
   case 'push':
   case 'unshift':
    inserted = args
    break
   case 'splice':
    inserted = args.slice(2)
    break
  }
  if (inserted) ob.observeArray(inserted)
  // notify change
  ob.dep.notify()
  return result
 })
})

贴出def部分的代码

/**
 * Define a property.
 */
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
 Object.defineProperty(obj, key, {
  value: val,
  enumerable: !!enumerable,
  writable: true,
  configurable: true
 })
}

array.js是对数组的一些方法进行变异,我们以push方法来举个例子。首先 就是要用original = arrayProto['push']来保存原生的push方法。

然后就是要定义变异的方法了,对于def函数,如果不深究的话,def(arrayMethods,method,function(){}),这个函数可以粗略的表示为arrayMethods[method] = function mutator(){};

假设在之后调用push方法,实际上调用的是mutator方法,在mutator方法中,第一件事就是调用保存了原生push方法的original,先求出实际的值。一堆文字看起来实在很抽象,那么写一段低配版的代码来表达源码的含义。

const push = Array.prototype.push;

Array.prototype.push = function mutator (...arg){
  const result = push.apply(this,arg);
  doSomething();
  return result
}

function doSomething(){
  console.log('do something');
}

const arr = [];
arr.push(1);
arr.push(2);
arr.push(3);

在控制台中查看结果为:。

浅谈Vue响应式(数组变异方法)

那么源码中的

const ob = this.__ob__
  let inserted
  switch (method) {
   case 'push':
   case 'unshift':
    inserted = args
    break
   case 'splice':
    inserted = args.slice(2)
    break
  }
  if (inserted) ob.observeArray(inserted)
  // notify change
  ob.dep.notify()

这段代码就是对应的doSomething()了

在该代码中,清清楚楚的写了2个单词的注释notify change,不认识这2个单词的同学就百度一下嘛,这里就由我代劳了,这俩单词的意思是发布改变!每次调用了该方法,都会求出值,然后做一些其他的事情,比如发布改变与观察新增的元素,响应的其他过程在本篇就不讨论了。

[
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
]

目前一共有这么些方法,只要用对方法就能改变女神的姿势哟!

小结

对于标题,我一改再改,一开始叫浅析Vue响应原理,但是后来一看 这个标题实在太大,那就从最简单的入手吧,先从数组入手,而且本篇也不会花费太多时间去阅读。如果本篇有什么地方写得有误,误导了他人,请一定指出,万分感激。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript 实用小技巧
Apr 07 Javascript
如何让div span等元素能响应键盘事件操作指南
Nov 13 Javascript
获取内联和链接中的样式(js代码)
Apr 11 Javascript
jquery 合并内容相同的单元格(示例代码)
Dec 13 Javascript
Node.js(安装,启动,测试)
Jun 09 Javascript
关于javascript中限定时间内防止按钮重复点击的思路详解
Aug 16 Javascript
如何用原生js写一个弹窗消息提醒插件
May 24 Javascript
JS操作json对象key、value的常用方法分析
Oct 29 Javascript
jQuery实现form表单基于ajax无刷新提交方法实例代码
Nov 04 jQuery
原生js实现碰撞检测
Mar 12 Javascript
vue + node如何通过一个Txt文件批量生成MP3并压缩成Zip
Jun 02 Javascript
浅析我对JS延迟异步脚本的思考
Oct 12 Javascript
在HTML文档中嵌入JavaScript的四种方法
May 07 #Javascript
详解JavaScript的BUG和错误
May 07 #Javascript
vue实现个人信息查看和密码修改功能
May 06 #Javascript
基于vue-element组件实现音乐播放器功能
May 06 #Javascript
VueJs组件之父子通讯的方式
May 06 #Javascript
vue自动化表单实例分析
May 06 #Javascript
node+koa2+mysql+bootstrap搭建一个前端论坛
May 06 #Javascript
You might like
php操作excel文件 基于phpexcel
2010/07/02 PHP
php创建桌面快捷方式实现方法
2015/12/31 PHP
PHP中加速、缓存扩展的区别和作用详解(eAccelerator、memcached、xcache、APC )
2016/07/09 PHP
windows 2008r2+php5.6.28环境搭建详细过程
2019/06/18 PHP
jQuery 入门级学习笔记及源码
2010/01/22 Javascript
让IE6支持min-width和max-width的方法
2010/06/25 Javascript
表单验证的完整应用案例探讨
2013/03/29 Javascript
JavaScript字符串对象toUpperCase方法入门实例(用于把字母转换为大写)
2014/10/17 Javascript
jQuery仿360导航页图标拖动排序效果代码分享
2015/08/24 Javascript
深入理解angularjs过滤器
2016/05/25 Javascript
浅谈bootstrap使用中的一些问题以及解决过程
2016/10/18 Javascript
JS中传递参数的几种不同方法比较
2017/01/20 Javascript
jquery编写日期选择器
2017/03/16 Javascript
如何通过非数字与字符的方式实现PHP WebShell详解
2017/07/02 Javascript
React Navigation 使用中遇到的问题小结
2018/05/08 Javascript
JS实现可针对算术表达式求值的计算器功能示例
2018/09/04 Javascript
详解Vue源码之数据的代理访问
2018/12/11 Javascript
layui 上传文件_批量导入数据UI的方法
2019/09/23 Javascript
python 数据加密代码
2008/12/24 Python
python实现socket客户端和服务端简单示例
2014/02/24 Python
Python回调函数用法实例详解
2015/07/02 Python
python创建临时文件夹的方法
2015/07/06 Python
python爬虫爬取淘宝商品信息
2018/02/23 Python
Python OpenCV实现视频分帧
2019/06/01 Python
Python Tkinter实例——模拟掷骰子
2020/10/24 Python
西班牙灯具网上商店:Lampara.es
2018/06/05 全球购物
几个数据库方面的面试题
2016/07/01 面试题
生物化工专业个人自荐信
2013/09/26 职场文书
教师自我评价范文
2013/12/16 职场文书
销售主管竞聘书
2014/03/31 职场文书
经销商年会策划方案
2014/05/29 职场文书
学校总务处领导班子民主生活会对照检查材料思想汇报
2014/09/27 职场文书
小学生安全保证书
2015/05/09 职场文书
从事会计工作年限证明
2015/06/23 职场文书
幼儿园元旦主持词
2015/07/06 职场文书
Netflix《海贼王》真人版剧集多张片场照曝光
2022/04/04 日漫