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)
}
}
 

 

 

 

 

 











posted @ 2022-03-27 14:49  疾风_剑豪  阅读(165)  评论(0编辑  收藏  举报