From: daiwei Date: Wed, 16 Jul 2025 13:44:27 +0000 (+0800) Subject: chore: Merge branch 'minor' into edison/feat/vaporTeleport X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f82ca3e297d79a042f442003e0461d82a7462188;p=thirdparty%2Fvuejs%2Fcore.git chore: Merge branch 'minor' into edison/feat/vaporTeleport --- f82ca3e297d79a042f442003e0461d82a7462188 diff --cc packages/runtime-vapor/src/apiCreateFor.ts index a0f780406c,426a5c56b5..0511240852 --- a/packages/runtime-vapor/src/apiCreateFor.ts +++ b/packages/runtime-vapor/src/apiCreateFor.ts @@@ -10,10 -9,16 +9,11 @@@ import shallowRef, toReactive, toReadonly, + watch, } from '@vue/reactivity' - import { getSequence, isArray, isObject, isString } from '@vue/shared' + import { isArray, isObject, isString } from '@vue/shared' import { createComment, createTextNode } from './dom/node' -import { - type Block, - VaporFragment, - insert, - remove as removeBlock, -} from './block' +import { type Block, insert, remove as removeBlock } from './block' import { warn } from '@vue/runtime-dom' import { currentInstance, isVaporComponent } from './component' import type { DynamicSlot } from './componentSlots' diff --cc packages/runtime-vapor/src/fragment.ts index 3e4fcb221c,0000000000..0b1bc3a363 mode 100644,000000..100644 --- a/packages/runtime-vapor/src/fragment.ts +++ b/packages/runtime-vapor/src/fragment.ts @@@ -1,69 -1,0 +1,69 @@@ - import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity' ++import { EffectScope, setActiveSub } from '@vue/reactivity' +import { createComment, createTextNode } from './dom/node' +import { type Block, type BlockFn, insert, isValidBlock, remove } from './block' + +export class VaporFragment { + nodes: Block + target?: ParentNode | null + targetAnchor?: Node | null + anchor?: Node + insert?: (parent: ParentNode, anchor: Node | null) => void + remove?: (parent?: ParentNode) => void + getNodes?: () => Block + + constructor(nodes: Block) { + this.nodes = nodes + } +} + +export class DynamicFragment extends VaporFragment { + anchor: Node + scope: EffectScope | undefined + current?: BlockFn + fallback?: BlockFn + + constructor(anchorLabel?: string) { + super([]) + this.anchor = + __DEV__ && anchorLabel ? createComment(anchorLabel) : createTextNode() + } + + update(render?: BlockFn, key: any = render): void { + if (key === this.current) { + return + } + this.current = key + - pauseTracking() ++ const prevSub = setActiveSub() + const parent = this.anchor.parentNode + + // teardown previous branch + if (this.scope) { + this.scope.stop() + parent && remove(this.nodes, parent) + } + + if (render) { + this.scope = new EffectScope() + this.nodes = this.scope.run(render) || [] + if (parent) insert(this.nodes, parent, this.anchor) + } else { + this.scope = undefined + this.nodes = [] + } + + if (this.fallback && !isValidBlock(this.nodes)) { + parent && remove(this.nodes, parent) + this.nodes = + (this.scope || (this.scope = new EffectScope())).run(this.fallback) || + [] + parent && insert(this.nodes, parent, this.anchor) + } + - resetTracking() ++ setActiveSub(prevSub) + } +} + +export function isFragment(val: NonNullable): val is VaporFragment { + return val instanceof VaporFragment +} diff --cc packages/runtime-vapor/src/hmr.ts index 63e5376896,c96c1afa13..17b1bd0f23 --- a/packages/runtime-vapor/src/hmr.ts +++ b/packages/runtime-vapor/src/hmr.ts @@@ -20,12 -18,7 +19,11 @@@ export function hmrRerender(instance: V const parent = normalized[0].parentNode! const anchor = normalized[normalized.length - 1].nextSibling remove(instance.block, parent) + if (instance.hmrRerenderEffects) { + instance.hmrRerenderEffects.forEach(e => e()) + instance.hmrRerenderEffects.length = 0 + } - const prev = currentInstance - simpleSetCurrentInstance(instance) + const prev = setCurrentInstance(instance) pushWarningContext(instance) devRender(instance) popWarningContext() @@@ -49,7 -41,6 +46,7 @@@ export function hmrReload instance.rawSlots, instance.isSingleRoot, ) - simpleSetCurrentInstance(prev, instance.parent) + setCurrentInstance(...prev) mountComponent(newInstance, parent, anchor) + handleTeleportRootComponentHmrReload(instance, newInstance) } diff --cc packages/runtime-vapor/src/index.ts index 051944443a,7a8aea5a0d..998143c480 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@@ -3,12 -3,15 +3,16 @@@ export { createVaporApp, createVaporSSR export { defineVaporComponent } from './apiDefineComponent' export { vaporInteropPlugin } from './vdomInterop' export type { VaporDirective } from './directives/custom' +export { VaporTeleportImpl as VaporTeleport } from './components/Teleport' // compiler-use only -export { insert, prepend, remove, isFragment, VaporFragment } from './block' +export { insert, prepend, remove } from './block' export { setInsertionState } from './insertionState' - export { createComponent, createComponentWithFallback } from './component' + export { + createComponent, + createComponentWithFallback, + isVaporComponent, + } from './component' export { renderEffect } from './renderEffect' export { createSlot } from './componentSlots' export { template } from './dom/template' diff --cc packages/runtime-vapor/src/renderEffect.ts index 227d7933e7,ac34e8863d..6da69ee2cf --- a/packages/runtime-vapor/src/renderEffect.ts +++ b/packages/runtime-vapor/src/renderEffect.ts @@@ -11,64 -11,81 +11,86 @@@ import import { type VaporComponentInstance, isVaporComponent } from './component' import { invokeArrayFns } from '@vue/shared' - export function renderEffect( - fn: () => void, - noLifecycle = false, - ): ReactiveEffect { - const instance = currentInstance as VaporComponentInstance | null - const scope = getCurrentScope() - if (__DEV__ && !__TEST__ && !scope && !isVaporComponent(instance)) { - warn('renderEffect called without active EffectScope or Vapor instance.') - } + class RenderEffect extends ReactiveEffect { + i: VaporComponentInstance | null + job: SchedulerJob + updateJob: SchedulerJob + + constructor(public render: () => void) { + super() + const instance = currentInstance as VaporComponentInstance | null + if (__DEV__ && !__TEST__ && !this.subs && !isVaporComponent(instance)) { + warn('renderEffect called without active EffectScope or Vapor instance.') + } - // renderEffect is always called after user has registered all hooks - const hasUpdateHooks = instance && (instance.bu || instance.u) - const renderEffectFn = noLifecycle - ? fn - : () => { - if (__DEV__ && instance) { - startMeasure(instance, `renderEffect`) - } - const prev = currentInstance - simpleSetCurrentInstance(instance) - if (scope) scope.on() - if (hasUpdateHooks && instance.isMounted && !instance.isUpdating) { - instance.isUpdating = true - instance.bu && invokeArrayFns(instance.bu) - fn() - queuePostFlushCb(() => { - instance.isUpdating = false - instance.u && invokeArrayFns(instance.u) - }) - } else { - fn() - } - if (scope) scope.off() - simpleSetCurrentInstance(prev, instance) - if (__DEV__ && instance) { - startMeasure(instance, `renderEffect`) - } + const job: SchedulerJob = () => { + if (this.dirty) { + this.run() } + } + this.updateJob = () => { + instance!.isUpdating = false + instance!.u && invokeArrayFns(instance!.u) + } + + if (instance) { + if (__DEV__) { + this.onTrack = instance.rtc + ? e => invokeArrayFns(instance.rtc!, e) + : void 0 + this.onTrigger = instance.rtg + ? e => invokeArrayFns(instance.rtg!, e) + : void 0 + } + job.i = instance + } - const effect = new ReactiveEffect(renderEffectFn) - const job: SchedulerJob = () => effect.dirty && effect.run() + this.job = job + this.i = instance - if (instance) { - if (__DEV__) { - effect.onTrack = instance.rtc - ? e => invokeArrayFns(instance.rtc!, e) - : void 0 - effect.onTrigger = instance.rtg - ? e => invokeArrayFns(instance.rtg!, e) - : void 0 + // TODO recurse handling + } + + fn(): void { + const instance = this.i + const scope = this.subs ? (this.subs.sub as EffectScope) : undefined + // renderEffect is always called after user has registered all hooks + const hasUpdateHooks = instance && (instance.bu || instance.u) + if (__DEV__ && instance) { + startMeasure(instance, `renderEffect`) + } + const prev = setCurrentInstance(instance, scope) + if (hasUpdateHooks && instance.isMounted && !instance.isUpdating) { + instance.isUpdating = true + instance.bu && invokeArrayFns(instance.bu) + this.render() + queuePostFlushCb(this.updateJob) + } else { + this.render() + } + setCurrentInstance(...prev) + if (__DEV__ && instance) { + startMeasure(instance, `renderEffect`) } - job.i = instance - job.id = instance.uid } - effect.scheduler = () => queueJob(job) + notify(): void { + const flags = this.flags + if (!(flags & EffectFlags.PAUSED)) { + queueJob(this.job, this.i ? this.i.uid : undefined) + } + } + } + -export function renderEffect(fn: () => void, noLifecycle = false): void { ++export function renderEffect( ++ fn: () => void, ++ noLifecycle = false, ++): RenderEffect { + const effect = new RenderEffect(fn) + if (noLifecycle) { + effect.fn = fn + } effect.run() + + return effect - // TODO recurse handling } diff --cc packages/runtime-vapor/src/vdomInterop.ts index d5bbc71466,1573a30692..5cc95304c7 --- a/packages/runtime-vapor/src/vdomInterop.ts +++ b/packages/runtime-vapor/src/vdomInterop.ts @@@ -35,8 -36,9 +36,10 @@@ import type { RawSlots, VaporSlot } fro import { renderEffect } from './renderEffect' import { createTextNode } from './dom/node' import { optimizePropertyLookup } from './dom/prop' +import { VaporFragment } from './fragment' + export const interopKey: unique symbol = Symbol(`interop`) + // mounting vapor components and slots in vdom const vaporInteropImpl: Omit< VaporInteropInterface,