vue生命周期及双向绑定

这篇文章不是原创,看了其他人的分析贴,记录下自己学到的。本篇主要记录一下vue内部流程,以及双向绑定原理。Vue的可爱之处在于他的双向绑定及Virtual DOM的思想。

vue内部流程

《vue生命周期及双向绑定》

如图所示,实例化组件时,调用init方法,初始化事件,属性,data等。初始化data,是实现双向绑定的重要一步(后面再详细说)。挂载($mount)时,根据传入的模版解析编译成 render function。 再把render function 转成 Virtual DOM tree(虚拟DOM树)。数据更新时,Virtual DOM tree转成 真正的DOM之前,经历patch方法,计算那些更新的节点,更新相应的节点,生成DOM.每个步骤拆开的话如下:

init

组件实例化时,调用init(),初始化组件的生命周期相关属性,事件,props,data等等。vue双向绑定也在这个期间完成的(后面再细说)。

compile

组件$mount(挂载)时,根据传入的模版或者节点标识(id等)把组件模版编译(compile)成render function。 编译过程分三步:

  • parse 解析模版中的指令,属性等数据,形成AST(抽象语法树),例如:
const element2 = {
    type: 1,
    tag: 'a',
    attrsList: [{name: ":href", value: "url"}, {name: "target", value: "_blank"}],
    attrsMap: {':href': 'url', 'target': '_blank'},
    parent: element1,
    children: []
  };
  • optimize 标记静态节点,更新视图时,根据diff算法计算更新的节点时,直接跳过静态节点,提高diff算法性能。
  • generate 把AST 转成render function.

render

render function 转成 VNode, 生成Virtual DOM tree(虚拟DOM树).VNode其实是一个描述DO节节点的javascript对象,抽象真正的DOM节点.为什么需要Virtual DOM ? 因为DOM操作总是很慢,操作数据对象不仅快,还有跨平台的能力。VNode比如:

{
    tag: 'div',                 
    children: [               
            tag: 'a',          
            text: 'click me'   
        }
    ]
}

patch

数据更新时,patch方法是新的 Virtual DOM 树和旧 Virtual DOM 的diff算法,算出更新了哪些节点,更新对应的DOM节点。

双向绑定实现

vue的双向绑定是通过Object.definerProperty加发布订阅模式实现的。
object.defineProperty具体知识移步这里。重点是它的get和set方法,比如这个例子:

var obj = {};
var a;
Object.defineProperty(obj, 'a', {
  get: function() {
    console.log('get val'); 
    return a;
  },
  set: function(newVal) {
    console.log('set val:' + newVal);
    a = newVal;
  }
});
obj.a;     // get val 
obj.a = '111'; // set val: 111

vue的双向绑定原理就是,比如data.number =’123′;每个组件用到number属性的时候,都会调object.defineProperty()的get函数,从而收集到依赖number属性的vue组件。每个组件实例话时都有一个watcher,就是订阅者。我们叫收集依赖的对象为Dep。
看下Dep对象

//依赖收集对象
class Dep {
    constructor () {
        this.subs = [];
    }
    //收集依赖
    addSub (sub) {
        this.subs.push(sub);
    }
    //发布变化
    notify () {
        this.subs.forEach((sub) => {
            sub.update();
        })
    }
}

watcher对象

class Watcher {
    constructor () {
        Dep.target = this;
    }
    //更新视图
    update () {
        console.log("视图更新了哦");
    }
}

初始化过后,Dep对象收集到依赖number属性的订阅者watcher1,watcher2(用到number属性的组件vue1,vue2),当number属性变化时,调用set函数,我们调用依赖收集对象的(发布者)update方法去发布更新,每个watcher(订阅者)都收到更新提示,更新视图。就是这个样子啦~其实不难。
对于那些,show me the code 的同学:demo代码

参考:

掘金[vue内部运行机制][4],、
源码讲解[vue2.0远吗解读][5]

    原文作者:echo_numb
    原文地址: https://segmentfault.com/a/1190000013813083
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