亮神知识库 亮神知识库
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)

亮神

前程明亮,未来可期
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)
  • Vue2

    • 响应式

    • 模版编译

    • 虚拟DOM

    • 整体流程

      • 生命周期
        • 说说对Vue生命周期的理解?
          • provide/inject
        • 渲染过程
        • beforeUpdate和updated
        • $destroy和teardown
        • 父组件和子组件的执行顺序?
      • 插件注册
      • 过滤器
    • vuex&vue-router

  • Vue3

  • Vue原理
  • Vue2
  • 整体流程
0zcl
2021-10-18
目录

生命周期

# 说说对Vue生命周期的理解?

Vue.prototype._init = function() {
  // ...
  vm._self = vm
  initLifecycle(vm)
  initEvents(vm)
  initRender(vm)
  callHook(vm, 'beforeCreate')
  initInjections(vm) // resolve injections before data/props
  initState(vm)
  initProvide(vm) // resolve provide after data/props
  callHook(vm, 'created')
  // ...
  if (vm.$options.el) {
    vm.$mount(vm.$options.el)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  1. new Vue()。实例化Vue对象,实例化Vue对象时,会执行Vue构造函数的原型的_init方法,生命周期核心流程封装在这个方法中。
  2. 在执行beforeCreate钩子之前做三件事
  • initLifecycle初始化生命周期,会把vm._watcher=null, vm.$refs={}等做初始化;
  • initEvents事件初始化,会把vm._events初始化为空对象Object.create(null);
  • initRender渲染初始化,会给vm组件实例绑定vm.$createElement函数,用来返回Vnode,也会给vm添加$attrs和$listeners(二次封装组件时,可以直接继承属性和方法)
  1. 执行完beforeCreate钩子后做了两件事
  • initInjections。把inject注入的数据转成result对象,再遍历result对象给vm添加响应式属性
  • initState。做props, methods, data等初始化工作。把data中的数据做响应式
  1. 执行created钩子。执行完create钩子后,最主要的处理是拿到render函数
  • 如果实例化Vue时没有传render函数,就得把tempalte转成render函数。会经历模版编译的过程:模牍转AST树 --> 标记静态节点 --> 生成render函数
  1. 执行beforeMount钩子。执行beforeMount钩子后,实例化渲染watcher,实例化watcher会执行回调,回调又执行_patch。第一次执行_patch, 会通过render生成真实的dom结点,插入到document,再把旧的$el结点删除
  2. 实例化渲染watcher后,把vm._isMounted=true并执行mounted钩子
  3. 在mounted到beforeDestory之间,如果响应式数据有变化,会用queue队列收集watcher,在nextTick循环执行watcher.before和watcher.run。渲染watcher会执行watcher.before, 渲染watcher.before中,会执行 <code>beforeUpdate </code>钩子; 渲染watcher.run会执行_patch,做re-render更新。遍历queue队列,如果找到watcher是渲染watcher并且vm._isMounted=true,则执行updated钩子
  4. 当vm.$destroy()执行时(比如keep-alive超过缓存数限制,LRU策略会将第一个删除,删除实际上就是执行vm.$destroy),会先执行beforeDestroy钩子。
  • 在beforeDestroy到destroyed之间,会遍历vm._watcher数组,每个watcher都执行teardown。teardown是把watcher从vm.watcher数组中移出,并查找所有的dep队列,把dep队列中的watcher移出
  1. 最后执行destroyed钩子,再执行vm.$off(),把vm对象的监听事件移除

# provide/inject

provide/inject (opens new window)

export function initInjections (vm: Component) {
  // 把inject注入的数据转成result对象
  const result = resolveInject(vm.$options.inject, vm)
  if (result) {
    toggleObserving(false)
    Object.keys(result).forEach(key => {
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production') {
        // 给vm添加响应式属性
        defineReactive(vm, key, result[key], () => {
          warn(
            `Avoid mutating an injected value directly since the changes will be ` +
            `overwritten whenever the provided component re-renders. ` +
            `injection being mutated: "${key}"`,
            vm
          )
        })
      } else {
        defineReactive(vm, key, result[key])
      }
    })
    toggleObserving(true)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 渲染过程

<strong>下面的代码非常核心!!</strong>

拿到render函数,再实例化渲染watcher(vm._watcher就是组件的渲染watcher)。渲染watcher的回调是_patch。具体分析见_patch (opens new window), 第一次patch,通过render生成真实的dom结点,插入到document,再把旧的$el结点删除

// src/platforms/web/runtime/index.js
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

//  src/platforms/web/entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)
  /* istanbul ignore if */
  if (el === document.body || el === document.documentElement) {
    process.env.NODE_ENV !== 'production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }

  const options = this.$options
  // resolve template/el and convert to render function
  if (!options.render) {
    let template = options.template
    if (template) {
      // ...
    } else if (el) {
      template = getOuterHTML(el)
    }
    if (template) {
      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns
    }
  }
  return mount.call(this, el, hydrating)
}

// src/core/instance/lifecycle.js 
export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  if (!vm.$options.render) {
    vm.$options.render = createEmptyVNode
    // ...
  }
  callHook(vm, 'beforeMount')

  let updateComponent
  // ...
  updateComponent = () => {
    vm._update(vm._render(), hydrating)
  }


  // 每个vm组件都有一个渲染watcher。实例化watcher时,会执行updateComponent
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
  hydrating = false

  // manually mounted instance, call mounted on self
  // mounted is called for render-created child components in its inserted hook
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  return vm
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

# beforeUpdate和updated

在执行watcher时,如果存在watcher.before,会先执行watcher.before,渲染watcher.before中,会执行beforeUpdate钩子

function flushSchedulerQueue () {
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    if (watcher.before) {
      watcher.before()
    }
    watcher.run()
  }
  const updatedQueue = queue.slice()
  callUpdatedHooks(updatedQueue)
}

function callUpdatedHooks (queue) {
  let i = queue.length
  while (i--) {
    const watcher = queue[i]
    const vm = watcher.vm
    if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {
      callHook(vm, 'updated')
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# $destroy和teardown

$destroy会遍历vm._watcher数组,每个watcher都执行teardown。teardown是把watcher从vm.watcher数组中移出,并查找所有的dep队列,把dep队列中的watcher移出

// src/core/instance/lifecycle.js
Vue.prototype.$destroy = function () {
  const vm: Component = this
  callHook(vm, 'beforeDestroy')
  vm._isBeingDestroyed = true
  // teardown watchers
  if (vm._watcher) {
    vm._watcher.teardown()
  }
  let i = vm._watchers.length
  while (i--) {
    vm._watchers[i].teardown()
  }
  // call the last hook...
  vm._isDestroyed = true
  // fire destroyed hook
  callHook(vm, 'destroyed')
  // turn off all instance listeners.
  vm.$off()
}

// src/core/observer/watcher.js
teardown () {
  if (this.active) {
    // remove self from vm's watcher list
    // this is a somewhat expensive operation so we skip it
    // if the vm is being destroyed.
    if (!this.vm._isBeingDestroyed) {
      remove(this.vm._watchers, this)
    }
    let i = this.deps.length
    while (i--) {
      this.deps[i].removeSub(this)
    }
    this.active = false
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 父组件和子组件的执行顺序?

父beforeCreate-> 父created -> 父beforeMounte -> 子beforeCreate ->子create ->子beforeMount ->子 mounted -> 父mounted

子组件更新:父beforeUpdate->子beforeUpdate->子updated->父updated

父组件更新过程:父beforeUpdate->父updated

销毁:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

编辑 (opens new window)
上次更新: 2025/07/20, 08:30:18
SSR
插件注册

← SSR 插件注册→

最近更新
01
2024年
07-20
02
2023年
07-20
03
2022年
07-20
更多文章>
Theme by Vdoing | Copyright © 2025-2025 亮神 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式