浅谈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 中的内存泄露模式
Aug 13 Javascript
GreyBox技术总结(转)
Nov 23 Javascript
E3 tree 1.6在Firefox下显示问题的修复方法
Jan 30 Javascript
js中array的sort()方法使用介绍
Feb 20 Javascript
理解js回收机制通俗易懂版
Feb 29 Javascript
Node.js中npm常用命令大全
Jun 09 Javascript
jQuery居中元素scrollleft计算方法示例
Jan 16 Javascript
JavaScript中的普通函数和箭头函数的区别和用法详解
Mar 21 Javascript
Mongoose实现虚拟字段查询的方法详解
Aug 15 Javascript
javaScript canvas实现(画笔大小 颜色 橡皮的实例)
Nov 28 Javascript
npm qs模块使用详解
Feb 07 Javascript
用几道面试题来看JavaScript执行机制
Apr 30 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
海贼王动画变成“真人”后,凯多神还原,雷利太帅了!
2020/04/09 日漫
php中文字母数字验证码实现代码
2008/04/25 PHP
smarty模板引擎之分配数据类型
2015/03/30 PHP
php实现数组按指定KEY排序的方法
2015/03/30 PHP
window.location.href = window.location.href 跳转无反应 a超链接onclick事件写法
2013/08/21 Javascript
JS实现字体选色板实例代码
2013/11/20 Javascript
使用JavaScript进行进制转换将字符串转换为十进制
2014/09/21 Javascript
javascript 回调函数详解
2014/11/11 Javascript
Backbone.js中的集合详解
2015/01/14 Javascript
js实现顶部可折叠的菜单工具栏效果实例
2015/05/09 Javascript
Bootstrap每天必学之缩略图与警示窗
2015/11/29 Javascript
Python使用文件锁实现进程间同步功能【基于fcntl模块】
2017/10/16 Python
Windows 7下Python Web环境搭建图文教程
2018/03/20 Python
python logging重复记录日志问题的解决方法
2018/07/12 Python
pandas pivot_table() 按日期分多列数据的方法
2018/11/16 Python
django小技巧之html模板中调用对象属性或对象的方法
2018/11/30 Python
简单瞅瞅Python vars()内置函数的实现
2019/09/27 Python
Python socket模块ftp传输文件过程解析
2019/11/05 Python
python实现PolynomialFeatures多项式的方法
2021/01/06 Python
Python 爬取淘宝商品信息栏目的实现
2021/02/06 Python
英国家喻户晓的高街品牌:River Island
2017/11/28 全球购物
美国最大的无人机经销商:DroneNerds
2018/03/20 全球购物
Chantelle仙黛尔内衣美国官网:法国第一品牌内衣
2018/07/26 全球购物
SQL中where和having的区别
2012/06/17 面试题
食品销售计划书
2014/04/26 职场文书
计算机应用专业毕业生求职信
2014/06/03 职场文书
上海世博会口号
2014/06/19 职场文书
个人主要事迹材料
2014/08/26 职场文书
神农溪导游词
2015/02/11 职场文书
行政经理岗位职责
2015/04/15 职场文书
赞助商致辞
2015/07/30 职场文书
2016年情人节问候语
2015/11/11 职场文书
2016高中社会实践心得体会范文
2016/01/14 职场文书
历史名人教你十五个读书方法,赶快Get起来!
2019/07/18 职场文书
python控制台打印log输出重复的解决方法
2021/05/14 Python
海康机器人重磅发布全新算法开发平台VM4.2
2022/04/21 数码科技