vue3源码理解1
creatApp({}).mount("#app")当我们执行这行代码时,发生了什么
packages/runtime-dom/src/index.ts 下的 70行 //通过ensureRenderer渲染器函数里的createApp函数生成app实例 export const createApp = ((...args) => { const app = ensureRenderer().createApp(...args) ... function ensureRenderer() { 当前文件43行左右 //单例模式 ,一开始是没有renderer所以会执行后面的createRenderer,创建一个renderer return ( renderer || (renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions)) ) } createRenderer 在packages/runtime-core/src/renderer.ts //300行左右 export function createRenderer< HostNode = RendererNode, HostElement = RendererElement >(options: RendererOptions<HostNode, HostElement>) { return baseCreateRenderer<HostNode, HostElement>(options) }
baseCreateRenderer 327行到2340行左右
然后内部执行了baseCreateRenderer工厂函数,这个函数创建了一个渲染器
baseCreateRenderer 是一个很大的工厂函数,也可以说是vue3里面最大的函数
内部最终结果return 了三个值
return {
render,//把接收到的vnode转换成dom,并插入到宿主元素上
hydrate, //用于SSR,服务端将一个vnode转换成html字符串
createApp: createAppAPI(render, hydrate) //还记得上面的ensureRenderer().createApp(...args)这个代码吗?就是这里抛出去的
}
render :注意:这是渲染器的渲染函数,而不是组件的渲染函数, 渲染器渲染函数,接收一个虚拟DOM,而不是生产一个虚拟DOM。然后把它转换成这个虚拟DOM对应的真实DOM,转换完成后挂载到#app这个宿主上面
我们可以看到baseCreateRenderer return 出的createApp是由createAppAPI这个工厂函数返回的,那下面我们去找找createAppAPI是哪里来的
嗯哼,找到了,在这········packages/runtime-core/src/apiCreateApp.ts 177行左右
createAppAPI内部,这才是createApp的终极奥义,生产发源地
export function createAppAPI<HostElement>( render: RootRenderFunction, hydrate?: RootHydrateFunction ): CreateAppFunction<HostElement> { return function createApp(rootComponent, rootProps = null) { //接收两个参数,第一个参数为根节点组件,第二个为配置项 if (rootProps != null && !isObject(rootProps)) { __DEV__ && warn(`root props passed to app.mount() must be an object.`) rootProps = null } ......
const app: App = (context.app = { //声明App实例
get config() {
return context.config
},
set config(v) {
...
},
use(plugin: Plugin, ...options: any[]) {
...
return app
},
mixin(mixin: ComponentOptions) {
....
return app
},
component(name: string, component?: Component): any {
...
return app
},
directive(name: string, directive?: Directive) {
...
return app
},
mount(//挂载,这是重点
rootContainer: HostElement, //这里把我们挂载点的html转换成了vnode
isHydrate?: boolean,
isSVG?: boolean
): any {
if (!isMounted) {
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
// store app context on the root VNode.
// this will be set on the root instance on initial mount.
vnode.appContext = context
// HMR root reload
if (__DEV__) {
context.reload = () => {
render(cloneVNode(vnode), rootContainer, isSVG)
}
}
if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
render(vnode, rootContainer, isSVG) //然后执行一个渲染器,这里执行,又回到了上面baseCreateRenderer 函数return 出的那个对象
}
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__) {}
},
unmount() {
...
},
provide(key, value) {
...
return app
}
}
.....
return { render, hydrate, createApp: createAppAPI(render, hydrate),刚刚我们在这工厂函数里的render是这里传进去的,那我们这次去看看render内部
}
render packages/runtime-core/src/renderer.ts 2309行左右
const render: RootRenderFunction = (vnode, container, isSVG) => {
//首次执行时 参数vnode不为null
if (vnode == null) {
if (container._vnode) {
unmount(container._vnode, null, null, true)
}
} else { //所以执行patch,
patch(//内部就是我们vue的diff了
container._vnode || null,//老节点
vnode,//新节点,首次执行时,没有老元素
container,//宿主元素
null,
null,
null,
isSVG
)
//patch是一个多功能函数,如果是首次执行,那执行的就是挂载,如果不是那就会执行diff操作
}
flushPostFlushCbs()
container._vnode = vnode
}
pacth packages/runtime-core/src/renderer.ts 360行左右
const patch: PatchFn = (
n1, //老节点
n2, //新节点
container,
anchor = null,
parentComponent = null,
parentSuspense = null,
isSVG = false,
slotScopeIds = null,
optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
) => {
if (n1 === n2) { //这里没想通,为啥直接这样比较也行??
return
}
// patching & not same type, unmount old tree
if (n1 && !isSameVNodeType(n1, n2)) {
anchor = getNextHostNode(n1)
unmount(n1, parentComponent, parentSuspense, true)
n1 = null
}
if (n2.patchFlag === PatchFlags.BAIL) {
optimized = false
n2.dynamicChildren = null
}
const { type, ref, shapeFlag } = n2 //从新节点里面,获取节点类型,然后执行不同的case
switch (type) {
case Text:
processText(n1, n2, container, anchor)
break
case Comment:
processCommentNode(n1, n2, container, anchor)
break
case Static:
if (n1 == null) {
mountStaticNode(n2, container, anchor, isSVG)
} else if (__DEV__) {
patchStaticNode(n1, n2, container, isSVG)
}
break
case Fragment:
processFragment(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
break
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
} else if (shapeFlag & ShapeFlags.COMPONENT) {//首次执行的case
processComponent( //挂载根组件,以后挂载组件也在这
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
} else if (shapeFlag & ShapeFlags.TELEPORT) {
;(type as typeof TeleportImpl).process(
n1 as TeleportVNode,
n2 as TeleportVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
internals
)
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
;(type as typeof SuspenseImpl).process(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
internals
)
} else if (__DEV__) {
warn('Invalid VNode type:', type, `(${typeof type})`)
}
}
// set ref
if (ref != null && parentComponent) {
setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2)
}
}