“This is the 17th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

Creating a Vue application instance is the beginning of a project. This article looks at what happens inside createApp. Of course, if you are interested in Vue3 responsive system, you can also take a look at these two articles:

Vue3 source | last – reactive deep understanding of reactive system

Vue3 source | deep understanding of reactive system next – effect

VUE3 use

Here’s how Vue3 initializes the application.

Import {createApp} from 'vue' import App from './ App 'const App = createApp(App) app.mount('# App ') // vue3 App initialization import {createApp} from 'vue' import App from' Component rendering and uncaught error configuration handler app.config.errorHandler = (err, VM, Info) = > {} / / add the global property app. Config. GlobalProperties. $HTTP = () = > {} / / here is equivalent to mount to Vue2 Vue. Prototype / / Specify a method to identify the Vue defined outside of the element of a custom app. The config. IsCustomElement = tag = > tag. The startsWith (' ion - ') / / registered components app.com ponent (' my - component, {}) // Retrieve component const MyComponent = app.component.ponent ('my-component') // Register directive app.directive('my-directive',{}) // Sets a value that can be injected into all components within the application. Components should use Inject to receive supplied values. app.provide('user', Import MyPlugin from './plugins/MyPlugin' app.use(MyPlugin)...Copy the code

Vue3 is similar to Vue2 initialization in that an application instance is created and mounted to a DOM node. It can also be seen that Vue renders the page through JS, which is different from the traditional DOM straight out of the page. DOM straight out, simply put, is the final rendering of the HTML page returned after the request.

CreateApp process

createApp

CreateApp is used to create an application instance.

export const createApp = ((... Args) => {const app = ensureRenderer().createApp(... The args) const {mount} = app/app/rewrite the mount method. The mount = (containerOrSelector: Element | string) : Any => {// Standardized container const container = normalizeContainer(containerOrSelector) if (! Container) return const component = app._component // If the component does not have the render function and template, use the innerHTML of the container as the component template. isFunction(component) && ! component.render && ! Component.template) {component.template = container. InnerHTML} The content of the empty container container. The innerHTML = '/ / mount container const proxy = mount (container) container. RemoveAttribute (' v - cloak) container.setAttribute('data-v-app', '') return proxy } return app }) as CreateAppFunction<Element>Copy the code

CreateApp does two things:

  1. Create an app instance and return it
  2. Override mount method

One of the two main questions that may exist after viewing it is “ensureRenderer”? Why override the mount method instead of using it directly?

ensureRenderer

The renderer core code is in run-time core/ SRC /renderer.ts.

BaseCreateRenderer // There are two types of renderer: The Renderer Element < > | create Renderer HydrationRenderer / / delay, when a user only USES reactive response library, Can do tree - shaking optimization function ensureRenderer () {return the renderer | | (the renderer = createRenderer < Node, Element>(rendererOptions)) } export function createRenderer< HostNode = RendererNode, HostElement = RendererElement >(options: RendererOptions<HostNode, HostElement>) { return baseCreateRenderer<HostNode, HostElement>(options)} // The HydrationRenderer is also created only when called, Export function createHydrationRenderer(options: RendererOptions<Node, Element> ) { return baseCreateRenderer(options, createHydrationFunctions) }Copy the code

It can be seen from the analysis that:

  1. There are two types of renderers, both based onbaseCreateRendererFunction created. This function is overloaded.
  2. Renderers are created by delay to make it easier to do when not in usetree-shaking.
baseCreateRenderer

This function is a big one, about 1800 lines of code, and contains the core logic for component rendering. Only the mount logic, which involves component updates and other rendering logic, will be removed for further study.

function baseCreateRenderer( options: RendererOptions, createHydrationFns? // Const render: : typeof createHydrationFunctions): any {// RootRenderFunction = (vnode, container) => {if (vnode == null) {// Virtual node does not exist, If (container._vnode) {unmount(container._vnode, null, null, true)}} else {// The virtual node exists, The update or create a patch (container. _vnode | | null, vnode, container)} flushPostFlushCbs () / / virtual node data cache, Container._vnode = vnode} return {render, hydrate, createApp: createAppAPI(render, hydrate)}}Copy the code

BaseCreateRenderer implements:

  1. Implement core logic of component rendering creation, update, unload, etc.
  2. Return the render function, and create the application instance method, and of coursehydrate.
createAppAPI

See below for the component rendering core, and here is the API implementation for creating the instance.

export function createAppAPI<HostElement>( render: RootRenderFunction, hydrate? : RootHydrateFunction ): CreateAppFunction<HostElement> {// createApp takes two parameters, 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 } // Create a context object const context = createAppContext() const installedPlugins = new Set() let isMounted = false const app: App = (context.app = { _component: rootComponent as Component, _props: rootProps, _container: null, _context: Context, version, // Exposed application instance configuration get config() {return context.config}, set config(v) { if (__DEV__) { warn( `app.config cannot be replaced. Modify individual options instead.` ) } }, // If a plugin is an object, you must have the install method; 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) } } return app }, component(name: string, component? : PublicAPIComponent): any {if (__DEV__) {validateComponentName(name, context.config)} // If (! component) { return context.components[name] } if (__DEV__ && context.components[name]) { warn(`Component "${name}" has Context.ponents [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): any { if (! IsMounted) {// create a virtual node based on the rootComponent const vnode = createVNode(rootComponent as Component, rootProps) vnode.appContext = context // HMR root reload if (__DEV__) { context.reload = () => { render(cloneVNode(vnode), rootContainer) } } render(vnode, rootContainer) isMounted = true app._container = rootContainer // for devtools and telemetry ; (rootContainer as any).__vue_app__ = app return vnode.component! .proxy } }, unmount() { if (isMounted) { render(null, app._container) devtoolsUnmountApp(app) } }, provide(key, value) { context.provides[key as string] = value return app } }) return app } }Copy the code

CreateAppAPI createAppAPI createAppAPI createAppAPI

  1. Create and define an instance context containing properties and methods
  2. Rewrite the extension context.app method to allow users to customize context-specific properties, i.e., API implementations exposed by application instances, such as custom directives, mixins, components, etc.
  3. Based on the root component and properties inmountMethod to complete the virtual nodevNodeConvert and passrenderCall finish render, about the render function inbaseCreateRenderAlready said.

conclusion

CreateApp (createApp) createApp (createApp) createApp (createApp)

  1. performcreateAppRenderers are created first. Note that there are two types of renderers, and both are created lazily to facilitate tree-shaking optimization when users only refer to reactive reactive frameworks. And both renderers are based onbaseCreateRenderMethod to implement.
  2. baseCreateRenderThe function returns after executionrenderRendering functions andcreateAppMethod, whereinrenderFunctions are the main core logical implementation for component creation, update, and uninstall. CreateApp is used to create and initialize an application instance.
  3. CreateAppAPI is used to generate the default application contextcontext, which defines the properties and methods that the application instance has and extends by overriding themcontext.appProperty to allow users to customize the context, such as custom components, directives, mixins, plug-in installations, and more. There is a mount method to convert the root component to a virtual nodevNode, and through therenderFunction completion pairvNodeThe rendering.

In the next article, we’ll build on this article and go inside the Render function, first to see what a vNode is and then to look at the DOM Diff process.