1.vue基本生命周期
vue源码中最终执行生命周期函数都是调用callHook
方法,callHook
函数的逻辑很简单,根据传入的生命周期类型 hook
,去拿到 vm.$options[hook]
对应的回调函数数组,然后遍历执行,执行的时候把 vm
作为函数执行的上下文。
1. new Vue(options)
:创建一个vm实例;
2. mergeOptions(resolveConstructorOptions(vm.constructor), options, vm)
:合并Vue构造函数里options和传入的options或合并父子的options。比如:在mergeOptions函数中会调用mergeHook方法合并生命周期的钩子函数,mergeHook方法原理是只有父时返回父,只有子时返回数组类型的子。父、子都存在时,将子添加在父的后面返回组合而成的数组。这也是父子均有钩子函数的时候,先执行父的后执行子的的原因;
3. initLifecycle(vm)、initEvents(vm)、initRender(vm)
:在创建的vm实例上初始化生命周期、事件、渲染相关的属性;
4. callHook(vm, 'beforeCreate')
:调用beforeCreate生命周期钩子函数;
5. initInjections(vm)、initState(vm)、initProvide(vm)
:初始化数据:inject、state、provide。initState 的作用是初始化 props、data、methods、watch、computed 等属性;
6. callHook(vm, 'created')
:调用created生命周期钩子函数;
7. vm.$mount(vm.$options.el)
:$mount
方法在多个文件中都有定义,如"src/platform/web/entry-runtime-with-compiler.js"、"src/platform/web/runtime/index.js"、"src/platform/weex/runtime/index.js"。因为$mount
方法的实现是和平台、构建方式相关的。以"entry-runtime-with-compiler.js"为例,关键步骤是查看vm.$options
中是否有render方法,如果没有则会根据el和template属性确定最终的template字符串,再调用compileToFunctions
方法将template字符串转为render方法,最后,调用原先原型上的$mount方法,即开始执行"lifecycle.js"中mountComponent
方法;
8. callHook(vm, 'beforeMount')
:调用beforeMount生命周期钩子函数;
9. vm._render()
=> vm._update()
=> vm.__patch__()
:先执行vm._render方法,即调用createElement生成虚拟DOM,即VNode ,每个VNode有children ,children 每个元素也是⼀个 VNode,这样就形成了⼀个 VNode Tree;再调用vm._update方法进行首次渲染,vm._update方法核心是调用vm.patch方法,这个方法跟vm.$mount一样跟平台相关;vm.patch方法则是根据生成的VNode Tree递归createElm方法创建真实Dom Tree挂载到Dom上;
10. callHook(vm, 'mount')
:调用mount生命周期钩子函数:VNode patch 到 Dom 之后会执行 'invokeInsertHook'函数,把insertedVnodeQueue
中保存的mount钩子函数执行一遍,insertedVnodeQueue队列中的钩子函数是在根据VNode Tree递归createElm方法创建真实Dom Tree过程生成的钩子函数顺序队列,因此mounted钩子函数的执行顺序是先子后父;
11. data changes
:数据更新,nextTick中执行flushSchedulerQueue
方法,该方法会执行watcher队列中的watcher;
12. callHook(vm, 'beforeUpdate')
:执行watcher时会执行watcher的before方法,即调用beforeUpdate生命周期钩子函数;
13. Virtual DOM re-render and patch
:重新render生成新的Virtual DOM,并且patch到DOM上;
14. callHook(vm, 'updated')
:调用updated生命周期钩子函数;
15. vm.$destroy()
:启动卸销毁过程;
16. callHook(vm, 'beforeDestroy')
:调用beforeDestroy生命周期钩子函数;
17. Teardown watchers, childcomponents and event listeners
:执行一系列销毁动作,在 $destroy 的执行过程中,它又会执行vm.__patch__(vm._vnode, null)
触发它子组件的销毁钩子函数,这样一层层的递归调用,所以 destroyed 钩子函数执行顺序是先子后父,和 mounted 过程一样。
18. callHook(vm, 'destroyed ')
:调用destroyed 生命周期钩子函数。
2. errorCaptured
生命周期钩子函数
当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
如果一个组件的继承或父级从属链路中存在多个 errorCaptured 钩子,则它们将会被相同的错误逐个唤起。
默认情况下,如果全局的 config.errorHandler 被定义,所有的错误仍会发送它,因此这些错误仍然会向单一的分析服务的地方进行汇报。
3. keep-alive组件
keep-alive组件是vue的内置抽象(abstract)组件,抽象组件在initLifecycle过程中 组件实例建立父子关系时会被忽略,因此他自身不会渲染一个DOM元素,也不会出现在父组件链中。
keep-alive作用是用于包裹动态组件,缓存不活动的组件实例。keep-alive自定义实现了render函数并利用了插槽slot,render函数中先获取它的默认插槽,再获取到它的第一个组件子节点,因此keep-alive组件只处理第一个子元素,所以一般和它搭配使用的有component动态组件或者router-view。
keep-alive组件在created钩子中定义了 this.cache 和 this.keys,本质上是去缓存已创建的 vnode,缓存策略采用LRU策略,每次缓存命中时将当前vnode移到缓存数组末尾,需要删除时则删除缓存数组第一个vnode。
-
keep-alive组件接收三个props:
1.
include
:数组、字符串或者正则表达式,只有匹配的组件才会缓存。2.
exclude
:数组、字符串或者正则表达式,任何匹配的组件都不会被缓存。3.
max
:字符串或数字,指定可以缓存的组件最大个数,如果个数超过max,则销毁缓存数组中的第一个组件。 -
keep-alive组件子组件渲染机制:
1. 首次渲染:和普通组件一样执行正常的init生命周期钩子函数,同时将生成的vnode缓存到内存中;
2. 组件切换:切换到新组件时,旧组件不会销毁,而是变成未激活状态,即不会执行destroy相关的钩子函数,而是执行
deactivated
生命周期钩子函数,如果新组件不在缓存数组中,则执行首次渲染,否则执行缓存渲染;3. 缓存渲染:缓存渲染即组件由未激活状态变成激活状态,因此不会执行created、mounted等钩子函数,而是执行
activated
生命周期钩子函数。