vue2源码-- 挂载阶段
Posted 在厕所喝茶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue2源码-- 挂载阶段相关的知识,希望对你有一定的参考价值。
目录
原理分析
1、$mounted
函数实际上调用的是mountComponent
函数,该函数首先判断是否存在render
函数,如果不存在,则给一个默认的渲染函数,该渲染函数会创建一个注释类型的VNode
节点,然后给出警告。
2、触发beforeMount
声明周期函数
3、定义updateComponent
函数,该函数内部首先执行render
渲染函数,得到一份最新的VNode
节点树,然后执行_update
方法对最新的Vnode
节点和上一次的VNode
节点进行对比,并更新DOM
元素(即patch
操作),完成一次渲染。这样子完成了挂载操作的一半工作。
4、另一半工作是开启对模板中的数据监控,当数据发生变化时,通知期其依赖进行视图更新。首先创建一个watcher
实例,并将定义好的updateComponent
函数作为第二个参数传入,watcher 构造函数的第二个参数支持 2 种类型,一种是数据路径,另外一种是函数。如果是数据路径,就会根据路径去读取这个数据,如果是函数,就会执行函数,一旦读取了数据或者执行了函数,就会触发数据的getter
方法,getter
方法会将watcher
实例添加到改数据的依赖列表中,,当数据发生变化时,就会通知依赖列表进行更新,当依赖接收到通知后,就会调用第四个参数回调函数去更新视图。
换句话来说,updateComponent
函数所使用到的数据都会被watcher
监控,只要这些数据发生了变化,那么watcher
都将会得到通知,从而掉用第四个参数回调函数去更新视图
源码
// hydrating是跟服务端渲染相关的
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component
el = el && inBrowser ? query(el) : undefined;
return mountComponent(this, el, hydrating);
;
// $mount实际调用的是这个函数
export function mountComponent(
vm: Component,
el: ?Element,
hydrating?: boolean
): Component
vm.$el = el;
// $mount最终需要的是render函数
if (!vm.$options.render)
// 没有渲染函数的情况下会默认给一个渲染函数
vm.$options.render = createEmptyVNode;
// ...
// 触发beforeMount钩子函数
callHook(vm, "beforeMount");
let updateComponent;
if (process.env.NODE_ENV !== "production" && config.performance && mark)
updateComponent = () =>
// ...
const vnode = vm._render();
vm._update(vnode, hydrating);
;
else
updateComponent = () =>
vm._update(vm._render(), hydrating);
;
// Watcher2个作用,一是初始化的时候执行回调函数,二是当vm实例中检测的数据发生变化就执行回调函数
// 渲染watcher
// 如果是渲染watcher,在初始化的时候会把vm._watcher赋值为watcher的实例
new Watcher(
vm,
updateComponent,
noop,
before()
if (vm._isMounted && !vm._isDestroyed)
// 如果数据发生了变化,并且组件在渲染状态并且没有销毁,触发beforeUpdate生命周期函数
callHook(vm, "beforeUpdate");
,
,
true /* isRenderWatcher */
);
hydrating = false;
// vm.$vnode:Vue 实例的父虚拟 Node,为null表示为根vue实例
if (vm.$vnode == null)
// 根实例
vm._isMounted = true; // 标识已经挂载
callHook(vm, "mounted");
return vm;
// 把vnode渲染成真实的dom
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean)
const vm: Component = this;
const prevEl = vm.$el;
const prevVnode = vm._vnode;
const restoreActiveInstance = setActiveInstance(vm);
vm._vnode = vnode;
// Vue.prototype.__patch__ is injected in entry points
// based on the rendering backend used.
if (!prevVnode)
// 没有旧prevVnode就是第一次渲染
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);
else
// 更新
vm.$el = vm.__patch__(prevVnode, vnode);
restoreActiveInstance();
// update __vue__ reference
if (prevEl)
prevEl.__vue__ = null;
if (vm.$el)
vm.$el.__vue__ = vm;
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode)
vm.$parent.$el = vm.$el;
;
Vue源码学习- Hook Event
Hook Event (钩子事件),也就是生命周期钩子函数,供开发者在特定的逻辑点添加额外的处理逻辑,比如:在组件挂载阶段提供了beforeMount和mounted两个生命周期钩子,供开发者在组件挂载阶段执行额外的逻辑处理,比如为组件来准备渲染所需的数据。
callHook
/src/core/instance/lifecycle.js
/**
* callHook(vm,'mounted')
* 执行实例指定的生命周期钩子函数
* 如果实例设置对应Hook Event ,比如:<comp @hook:mounted="method"/>,执行完生命周期函数之后,触发该事件的执行
*/
export function callHook(vm:Component, hook:string)
//在执行生命周期钩子函数期间禁止收集依赖
pushTarget()
//从实例配置对象中获取指定钩子函数,比如mounted
const hnalders = vm.$options[hook]
//mounted hook
const info = `$hook hook`
if(handlers)
//通过invokewithErrorHandler 执行生命周期钩子
for(let i = 0,j=handlers.length;i<j;i++)
invokeWithErrorHandling(hanlders[i],vm,null,vm,info)
// HookEvent ,如果设置了Hook Event ,比如<comp @hook:mounted = 'method"/>则通过$emit触发该事件
//vm._hasHookEvent 标识组件是否有hook event,这是在vm.$on中处理组件自定义事件时设置的
if(vm._hasHookEvent)
vm.$emit('hook:'+hook)
//关闭依赖收集
popTarget()
invokeWithErrorHanding
/src/core/util/error.js
/**
* 通用函数,执行指定函数handler
* 传递进来的函数会调用try catch包裹,进行异常捕获处理
*/
export function invokeWithErrorHandling(
handler: Function,
context: any,
args: null | any[],
vm: any,
info: string
)
let res
try
res = args ? handler.apply(context,args) : handler.call(context)
if(res && !res._isVue && isPromise(res) && !res._handled)
res.catch(e => handleError(e,vm,info+'(Promise/async)'))
res._handled = true
catch(e)
handleError(e,vm,info)
return res
- Hook Event 时如何实现的?
<comp @hook:lifecycleMethod='method'/>
- 处理组件自定义事件的时候(vm.$on)如果发现组件有hook:xx格式的事件(xx为Vue的生命周期函数),则将vm._hasHookEvent置为true,表示该组件有Hook Event
- 在组件生命周期方法被触发的时候,内部会通过callHook方法来执行这些生命周期函数,在生命周期函数执行之后,如果发现vm._hasHookEvent为true,则表示当前组件有Hook Event ,通过vm.$emit(‘hook:xx’)触发Hook Event的执行。
这就是Hook Event的实现原理;
以上是关于vue2源码-- 挂载阶段的主要内容,如果未能解决你的问题,请参考以下文章