Vue 源码
你想要的 vue 源码分析 · 11
mounted 为什么能够访问到 dom 如果是组件,是在 vnode patch 的 insert 钩子函数中执行,insert 是先子后父,所以此时的 mounted 执行实际也是先子后父 如果不是组件,vnode patch 了,最终成了真正的 dom,才会执行 两者的相同点是 vm._render()之后,vm._render()已经把 vnode 渲染成真实的 dom 了 生命周期的定义…
mounted 为什么能够访问到 dom 如果是组件,是在 vnode patch 的 insert 钩子函数中执行,insert 是先子后父,所以此时的 mounted 执行实际也是先子后父 如果不是组件,vnode patch 了,最终成了真正的 dom,才会执行 两者的相同点是 vm._render()之后,vm._render()已经把 vnode 渲染成真实的 dom 了 生命周期的定义…

如果是组件,是在 vnode patch 的 insert 钩子函数中执行,insert 是先子后父,所以此时的 mounted 执行实际也是先子后父
如果不是组件,vnode patch 了,最终成了真正的 dom,才会执行
两者的相同点是 vm._render()之后,vm._render()已经把 vnode 渲染成真实的 dom 了
每一个 Vue 实例,在创建前都要进行数据监听,编译模板,挂载实例到 DOM,监听数据变化后更新 DOM 等过程,此过程中,Vue 会在不同时期暴露不同的 API,用于开发者在想要的时期添加自己的代码
1.lifecycle// src/core/instance/lifecycle
// 根据传入的字符串hook,去拿到vm.$options[hook]对应的回调函数数组,遍历数组的同时把vm作为上下文
// 在vue合并的过程中,会把生命周期函数也合并到vm.$options中
// callHook的功能就是调用某个生命周期注册的所有钩子函数
export function callHook(vm: Component, hook: string) {
pushTarget();
const handlers = vm.$options[hook];
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
try {
handlers[i].call(vm);
} catch (e) {
handleError(e, vm, `${hook} hook`);
}
}
}
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook);
}
popTarget();
}
2.beforeCreate & created// src/core/instance/init.js
// beforeCreate & created都是在实例化Vue阶段执行的
// foreCreate不能获取到props,data,methods,watch,computed这些字段
// 而create可以获取到
Vue.prototype._init = function(options?: Object) {
// ...
initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook(vm, 'beforeCreate');
initInjections(vm); // resolve injections before data/props
// initState是初始化props,data,methods,watch,computed等属性
// 所以beforeCreate不能获取到这些字段,created可以获取到
initState(vm);
initProvide(vm); // resolve provide after data/props
callHook(vm, 'created');
// ...
};
3.beforeMount & mounted// src/core/instance/lifecycle.js
// beforeMount在挂载DOM之前执行的
// mounted根据是组件,如果是组件,是在vnode patch的 insert钩子函数中执行,insert是先子后父,所以此时的mounted也是先子后父
// mounted不是组件,vnode patch了,最终成了真正的dom,才会执行
export function mountComponent(vm: Component, el: ?Element, hydrating?: boolean): Component {
vm.$el = el;
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode;
if (process.env.NODE_ENV !== 'production') {
/* istanbul ignore if */
if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') || vm.$options.el || el) {
warn('You are using the runtime-only build of Vue where the template ' + 'compiler is not available. Either pre-compile the templates into ' + 'render functions, or use the compiler-included build.', vm);
} else {
warn('Failed to mount component: template or render function not defined.', vm);
}
}
}
// 在执行 vm._render() 函数渲染 VNode 之前,执行了beforeMount钩子函数
callHook(vm, 'beforeMount');
let updateComponent;
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
updateComponent = () => {
const name = vm._name;
const id = vm._uid;
const startTag = `vue-perf-start:${id}`;
const endTag = `vue-perf-end:${id}`;
mark(startTag);
const vnode = vm._render();
mark(endTag);
measure(`vue ${name} render`, startTag, endTag);
mark(startTag);
vm._update(vnode, hydrating);
mark(endTag);
measure(`vue ${name} patch`, startTag, endTag);
};
} else {
updateComponent = () => {
vm._update(vm._render(), hydrating);
};
}
// 真正的组件mounted是在vnode patch过程中的insert钩子函数中
// insert的过程是先子后父
// 所以组件mounted也是先子后父
new Watcher(
vm,
updateComponent,
noop,
{
before() {
if (vm._isMounted) {
callHook(vm, 'beforeUpdate');
}
}
},
true /* isRenderWatcher */
);
hydrating = false;
// 执行完vm._update()把Vnode patch成真实的dom后,执行mounted钩子
// 如果vm.$vnode为null,表示不是一个组件初始化的过程,是一个外部使用new Vue的过程
// 以下过程是外部new Vue初始化的mounted
if (vm.$vnode == null) {
vm._isMounted = true;
callHook(vm, 'mounted');
}
return vm;
}
4.beforeUpdate & updated// src/core/instance/lifecycle.js
// beforeUpdate是在Watcher的before函数中,而且必须是组件已经mounted之后,才能调用beforeUpdate钩子函数
// 数据更新就涉及Vue的双向绑定
export function mountComponent(vm: Component, el: ?Element, hydrating?: boolean): Component {
// ...
new Watcher(
vm,
updateComponent,
noop,
{
before() {
if (vm._isMounted) {
// 可以看出,beforeUpdate是在Watcher的before函数中
// 而且必须是组件已经mounted之后,才能调用beforeUpdate钩子函数
callHook(vm, 'beforeUpdate');
}
}
},
true /* isRenderWatcher */
);
// ...
}
// src/core/observer/scheduler.js
// update是在更新了watcher数组后执行,必须满足当前watcher为vm._watcher 并且组件已经mounted
// mounted时会实例渲染一个watcher去监听vm的数据变化,如果数据变了,就会重新渲染
// 在mounted 实例化watcher的时候,isRenderWatcher为true,把watcher实例赋给vm,_watcher
// 在mounted实例化watcher的时候,isRenderWatcher为true,也会把watcher push 到vm._watchers
// vm._watcher是为了监听vm上的数据变化,是一个渲染相关的watcher,是一个数组
function flushSchedulerQueue() {
// ...
// 获取到 updatedQueue
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) {
callHook(vm, 'updated');
}
}
}
// 这个就是isRenderWatcher为true的操作
export default class Watcher {
// ...
constructor(vm: Component, expOrFn: string | Function, cb: Function, options?: ?Object, isRenderWatcher?: boolean) {
this.vm = vm;
if (isRenderWatcher) {
// 把watcher实例赋给vm,_watcher
vm._watcher = this;
}
// push到vm._watchers
vm._watchers.push(this);
// ...
}
}
5.beforeDestroy & destroyed// src/core/instance/lifecycle.js
// 销毁动作有:从parent的$children删除本身,删除watcher,当前渲染的vnode执行销毁钩子函数
// beforeDestroy执行在组件执行销毁动作之前
// destroyed,销毁动作执行完,执行顺序先子后父
Vue.prototype.$destroy = function() {
const vm: Component = this;
if (vm._isBeingDestroyed) {
return;
}
callHook(vm, 'beforeDestroy');
vm._isBeingDestroyed = true;
// remove self from parent
const parent = vm.$parent;
if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
remove(parent.$children, vm);
}
// teardown watchers
if (vm._watcher) {
vm._watcher.teardown();
}
let i = vm._watchers.length;
while (i--) {
vm._watchers[i].teardown();
}
// remove reference from data ob
// frozen object may not have observer.
if (vm._data.__ob__) {
vm._data.__ob__.vmCount--;
}
// call the last hook...
vm._isDestroyed = true;
// 触发它子组件的销毁钩子函数
// 所以destroyed也是先子后父
vm.__patch__(vm._vnode, null);
// fire destroyed hook
callHook(vm, 'destroyed');
// turn off all instance listeners.
vm.$off();
// remove __vue__ reference
if (vm.$el) {
vm.$el.__vue__ = null;
}
// release circular reference (#6759)
if (vm.$vnode) {
vm.$vnode.parent = null;
}
};
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论