Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime): support delay initializing props (fix #2325) #2353

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions packages/runtime-core/__tests__/rendererElement.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {
createRenderer,
h,
render,
nodeOps,
TestElement,
serializeInner as inner
render,
serializeInner as inner,
TestElement
} from '@vue/runtime-test'
import { extend } from '@vue/shared'

describe('renderer: element', () => {
let root: TestElement
Expand Down Expand Up @@ -48,4 +50,50 @@ describe('renderer: element', () => {
render(h('div', { id: 'baz', class: 'bar' }, ['foo']), root)
expect(inner(root)).toBe('<div id="baz" class="bar">foo</div>')
})

it('should delay the initializing of specific props', () => {
const queue: string[] = []
const { render } = createRenderer(
extend(
{
patchProp(
el: TestElement,
key: string,
prevValue: any,
nextValue: any
) {
el.props[key] = nextValue
queue.push(key)
},
delayInitProp(el: Element, key: string) {
return key.startsWith('delay')
? key.startsWith('delay-weak')
? -1
: 1
: 0
}
},
nodeOps
)
)

render(
h('div', {
'delay-a': 'a',
'delay-b': 'b',
'delay-weak-a': 'a',
id: 'baz'
}),
root
)
expect(queue.length).toBe(4)
expect(queue[0]).toBe('id')
expect(queue[1]).toBe('delay-weak-a')
expect(queue[2]).toBe('delay-a')
expect(queue[3]).toBe('delay-b')

expect(inner(root)).toBe(
'<div id="baz" delay-weak-a="a" delay-a="a" delay-b="b"></div>'
)
})
})
88 changes: 75 additions & 13 deletions packages/runtime-core/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,24 +119,40 @@ export interface RendererOptions<
parentSuspense?: SuspenseBoundary | null,
unmountChildren?: UnmountChildrenFn
): void

forcePatchProp?(el: HostElement, key: string): boolean

delayInitProp?(el: HostElement, key: string): number | boolean

insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void

remove(el: HostNode): void

createElement(
type: string,
isSVG?: boolean,
isCustomizedBuiltIn?: string,
vnodeProps?: (VNodeProps & { [key: string]: any }) | null
): HostElement

createText(text: string): HostNode

createComment(text: string): HostNode

setText(node: HostNode, text: string): void

setElementText(node: HostElement, text: string): void

parentNode(node: HostNode): HostElement | null

nextSibling(node: HostNode): HostNode | null

querySelector?(selector: string): HostElement | null

setScopeId?(el: HostElement, id: string): void

cloneNode?(node: HostNode): HostNode

