“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:
- Create an app instance and return it
- 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:
- There are two types of renderers, both based on
baseCreateRenderer
Function created. This function is overloaded. - Renderers are created by delay to make it easier to do when not in use
tree-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:
- Implement core logic of component rendering creation, update, unload, etc.
- Return the render function, and create the application instance method, and of course
hydrate
.
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
- Create and define an instance context containing properties and methods
- 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.
- Based on the root component and properties in
mount
Method to complete the virtual nodevNode
Convert and passrender
Call finish render, about the render function inbaseCreateRender
Already said.
conclusion
CreateApp (createApp) createApp (createApp) createApp (createApp)
- perform
createApp
Renderers 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 onbaseCreateRender
Method to implement. baseCreateRender
The function returns after executionrender
Rendering functions andcreateApp
Method, whereinrender
Functions are the main core logical implementation for component creation, update, and uninstall. CreateApp is used to create and initialize an application instance.- CreateAppAPI is used to generate the default application context
context
, which defines the properties and methods that the application instance has and extends by overriding themcontext.app
Property 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 therender
Function completion pairvNode
The 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.