wip(ssr): basic element hydration
This commit is contained in:
parent
35d91f4e18
commit
6b505dcd23
@ -5,8 +5,8 @@ exports[`scopeId compiler support should push scopeId for hoisted nodes 1`] = `
|
|||||||
const _withId = _withScopeId(\\"test\\")
|
const _withId = _withScopeId(\\"test\\")
|
||||||
|
|
||||||
_pushScopeId(\\"test\\")
|
_pushScopeId(\\"test\\")
|
||||||
const _hoisted_1 = _createVNode(\\"div\\", null, \\"hello\\", -1)
|
const _hoisted_1 = _createVNode(\\"div\\", null, \\"hello\\", -2 /* HOISTED */)
|
||||||
const _hoisted_2 = _createVNode(\\"div\\", null, \\"world\\", -1)
|
const _hoisted_2 = _createVNode(\\"div\\", null, \\"world\\", -2 /* HOISTED */)
|
||||||
_popScopeId()
|
_popScopeId()
|
||||||
|
|
||||||
export const render = _withId(function render(_ctx, _cache) {
|
export const render = _withId(function render(_ctx, _cache) {
|
||||||
|
@ -4,6 +4,8 @@ import {
|
|||||||
PUSH_SCOPE_ID,
|
PUSH_SCOPE_ID,
|
||||||
POP_SCOPE_ID
|
POP_SCOPE_ID
|
||||||
} from '../src/runtimeHelpers'
|
} from '../src/runtimeHelpers'
|
||||||
|
import { PatchFlags } from '@vue/shared'
|
||||||
|
import { genFlagText } from './testUtils'
|
||||||
|
|
||||||
describe('scopeId compiler support', () => {
|
describe('scopeId compiler support', () => {
|
||||||
test('should only work in module mode', () => {
|
test('should only work in module mode', () => {
|
||||||
@ -81,8 +83,12 @@ describe('scopeId compiler support', () => {
|
|||||||
expect(code).toMatch(
|
expect(code).toMatch(
|
||||||
[
|
[
|
||||||
`_pushScopeId("test")`,
|
`_pushScopeId("test")`,
|
||||||
`const _hoisted_1 = _createVNode("div", null, "hello", -1)`,
|
`const _hoisted_1 = _createVNode("div", null, "hello", ${genFlagText(
|
||||||
`const _hoisted_2 = _createVNode("div", null, "world", -1)`,
|
PatchFlags.HOISTED
|
||||||
|
)})`,
|
||||||
|
`const _hoisted_2 = _createVNode("div", null, "world", ${genFlagText(
|
||||||
|
PatchFlags.HOISTED
|
||||||
|
)})`,
|
||||||
`_popScopeId()`
|
`_popScopeId()`
|
||||||
].join('\n')
|
].join('\n')
|
||||||
)
|
)
|
||||||
|
@ -4,7 +4,7 @@ exports[`compiler: hoistStatic transform hoist element with static key 1`] = `
|
|||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
const { createVNode: _createVNode } = _Vue
|
const { createVNode: _createVNode } = _Vue
|
||||||
|
|
||||||
const _hoisted_1 = _createVNode(\\"div\\", { key: \\"foo\\" }, null, -1)
|
const _hoisted_1 = _createVNode(\\"div\\", { key: \\"foo\\" }, null, -2 /* HOISTED */)
|
||||||
|
|
||||||
return function render(_ctx, _cache) {
|
return function render(_ctx, _cache) {
|
||||||
with (this) {
|
with (this) {
|
||||||
@ -24,7 +24,7 @@ const { createVNode: _createVNode } = _Vue
|
|||||||
const _hoisted_1 = _createVNode(\\"p\\", null, [
|
const _hoisted_1 = _createVNode(\\"p\\", null, [
|
||||||
_createVNode(\\"span\\"),
|
_createVNode(\\"span\\"),
|
||||||
_createVNode(\\"span\\")
|
_createVNode(\\"span\\")
|
||||||
], -1)
|
], -2 /* HOISTED */)
|
||||||
|
|
||||||
return function render(_ctx, _cache) {
|
return function render(_ctx, _cache) {
|
||||||
with (this) {
|
with (this) {
|
||||||
@ -43,7 +43,7 @@ const { createVNode: _createVNode, createCommentVNode: _createCommentVNode } = _
|
|||||||
|
|
||||||
const _hoisted_1 = _createVNode(\\"div\\", null, [
|
const _hoisted_1 = _createVNode(\\"div\\", null, [
|
||||||
_createCommentVNode(\\"comment\\")
|
_createCommentVNode(\\"comment\\")
|
||||||
], -1)
|
], -2 /* HOISTED */)
|
||||||
|
|
||||||
return function render(_ctx, _cache) {
|
return function render(_ctx, _cache) {
|
||||||
with (this) {
|
with (this) {
|
||||||
@ -60,8 +60,8 @@ exports[`compiler: hoistStatic transform hoist siblings with common non-hoistabl
|
|||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
const { createVNode: _createVNode } = _Vue
|
const { createVNode: _createVNode } = _Vue
|
||||||
|
|
||||||
const _hoisted_1 = _createVNode(\\"span\\", null, null, -1)
|
const _hoisted_1 = _createVNode(\\"span\\", null, null, -2 /* HOISTED */)
|
||||||
const _hoisted_2 = _createVNode(\\"div\\", null, null, -1)
|
const _hoisted_2 = _createVNode(\\"div\\", null, null, -2 /* HOISTED */)
|
||||||
|
|
||||||
return function render(_ctx, _cache) {
|
return function render(_ctx, _cache) {
|
||||||
with (this) {
|
with (this) {
|
||||||
@ -79,7 +79,7 @@ exports[`compiler: hoistStatic transform hoist simple element 1`] = `
|
|||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
const { createVNode: _createVNode } = _Vue
|
const { createVNode: _createVNode } = _Vue
|
||||||
|
|
||||||
const _hoisted_1 = _createVNode(\\"span\\", { class: \\"inline\\" }, \\"hello\\", -1)
|
const _hoisted_1 = _createVNode(\\"span\\", { class: \\"inline\\" }, \\"hello\\", -2 /* HOISTED */)
|
||||||
|
|
||||||
return function render(_ctx, _cache) {
|
return function render(_ctx, _cache) {
|
||||||
with (this) {
|
with (this) {
|
||||||
@ -105,7 +105,7 @@ return function render(_ctx, _cache) {
|
|||||||
const _directive_foo = _resolveDirective(\\"foo\\")
|
const _directive_foo = _resolveDirective(\\"foo\\")
|
||||||
|
|
||||||
return (_openBlock(), _createBlock(\\"div\\", null, [
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
_withDirectives(_createVNode(\\"div\\", _hoisted_1, null, 32 /* NEED_PATCH */), [
|
_withDirectives(_createVNode(\\"div\\", _hoisted_1, null, -1 /* NEED_PATCH */), [
|
||||||
[_directive_foo]
|
[_directive_foo]
|
||||||
])
|
])
|
||||||
]))
|
]))
|
||||||
@ -172,7 +172,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t
|
|||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
const { createVNode: _createVNode } = _Vue
|
const { createVNode: _createVNode } = _Vue
|
||||||
|
|
||||||
const _hoisted_1 = _createVNode(\\"span\\", null, \\"foo \\" + _toDisplayString(1) + \\" \\" + _toDisplayString(true), -1)
|
const _hoisted_1 = _createVNode(\\"span\\", null, \\"foo \\" + _toDisplayString(1) + \\" \\" + _toDisplayString(true), -2 /* HOISTED */)
|
||||||
|
|
||||||
return function render(_ctx, _cache) {
|
return function render(_ctx, _cache) {
|
||||||
with (this) {
|
with (this) {
|
||||||
@ -189,7 +189,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t
|
|||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
const { createVNode: _createVNode } = _Vue
|
const { createVNode: _createVNode } = _Vue
|
||||||
|
|
||||||
const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toDisplayString(1), -1)
|
const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toDisplayString(1), -2 /* HOISTED */)
|
||||||
|
|
||||||
return function render(_ctx, _cache) {
|
return function render(_ctx, _cache) {
|
||||||
with (this) {
|
with (this) {
|
||||||
@ -323,7 +323,7 @@ return function render(_ctx, _cache) {
|
|||||||
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
return (_openBlock(), _createBlock(\\"div\\", null, [
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
_createVNode(\\"div\\", { ref: foo }, null, 32 /* NEED_PATCH */)
|
_createVNode(\\"div\\", { ref: foo }, null, -1 /* NEED_PATCH */)
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@ -346,7 +346,7 @@ exports[`compiler: hoistStatic transform should hoist v-for children if static 1
|
|||||||
const { createVNode: _createVNode } = _Vue
|
const { createVNode: _createVNode } = _Vue
|
||||||
|
|
||||||
const _hoisted_1 = { id: \\"foo\\" }
|
const _hoisted_1 = { id: \\"foo\\" }
|
||||||
const _hoisted_2 = _createVNode(\\"span\\", null, null, -1)
|
const _hoisted_2 = _createVNode(\\"span\\", null, null, -2 /* HOISTED */)
|
||||||
|
|
||||||
return function render(_ctx, _cache) {
|
return function render(_ctx, _cache) {
|
||||||
with (this) {
|
with (this) {
|
||||||
@ -371,7 +371,7 @@ const _hoisted_1 = {
|
|||||||
key: 0,
|
key: 0,
|
||||||
id: \\"foo\\"
|
id: \\"foo\\"
|
||||||
}
|
}
|
||||||
const _hoisted_2 = _createVNode(\\"span\\", null, null, -1)
|
const _hoisted_2 = _createVNode(\\"span\\", null, null, -2 /* HOISTED */)
|
||||||
|
|
||||||
return function render(_ctx, _cache) {
|
return function render(_ctx, _cache) {
|
||||||
with (this) {
|
with (this) {
|
||||||
|
@ -142,7 +142,7 @@ return function render(_ctx, _cache) {
|
|||||||
const _directive_foo = _resolveDirective(\\"foo\\")
|
const _directive_foo = _resolveDirective(\\"foo\\")
|
||||||
|
|
||||||
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(list, (i) => {
|
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(list, (i) => {
|
||||||
return _withDirectives((_openBlock(), _createBlock(\\"div\\", null, null, 32 /* NEED_PATCH */)), [
|
return _withDirectives((_openBlock(), _createBlock(\\"div\\", null, null, -1 /* NEED_PATCH */)), [
|
||||||
[_directive_foo]
|
[_directive_foo]
|
||||||
])
|
])
|
||||||
}), 256 /* UNKEYED_FRAGMENT */))
|
}), 256 /* UNKEYED_FRAGMENT */))
|
||||||
|
@ -52,7 +52,8 @@ function walk(
|
|||||||
) {
|
) {
|
||||||
if (!doNotHoistNode && isStaticNode(child, resultCache)) {
|
if (!doNotHoistNode && isStaticNode(child, resultCache)) {
|
||||||
// whole tree is static
|
// whole tree is static
|
||||||
;(child.codegenNode as VNodeCall).patchFlag = PatchFlags.HOISTED + ``
|
;(child.codegenNode as VNodeCall).patchFlag =
|
||||||
|
PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``)
|
||||||
const hoisted = context.transformHoist
|
const hoisted = context.transformHoist
|
||||||
? context.transformHoist(child, context)
|
? context.transformHoist(child, context)
|
||||||
: child.codegenNode!
|
: child.codegenNode!
|
||||||
|
@ -20,7 +20,7 @@ import {
|
|||||||
DirectiveArguments,
|
DirectiveArguments,
|
||||||
createVNodeCall
|
createVNodeCall
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { PatchFlags, PatchFlagNames, isSymbol } from '@vue/shared'
|
import { PatchFlags, PatchFlagNames, isSymbol, isOn } from '@vue/shared'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
import {
|
import {
|
||||||
RESOLVE_DIRECTIVE,
|
RESOLVE_DIRECTIVE,
|
||||||
@ -159,12 +159,18 @@ export const transformElement: NodeTransform = (node, context) => {
|
|||||||
// patchFlag & dynamicPropNames
|
// patchFlag & dynamicPropNames
|
||||||
if (patchFlag !== 0) {
|
if (patchFlag !== 0) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
|
if (patchFlag < 0) {
|
||||||
|
// special flags (negative and mutually exclusive)
|
||||||
|
vnodePatchFlag = patchFlag + ` /* ${PatchFlagNames[patchFlag]} */`
|
||||||
|
} else {
|
||||||
|
// bitwise flags
|
||||||
const flagNames = Object.keys(PatchFlagNames)
|
const flagNames = Object.keys(PatchFlagNames)
|
||||||
.map(Number)
|
.map(Number)
|
||||||
.filter(n => n > 0 && patchFlag & n)
|
.filter(n => n > 0 && patchFlag & n)
|
||||||
.map(n => PatchFlagNames[n])
|
.map(n => PatchFlagNames[n])
|
||||||
.join(`, `)
|
.join(`, `)
|
||||||
vnodePatchFlag = patchFlag + ` /* ${flagNames} */`
|
vnodePatchFlag = patchFlag + ` /* ${flagNames} */`
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
vnodePatchFlag = String(patchFlag)
|
vnodePatchFlag = String(patchFlag)
|
||||||
}
|
}
|
||||||
@ -256,20 +262,27 @@ export function buildProps(
|
|||||||
let hasRef = false
|
let hasRef = false
|
||||||
let hasClassBinding = false
|
let hasClassBinding = false
|
||||||
let hasStyleBinding = false
|
let hasStyleBinding = false
|
||||||
|
let hasHydrationEventBinding = false
|
||||||
let hasDynamicKeys = false
|
let hasDynamicKeys = false
|
||||||
const dynamicPropNames: string[] = []
|
const dynamicPropNames: string[] = []
|
||||||
|
|
||||||
const analyzePatchFlag = ({ key, value }: Property) => {
|
const analyzePatchFlag = ({ key, value }: Property) => {
|
||||||
if (key.type === NodeTypes.SIMPLE_EXPRESSION && key.isStatic) {
|
if (key.type === NodeTypes.SIMPLE_EXPRESSION && key.isStatic) {
|
||||||
|
const name = key.content
|
||||||
|
if (!isComponent && isOn(name) && name.toLowerCase() !== 'onclick') {
|
||||||
|
// This flag is for hydrating event handlers only. We omit the flag for
|
||||||
|
// click handlers becaues hydration gives click dedicated fast path.
|
||||||
|
hasHydrationEventBinding = true
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
value.type === NodeTypes.JS_CACHE_EXPRESSION ||
|
value.type === NodeTypes.JS_CACHE_EXPRESSION ||
|
||||||
((value.type === NodeTypes.SIMPLE_EXPRESSION ||
|
((value.type === NodeTypes.SIMPLE_EXPRESSION ||
|
||||||
value.type === NodeTypes.COMPOUND_EXPRESSION) &&
|
value.type === NodeTypes.COMPOUND_EXPRESSION) &&
|
||||||
isStaticNode(value))
|
isStaticNode(value))
|
||||||
) {
|
) {
|
||||||
|
// skip if the prop is a cached handler or has constant value
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const name = key.content
|
|
||||||
if (name === 'ref') {
|
if (name === 'ref') {
|
||||||
hasRef = true
|
hasRef = true
|
||||||
} else if (name === 'class') {
|
} else if (name === 'class') {
|
||||||
@ -430,9 +443,12 @@ export function buildProps(
|
|||||||
if (dynamicPropNames.length) {
|
if (dynamicPropNames.length) {
|
||||||
patchFlag |= PatchFlags.PROPS
|
patchFlag |= PatchFlags.PROPS
|
||||||
}
|
}
|
||||||
|
if (hasHydrationEventBinding) {
|
||||||
|
patchFlag |= PatchFlags.HYDRATE_EVENTS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (patchFlag === 0 && (hasRef || runtimeDirectives.length > 0)) {
|
if (patchFlag === 0 && (hasRef || runtimeDirectives.length > 0)) {
|
||||||
patchFlag |= PatchFlags.NEED_PATCH
|
patchFlag = PatchFlags.NEED_PATCH
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -7,7 +7,7 @@ return function render(_ctx, _cache) {
|
|||||||
with (this) {
|
with (this) {
|
||||||
const { vShow: _vShow, createVNode: _createVNode, withDirectives: _withDirectives, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
const { vShow: _vShow, createVNode: _createVNode, withDirectives: _withDirectives, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
return _withDirectives((_openBlock(), _createBlock(\\"div\\", null, null, 32 /* NEED_PATCH */)), [
|
return _withDirectives((_openBlock(), _createBlock(\\"div\\", null, null, -1 /* NEED_PATCH */)), [
|
||||||
[_vShow, a]
|
[_vShow, a]
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,8 @@ import {
|
|||||||
isReservedProp,
|
isReservedProp,
|
||||||
isFunction,
|
isFunction,
|
||||||
PatchFlags,
|
PatchFlags,
|
||||||
NOOP
|
NOOP,
|
||||||
|
isOn
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
queueJob,
|
queueJob,
|
||||||
@ -188,6 +189,7 @@ export function createRenderer<
|
|||||||
options: RendererOptions<HostNode, HostElement>
|
options: RendererOptions<HostNode, HostElement>
|
||||||
): {
|
): {
|
||||||
render: RootRenderFunction<HostNode, HostElement>
|
render: RootRenderFunction<HostNode, HostElement>
|
||||||
|
hydrate: RootRenderFunction<HostNode, HostElement>
|
||||||
createApp: CreateAppFunction<HostElement>
|
createApp: CreateAppFunction<HostElement>
|
||||||
} {
|
} {
|
||||||
type HostVNode = VNode<HostNode, HostElement>
|
type HostVNode = VNode<HostNode, HostElement>
|
||||||
@ -426,9 +428,10 @@ export function createRenderer<
|
|||||||
// props
|
// props
|
||||||
if (props != null) {
|
if (props != null) {
|
||||||
for (const key in props) {
|
for (const key in props) {
|
||||||
if (isReservedProp(key)) continue
|
if (!isReservedProp(key)) {
|
||||||
hostPatchProp(el, key, props[key], null, isSVG)
|
hostPatchProp(el, key, props[key], null, isSVG)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (props.onVnodeBeforeMount != null) {
|
if (props.onVnodeBeforeMount != null) {
|
||||||
invokeDirectiveHook(props.onVnodeBeforeMount, parentComponent, vnode)
|
invokeDirectiveHook(props.onVnodeBeforeMount, parentComponent, vnode)
|
||||||
}
|
}
|
||||||
@ -1813,8 +1816,118 @@ export function createRenderer<
|
|||||||
container._vnode = vnode
|
container._vnode = vnode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hydrate(vnode: HostVNode, container: any) {
|
||||||
|
hydrateNode(container.firstChild, vnode, container)
|
||||||
|
flushPostFlushCbs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO handle mismatches
|
||||||
|
function hydrateNode(
|
||||||
|
node: any,
|
||||||
|
vnode: HostVNode,
|
||||||
|
container: any,
|
||||||
|
parentComponent: ComponentInternalInstance | null = null
|
||||||
|
): any {
|
||||||
|
const { type, shapeFlag } = vnode
|
||||||
|
switch (type) {
|
||||||
|
case Text:
|
||||||
|
case Comment:
|
||||||
|
case Static:
|
||||||
|
vnode.el = node
|
||||||
|
return node.nextSibling
|
||||||
|
case Fragment:
|
||||||
|
vnode.el = node
|
||||||
|
const anchor = (vnode.anchor = hydrateChildren(
|
||||||
|
node.nextSibling,
|
||||||
|
vnode.children as HostVNode[],
|
||||||
|
container,
|
||||||
|
parentComponent
|
||||||
|
))
|
||||||
|
return anchor.nextSibling
|
||||||
|
case Portal:
|
||||||
|
// TODO
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
if (shapeFlag & ShapeFlags.ELEMENT) {
|
||||||
|
return hydrateElement(node, vnode, parentComponent)
|
||||||
|
} else if (shapeFlag & ShapeFlags.COMPONENT) {
|
||||||
|
// TODO
|
||||||
|
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
|
||||||
|
// TODO
|
||||||
|
} else if (__DEV__) {
|
||||||
|
warn('Invalid HostVNode type:', type, `(${typeof type})`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hydrateElement(
|
||||||
|
el: any,
|
||||||
|
vnode: HostVNode,
|
||||||
|
parentComponent: ComponentInternalInstance | null
|
||||||
|
) {
|
||||||
|
vnode.el = el
|
||||||
|
const { props, patchFlag } = vnode
|
||||||
|
// skip props & children if this is hoisted static nodes
|
||||||
|
if (patchFlag !== PatchFlags.HOISTED) {
|
||||||
|
// props
|
||||||
|
if (props !== null) {
|
||||||
|
if (
|
||||||
|
patchFlag & PatchFlags.FULL_PROPS ||
|
||||||
|
patchFlag & PatchFlags.HYDRATE_EVENTS
|
||||||
|
) {
|
||||||
|
for (const key in props) {
|
||||||
|
if (!isReservedProp(key) && isOn(key)) {
|
||||||
|
hostPatchProp(el, key, props[key], null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (props.onClick != null) {
|
||||||
|
// Fast path for click listeners (which is most often) to avoid
|
||||||
|
// iterating through props.
|
||||||
|
hostPatchProp(el, 'onClick', props.onClick, null)
|
||||||
|
}
|
||||||
|
// vnode mounted hook
|
||||||
|
const { onVnodeMounted } = props
|
||||||
|
if (onVnodeMounted != null) {
|
||||||
|
queuePostFlushCb(() => {
|
||||||
|
invokeDirectiveHook(onVnodeMounted, parentComponent, vnode, null)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// children
|
||||||
|
if (
|
||||||
|
vnode.shapeFlag & ShapeFlags.ARRAY_CHILDREN &&
|
||||||
|
// skip if element has innerHTML / textContent
|
||||||
|
!(props !== null && (props.innerHTML || props.textContent))
|
||||||
|
) {
|
||||||
|
hydrateChildren(
|
||||||
|
el.firstChild,
|
||||||
|
vnode.children as HostVNode[],
|
||||||
|
el,
|
||||||
|
parentComponent
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return el.nextSibling
|
||||||
|
}
|
||||||
|
|
||||||
|
function hydrateChildren(
|
||||||
|
node: any,
|
||||||
|
vnodes: HostVNode[],
|
||||||
|
container: any,
|
||||||
|
parentComponent: ComponentInternalInstance | null = null
|
||||||
|
) {
|
||||||
|
for (let i = 0; i < vnodes.length; i++) {
|
||||||
|
// TODO can skip normalizeVNode in optimized mode
|
||||||
|
// (need hint on rendered markup?)
|
||||||
|
const vnode = (vnodes[i] = normalizeVNode(vnodes[i]))
|
||||||
|
node = hydrateNode(node, vnode, container, parentComponent)
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
render,
|
render,
|
||||||
|
hydrate,
|
||||||
createApp: createAppAPI(render)
|
createApp: createAppAPI(render)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,8 @@ import {
|
|||||||
EMPTY_ARR,
|
EMPTY_ARR,
|
||||||
extend,
|
extend,
|
||||||
normalizeClass,
|
normalizeClass,
|
||||||
normalizeStyle
|
normalizeStyle,
|
||||||
|
PatchFlags
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
ComponentInternalInstance,
|
ComponentInternalInstance,
|
||||||
@ -277,7 +278,11 @@ export function createVNode(
|
|||||||
if (
|
if (
|
||||||
shouldTrack > 0 &&
|
shouldTrack > 0 &&
|
||||||
currentBlock !== null &&
|
currentBlock !== null &&
|
||||||
|
// the EVENTS flag is only for hydration and if it is the only flag, the
|
||||||
|
// vnode should not be considered dynamic.
|
||||||
|
patchFlag !== PatchFlags.HYDRATE_EVENTS &&
|
||||||
(patchFlag > 0 ||
|
(patchFlag > 0 ||
|
||||||
|
patchFlag === PatchFlags.NEED_PATCH ||
|
||||||
shapeFlag & ShapeFlags.SUSPENSE ||
|
shapeFlag & ShapeFlags.SUSPENSE ||
|
||||||
shapeFlag & ShapeFlags.STATEFUL_COMPONENT ||
|
shapeFlag & ShapeFlags.STATEFUL_COMPONENT ||
|
||||||
shapeFlag & ShapeFlags.FUNCTIONAL_COMPONENT)
|
shapeFlag & ShapeFlags.FUNCTIONAL_COMPONENT)
|
||||||
|
@ -9,13 +9,18 @@ import { patchProp } from './patchProp'
|
|||||||
// Importing from the compiler, will be tree-shaken in prod
|
// Importing from the compiler, will be tree-shaken in prod
|
||||||
import { isFunction, isString, isHTMLTag, isSVGTag } from '@vue/shared'
|
import { isFunction, isString, isHTMLTag, isSVGTag } from '@vue/shared'
|
||||||
|
|
||||||
const { render: baseRender, createApp: baseCreateApp } = createRenderer({
|
const {
|
||||||
|
render: baseRender,
|
||||||
|
hydrate: baseHydrate,
|
||||||
|
createApp: baseCreateApp
|
||||||
|
} = createRenderer({
|
||||||
patchProp,
|
patchProp,
|
||||||
...nodeOps
|
...nodeOps
|
||||||
})
|
})
|
||||||
|
|
||||||
// use explicit type casts here to avoid import() calls in rolled-up d.ts
|
// use explicit type casts here to avoid import() calls in rolled-up d.ts
|
||||||
export const render = baseRender as RootRenderFunction<Node, Element>
|
export const render = baseRender as RootRenderFunction<Node, Element>
|
||||||
|
export const hydrate = baseHydrate as RootRenderFunction<Node, Element>
|
||||||
|
|
||||||
export const createApp: CreateAppFunction<Element> = (...args) => {
|
export const createApp: CreateAppFunction<Element> = (...args) => {
|
||||||
const app = baseCreateApp(...args)
|
const app = baseCreateApp(...args)
|
||||||
|
@ -40,12 +40,9 @@ export const enum PatchFlags {
|
|||||||
// exclusive with CLASS, STYLE and PROPS.
|
// exclusive with CLASS, STYLE and PROPS.
|
||||||
FULL_PROPS = 1 << 4,
|
FULL_PROPS = 1 << 4,
|
||||||
|
|
||||||
// Indicates an element that only needs non-props patching, e.g. ref or
|
// Indicates an element with event listeners (which need to be attached
|
||||||
// directives (onVnodeXXX hooks). It simply marks the vnode as "need patch",
|
// during hydration)
|
||||||
// since every patched vnode checks for refs and onVnodeXXX hooks.
|
HYDRATE_EVENTS = 1 << 5,
|
||||||
// This flag is never directly matched against, it simply serves as a non-zero
|
|
||||||
// value.
|
|
||||||
NEED_PATCH = 1 << 5,
|
|
||||||
|
|
||||||
// Indicates a fragment whose children order doesn't change.
|
// Indicates a fragment whose children order doesn't change.
|
||||||
STABLE_FRAGMENT = 1 << 6,
|
STABLE_FRAGMENT = 1 << 6,
|
||||||
@ -61,14 +58,28 @@ export const enum PatchFlags {
|
|||||||
// Components with this flag are always force updated.
|
// Components with this flag are always force updated.
|
||||||
DYNAMIC_SLOTS = 1 << 9,
|
DYNAMIC_SLOTS = 1 << 9,
|
||||||
|
|
||||||
// A special flag that indicates a hoisted, static vnode.
|
// SPECIAL FLAGS -------------------------------------------------------------
|
||||||
HOISTED = -1,
|
|
||||||
|
// Special flags are negative integers. They are never matched against using
|
||||||
|
// bitwise operators (bitwise matching should only happen in branches where
|
||||||
|
// patchFlag > 0), and are mutually exclusive. When checking for a speical
|
||||||
|
// flag, simply check patchFlag === FLAG.
|
||||||
|
|
||||||
|
// Indicates an element that only needs non-props patching, e.g. ref or
|
||||||
|
// directives (onVnodeXXX hooks). since every patched vnode checks for refs
|
||||||
|
// and onVnodeXXX hooks, itt simply marks the vnode so that a parent block
|
||||||
|
// will track it.
|
||||||
|
NEED_PATCH = -1,
|
||||||
|
|
||||||
|
// Indicates a hoisted static vnode. This is a hint for hydration to skip
|
||||||
|
// the entire sub tree since static content never needs to be updated.
|
||||||
|
HOISTED = -2,
|
||||||
|
|
||||||
// A special flag that indicates that the diffing algorithm should bail out
|
// A special flag that indicates that the diffing algorithm should bail out
|
||||||
// of optimized mode. This is only on block fragments created by renderSlot()
|
// of optimized mode. This is only on block fragments created by renderSlot()
|
||||||
// when encountering non-compiler generated slots (i.e. manually written
|
// when encountering non-compiler generated slots (i.e. manually written
|
||||||
// render functions, which should always be fully diffed)
|
// render functions, which should always be fully diffed)
|
||||||
BAIL = -2
|
BAIL = -3
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtime object for public consumption
|
// runtime object for public consumption
|
||||||
@ -91,11 +102,13 @@ export const PatchFlagNames = {
|
|||||||
[PatchFlags.CLASS]: `CLASS`,
|
[PatchFlags.CLASS]: `CLASS`,
|
||||||
[PatchFlags.STYLE]: `STYLE`,
|
[PatchFlags.STYLE]: `STYLE`,
|
||||||
[PatchFlags.PROPS]: `PROPS`,
|
[PatchFlags.PROPS]: `PROPS`,
|
||||||
[PatchFlags.NEED_PATCH]: `NEED_PATCH`,
|
|
||||||
[PatchFlags.FULL_PROPS]: `FULL_PROPS`,
|
[PatchFlags.FULL_PROPS]: `FULL_PROPS`,
|
||||||
|
[PatchFlags.HYDRATE_EVENTS]: `HYDRATE_EVENTS`,
|
||||||
[PatchFlags.STABLE_FRAGMENT]: `STABLE_FRAGMENT`,
|
[PatchFlags.STABLE_FRAGMENT]: `STABLE_FRAGMENT`,
|
||||||
[PatchFlags.KEYED_FRAGMENT]: `KEYED_FRAGMENT`,
|
[PatchFlags.KEYED_FRAGMENT]: `KEYED_FRAGMENT`,
|
||||||
[PatchFlags.UNKEYED_FRAGMENT]: `UNKEYED_FRAGMENT`,
|
[PatchFlags.UNKEYED_FRAGMENT]: `UNKEYED_FRAGMENT`,
|
||||||
[PatchFlags.DYNAMIC_SLOTS]: `DYNAMIC_SLOTS`,
|
[PatchFlags.DYNAMIC_SLOTS]: `DYNAMIC_SLOTS`,
|
||||||
|
[PatchFlags.NEED_PATCH]: `NEED_PATCH`,
|
||||||
|
[PatchFlags.HOISTED]: `HOISTED`,
|
||||||
[PatchFlags.BAIL]: `BAIL`
|
[PatchFlags.BAIL]: `BAIL`
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user