import { ErrorCodes, callWithErrorHandling } from './errorHandling'
import { KeepAliveSink, isKeepAlive } from './components/KeepAlive'
import { registerHMR, unregisterHMR } from './hmr'
-import { createHydrateFn } from './hydration'
+import { createHydrationFunctions } from './hydration'
const __HMR__ = __BUNDLER__ && __DEV__
HostNode extends object = any,
HostElement extends HostNode = any
>(options: RendererOptions<HostNode, HostElement>) {
+ const res = baseCreateRenderer(options)
+ return res as typeof res & {
+ hydrate: undefined
+ }
+}
+
+// Separate API for creating hydration-enabled renderer.
+// Hydration logic is only used when calling this function, making it
+// tree-shakable.
+export function createHydrationRenderer<
+ HostNode extends object = any,
+ HostElement extends HostNode = any
+>(options: RendererOptions<HostNode, HostElement>) {
+ const res = baseCreateRenderer(options, createHydrationFunctions)
+ return res as typeof res & {
+ hydrate: ReturnType<typeof createHydrationFunctions>[0]
+ }
+}
+
+function baseCreateRenderer<
+ HostNode extends object = any,
+ HostElement extends HostNode = any
+>(
+ options: RendererOptions<HostNode, HostElement>,
+ createHydrationFns?: typeof createHydrationFunctions
+) {
type HostVNode = VNode<HostNode, HostElement>
type HostVNodeChildren = VNodeArrayChildren<HostNode, HostElement>
type HostSuspenseBoundary = SuspenseBoundary<HostNode, HostElement>
options
}
+ let hydrate: ReturnType<typeof createHydrationFunctions>[0] | undefined
+ let hydrateNode: ReturnType<typeof createHydrationFunctions>[1] | undefined
+ if (createHydrationFns) {
+ ;[hydrate, hydrateNode] = createHydrationFns(mountComponent, hostPatchProp)
+ }
+
function patch(
n1: HostVNode | null, // null means this is a mount
n2: HostVNode,
if (instance.bm !== null) {
invokeHooks(instance.bm)
}
- if (initialVNode.el) {
+ if (initialVNode.el && hydrateNode) {
// vnode has adopted host node - perform hydration instead of mount.
hydrateNode(initialVNode.el as Node, subTree, instance)
} else {
container._vnode = vnode
}
- const [hydrate, hydrateNode] = createHydrateFn(mountComponent, hostPatchProp)
-
return {
render,
hydrate,
import {
createRenderer,
+ createHydrationRenderer,
warn,
RootRenderFunction,
- CreateAppFunction
+ CreateAppFunction,
+ VNode,
+ App
} from '@vue/runtime-core'
import { nodeOps } from './nodeOps'
import { patchProp } from './patchProp'
// Importing from the compiler, will be tree-shaken in prod
import { isFunction, isString, isHTMLTag, isSVGTag } from '@vue/shared'
-const {
- render: baseRender,
- hydrate: baseHydrate,
- createApp: baseCreateApp
-} = createRenderer({
+const rendererOptions = {
patchProp,
...nodeOps
-})
+}
+
+// lazy create the renderer - this makes core renderer logic tree-shakable
+// in case the user only imports reactivity utilities from Vue.
+let renderer:
+ | ReturnType<typeof createRenderer>
+ | ReturnType<typeof createHydrationRenderer>
+
+let enabledHydration = false
+
+function ensureRenderer() {
+ return renderer || (renderer = createRenderer(rendererOptions))
+}
+
+function ensureHydrationRenderer() {
+ renderer = enabledHydration
+ ? renderer
+ : createHydrationRenderer(rendererOptions)
+ enabledHydration = true
+ return renderer as ReturnType<typeof createHydrationRenderer>
+}
// use explicit type casts here to avoid import() calls in rolled-up d.ts
-export const render = baseRender as RootRenderFunction<Node, Element>
-export const hydrate = baseHydrate as RootRenderFunction<Node, Element>
+export const render = ((...args) => {
+ ensureRenderer().render(...args)
+}) as RootRenderFunction<Node, Element>
+
+export const hydrate = ((...args) => {
+ ensureHydrationRenderer().hydrate(...args)
+}) as (vnode: VNode, container: Element) => void
-export const createApp: CreateAppFunction<Element> = (...args) => {
- const app = baseCreateApp(...args)
+export const createApp = ((...args) => {
+ const app = ensureRenderer().createApp(...args)
if (__DEV__) {
- // Inject `isNativeTag`
- // this is used for component name validation (dev only)
- Object.defineProperty(app.config, 'isNativeTag', {
- value: (tag: string) => isHTMLTag(tag) || isSVGTag(tag),
- writable: false
- })
+ injectNativeTagCheck(app)
}
const { mount } = app
- app.mount = (container: Element | string): any => {
- if (isString(container)) {
- container = document.querySelector(container)!
- if (!container) {
- __DEV__ &&
- warn(`Failed to mount app: mount target selector returned null.`)
- return
- }
- }
+ app.mount = (containerOrSelector: Element | string): any => {
+ const container = normalizeContainer(containerOrSelector)
+ if (!container) return
const component = app._component
if (
__RUNTIME_COMPILE__ &&
) {
component.template = container.innerHTML
}
- const isHydrate = container.hasAttribute('data-server-rendered')
- if (!isHydrate) {
- // clear content before mounting
- container.innerHTML = ''
+ // clear content before mounting
+ container.innerHTML = ''
+ return mount(container)
+ }
+
+ return app
+}) as CreateAppFunction<Element>
+
+export const createSSRApp = ((...args) => {
+ const app = ensureHydrationRenderer().createApp(...args)
+
+ if (__DEV__) {
+ injectNativeTagCheck(app)
+ }
+
+ const { mount } = app
+ app.mount = (containerOrSelector: Element | string): any => {
+ const container = normalizeContainer(containerOrSelector)
+ if (container) {
+ return mount(container, true)
}
- return mount(container, isHydrate)
}
return app
+}) as CreateAppFunction<Element>
+
+function injectNativeTagCheck(app: App) {
+ // Inject `isNativeTag`
+ // this is used for component name validation (dev only)
+ Object.defineProperty(app.config, 'isNativeTag', {
+ value: (tag: string) => isHTMLTag(tag) || isSVGTag(tag),
+ writable: false
+ })
+}
+
+function normalizeContainer(container: Element | string): Element | null {
+ if (isString(container)) {
+ const res = document.querySelector(container)
+ if (__DEV__ && !res) {
+ warn(`Failed to mount app: mount target selector returned null.`)
+ }
+ return res
+ }
+ return container
}
// DOM-only runtime directive helpers