首先安利一个非常好用的小站,清晰地梳理了Vue3源码的一些核心模块,结合ChatGPT插件的代码解释功能一起阅读,无比丝滑!下面就记录一下AI帮我分析的源码相关内容。
再次感谢原作者,本篇对应的原文章链接: CreateApp
目录
rendererOptions和ensureRenderer实现
关于queueJob及queuePostFlushCb的调用时机
createApp
概览
createApp函数是Vue的启动函数,用于返回一个Vue应用程序实例。这部分的文章讨论了CreateApp函数的实现细节,并解释了其中的关键部分,如ensureRenderer和baseCreateRenderer。ensureRenderer用于创建渲染器,而baseCreateRenderer用于实现vnode、diff和patch,最终返回一个对象,其中包含处理DOM节点操作和其他相关内容的方法。createApp方法用于创建一个Vue应用程序实例,涉及到了createAppContext、baseCreateRenderer和createAppAPI等方法的实现。
createApp实现
export const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args)
if (__DEV__) {
injectNativeTagCheck(app)
}
const { mount } = app
app.mount = (containerOrSelector: Element | string): any => {
const container = normalizeContainer(containerOrSelector)
if (!container) return
const component = app._component
if (!isFunction(component) && !component.render && !component.template) {
component.template = container.innerHTML
}
// clear content before mounting
container.innerHTML = ''
const proxy = mount(container)
container.removeAttribute('v-cloak')
return proxy
}
return app
}) as CreateAppFunction<Element>
这段代码定义了一个 createApp 函数,该函数返回一个应用程序实例,该实例包含 mount 方法,用于将应用程序挂载到指定的容器上。
createApp 函数接受任意个数的参数,并使用 ensureRenderer 函数创建一个应用程序实例 app。如果处于开发环境下,会调用 injectNativeTagCheck 函数注入本地标签检查。
在 mount 方法中,首先使用 normalizeContainer 函数将传入的容器参数转换为 Element 类型的容器元素。如果容器不存在,则直接返回。接着判断组件是否为函数式组件或者具有 render 方法或者具有 template 属性,如果不是,则将容器的 innerHTML 赋值给组件的 template 属性。
然后清空容器的内容,并调用 app 实例的 mount 方法将应用程序挂载到容器上。最后移除容器上的 v-cloak 属性,并返回 mount 方法的返回值作为代理。
最后,使用 as 关键字将 createApp 函数转换为 CreateAppFunction<Element> 类型的函数。
rendererOptions和ensureRenderer实现
const rendererOptions = {
patchProp, // 处理 props 属性
...nodeOps // 处理 DOM 节点操作
}
// lazy create the renderer - this makes core renderer logic tree-shakable
// in case the user only imports reactivity utilities from Vue.
let renderer: Renderer | HydrationRenderer
let enabledHydration = false
function ensureRenderer() {
return renderer || (renderer = createRenderer(rendererOptions))
}
这段代码定义了一个 rendererOptions 对象,该对象包含了 patchProp 属性和 nodeOps 对象。其中 patchProp 属性用于处理组件的 props 属性,nodeOps 对象用于处理 DOM 节点操作。
接着定义了一个 renderer 变量和一个 enabledHydration 变量。renderer 变量用于存储渲染器实例,如果渲染器实例不存在,则通过 createRenderer 函数创建一个渲染器实例。enabledHydration 变量用于存储是否启用了服务端渲染的标志。
最后定义了 ensureRenderer 函数,该函数用于确保渲染器实例存在。如果渲染器实例已经存在,则直接返回该实例。否则,使用 createRenderer 函数创建一个渲染器实例,并将该实例赋值给 renderer 变量,然后返回该实例。这样做可以使得核心渲染器逻辑具有可tree-shaking的特性,以便用户只导入 Vue 的响应式工具时可以减小打包体积。
createRenderer实现
export function createRenderer<
HostNode = RendererNode,
HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
return baseCreateRenderer<HostNode, HostElement>(options)
}
这段代码定义了一个 createRenderer 函数,该函数接受一个泛型类型参数 HostNode 和 HostElement,以及一个 RendererOptions 对象作为参数。该函数的返回值是一个渲染器实例。
在函数内部,调用了 baseCreateRenderer 函数,并将传入的 options 参数作为参数传递给 baseCreateRenderer 函数。baseCreateRenderer 函数是 Vue.js 内部的一个函数,它接受一个 RendererOptions 对象作为参数,并返回一个渲染器实例。
createRenderer 函数的作用是封装 baseCreateRenderer 函数,同时通过泛型类型参数 HostNode 和 HostElement,可以实现对不同宿主环境的支持。例如,在浏览器环境下,HostNode 可以是 HTMLElement 类型,HostElement 可以是 Element 类型。而在 Node.js 环境下,HostNode 可以是 Node 类型,HostElement 可以是 DocumentFragment 类型。