返回

从 new Vue 到所有组件 mounted 的全过程

前端

从 new Vue 到所有组件 mounted 的全过程

从 new Vue({ render: h => h(App) }).mount('#app') 到页面上出现对应的 DOM 内容,这中间的过程是如何发生的?主要分为以下几小结:

  • Vue 以及由它创建的 vm 实例的属性是如何来的
  • vnode 和 vm 实例的 props 数据是如何绑定的
  • 如何根据 vnode 创建真实 DOM 的
  • 如何进行 diff 算法的比较
  • 如何更新 DOM 的
  • Vue.nextTick 是如何工作的

Vue 以及由它创建的 vm 实例的属性是如何来的

Vue 实例的属性

Vue 实例的属性分为两类:一是 data 选项中声明的属性,二是计算属性。

  • data 选项中声明的属性

data 选项中声明的属性是 Vue 实例最基本的数据属性,它们直接存储在 Vue 实例的 _data 属性中。

const vm = new Vue({
  data: {
    name: 'John',
    age: 20
  }
})

console.log(vm._data) // { name: 'John', age: 20 }
  • 计算属性

计算属性是 Vue 实例中另一种常见的数据属性。计算属性的值不是直接存储在 Vue 实例的 _data 属性中,而是通过一个 getter 函数计算得到。

const vm = new Vue({
  data: {
    name: 'John',
    age: 20
  },
  computed: {
    fullName() {
      return this.name + ' ' + this.age
    }
  }
})

console.log(vm.fullName) // 'John 20'

vm 实例的其他属性

除了 data 选项中声明的属性和计算属性外,vm 实例还有许多其他属性,这些属性都是由 Vue 内部创建的,用于支持 Vue 实例的各种功能。

例如,vm 实例有一个 $el 属性,指向挂载 Vue 实例的 DOM 元素。vm 实例还有一个 $refs 属性,包含了所有使用 ref 属性的子组件的引用。

const vm = new Vue({
  el: '#app',
  data: {
    name: 'John',
    age: 20
  },
  template: `
    <div>
      <p>{{ name }}</p>
      <p>{{ age }}</p>
    </div>
  `
})

console.log(vm.$el) // <div id="app">...</div>
console.log(vm.$refs) // { child: { name: 'John', age: 20 } }

vnode 和 vm 实例的 props 数据是如何绑定的

vnode 是 Vue 中虚拟 DOM 的最小单元,它代表了 DOM 树中的一个节点。vnode 有一个 props 属性,包含了该 vnode 对应的组件的 props 数据。

当 Vue 实例被创建时,它会根据 template 选项或 render 函数创建一个 vnode。然后,Vue 实例会将 vnode 的 props 数据绑定到组件的实例上。

const vm = new Vue({
  data: {
    name: 'John',
    age: 20
  },
  template: `
    <div>
      <child-component :name="name" :age="age"></child-component>
    </div>
  `
})

console.log(vm.$children[0].$props) // { name: 'John', age: 20 }

如何根据 vnode 创建真实 DOM 的

当 Vue 实例被创建时,它会根据 vnode 创建一个真实的 DOM 元素。这个过程叫做 patching

Patching 过程是递归的,它从根 vnode 开始,然后逐层遍历 vnode 树,为每个 vnode 创建一个真实的 DOM 元素。

const vm = new Vue({
  data: {
    name: 'John',
    age: 20
  },
  template: `
    <div>
      <p>{{ name }}</p>
      <p>{{ age }}</p>
    </div>
  `
})

const rootNode = vm._vnode
const realDOM = patch(rootNode)

console.log(realDOM) // <div>...</div>

如何进行 diff 算法的比较

当 Vue 实例更新时,它会使用 diff 算法来比较新旧 vnode 的差异。diff 算法会找出新旧 vnode 之间的差异,并只更新那些发生变化的 DOM 元素。

Diff 算法是 Vue 性能优化非常重要的一部分。如果没有 diff 算法,Vue 就需要在每次更新时重新渲染整个组件树,这会极大地降低 Vue 的性能。

如何更新 DOM 的

当 diff 算法找出新旧 vnode 之间的差异后,Vue 实例就会更新 DOM。

Vue 实例会使用 patch 函数来更新 DOM。patch 函数会根据新旧 vnode 之间的差异,对 DOM 进行相应的更新。

const vm = new Vue({
  data: {
    name: 'John',
    age: 20
  },
  template: `
    <div>
      <p>{{ name }}</p>
      <p>{{ age }}</p>
    </div>
  `
})

vm.name = 'Mary'

patch(vm._vnode, vm._vnode)

Vue.nextTick 是如何工作的

Vue.nextTick 是 Vue 中一个非常重要的函数,它可以让我们在 Vue 实例更新完成之后执行某些操作。

Vue.nextTick 的工作原理是将一个回调函数推入到一个队列中,然后在下次 DOM 更新循环结束之后执行这个队列中的所有回调函数。

const vm = new Vue({
  data: {
    name: 'John',
    age: 20
  },
  template: `
    <div>
      <p>{{ name }}</p>
      <p>{{ age }}</p>
    </div>
  `
})

vm.name = 'Mary'

Vue.nextTick(() => {
  console.log(vm.name) // 'Mary'
})