Vue 实例化过程关于数据方面的处理

var vm = new Vue({ data: ..., ... })
  1. 使用构造函数的数据初始化实例的 _data 属性(即 vm._data)

  2. _data 属性,使用 gettersetter 代理到 Vue 实例 vm 上 即访问、修改vue的属性的时候,只是简单地通过 gettersetter 访问 vm._data 属性。

  3. 观察 vm._data 属性(这部分功能交由 Observer 类进行)

  • 生成一个 Observer 对象 obs,该对象持有 vm._data (obs.value === vm._data), 通过递归,将 vm._data 的数据属性,全部用 Object.defineProperty 转换成存取器属性
  • 针对每个转换后的存取器属性,会实例化一个依赖管理器 dep (Dep 类型)对象来维护依赖该属性的观察者(Watcher 类型)列表 dep.subs
  • 当某个属性的值被修改时,obssetter 中,会递归地观察新的属性值,并最终让该属性的依赖管理器 dep 去通知所有 watcher(订阅者)执行其 update 方法(dep.notify(),内部遍历 watcher 逐个执行 watcher.update())
  • 每当属性值被读取,会在 getter 中,检测 Dep.target(Dep 类的静态属性)是否有值,有值则是一个 Watcher 对值的读取(Watcher 类的 getter 中,读取属性的时候,会临时设置 Dep.targetwatcher 实例自身),这时候把该 watcher 加入当前属性的依赖管理器 dep 里面,这样该属性值下次改变,该 watcher 就能获得通知并 update
  1. Vue 实例中,会暴露一个 $watch(expOrFn, cb) 接口,主要在指令中使用订阅者,调用该接口,会创建一个 Watcher 类型的对象,Watcher 对象创建过程,会读取 vm._data 的对应属性,从而触发上述的 obsgetter,进而自动将本 watcher 加入对应属性的依赖管理器 dep 里,以便在属性下次变化时获得通知有机会执行 update 方法。

总的来讲:

Observer类

将数据属性转换成存取器属性,并:

  • 在每个属性的 getter 里面,提供值之前,分析是否是新的 watcher,若是,则将其加入对应属性的依赖管理器
  • 在每个属性的 setter 里面,修改值之后,让依赖管理器去调用所有 watcherupdate 方法

Dep类

用于管理某个 data 的属性的所有订阅者,拥有 notify 方法,该方法在关联的 data 属性发生变化时被调用,内部会执行所有订阅者的 update 方法

Watcher类

用于订阅某个data属性,当属性变化时,执行实例化时构造器中传入的回调函数

Vue类

  • 构造时,自动使用 Observer 类来转换、维护数据
  • 暴露一个 $watch 方法,该方法可以生成一个 watcher 用于订阅 data 数据

模版编译

编译元素 compileElement(el, options)

  • 检查 el 的 attributes,
  • 将 attributes 转成数组 attrs,
  • 调用 compileDirectives(attrs, options) 进行指令编译,返回一个 nodeLinkFn,这是一个链接函数,作用是绑定指令和 Vue 实例
  • nodeLinkFn 作为最终结果返回

编译指令 compileDirectives(attrs, options)

  • 解析每个 attribute,提取修改器(modifier)、过渡效果(transiton)、动态绑定(v-bind:),普通指令(v-textv-ref:xxxv-el:xxx)等等信息
  • 每个成功解析出来的结果,就是一个指令描述(directive descriptor),将所有解析结果推入一个数组待用
    // 指令描述数据结构
    {
    name: 'text', // 指令名称
    attr:'v-text', // 指令原始名
    raw:'值内容', // 指令原始值
    def: { // 指令定义
    bind(){},
    update() {}
    },
    arg: arg, // 参数,`v-ref:xxx`中`xxx`部分
    modifier: modifier, // 修改器
    expression: parsed && parsed.expression, // 指令原始值中的表达式部分
    filters: parsed && parsed.filters, // 指令原始值中的过滤器部分
    interp: interpTokens,
    hasOneTime: hasOneTimeToken
    }
  • 指令描述数组作为参数,调用 makeNodeLinkFn(directiveDiscriptors),该函数返回一个新函数 nodeLinkFn(vm, el, host, scope, frag) nodeLinkFn 作为闭包可以访问 makeNodeLinkFn 的参数 directiveDiscriptorsnodeLinkFn 执行时为每个 directive discriptor 逐一调用 vm._bindDir 方法
  • nodeLinkFn 作为结果返回

Vue.prototype._bindDir(descriptor, node, host, scope, frag)

  • 使用 new Directive(descriptor, this, node, host, scope, frag) 得到一个 Directive 实例。
  • Directive 实例 push 入 Vue 实例的 _directive 数组属性里