实现双向数据绑定

MVVM框架主要包含3个部分:modelviewviewmodel

  1. Model:指的是数据部分,对应到前端就是javascript对象
  2. View:指的是视图部分,对应前端就是dom
  3. Viewmodel:就是连接视图与数据的中间件
1.双向数据绑定的实现方式

简单的来说,就是框架的控制器层(这里的控制器层是一个泛指,可以理解为控制view行为和联系model层的中间件)和UI展示层(view层)建立一个双向的数据通道。当这两层中的任何一方发生变化时,另一层将会自动作出相应的变化。

vue-MVVM

一般来说要实现这种双向数据绑定,在前端我目前了解的有三种形式:

  • 基于脏检查 angular,regular(网易开发)
  • 观察机制
  • 封装属性访问器

2.基于脏检查

目前angular,regular的实现都是基于脏检查。当发生某些特定的事情的时候,框架会调用相关的digest方法。内部逻辑就是遍历所有的watcher,对监控的属性做对比。如果值发生了变化,则执行相应的handler

2.1基于regular详细介绍一下:
watcher对象看起来是这样的:
{
  get: function(context){...}  //获得表达式当前求值,此函数在解析时,已经生成
  set: function(){} // 有些表达式可以生成set函数,用于处理赋  值,这个一般用于双向绑定的场景
  once: false // 此监听器是否只生效一次
  last: undefined// 上一次表达式的求值结果
  fn: function(newvalue, oldvalue){} // 即你传入$watch的第二个参数,当值改变时,会调用此函数
  // ...
}

当系统进入脏检查阶段,遍历所有的$watch绑定的watcher,然后对比watcher.get()watcher.last,如果不同则运行对应的watcher.fn(newvalue, oldvalue)。然后再进入下一个watcher的检查。

何时进行脏检查?

在Regular中,digest阶段是由$update
方法触发的。
具体源码在regular/src/helper/watcher.js里面

由于regularjs是基于脏检查,所以当不是由regularjs本身控制的操作(如事件、指令)引起的数据操作,可能需要你手动的去同步data与view的数据. $update方法即帮助将你的data同步到view层.

//手动同步
var component = new Regular();
component.data.name = 'leeluolee'
// you need call $update to Synchronize data and view 
component.$update();  

//自动进入
<div on-click={blog.title='Hello'}>{blog.title}</div>
regular-源码片段,基于select的r-model指令

总结:但是很显然,脏检查是低效的,它的效率基本上取决于你绑定的观察者数量,在Regular中,你可以通过[@(Expression)

](https://regularjs.github.io/reference?syntax-zh#bind-once)元素来控制你的观察者数量。

3.观察者机制

使用ES7中的 Object.observe 方法对对象(或者其属性)进行监控观察,一旦其发生变化时,将会执行相应的handler。这是目前监控属性数据变更最完美的一种方法,语言(浏览器)原生支持,没有什么比这个更好了。唯一的遗憾就是目前支持广度还不行,有待全面推广。

4.封装属性访问器(存取器get,set)

vue.js和avalon.js实现数据双向绑定的原理就是属性访问器。
它使用了ES5中的定义标准属性的Object.defineProperty 方法。

Object.defineProperty(obj, prop, descriptor)
//obj 待修改的对象
//prop 待修改的属性名称
//descriptor 待修改属性的相关描述,要求传入一个对象。
//@{param} descriptor
1.configurable ,属性是否可配置。可配置的含义包括:是否可以删除属性( delete ),是否可以修改属性的 writable 、     enumerable 、 configurable 属性。
2.enumerable ,属性是否可枚举。可枚举的含义包括:是否可以通过 for...in 遍历到,是否可以通过 Object.keys() 方法获取属性名称。
3.writable ,属性是否可重写。可重写的含义包括:是否可以对属性进行重新赋值。
4.value ,属性的默认值。
5.set ,属性的重写器。一旦属性被重新赋值,此方法被自动调用。
6.get ,属性的读取器。一旦属性被访问读取,此方法被自动调用。

Object.defineProperty使用示例:

var o = {};
Object.defineProperty(o, 'name', { value: 'erik'});
console.log(Object.getOwnPropertyDescriptor(o, 'name'));
   // {
        value: "erik", 
        writable: false, 
        enumerable:false,
        configurable: false
     }

Object.defineProperty(o, 'age', {
    value: 23,
    configurable: true,
    writable: true
});
console.log(o.age); // 23
o.age = 18;
console.log(o.age); // 18. 因为age属性是可重写的
console.log(Object.keys(o)); // []. name和age属性都不是可枚举的
Object.defineProperty(o, 'sex', {
    value: '女',
    writable: false
});
o.sex = '男'; // 这里的赋值其实是不起作用的
console.log(o.sex); // '女';
delete o.sex; // false, 属性删除的动作也是无效的

注意:

  • Object.defineProperty() 方法设置属性时,属性不能同时声明访问器属性( set 和 get )和 writable 或者 value 属性。 意思就是,某个属性设置了 writable 或者 value 属性,那么这个属性就不能声明 get 和 set 了,反之亦然。
  • 因为 Object.defineProperty() 在声明一个属性时,不允许同一个属性出现两种以上存取访问控制。
4.1基于vue使用封装属性访问器

首先,vuejs在实例化的过程中,会对遍历传给实例化对象选项中的data 选项,遍历其所有属性并使用 Object.defineProperty 把这些属性全部转为 getter/setter。

同时每一个实例对象都有一个watcher实例对象,他会在模板编译的过程中,用getter去访问data的属性,watcher此时就会把用到的data属性记为依赖,这样就建立了视图与数据之间的联系。当之后我们渲染视图的数据依赖发生改变(即数据的setter被调用)的时候,watcher会对比前后两个的数值是否发生变化,然后确定是否通知视图进行重新渲染。这样就实现了所谓的双向数据绑定。

defineProperty
效果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容