insertStaticContent?(
content: string,
parent: HostElement,
Expand Down Expand Up @@ -463,6 +479,7 @@ function baseCreateRenderer(
remove: hostRemove,
patchProp: hostPatchProp,
forcePatchProp: hostForcePatchProp,
delayInitProp: hostDelayInitProp,
createElement: hostCreateElement,
createText: hostCreateText,
createComment: hostCreateComment,
Expand Down Expand Up @@ -780,21 +797,66 @@ function baseCreateRenderer(
}
// props
if (props) {
for (const key in props) {
if (!isReservedProp(key)) {
hostPatchProp(
el,
key,
null,
props[key],
isSVG,
vnode.children as VNode[],
parentComponent,
parentSuspense,
unmountChildren
)
if (!hostDelayInitProp) {
for (const key in props) {
if (!isReservedProp(key)) {
hostPatchProp(
el,
key,
null,
props[key],
isSVG,
vnode.children as VNode[],
parentComponent,
parentSuspense,
unmountChildren
)
}
}
} else {
let delayed: string[] | undefined
let priority
for (const key in props) {
if (!isReservedProp(key)) {
if ((priority = hostDelayInitProp(el, key))) {
!delayed && (delayed = [])
if (priority > 0 || priority === true) {
delayed.push(key)
} else {
delayed.unshift(key)
}
} else {
hostPatchProp(
el,
key,
null,
props[key],
isSVG,
vnode.children as VNode[],
parentComponent,
parentSuspense,
unmountChildren
)
}
}
}
if (delayed) {
for (const key of delayed) {
hostPatchProp(
el,
key,
null,
props[key],
isSVG,
vnode.children as VNode[],
parentComponent,
parentSuspense,
unmountChildren
)
}
}
}

if ((vnodeHook = props.onVnodeBeforeMount)) {
invokeVNodeHook(vnodeHook, parentComponent, vnode)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-dom/__tests__/patchAttrs.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { patchProp } from '../src/patchProp'
import { patchProp } from '../src/propOps'
import { xlinkNS } from '../src/modules/attrs'

describe('runtime-dom: attrs patching', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-dom/__tests__/patchClass.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { patchProp } from '../src/patchProp'
import { patchProp } from '../src/propOps'
import { ElementWithTransition } from '../src/components/Transition'
import { svgNS } from '../src/nodeOps'

Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-dom/__tests__/patchEvents.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { patchProp } from '../src/patchProp'
import { patchProp } from '../src/propOps'

const timeout = () => new Promise(r => setTimeout(r))

Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-dom/__tests__/patchProps.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { patchProp } from '../src/patchProp'
import { patchProp } from '../src/propOps'
import { render, h } from '../src'

describe('runtime-dom: props patching', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-dom/__tests__/patchStyle.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { patchProp } from '../src/patchProp'
import { patchProp } from '../src/propOps'

describe(`runtime-dom: style patching`, () => {
it('string', () => {
Expand Down
9 changes: 4 additions & 5 deletions packages/runtime-dom/src/directives/vModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ type ModelDirective<T> = ObjectDirective<T & { _assign: AssignerFn }>
export const vModelText: ModelDirective<
HTMLInputElement | HTMLTextAreaElement
> = {
created(el, { modifiers: { lazy, trim, number } }, vnode) {
created(el, { value, modifiers: { lazy, trim, number } }, vnode) {
// set value by props. #2325
// vnode.props at least contains onUpdate:modelValue
vnode.props!['value'] = value == null ? '' : value
el._assign = getModelAssigner(vnode)
const castToNumber = number || el.type === 'number'
addEventListener(el, lazy ? 'change' : 'input', e => {
Expand Down Expand Up @@ -75,10 +78,6 @@ export const vModelText: ModelDirective<
addEventListener(el, 'change', onCompositionEnd)
}
},
// set value on mounted so it's after min/max for type="range"
mounted(el, { value }) {
el.value = value == null ? '' : value
},
beforeUpdate(el, { value, modifiers: { trim, number } }, vnode) {
el._assign = getModelAssigner(vnode)
// avoid clearing unresolved text. #2302
Expand Down
7 changes: 5 additions & 2 deletions packages/runtime-dom/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
compatUtils
} from '@vue/runtime-core'
import { nodeOps } from './nodeOps'
import { patchProp, forcePatchProp } from './patchProp'
import { patchProp, forcePatchProp, delayInitProp } from './propOps'
// Importing from the compiler, will be tree-shaken in prod
import { isFunction, isString, isHTMLTag, isSVGTag, extend } from '@vue/shared'

Expand All @@ -24,7 +24,10 @@ declare module '@vue/reactivity' {
}
}

const rendererOptions = extend({ patchProp, forcePatchProp }, nodeOps)
const rendererOptions = extend(
{ patchProp, forcePatchProp, delayInitProp },
nodeOps
)

// lazy create the renderer - this makes core renderer logic tree-shakable
// in case the user only imports reactivity utilities from Vue.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ type DOMRendererOptions = RendererOptions<Node, Element>
export const forcePatchProp: DOMRendererOptions['forcePatchProp'] = (_, key) =>
key === 'value'

export const delayInitProp: DOMRendererOptions['delayInitProp'] = (_, key) =>
key === 'value'

export const patchProp: DOMRendererOptions['patchProp'] = (
el,
key,
Expand Down