vue核心之双向绑定

双向绑定如何实现:

        1、我们需要一个方法来识别视图中哪个元素被设置了双向绑定。

        2、我们需要监视视图和数据的变化。

        3、我们需要将所有变化传播到绑定视图或者数据。

几种实现数据双向绑定的做法:1、发布者-订阅者模式(backbone.js)、脏值检查(angular.js)、数据劫持(vue.js)。

发布者-订阅者模式

        视图驱动数据变化,主要应用于input、select、textarea等元素,通过监听dom的change、keypress、keyup等事件来触发事件改变数据层数据。

        数据驱动视图变化,一个数组对象el存放支持双绑的dom对象,一个储存 绑定值的对象data,一个储存 设置值函数的对象fun,一个调用函数set(循环每个el,再循环每个el的属性,如果某个指定的属性存在,就调用对应fun),一旦改变数据就调用set函数。

脏值检查:

        和上种方式类似,但在数据驱动视图变化,不是改完值手动触发set函数触发视图更新,而是通过setInterval()定时轮询检测数据变化触发set函数。

数据劫持:    

        vue.js是通过数据劫持(object.defineProperty()的set和get)结合发布者-订阅者方式,在数据变动时发布消息给订阅者,触发相应回调监听。

        1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者。

        2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模版替换数据,以及绑定相应的更新函数。

        3、实现一个watcher,做为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知。执行指令绑定的相应回调函数,从而更新视图。

        4、mvvm入口函数,整合以上三者。

流程图

分析

实现mvvm主要包括两个方面,数据变化更新视图,视图变化更新数据。关键在于前者数据变化更新视图,后者可以通过事件监听即可。所以我们着重看前者。我们可以通过Object.defineProperty()对属性设置一个set函数来观察数据变化做出反应,我们来具体实现一下。

实现

我们需要一个观察者Observer,以 遍历递归 方式 为每个对象属性通过Object.defineProperty()添加观察,在 获取对象属性getter函数中 需要在订阅者Watcher初始化时把他装进订阅器Dep,在 对象属性值更新setter函数中 需要 改变oldVal—newVal,并通知订阅者触发相应函数。

我们需要一个订阅者Watcher,它需要有初始化功能( 结合Observer的getter函数初始化时把自己添加进Dep ),需要在数据属性值变化时,调用相关函数重新赋予订阅者新值。

我们需要一个解析者Compile,遍历+递归 去解析dom节点,若解析到绑定相关字符串,就将初始化的数据初始化到视图中,实例化一个订阅者并绑定更新函数。

        1、实现一个观察者Observer,用来劫持并观察所有属性,如果有变动,就通知订阅者。

        2、实现一个订阅者Watcher,可以收到属性的变化并执行相应的函数,从而更新视图。

        3、实现一个解析者compile,可以扫描和解析每个节点的相关指令,并根据初始化模版数据去初始化相应的订阅器。

维护订阅器

订阅器

维护一个订阅器,负责实例化订阅者,当初始化和更新时,调用相关函数。

Dep为一个构造函数,有subs数组储存订阅者,addSub和notify两个函数,addSub负责在初始化订阅者初始化时(当Compile解析dom检测到相关字符串 进行订阅者初始化)添加到订阅器中,notify负责在观察到数据更新时被触发 去调用订阅者的更新函数。

实现Observe

observ

Observer主要是对 对象属性 通过 defineProperty()进行监听,getter时帮助订阅者初始化时加入订阅器,setter时更新对象属性及通知订阅者调用函数更新订阅者值。

一个构造函数Observer,一个触发函数observe。

observe函数判断参数是否是对象,是的话实例化一个Observe对象(对象属性 遍历递归 时判断子属性值 是否是对象)。

Observe接受一个对象,有Walk、defineReactive两个函数,Walk负责遍历对象每个属性调用defineReactive。defineReactive负责递归每个对象属性 设置监听器。

Q/A

每次defineReactive都会new Dep()再在getter中初始化push订阅者,Dep中怎么会有所有订阅者?

其实只有一个订阅者,每次都会实例一个Dep,有多个Dep。

setter时循环订阅器中每个订阅者调用update函数,update函数做了什么事情?

监听器更新数据时触发的更新函数判断 new/old数据是否相同,不相同就把旧Value赋予新值,并在全局执行回调函数(传入新旧值)

实现订阅者

订阅者

        每个订阅者实例有4个对象属性,cb(监听器更新数据时触发的函数)vm(组件对象),exp(绑定的属性key)value(绑定的属性值)。run和get两个函数,run为监听器更新数据时触发的更新函数判断 new/old数据是否相同,不相同就把旧Value赋予新值,并在全局执行回调函数(传入新旧值)。get为初始化时把自己添加进订阅器Dep()中。

实现Compile

