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











  • 相关阅读:
    ATI Radeon HD 5450 with full QE/CI Support ( 转载 )
    从零开始学iPhone开发(5)——使用MapKit
    从零开始学iPhone开发(4)——使用WebView
    从零开始学iPhone开发(3)——视图及控制器的使用
    从零开始学iPhone开发(2)——控件的使用
    从零开始学iPhone开发(1)——工具的使用
    实战做项目如何选择开源许可协议(一)-了解协议
    git使用3
    git团队开发操作
    git分支管理
  • 原文地址:https://www.cnblogs.com/maomao93/p/16062820.html
Copyright © 2020-2023  润新知