• vue3源码学习api-createApp-amount


    vue3 地址 https://github.com/vuejs/core

    首先看看vue文档什么是 Vue?


    Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue 都可以胜任。

    下面是一个最基本的示例:

    import { createApp, ref } from 'vue'
    
    createApp({
      setup() {
        return {
          count: ref(0)
        }
      }
    }).mount('#app')
    

    学习vue 开发 都是从这个例子开始学习,在这个例子中涉及了这些api

    • 1 createApp
    • 2 mount
    • 3 ref
    • 4 setup

    其中 ref 属于reactivity:反应系统 就暂时先不深究了

    setup 属于vue3 新的语法糖 也先不深究了
    就先看看最简单的createApp 和mount

    createApp

    先看看vue 仓库中的packages/vue/src/index.ts
    地址 https://github.com/vuejs/core/blob/main/packages/vue/src/index.ts

    export { compileToFunction as compile }
    export * from '@vue/runtime-dom'
    
    

    可以看到到处了一个编译的方法 和 @vue/runtime-dom 中的方法
    根据上文
    runtime-dom:针对浏览器的运行时。包括原生 DOM API、属性、属性、事件处理程序等的处理。
    在runtime-dom 包中找到相关的方法
    地址 https://github.com/vuejs/core/blob/main/packages/runtime-dom/src/index.ts

    export const createApp = ((...args) => {
      const app = ensureRenderer().createApp(...args)
    
      if (__DEV__) {
        injectNativeTagCheck(app)
        injectCompilerOptionsCheck(app)
      }
    
      const { mount } = app
      app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
        const container = normalizeContainer(containerOrSelector)
        if (!container) return
    
        const component = app._component
        if (!isFunction(component) && !component.render && !component.template) {
          // __UNSAFE__
          // Reason: potential execution of JS expressions in in-DOM template.
          // The user must make sure the in-DOM template is trusted. If it's
          // rendered by the server, the template should not contain any user data.
          component.template = container.innerHTML
          // 2.x compat check
          if (__COMPAT__ && __DEV__) {
            for (let i = 0; i < container.attributes.length; i++) {
              const attr = container.attributes[i]
              if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) {
                compatUtils.warnDeprecation(
                  DeprecationTypes.GLOBAL_MOUNT_CONTAINER,
                  null
                )
                break
              }
            }
          }
        }
    
        // clear content before mounting
        container.innerHTML = ''
        const proxy = mount(container, false, container instanceof SVGElement)
        if (container instanceof Element) {
          container.removeAttribute('v-cloak')
          container.setAttribute('data-v-app', '')
        }
        return proxy
      }
    
      return app
    }) as CreateAppFunction<Element>
    
    
    function ensureRenderer() {
      return (
        renderer ||
        (renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))
      )
    }
    
    

    走查代码可以发现 选是创建了一个渲染器Renderer
    然后调用了渲染器的方法 createApp
    查看具体方法

    地址 https://github.com/vuejs/core/blob/main/packages/runtime-core/src/renderer.ts

    创建渲染器的方法是一个比较长的方法
    里面的很多方法看名称,更多的设计对dom的操作,不过我们还是先关注createApp 干了些什么

    function baseCreateRenderer(
      options: RendererOptions,
      createHydrationFns?: typeof createHydrationFunctions
    ): any {
        ......
      return {
        render,
        hydrate,
        createApp: createAppAPI(render, hydrate)
      }
    }
    

    发现createAppAPI 来自apiCreateApp 文件

    地址 https://github.com/vuejs/core/blob/main/packages/runtime-core/src/apiCreateApp.ts

    import { createAppAPI, CreateAppFunction } from './apiCreateApp'
    

    代码如下

    export function createAppAPI<HostElement>(
      render: RootRenderFunction<HostElement>,
      hydrate?: RootHydrateFunction
    ): CreateAppFunction<HostElement> {
      return function createApp(rootComponent, rootProps = null) {
        if (!isFunction(rootComponent)) {
          rootComponent = extend({}, rootComponent)
        }
    
        if (rootProps != null && !isObject(rootProps)) {
          __DEV__ && warn(`root props passed to app.mount() must be an object.`)
          rootProps = null
        }
    
        const context = createAppContext()
    
        // TODO remove in 3.4
        if (__DEV__) {
          Object.defineProperty(context.config, 'unwrapInjectedRef', {
            get() {
              return true
            },
            set() {
              warn(
                `app.config.unwrapInjectedRef has been deprecated. ` +
                  `3.3 now always unwraps injected refs in Options API.`
              )
            }
          })
        }
    
        const installedPlugins = new WeakSet()
    
        let isMounted = false
    
        const app: App = (context.app = {
          _uid: uid++,
          _component: rootComponent as ConcreteComponent,
          _props: rootProps,
          _container: null,
          _context: context,
          _instance: null,
    
          version,
    
          get config() {
            return context.config
          },
    
          set config(v) {
            if (__DEV__) {
              warn(
                `app.config cannot be replaced. Modify individual options instead.`
              )
            }
          },
    
          use(plugin: Plugin, ...options: any[]) {
            if (installedPlugins.has(plugin)) {
              __DEV__ && warn(`Plugin has already been applied to target app.`)
            } else if (plugin && isFunction(plugin.install)) {
              installedPlugins.add(plugin)
              plugin.install(app, ...options)
            } else if (isFunction(plugin)) {
              installedPlugins.add(plugin)
              plugin(app, ...options)
            } else if (__DEV__) {
              warn(
                `A plugin must either be a function or an object with an "install" ` +
                  `function.`
              )
            }
            return app
          },
    
          mixin(mixin: ComponentOptions) {
            if (__FEATURE_OPTIONS_API__) {
              if (!context.mixins.includes(mixin)) {
                context.mixins.push(mixin)
              } else if (__DEV__) {
                warn(
                  'Mixin has already been applied to target app' +
                    (mixin.name ? `: ${mixin.name}` : '')
                )
              }
            } else if (__DEV__) {
              warn('Mixins are only available in builds supporting Options API')
            }
            return app
          },
    
          component(name: string, component?: Component): any {
            if (__DEV__) {
              validateComponentName(name, context.config)
            }
            if (!component) {
              return context.components[name]
            }
            if (__DEV__ && context.components[name]) {
              warn(`Component "${name}" has already been registered in target app.`)
            }
            context.components[name] = component
            return app
          },
    
          directive(name: string, directive?: Directive) {
            if (__DEV__) {
              validateDirectiveName(name)
            }
    
            if (!directive) {
              return context.directives[name] as any
            }
            if (__DEV__ && context.directives[name]) {
              warn(`Directive "${name}" has already been registered in target app.`)
            }
            context.directives[name] = directive
            return app
          },
    
          mount(
            rootContainer: HostElement,
            isHydrate?: boolean,
            isSVG?: boolean
          ): any {
            if (!isMounted) {
              // #5571
              if (__DEV__ && (rootContainer as any).__vue_app__) {
                warn(
                  `There is already an app instance mounted on the host container.\n` +
                    ` If you want to mount another app on the same host container,` +
                    ` you need to unmount the previous app by calling \`app.unmount()\` first.`
                )
              }
              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
    
              // 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)
              }
              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)\``
              )
            }
          },
    
          unmount() {
            if (isMounted) {
              render(null, app._container)
              if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
                app._instance = null
                devtoolsUnmountApp(app)
              }
              delete app._container.__vue_app__
            } else if (__DEV__) {
              warn(`Cannot unmount an app that is not mounted.`)
            }
          },
    
          provide(key, value) {
            if (__DEV__ && (key as string | symbol) in context.provides) {
              warn(
                `App already provides property with key "${String(key)}". ` +
                  `It will be overwritten with the new value.`
              )
            }
    
            context.provides[key as string | symbol] = value
    
            return app
          },
    
          runWithContext(fn) {
            currentApp = app
            try {
              return fn()
            } finally {
              currentApp = null
            }
          }
        })
    
        if (__COMPAT__) {
          installAppCompatProperties(app, context, render)
        }
    
        return app
      }
    }
    

    可以发现createApp 的第一个参数是rootComponent
    需要传递的是一个组件,作为根组件
    第二个参数rootProps是这个给这个组件传递的参数

    通过走查 文件可以发现一些常用的api 也是出现在这里
    例如 use、mixin、component、directive、mount、unmount、provide

    我们要找的mount 也是对这里mount的调用

    mount

    可以看到 mount主要是参数是rootContainer 另外两个是可选参数
    在通过createVNode 创建一个vnode 之后
    调用getExposeProxy
    返回当前刚才创建的vnode的代理

    export function getExposeProxy(instance: ComponentInternalInstance) {
      if (instance.exposed) {
        return (
          instance.exposeProxy ||
          (instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), {
            get(target, key: string) {
              if (key in target) {
                return target[key]
              } else if (key in publicPropertiesMap) {
                return publicPropertiesMap[key](instance)
              }
            },
            has(target, key: string) {
              return key in target || key in publicPropertiesMap
            }
          }))
        )
      }
    }
    

    代码中实际调用的mount

    依然在createAppAPI 里面 这里的传参更友好 了可以传入Selector 方便选择dom节点

    app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
        const container = normalizeContainer(containerOrSelector)
        if (!container) return
    
        const component = app._component
        if (!isFunction(component) && !component.render && !component.template) {
          // __UNSAFE__
          // Reason: potential execution of JS expressions in in-DOM template.
          // The user must make sure the in-DOM template is trusted. If it's
          // rendered by the server, the template should not contain any user data.
          component.template = container.innerHTML
          // 2.x compat check
          if (__COMPAT__ && __DEV__) {
            for (let i = 0; i < container.attributes.length; i++) {
              const attr = container.attributes[i]
              if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) {
                compatUtils.warnDeprecation(
                  DeprecationTypes.GLOBAL_MOUNT_CONTAINER,
                  null
                )
                break
              }
            }
          }
        }
    
        // clear content before mounting
        container.innerHTML = ''
        const proxy = mount(container, false, container instanceof SVGElement)
        if (container instanceof Element) {
          container.removeAttribute('v-cloak')
          container.setAttribute('data-v-app', '')
        }
        return proxy
      }
    
  • 相关阅读:
    xlsx使用table_to_book报错Uncaught Unsupported origin when DIV is not a TABLE
    【@胡锡进】大模型量化分析- 南京银行 601009.SH
    Collction的List方法,list特有方法,遍历方式,迭代器选择
    高并发技巧-流量聚合和高并发写入处理技巧
    ADB 操作命令详解及用法大全
    针对 jQuery 的优化方法有哪些
    《AI一键生成抖音商品种草文案》让你秒变带货王!
    水果店活动朋友圈文案怎么写,水果店文字文案这么写
    【微信小程序】无纸化会议OA系统之首页搭建
    支持 equals 相等的对象(可重复对象)作为 WeakHashMap 的 Key
  • 原文地址:https://www.cnblogs.com/qqloving/p/17809544.html