数据驱动

# 数据驱动

# 1. new Vue

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
1
2
3
4
5
6

# 2. init

  • 调用 this._init(options) 进行初始化
    • mergeOptions 合并配置
    • initLifecycle(vm) 初始化生命周期,调用生命周期钩子函数 callHook(vm, 'beforeCreate')
    • initEvents(vm) 初始化事件中心
    • initRender(vm) 初始化渲染
    • 初始化 data、props、computed、watcher 等等

# 3. Vue 实例挂载 $mount

$mount 这个方法的实现是和平台、构建方式都相关的。我们分析带 compiler 版本的 $mount 实现。在 Vue 2.0 版本中,所有 Vue 的组件最终都会转换成 render 方法。

  • 它对 el 做了限制,Vue 不能挂载在 body、html 这样的根节点上。
  • 如果没有定义 render 方法,则会调用 compileToFunctions 方法把 el 或者 template 字符串转换成 render 方法。
  • mountComponent:核心就是先实例化一个渲染Watcher,在它的回调函数中会调用 updateComponent 方法,在此方法中调用 vm._render 方法先生成虚拟 Node,最终调用 vm._update 更新 DOM。
  • 将 vm._isMounted 设置为 true,表示已经挂载
  • 执行 mounted 钩子函数:callHook(vm, 'mounted')

# 4. compile

在 Vue 2.0 版本中,所有 Vue 的组件的渲染最终都需要 render 方法,无论我们是用单文件 .vue 方式开发组件,还是写了 el 或者 template 属性,最终都会转换成 render 方法,那么这个过程是 Vue 的一个“在线编译”的过程,它是调用 compileToFunctions 方法实现的。

# 5. render

render: Vue 的 _render 方法是实例的一个私有方法,最终会把实例渲染成一个虚拟 Node。

vm._render 最终是通过执行 createElement 方法并返回的是 vnode,它是一个虚拟 Node

# 6. Virtual DOM(虚拟 dom)

Virtual DOM(虚拟 dom): 本质上是一个原生的 JS 对象,用 class 来定义。

  • 核心定义:几个关键属性,标签名、数据、子节点、键值等,其它属性都是都是用来扩展 VNode 的灵活性以及实现一些特殊 feature 的。
  • 映射到真实的 DOM ,实际上要经历 VNode 的 create、diff、patch 等过程。
  • createElement: 创建 VNode

创建VNode

  • children 的规范化:由于 Virtual DOM 实际上是一个树状结构,每一个 VNode 可能会有若干个子节点,这些子节点应该也是 VNode 的类型。因为子节点 children 是任意类型的,因此需要把它们规范成 VNode 类型。
    • simpleNormalizeChildren:调用场景是 render 函数是编译生成的。
    • normalizeChildren
      • 一个场景是 render 函数是用户手写的,当 children 只有一个节点的时候,Vue.js 从接口层面允许用户把 children 写成基础类型用来创建单个简单的文本节点,这种情况会调用 createTextVNode 创建一个文本节点的 VNode。
      • 另一个场景是当编译 slot、v-for 的时候会产生嵌套数组的情况,会调用 normalizeArrayChildren 方法,遍历 children (可能会递归调用 normalizeArrayChildren )。
    • 总结
      • 经过对 children 的规范化,children 变成了一个类型为 VNode 的 Array
  • VNode 的创建
    • 规范化 children 后,会去创建一个 VNode 的实例。
    • 或者通过 createComponent 创建一个组件类型的 VNode,本质上它还是返回了一个 VNode。
    • 总结
      • 每个 VNode 有 children,children 每个元素也是一个 VNode,这样就形成了一个 VNode Tree,它很好的描述了我们的 DOM Tree。
  • update:通过 Vue 的 _update 方法,_update 方法的作用是把 VNode 渲染成真实的 DOM。_update 的核心就是调用 vm.patch 方法,__patch__在不同的平台,比如 web 和 weex 上的定义是不一样的。

# 7. update 的核心

update 的核心:调用 vm.patch 方法

update:通过 Vue 的 _update 方法,_update 方法的作用是把 VNode 渲染成真实的 DOM。_update 的核心就是调用 vm.patch 方法,__patch__在不同的平台,比如 web 和 weex 上的定义是不一样的。

  • 首次渲染
    • 通过 createElm 方法,把虚拟节点创建真实的 DOM 并插入到它的父节点中。
    • 然后调用 createChildren 方法去创建子元素,实际上是遍历子虚拟节点,递归调用 createElm。
    • 接着再调用 invokeCreateHooks 方法执行所有的 create 的钩子并把 vnode push 到 insertedVnodeQueue
    • 最后调用 insert 方法把 DOM 插入到父节点中,因为是递归调用,子元素会优先调用 insert,所以整个 vnode 树节点的插入顺序是先子后父。
    • 总结
      • 其实就是调用原生 DOM 的 API 进行 DOM 操作,Vue 就是这样动态创建的 DOM。
  • 数据更新

# 8. DOM

DOM:Vue 最终创建的 DOM。

# 9. 总结

初始化 Vue 到最终渲染的整个过程:

new Vue => init => $mounted => compile => render => vnode => patch => DOM

上次更新: 2022/7/4 下午9:00:37