mirror of https://github.com/vuejs/core.git
feat(runtime-vapor): add support for v-once
This commit is contained in:
parent
1ef6e6edb7
commit
9087cdf3cb
|
@ -5,6 +5,7 @@ import {
|
||||||
onUpdated,
|
onUpdated,
|
||||||
provide,
|
provide,
|
||||||
ref,
|
ref,
|
||||||
|
useAttrs,
|
||||||
watch,
|
watch,
|
||||||
watchEffect,
|
watchEffect,
|
||||||
} from '@vue/runtime-dom'
|
} from '@vue/runtime-dom'
|
||||||
|
@ -12,6 +13,7 @@ import {
|
||||||
createComponent,
|
createComponent,
|
||||||
createIf,
|
createIf,
|
||||||
createTextNode,
|
createTextNode,
|
||||||
|
defineVaporComponent,
|
||||||
renderEffect,
|
renderEffect,
|
||||||
template,
|
template,
|
||||||
} from '../src'
|
} from '../src'
|
||||||
|
@ -288,6 +290,66 @@ describe('component', () => {
|
||||||
expect(i.scope.effects.length).toBe(0)
|
expect(i.scope.effects.length).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('work with v-once + props', () => {
|
||||||
|
const Child = defineVaporComponent({
|
||||||
|
props: {
|
||||||
|
count: Number,
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const n0 = template(' ')() as any
|
||||||
|
renderEffect(() => setText(n0, props.count))
|
||||||
|
return n0
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const count = ref(0)
|
||||||
|
const { html } = define({
|
||||||
|
setup() {
|
||||||
|
return createComponent(
|
||||||
|
Child,
|
||||||
|
{ count: () => count.value },
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true, // v-once
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}).render()
|
||||||
|
|
||||||
|
expect(html()).toBe('0')
|
||||||
|
|
||||||
|
count.value++
|
||||||
|
expect(html()).toBe('0')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('work with v-once + attrs', () => {
|
||||||
|
const Child = defineVaporComponent({
|
||||||
|
setup() {
|
||||||
|
const attrs = useAttrs()
|
||||||
|
const n0 = template(' ')() as any
|
||||||
|
renderEffect(() => setText(n0, attrs.count as string))
|
||||||
|
return n0
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const count = ref(0)
|
||||||
|
const { html } = define({
|
||||||
|
setup() {
|
||||||
|
return createComponent(
|
||||||
|
Child,
|
||||||
|
{ count: () => count.value },
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true, // v-once
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}).render()
|
||||||
|
|
||||||
|
expect(html()).toBe('0')
|
||||||
|
|
||||||
|
count.value++
|
||||||
|
expect(html()).toBe('0')
|
||||||
|
})
|
||||||
|
|
||||||
test('should mount component only with template in production mode', () => {
|
test('should mount component only with template in production mode', () => {
|
||||||
__DEV__ = false
|
__DEV__ = false
|
||||||
const { component: Child } = define({
|
const { component: Child } = define({
|
||||||
|
|
|
@ -41,6 +41,7 @@ const mountApp: AppMountFn<ParentNode> = (app, container) => {
|
||||||
app._props as RawProps,
|
app._props as RawProps,
|
||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
app._context,
|
app._context,
|
||||||
)
|
)
|
||||||
mountComponent(instance, container)
|
mountComponent(instance, container)
|
||||||
|
@ -61,6 +62,7 @@ const hydrateApp: AppMountFn<ParentNode> = (app, container) => {
|
||||||
app._props as RawProps,
|
app._props as RawProps,
|
||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
app._context,
|
app._context,
|
||||||
)
|
)
|
||||||
mountComponent(instance, container)
|
mountComponent(instance, container)
|
||||||
|
|
|
@ -10,6 +10,7 @@ export function createDynamicComponent(
|
||||||
rawProps?: RawProps | null,
|
rawProps?: RawProps | null,
|
||||||
rawSlots?: RawSlots | null,
|
rawSlots?: RawSlots | null,
|
||||||
isSingleRoot?: boolean,
|
isSingleRoot?: boolean,
|
||||||
|
once?: boolean,
|
||||||
): VaporFragment {
|
): VaporFragment {
|
||||||
const frag = __DEV__
|
const frag = __DEV__
|
||||||
? new DynamicFragment('dynamic-component')
|
? new DynamicFragment('dynamic-component')
|
||||||
|
@ -23,6 +24,7 @@ export function createDynamicComponent(
|
||||||
rawProps,
|
rawProps,
|
||||||
rawSlots,
|
rawSlots,
|
||||||
isSingleRoot,
|
isSingleRoot,
|
||||||
|
once,
|
||||||
),
|
),
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
|
|
|
@ -134,6 +134,7 @@ export function createComponent(
|
||||||
rawProps?: LooseRawProps | null,
|
rawProps?: LooseRawProps | null,
|
||||||
rawSlots?: LooseRawSlots | null,
|
rawSlots?: LooseRawSlots | null,
|
||||||
isSingleRoot?: boolean,
|
isSingleRoot?: boolean,
|
||||||
|
once?: boolean,
|
||||||
appContext: GenericAppContext = (currentInstance &&
|
appContext: GenericAppContext = (currentInstance &&
|
||||||
currentInstance.appContext) ||
|
currentInstance.appContext) ||
|
||||||
emptyContext,
|
emptyContext,
|
||||||
|
@ -180,6 +181,7 @@ export function createComponent(
|
||||||
rawProps as RawProps,
|
rawProps as RawProps,
|
||||||
rawSlots as RawSlots,
|
rawSlots as RawSlots,
|
||||||
appContext,
|
appContext,
|
||||||
|
once,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
|
@ -380,6 +382,7 @@ export class VaporComponentInstance implements GenericComponentInstance {
|
||||||
rawProps?: RawProps | null,
|
rawProps?: RawProps | null,
|
||||||
rawSlots?: RawSlots | null,
|
rawSlots?: RawSlots | null,
|
||||||
appContext?: GenericAppContext,
|
appContext?: GenericAppContext,
|
||||||
|
once?: boolean,
|
||||||
) {
|
) {
|
||||||
this.vapor = true
|
this.vapor = true
|
||||||
this.uid = nextUid()
|
this.uid = nextUid()
|
||||||
|
@ -420,7 +423,7 @@ export class VaporComponentInstance implements GenericComponentInstance {
|
||||||
this.rawProps = rawProps || EMPTY_OBJ
|
this.rawProps = rawProps || EMPTY_OBJ
|
||||||
this.hasFallthrough = hasFallthroughAttrs(comp, rawProps)
|
this.hasFallthrough = hasFallthroughAttrs(comp, rawProps)
|
||||||
if (rawProps || comp.props) {
|
if (rawProps || comp.props) {
|
||||||
const [propsHandlers, attrsHandlers] = getPropsProxyHandlers(comp)
|
const [propsHandlers, attrsHandlers] = getPropsProxyHandlers(comp, once)
|
||||||
this.attrs = new Proxy(this, attrsHandlers)
|
this.attrs = new Proxy(this, attrsHandlers)
|
||||||
this.props = comp.props
|
this.props = comp.props
|
||||||
? new Proxy(this, propsHandlers!)
|
? new Proxy(this, propsHandlers!)
|
||||||
|
@ -465,9 +468,10 @@ export function createComponentWithFallback(
|
||||||
rawProps?: LooseRawProps | null,
|
rawProps?: LooseRawProps | null,
|
||||||
rawSlots?: LooseRawSlots | null,
|
rawSlots?: LooseRawSlots | null,
|
||||||
isSingleRoot?: boolean,
|
isSingleRoot?: boolean,
|
||||||
|
once?: boolean,
|
||||||
): HTMLElement | VaporComponentInstance {
|
): HTMLElement | VaporComponentInstance {
|
||||||
if (!isString(comp)) {
|
if (!isString(comp)) {
|
||||||
return createComponent(comp, rawProps, rawSlots, isSingleRoot)
|
return createComponent(comp, rawProps, rawSlots, isSingleRoot, once)
|
||||||
}
|
}
|
||||||
|
|
||||||
const el = document.createElement(comp)
|
const el = document.createElement(comp)
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
} from '@vue/runtime-dom'
|
} from '@vue/runtime-dom'
|
||||||
import { normalizeEmitsOptions } from './componentEmits'
|
import { normalizeEmitsOptions } from './componentEmits'
|
||||||
import { renderEffect } from './renderEffect'
|
import { renderEffect } from './renderEffect'
|
||||||
|
import { pauseTracking, resetTracking } from '@vue/reactivity'
|
||||||
|
|
||||||
export type RawProps = Record<string, () => unknown> & {
|
export type RawProps = Record<string, () => unknown> & {
|
||||||
// generated by compiler for :[key]="x" or v-bind="x"
|
// generated by compiler for :[key]="x" or v-bind="x"
|
||||||
|
@ -42,6 +43,7 @@ export function resolveSource(
|
||||||
|
|
||||||
export function getPropsProxyHandlers(
|
export function getPropsProxyHandlers(
|
||||||
comp: VaporComponent,
|
comp: VaporComponent,
|
||||||
|
once?: boolean,
|
||||||
): [
|
): [
|
||||||
ProxyHandler<VaporComponentInstance> | null,
|
ProxyHandler<VaporComponentInstance> | null,
|
||||||
ProxyHandler<VaporComponentInstance>,
|
ProxyHandler<VaporComponentInstance>,
|
||||||
|
@ -107,9 +109,18 @@ export function getPropsProxyHandlers(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getPropValue = once
|
||||||
|
? (...args: Parameters<typeof getProp>) => {
|
||||||
|
pauseTracking()
|
||||||
|
const value = getProp(...args)
|
||||||
|
resetTracking()
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
: getProp
|
||||||
|
|
||||||
const propsHandlers = propsOptions
|
const propsHandlers = propsOptions
|
||||||
? ({
|
? ({
|
||||||
get: (target, key) => getProp(target, key),
|
get: (target, key) => getPropValue(target, key),
|
||||||
has: (_, key) => isProp(key),
|
has: (_, key) => isProp(key),
|
||||||
ownKeys: () => Object.keys(propsOptions),
|
ownKeys: () => Object.keys(propsOptions),
|
||||||
getOwnPropertyDescriptor(target, key) {
|
getOwnPropertyDescriptor(target, key) {
|
||||||
|
@ -117,7 +128,7 @@ export function getPropsProxyHandlers(
|
||||||
return {
|
return {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
get: () => getProp(target, key),
|
get: () => getPropValue(target, key),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -145,8 +156,17 @@ export function getPropsProxyHandlers(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAttrValue = once
|
||||||
|
? (...args: Parameters<typeof getAttr>) => {
|
||||||
|
pauseTracking()
|
||||||
|
const value = getAttr(...args)
|
||||||
|
resetTracking()
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
: getAttr
|
||||||
|
|
||||||
const attrsHandlers = {
|
const attrsHandlers = {
|
||||||
get: (target, key: string) => getAttr(target.rawProps, key),
|
get: (target, key: string) => getAttrValue(target.rawProps, key),
|
||||||
has: (target, key: string) => hasAttr(target.rawProps, key),
|
has: (target, key: string) => hasAttr(target.rawProps, key),
|
||||||
ownKeys: target => getKeysFromRawProps(target.rawProps).filter(isAttr),
|
ownKeys: target => getKeysFromRawProps(target.rawProps).filter(isAttr),
|
||||||
getOwnPropertyDescriptor(target, key: string) {
|
getOwnPropertyDescriptor(target, key: string) {
|
||||||
|
@ -154,7 +174,7 @@ export function getPropsProxyHandlers(
|
||||||
return {
|
return {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
get: () => getAttr(target.rawProps, key),
|
get: () => getAttrValue(target.rawProps, key),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -210,7 +230,8 @@ export function hasAttrFromRawProps(rawProps: RawProps, key: string): boolean {
|
||||||
if (dynamicSources) {
|
if (dynamicSources) {
|
||||||
let i = dynamicSources.length
|
let i = dynamicSources.length
|
||||||
while (i--) {
|
while (i--) {
|
||||||
if (hasOwn(resolveSource(dynamicSources[i]), key)) {
|
const source = resolveSource(dynamicSources[i])
|
||||||
|
if (source && hasOwn(source, key)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue