浅谈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 相关文章推荐
js插件方式打开pdf文件(浏览器pdf插件分享)
Dec 20 Javascript
JS二维数组的定义说明
Mar 03 Javascript
JQuery DIV 动态隐藏和显示的方法
Jun 23 Javascript
jquery.tableSort.js表格排序插件使用方法详解
Aug 12 Javascript
Vue2.x中的父组件传递数据至子组件的方法
May 01 Javascript
PHP7新特性简述
Jun 11 Javascript
bootstrap选项卡扩展功能详解
Jun 14 Javascript
Vue利用路由钩子token过期后跳转到登录页的实例
Oct 26 Javascript
JavaScript定义及输出螺旋矩阵的方法详解
Dec 01 Javascript
开源一个微信小程序仪表盘组件过程解析
Jul 30 Javascript
vue css 引入asstes中的图片无法显示的四种解决方法
Mar 16 Javascript
Vue前端判断数据对象是否为空的实例
Sep 02 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+Mysql+jQuery实现动态展示信息
2011/10/08 PHP
解析PHP获取当前网址及域名的实现代码
2013/06/23 PHP
PHP判断文章里是否有图片的简单方法
2014/07/26 PHP
PHP数组操作简单案例分析
2016/10/15 PHP
thinkPHP5.0框架API优化后的友好性分析
2017/03/17 PHP
php的扩展写法总结
2019/05/14 PHP
Jquery对select的增、删、改、查操作
2015/02/06 Javascript
js动态获取子复选项并设计全选及提交的实现方法
2016/06/24 Javascript
AngularJS入门教程之Scope(作用域)
2016/07/27 Javascript
js 能实现监听F5页面刷新子iframe 而父页面不刷新的方法
2016/11/09 Javascript
JavaScript实现汉字转换为拼音的库文件示例
2016/12/22 Javascript
Django1.7+JQuery+Ajax验证用户注册集成小例子
2017/04/08 jQuery
vue + element-ui实现简洁的导入导出功能
2017/12/22 Javascript
微信小程序性能优化之checkSession的使用
2019/03/06 Javascript
JS div匀速移动动画与变速移动动画代码实例
2019/03/26 Javascript
JS前端知识点 运算符优先级,URL编码与解码,String,Math,arguments操作整理总结
2019/06/27 Javascript
vue-cli history模式实现tomcat部署报404的解决方式
2019/09/06 Javascript
JS实现排行榜文字向上滚动轮播效果
2019/11/26 Javascript
JavaScript setInterval()与setTimeout()计时器
2019/12/27 Javascript
[58:21]DOTA2亚洲邀请赛 4.3 突围赛 Liquid vs VGJ.T 第二场
2018/04/04 DOTA
python方向键控制上下左右代码
2018/01/20 Python
python 对任意数据和曲线进行拟合并求出函数表达式的三种解决方案
2020/02/18 Python
Pytorch 高效使用GPU的操作
2020/06/27 Python
欧洲最大的美妆零售网站:Feelunique
2017/01/14 全球购物
德国圣伯纳德草药屋:Kräuterhaus Sanct Bernhard(有中文站)
2018/08/05 全球购物
迪士尼法国在线商店:shopDisney FR
2020/12/03 全球购物
土木工程毕业生自荐信
2013/09/21 职场文书
大学生社会实践方案
2014/05/11 职场文书
环保建议书500字
2014/05/14 职场文书
中职三好学生事迹材料
2014/08/24 职场文书
2014小学二年级班主任工作总结
2014/12/05 职场文书
保研专家推荐信范文
2015/03/25 职场文书
python读取pdf格式文档的实现代码
2021/04/01 Python
MySQL中varchar和char类型的区别
2021/11/17 MySQL
SQL Server实现分页方法介绍
2022/03/16 SQL Server
mysql 8.0.27 绿色解压版安装教程及配置方法
2022/04/20 MySQL