关于vue.js:Vue大致构建流程

1次阅读

共计 3003 个字符,预计需要花费 8 分钟才能阅读完成。

构建流程大抵框图

构建与数据更新要点

该程序并不代表真正的构建流程,只是对重要内容的拆解

初始化与挂载

在应用 new Vue() 后 Vue 会调用 _init 函数进行初始化,其会

  • 初始化 options 参数
  • 初始化生命周期
  • 初始化 vm 状态,其中蕴含 props/methods/data/computed 与 watch 等
  • 初始化实现后挂载实例

数据初始化过程中还会递归应用 Object.defineProperty (Vue2.X) / Proxy (Vue3) 设置 setter 与 getter 函数实现响应式与依赖收集

挂载后编译

挂载实现后为生成 render function,vue 会将 template 进行编译,该过程大抵分为三阶段

parse

【阶段指标】生成 AST 语法树

parse 会应用正则表达式解析 template 模板中的指令、class、style 等数据,最终生成 AST 语法树

optimize

【阶段指标】削减优化标记

optimize 次要作用为标记 static 动态节点,该阶段次要是对后序页面更新后比对过程的优化

generate

【阶段指标】生成 render function

generate 是将 AST 语法树转换为 render function 字符串的过程

构建 vDom

vDom 相当于一个缓冲层,其可能实现对实在 Dom 的起码操作从而优化性能,而且因为其自身是 JS 对象,在不同环境中领有良好的跨平台性

转换过程

在获取到 render function 后其会被转换为 VNode 节点,虚构 Dom 本质上就是由 VNode 组合成的树,在逻辑上是对实在 Dom 的形象

VNode

其归根结底是一个 JS 对象,应用对象的属性来形容以后节点的一些状态,最终应用 VNode 节点的模式模仿 Dom 树

// 简略范例
class VNode {constructor (tag, data, children, text, elm) {
        /* 以后节点的标签名 */
        this.tag = tag;
        /* 以后节点的一些数据信息,比方 props、attrs 等数据 */
        this.data = data;
        /* 以后节点的子节点,是一个数组 */
        this.children = children;
        /* 以后节点的文本 */
        this.text = text;
        /* 以后虚构节点对应的实在 dom 节点 */
        this.elm = elm;
    }
}

公布订阅者模式

发布者

发布者保护一个订阅者列表,当该发布者被援用时则将援用的对象置入订阅者列表中,每当该发布者扭转时就会告诉订阅者更新视图

class Dep {constructor () {
        /* 用来寄存订阅者对象的数组 */
        this.subs = [];}


    /* 在订阅者数组中增加一个订阅对象 */
    addSub (sub) {this.subs.push(sub);
    }


    /* 告诉所有订阅对象更新视图 */
    notify () {this.subs.forEach((sub) => {sub.update();
        })
    }
}

订阅者(观察者)

订阅者在援用公布对象时会被收集进入公布对象的订阅者列表中

class Watcher {constructor () {
        /* 在 new 一个订阅者对象时将该对象赋值给 Dep.target,在 get 中会用到 */
        Dep.target = this;
    }


    /* 更新视图的办法 */
    update () {console.log("视图更新啦~");
    }
}
Dep.target = null;

依赖收集

在实现上述逻辑后须要明确发布者如何得悉订阅者援用了本人,这时就须要应用 getter 逻辑,使发布者在本身被援用时实现捕捉逻辑

具体体现在利用办法上 Vue2.X 应用的是 Object.defineProperty,Vue3 应用的是 Proxy

function defineReactive (obj, key, val) {
    /* new 一个发布者对象对象 */
    const dep = new Dep();
    
    Object.defineProperty(obj, key, {   // 此处以 Vue2.X 举例
        enumerable: true,
        configurable: true,
        /* 数据被读取时触发 get 内逻辑 */
        get: function reactiveGetter () {
            /* 将 Dep.target(即以后的订阅对象存入发布者的订阅者列表中)*/
            dep.addSub(Dep.target);
            return val;         
        },
        /* 数据被操作时触发 set 内逻辑 */
        set: function reactiveSetter (newVal) {if (newVal === val) return;
            /* 在 set 时触发发布者的告诉逻辑来告诉所有的订阅者对象更新视图 */
            dep.notify();}
    });
}


class Vue {constructor(options) {
        this._data = options.data;
        /* 此处实现数据的响应式解决 */
        observer(this._data);
        /* 新建一个订阅者对象,这时候 Dep.target 会指向这个订阅者对象 */
        new Watcher();
        /* 在这里模仿数据渲染的过程,其会触发 test 属性的 get 逻辑 */
        console.log('render~', this._data.test);
    }
}

注:该阶段体现在 Vue 构建过程中其实能够分成两局部,在数据初始化(init)时对参数实现公布订阅者逻辑的创立,在渲染(render)时收集局部依赖,最终实现响应式设计

数据更新比对

在页面中对数据进行操作时会触发发布者的告诉逻辑从而使订阅者执行相应逻辑并生成新的 VNode,新老 VNode 会进行一个 patch 过程比对得出差别,最终将差别更新至 Dom 实现视图的更新

patch

比对的外围为 diff 算法,diff 算法是通过同层的树节点进行比对,其工夫复杂度只有 O(n)
图中雷同色块的节点会进行比对,得出的差别最终会更新至视图
须要留神的是 Vue 与 React 失去 diff 算法虽说都是疏忽跨级比拟,只进行同级比拟,然而

  1. Vue 比照节点时,当节点元素类型雷同然而 className 不同则认定它们为不同类型元素并删除重建,而 React 则会认为其为同类型节点只批改节点属性

    1. Vue 同级比照时采纳从两端至核心的比照形式,而 React 采纳从左向右顺次比照,这样的区别是当最右节点挪动至最左端时,React 会将后面的节点顺次挪动而 Vue 只会挪动一个节点
    2. Vue 组件数据变动时只更新本人,React 组件数据变动会更新本人及所有子组件

碍于篇幅具体比照流程请自行搜寻,这里不多赘述

更新渲染

在更新渲染阶段,Vue 应用了异步更新策略

为什么须要异步更新

Vue 的异步更新策略更多的是出于性能优化的考量

举一个栗子,页面某值循环执行 1000 次自增(模仿多 effect 场景),此时若是遵循同步更新逻辑则会触发 1000 次 setter=>Dep=>watcher=>update 流程,这就导致会有大量的性能损耗

而 Vue 应用的异步更新策略会创立一个更新栈,而后将更新压入栈中并进行去重解决(去重操作的目标就在于优化本例呈现的屡次执行状况),在当轮同步代码执行实现后开始执行更新栈中的更新工作,最终将扭转反馈至视图中

异步更新流程

Vue 的异步更新机制借用了事件循环机制内容,其将更新表明 id 并放入微工作队列中,待当轮同步工作执行实现后就会开始执行微工作队列中所有更新工作,最终将更新渲染至视图

当微工作队列中被置入雷同 id 的更新工作时,Vue 异步更新策略只会保留最初的更新工作,实现性能优化成果

正文完
 0