Vue 源码
你想要的 vue 源码分析 · 13
为了减少首屏加载时间,按需加载组件 源码 异步组件的公共方法 once 封装 ensureCtor $forceUpdate createAsyncPlaceholder 总结 异步组件的本质是两次渲染,第一次先渲染成占位符(注释节点),等待异步组件获取成功后,再通过 forceRender 强制重新渲染,生成异步加载的组件 0 delay 的高级异步组件第一次直接渲染成 loading 组件
为了减少首屏加载时间,按需加载组件 源码 异步组件的公共方法 once 封装 ensureCtor $forceUpdate createAsyncPlaceholder 总结 异步组件的本质是两次渲染,第一次先渲染成占位符(注释节点),等待异步组件获取成功后,再通过 forceRender 强制重新渲染,生成异步加载的组件 0 delay 的高级异步组件第一次直接渲染成 loading 组件
为了减少首屏加载时间,按需加载组件
// 组件的定义不是一个普通对象,所以不会执行Vue.extend的逻辑把它变成一个组件的构造函数
// 但仍然可以执行createComponent函数
// 3种异步组件创建方式
// 方式1 工厂函数形式
Vue.component('async-example', function(resolve, reject) {
require(['./my-async-component'], function(res) {
// res就是组件定义
// 这时候执行resolveAsyncComponent的resolve
resolve(res);
});
});
// 方式2 promise形式
Vue.component(
'async-webpack-example',
// 该 `import` 函数返回一个 `Promise` 对象。
() => import('./my-async-component')
);
// 方式3 高级异步形式
const AsyncComp = () => ({
// 需要加载的组件。应当是一个 Promise
component: import('./MyComp.vue'),
// 加载中应当渲染的组件
loading: '<div>loading</div>',
// 出错时渲染的组件
error: '<div>error</div>',
// 渲染加载中组件前的等待时间。默认:200ms。
delay: 200,
// 最长等待时间。超出此时间则渲染错误组件。默认:Infinity
timeout: 3000
});
Vue.component('async-example', AsyncComp);
// src/core/vdom/create-component/js
// 这时候的Ctor是一个函数,所以Ctor.cid是undefined
export function createComponent(Ctor: Class<Component> | Function | Object | void, data: ?VNodeData, context: Component, children: ?Array<VNode>, tag?: string): VNode | Array<VNode> | void {
if (isUndef(Ctor)) {
return;
}
const baseCtor = context.$options._base;
// 当前Ctor不是对象
if (isObject(Ctor)) {
Ctor = baseCtor.extend(Ctor);
}
// ...
// Ctor.cid是undefined
let asyncFactory;
if (isUndef(Ctor.cid)) {
asyncFactory = Ctor;
// 处理3种异步组件的创建方式(文章一开头写了)
Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context);
if (Ctor === undefined) {
// 异步组件 patch
// 建一个注释节点作为占位符
// 然后执行回调函数的resolve
return createAsyncPlaceholder(asyncFactory, data, context, children, tag);
}
}
}
// src/core/vdom/helpers/resolve-async-component.js
// 处理了3种异步组件的创建方式(文章一开头写了)
export function resolveAsyncComponent(factory: Function, baseCtor: Class<Component>, context: Component): Class<Component> | void {
// 这个时候,返回factory.errorComp,直接渲染error组件
if (isTrue(factory.error) && isDef(factory.errorComp)) {
return factory.errorComp;
}
// 这个时候,返回factory.resolved,直接渲染加载成功组件
if (isDef(factory.resolved)) {
return factory.resolved;
}
// 如果异步组件加载中并未返回,渲染loading组件
if (isTrue(factory.loading) && isDef(factory.loadingComp)) {
return factory.loadingComp;
}
// 对factory.contexts进行判断,可能多个地方同时初始化一个异步组件
// 实际只加载一次,所以走到了else
if (isDef(factory.contexts)) {
// already pending
factory.contexts.push(context);
} else {
const contexts = (factory.contexts = [context]);
let sync = true;
// 定义forceRender
const forceRender = () => {
for (let i = 0, l = contexts.length; i < l; i++) {
// $forceUpdate调用渲染 watcher 的 update 方法,让渲染watcher对应的回调函数执行,触发组件的重新渲染
// 强制渲染后,context就不是undefined了,那么就会走render和patch的过程,新老node进行响应式渲染
contexts[i].$forceUpdate();
}
};
// 定义resolve
// 用once进行封装,保证只执行一次
const resolve = once((res: Object | Class<Component>) => {
// 为了找到异步组件js定义的组件对象,如果是一个普通对象,则调用Vue.extend转换成一个组件的构造函数
// 异步组件加载成功这个就是true,这是高级异步组件的操作,进行强制渲染,又一次执行resolveAsyncComponent
factory.resolved = ensureCtor(res, baseCtor);
// ssr的sync为true
// 当下为false
if (!sync) {
// forceRender 会遍历factory.contexts,拿到每一个调用异步组件的实例vm,执行vm.$forceUpdate()
// $forceUpdate调用渲染 watcher 的 update 方法,让渲染watcher对应的回调函数执行,触发组件的重新渲染
forceRender();
}
});
// 定义reject
// 当异步组件加载失败,执行reject函数
const reject = once((reason) => {
process.env.NODE_ENV !== 'production' && warn(`Failed to resolve async component: ${String(factory)}` + (reason ? `\nReason: ${reason}` : ''));
if (isDef(factory.errorComp)) {
// 会把 factory.error 设置为 true,这是高级异步组件的操作,进行强制渲染,又一次执行resolveAsyncComponent
factory.error = true;
forceRender();
}
});
// 执行组件的定义
// 工厂方法,res的返回值就是require(['./my-async-component'], resolve);
// promise方法 res的返回值就是 import('./my-async-component') 的返回值
// 高级异步组件 res的返回值就是AsyncComp,是一个对象,不是promise,对象里面的components是一个promise
const res = factory(resolve, reject);
if (isObject(res)) {
if (typeof res.then === 'function') {
// promise异步组件走的位置
if (isUndef(factory.resolved)) {
// 当组件异步加载成功后,执行release,失败则执行reject
res.then(resolve, reject);
}
} else if (isDef(res.component) && typeof res.component.then === 'function') {
res.component.then(resolve, reject);
// 有没有定义error组件
if (isDef(res.error)) {
// 有就赋给factory.errorComp
// 找到异步组件js定义的组件对象,如果是一个普通对象,则调用Vue.extend转换成一个组件的构造函数
factory.errorComp = ensureCtor(res.error, baseCtor);
}
// 是否定义了loading组件
if (isDef(res.loading)) {
// 有就赋给factory.loadingComp
// 找到异步组件js定义的组件对象,如果是一个普通对象,则调用Vue.extend转换成一个组件的构造函数
factory.loadingComp = ensureCtor(res.loading, baseCtor);
// 延迟为0
if (res.delay === 0) {
factory.loading = true;
} else {
// 延迟delay执行
setTimeout(() => {
if (isUndef(factory.resolved) && isUndef(factory.error)) {
factory.loading = true;
forceRender();
}
}, res.delay || 200);
}
}
// res.timeout是否存在
if (isDef(res.timeout)) {
// 在res.timeout时间后,如果组件没有成功加载,就执行reject
setTimeout(() => {
if (isUndef(factory.resolved)) {
reject(process.env.NODE_ENV !== 'production' ? `timeout (${res.timeout}ms)` : null);
}
}, res.timeout);
}
}
}
sync = false;
// 如果delay为0,直接渲染loading,否则延迟delay执行forceRender,强制组件渲染,那么又一次执行resolveAsyncComponent
return factory.loading ? factory.loadingComp : factory.resolved;
}
}
// src/shared/util.js
// 传入一个函数,返回一个新函数
// 利用闭包和标志位,保证了它包装的函数只执行一次
// 确保resolve和reject只执行一次
export function once(fn: Function): Function {
let called = false;
return function() {
if (!called) {
called = true;
fn.apply(this, arguments);
}
};
}
// src/core/vdom/helpers/resolve-async-component.js
// 为了找到异步组件js定义的组件对象,如果是一个普通对象,则调用Vue.extend转换成一个组件的构造函数
function ensureCtor(comp: any, base) {
if (comp.__esModule || (hasSymbol && comp[Symbol.toStringTag] === 'Module')) {
comp = comp.default;
}
return isObject(comp) ? base.extend(comp) : comp;
}
// src / core / instance / lifecycle.js;
// $forceUpdate调用渲染 watcher 的 update 方法,让渲染watcher对应的回调函数执行,触发组件的重新渲染
// 因为Vue是数据驱动视图重新渲染,但是整个异步组件加载过程中没有数据变化的,所以通过$forceUpdate重新渲染一次
Vue.prototype.$forceUpdate = function() {
const vm: Component = this;
if (vm._watcher) {
vm._watcher.update();
}
};
// src/core/vdom/helpers/resolve-async-components.js
// 创建一个注释vnode,同时把 asyncFactory 和 asyncMeta 赋值给当前 vnode
export function createAsyncPlaceholder(factory: Function, data: ?VNodeData, context: Component, children: ?Array<VNode>, tag: ?string): VNode {
const node = createEmptyVNode();
node.asyncFactory = factory;
node.asyncMeta = { data, context, children, tag };
return node;
}
异步组件的本质是两次渲染,第一次先渲染成占位符(注释节点),等待异步组件获取成功后,再通过 forceRender 强制重新渲染,生成异步加载的组件
0 delay 的高级异步组件第一次直接渲染成 loading 组件
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论