Vue.js深度解析:底层原理篇(一)

发表时间: 2018-11-30 21:25

学过Angularjs的人应该知道,Angularjs 是一个MVVM前端框架,其中最亮眼的因该是他的双向数据绑定了,所谓双向数据绑定(Two-way data binding)就是页面元素变化会触发 View-model 中对应数据改变, 反过来 View-model 中数据变化也会引发所绑定的 UI 元素数据更新。操作数据就等同于操作 UI。

在实际操作中,从UI到View-Model的数据改变应该很容易实现,直接给UI元素帮顶例如 input ,change 等事件就可以实现,但是 从 View-Model 到UI的变化却不是那么容易了。

了解决这个问题,Angularjs 在它的设计中引入了脏数据检查机制 。其大致流程如下

  1. 为页面上每个需要进行双向绑定的数据建立一个watcher,这些watcher位于其对应的scope上
  2. watcher上增加一个属性 watch.last 用于保存属性最近的值
  3. 当用户触发UI操作,或者进行XHR请求后,angular会进行脏检查循环,即$digest()循环,

该循环会递归检查scope下所有被双向绑定的表达式,比较他们最新的值和上一次的值(watch.last),如果发生了变化,则出发相应更新对应UI的操作。

上述这种脏检查机制在当watcher只有几十或者几百的情况下,对性能可能影响不大,但是当watcher上千的时候,这种机制带来的性能弊端就很明显了。

在这之后,新生的MVVM框架 vue.js和avalon.js等摒弃了这种脏检查机制,巧妙地利用了

Object.defineProperty 这一方法解决了这个问题!

所以,如果你不了解这个方法,千万不要说自己精通 vue.js , 因为这个方法是vue.js底层实现数据双向绑定最根本的方法,那么,现在就带大家一起来了解一下这个方法吧:

平常我们声明一个对象属性的时候,更多的用到以下两种形式:

  1. 对象字面量形式
var a = { "foo":1, "bar":2}

2. 直接赋值的形式

var a= {}a.foo = 1;a.bar = 2;

但其实还有第三中形式,那就是使用Object.defineProperty这一方法!

var a= {};Object.defineProperty(a,"foo",{ value:1});Object.defineProperty(a,"bar",{ value:2});console.log(a.foo);// 1console.log(a.bar);// 2

语法

Object.defineProperty(obj, prop, descriptor)

参数说明

obj要在其上定义属性的对象。prop要定义或修改的属性的名称。descriptor将被定义或修改的属性描述符。

返回值

被传递给函数的对象

描述符(descriptor)说明

前两个参数 obj 和 prop 不必多说,下面来看看 descriptor 这个参数的具体用法,在给一个对象修改或添加一个属性时,可以给出如下的一些属性描述:

var a={};Object.defineProperty(obj,"foo",{ configurable:true | false, enumerable:true | false, value:任意类型的值, writable:true | false, get : function | undefined, set : function | undefined});

configurable

仅当设置的属性的描述符需要被修改或需要通过delete来删除该属性时,configurable属性设置为true。默认为false。

enumerable

仅当设置的属性需要被枚举器(如for..in)访问时设置为true。默认为false。

value

设置属性的值,可以是任何JavaScript值类型(number,object,function等类型)。默认为undefined。

writable

仅当属性的值可以被赋值操作修改时设置为true。默认为false。

get

属性的getter方法,若属性没有getter方法则为undefined。该方法的返回为属性的值。默认为undefined。

set

属性的setter方法,若属性没有setter方法则为undefined。该方法接收唯一的参数,作为属性的新值。默认为undefined。

要注意的一点是:在 descriptor 中不能同时设置访问器(get 和 set)和 wriable 或 value,否则会错,就是说想用 get 和 set,就不能用 writable 或 value 中的任何一个。

在 vue.js底层,正式巧妙地利用了这个函数的属性访问器(get 和 set)的特点,当被绑定数据的属性被重新赋值时,就会触发访问器 set 函数,在set 函数中通知UI更新,避免了像angular一样从model --> view 的脏检查。

var viewModel = {};Object.defineProperty(viewModel,"test",{ configurable:true , enumerable:true , set:function(newValue){ // 在这里去通知 UI 更新 return newValue; }});

当然,实际的情况并不像我上面说的那么简单,如果想知道更多关于vue.js双向绑定的实现,咱们就下回分解吧!