随着前端越来越卷 并且面试题越来越常见的问到vue3源码中的问题 现在不得把源码学习起来,此次记录只会一条线一条线的走下去
一、下载vue3源码并运行
源码下载网上有很多推荐链接vue3源码安装推荐连接
二、初始化
根据上面的连接操作过后会我们新建一个test.html在路径/packages/vue/examples
<script src="../../dist/vue.global.js"></script>
<div id="counter">
</div>
<script>
const App = {
}
Vue.createApp(App).mount('#counter')
</script>
入口文件 /packages/vue/src/index.ts
目前这个流程我们只需要看最后一行export * from '@vue/runtime-dom'这个代码
点进去 进入/package/runtime-dome/index.ts 找到createApp方法
我们只看其中关键的代码
export const createApp = ((...args) => {
//初始化vue
const app = ensureRenderer().createApp(...args) //初始化component,config,directive,mixin,mount,provide,runWithContext,unmount,use
const { mount } = app
app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
// clear content before mounting
container.innerHTML = ''
const proxy = mount(container, false, resolveRootNamespace(container))
if (container instanceof Element) {
container.removeAttribute('v-cloak')
container.setAttribute('data-v-app', '')
}
return proxy
}
return app
}) as CreateAppFunction<Element>
解析 const app = ensureRenderer().createApp(...args)
找到ensureRenderer
function ensureRenderer() {
return (
renderer ||
(renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))
)
}
找到createRenderer 来到/package/runtime-core/renderer.ts
export function createRenderer<
HostNode = RendererNode,
HostElement = RendererElement,
>(options: RendererOptions<HostNode, HostElement>) {
return baseCreateRenderer<HostNode, HostElement>(options)
}
找到baseCreateRenderer
function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?: typeof createHydrationFunctions,
): any {
`....省略里面有很多方法 大概两千行左右这里我们只看
return {
···省略,
createApp: createAppAPI(render, hydrate),//初始化mount的时候没有用到这两个参数
}
}
进入createAppAPI
export function createAppAPI<HostElement>(
render: RootRenderFunction<HostElement>,
hydrate?: RootHydrateFunction,
): CreateAppFunction<HostElement> {
debugger
return function createApp(rootComponent, rootProps = null) {
const app: App = (context.app = {
//省略
mount(
rootContainer: HostElement,
isHydrate?: boolean,
namespace?: boolean | ElementNamespace,
): any {
if (!isMounted) {
const vnode = createVNode(rootComponent, rootProps)
// store app context on the root VNode.
// this will be set on the root instance on initial mount.
vnode.appContext = context
if (namespace === true) {
namespace = 'svg'
} else if (namespace === false) {
namespace = undefined
}
// HMR root reload
if (__DEV__) {
context.reload = () => {
// casting to ElementNamespace because TS doesn't guarantee type narrowing
// over function boundaries
render(
cloneVNode(vnode),
rootContainer,
namespace as ElementNamespace,
)
}
}
if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
//初始化的时候走这里
render(vnode, rootContainer, namespace)
}
isMounted = true
app._container = rootContainer
// for devtools and telemetry
;(rootContainer as any).__vue_app__ = app
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
app._instance = vnode.component
devtoolsInitApp(app, version)
}
return getExposeProxy(vnode.component!) || vnode.component!.proxy
} else if (__DEV__) {
warn(
`App has already been mounted.\n` +
`If you want to remount the same app, move your app creation logic ` +
`into a factory function and create fresh app instances for each ` +
`mount - e.g. \`const createMyApp = () => createApp(App)\``,
)
}
},
})
return app
}
}
最终找到了我们的mount方法 实际上首次Vue.createApp(App).mount('#counter')最终走到了这里