解析器主要作用是 遍历递归解析dom节点,解析到双向绑定的指令,将初始化的数据初始化到视图中,实例化订阅器并绑定更新函数。

第一部分

        Compile构造函数有3个属性,vm(全局环境)el(html最高节点)fragment用来存放dom节点(我们数据更新dom时需要多次操作dom,通过createDocumentFragment创建一个虚拟父节点fragment,把dom移入fragment进行操作,操作完了直接替换整个dom(一次性替换操作效率更高比一次次操作块70%)。

init()调用了nodeToFragment、compileElement、compile三个函数。

nodeToFragment,把dom塞入fragment虚拟父节点。

compileElement,遍历递归fragment中dom,判断是元素节点的话执行compile函数,是文本节点且有'{{}}'的话执行compileText函数。如果节点有子节点继续递归执行compileElement。

compile,对dom节点的属性节点进行遍历,若有"v-"相关字段属性name,若有":on"相关字段则绑定的是事件,执行compileEvent事件,否则执行compileMdole事件

第二部分

"{{}}"对应的compileText函数,负责初始化节点textContent数据,并新增一个订阅者。

"v-on:"对应的compileEvent函数,负责取得事件名和事件值 通过addEventListener监听函数触发执行对应事件。

"v-model"对应compileModel函数,负责初始化节点value数据,并新增一个订阅者,再通过对node.addEventListener('input', function(...))在input数据变化时实时改变对象数据

第三部分

mvvm入口

入口

我们把整个流程结合起来看一遍

        入口构造函数,需要一个数据对象data,需要一个函数对象methods(当data中数据变化时调用)。

        有一个proxyKeys函数,作用,在访问selfVue的属性时代理为selfVue.data属性(this.data.name = 'canfoo'我们可以用更简洁的方式 this.name = 'canfoo' ),也是通过遍历每个data属性为每个属性添加监听器object.defineProperty(),在get内把对this.key的访问替换成this.data.key的属性值来处理。

        监听器observe对数据对象进行监听。

        实例化compile对象,把节点传入,在compile会对dom节点进行遍历递归,处理3种情况。1、"{{}}",初始化节点texteContent数值,实例化一个订阅者。2、"v-model",初始化节点value数值,实例化一个订阅者,并监听input事件实时对数据更新。3、"v-on:"把对应事件名和methods中事件进行绑定监听addEventListener。

        实例化订阅者Watcher,会在初始化时把自己添加进订阅器Dep(),在数据更新时会通过this.c.call()触发传进来的函数 处理数据。

        所有事情处理好后执行mounted函数。

二次理解:

我们定义好基本数据

基本数据

会有一个入口

入口

        入口及之后做的事情:1、取到定义好的数据。2、把对this.xxx的访问代理到this.data.xxx(写法更简洁)。3、我们需要一个订阅器Dep,来收集订阅者Watcher,在观察器检测到数据变化时,调用订阅者Watcher的相关处理函数。4、执行观察者(Observe)遍历递归 data属性,通过访问器属性Object.defineProperty() 给他们都绑定观察器。5、执行编译者(Compile)分析编译Dom结构,检测到 相应字符串 实例化一个订阅者(Watcher),实例化时它会主动触发观察者的getter函数把自己push进订阅器Dep。

首次数据赋值在什么时候?

        首次数据赋值在解析者Compile解析到相关字符串时 实例化订阅者Watcher的同时进行了赋值。

之后数据更新 执行流程如何?

        观察者Observe会在数据更新时观察到,会执行setter函数调用dep中notify函数,notify函数再调用订阅者的Update函数去调用相关方法处理。

订阅器Dep存在的意义?

        不知道????  每个Observer.prototype.defineReactive都会new Dep。相当于每个Watcher订阅者都有一个Dep()。

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

推荐阅读更多精彩内容

  • vue理解浅谈 一 理解vue的核心理念 使用vue会让人感到身心愉悦,它同时具备angular和react的优点...
    ambeer阅读 24,118评论 2 18
  • 前言 在之前面试中,有被问到这个问题,虽然了解过是劫持Object.defineProperty方法,但是其细节并...
    Aleph_Zheng阅读 1,070评论 0 5
  • 我的github: vue双向绑定原理 MVC模式 以往的MVC模式是单向绑定,即Model绑定到View,当我们...
    KlausXu阅读 44,847评论 7 91
  • 这两天在读余华的作品《活着》,在拿到这本书,看到名字就想这一定是讲某个时代悲情的故事。讲的肯定是主人公如何艰难的一...
    娴静雅之阅读 270评论 0 0
  • 今后一定还会有好事发生的 有些时候 这个世界会让你很沮丧 不过有件事是肯定的 要么好好活着 要么赶紧死 人生在世 ...
    卫校一七药二阅读 727评论 0 19