wip: refactor

This commit is contained in:
daiwei 2025-04-09 15:49:00 +08:00
parent 61d6f4801b
commit dccc47c265
6 changed files with 67 additions and 59 deletions

View File

@ -73,7 +73,7 @@ export interface KeepAliveContext extends ComponentRenderContext {
deactivate: (vnode: VNode) => void
}
export const isKeepAlive = (vnode: VNode): boolean =>
export const isKeepAlive = (vnode: any): boolean =>
(vnode.type as any).__isKeepAlive
const KeepAliveImpl: ComponentOptions = {
@ -478,7 +478,7 @@ function injectToKeepAliveRoot(
}, target)
}
function resetShapeFlag(vnode: VNode) {
export function resetShapeFlag(vnode: any): void {
// bitwise operations to remove keep alive flags
vnode.shapeFlag &= ~ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
vnode.shapeFlag &= ~ShapeFlags.COMPONENT_KEPT_ALIVE

View File

@ -564,7 +564,7 @@ export { getComponentName } from './component'
/**
* @internal
*/
export { matches, isKeepAlive } from './components/KeepAlive'
export { matches, isKeepAlive, resetShapeFlag } from './components/KeepAlive'
/**
* @internal
*/

View File

@ -9,8 +9,8 @@ import {
} from 'vue'
import type { VaporComponent } from '../../src/component'
import { makeRender } from '../_utils'
import { VaporKeepAliveImpl as VaporKeepAlive } from '../../src/components/KeepAlive'
import {
VaporKeepAlive,
child,
createComponent,
createDynamicComponent,
@ -144,7 +144,7 @@ describe('VaporKeepAlive', () => {
const { mount } = define({
setup() {
const setTemplateRef = createTemplateRefSetter()
const n4 = createComponent(VaporKeepAlive as any, null, {
const n4 = createComponent(VaporKeepAlive, null, {
default: () => {
const n0 = createDynamicComponent(() => views[viewRef.value]) as any
setTemplateRef(n0, instanceRef)
@ -180,7 +180,7 @@ describe('VaporKeepAlive', () => {
return createIf(
() => toggle.value,
() =>
createComponent(VaporKeepAlive as any, null, {
createComponent(VaporKeepAlive, null, {
default: () => createDynamicComponent(() => views[viewRef.value]),
}),
)
@ -235,7 +235,7 @@ describe('VaporKeepAlive', () => {
return createIf(
() => toggle.value,
() =>
createComponent(VaporKeepAlive as any, null, {
createComponent(VaporKeepAlive, null, {
default: () => createDynamicComponent(() => views[viewRef.value]),
}),
)
@ -295,7 +295,7 @@ describe('VaporKeepAlive', () => {
const toggle = ref(true)
const { html } = define({
setup() {
return createComponent(VaporKeepAlive as any, null, {
return createComponent(VaporKeepAlive, null, {
default() {
return createIf(
() => toggle.value,
@ -350,7 +350,7 @@ describe('VaporKeepAlive', () => {
const toggle = ref(true)
const { html } = define({
setup() {
return createComponent(VaporKeepAlive as any, null, {
return createComponent(VaporKeepAlive, null, {
default() {
return createIf(
() => toggle.value,
@ -375,7 +375,7 @@ describe('VaporKeepAlive', () => {
onActivated(() => oneHooks.activated())
onDeactivated(() => oneHooks.deactivated())
onUnmounted(() => oneHooks.unmounted())
return createComponent(VaporKeepAlive as any, null, {
return createComponent(VaporKeepAlive, null, {
default() {
return createIf(
() => toggle2.value,
@ -389,7 +389,7 @@ describe('VaporKeepAlive', () => {
const toggle1 = ref(true)
const { html } = define({
setup() {
return createComponent(VaporKeepAlive as any, null, {
return createComponent(VaporKeepAlive, null, {
default() {
return createIf(
() => toggle1.value,

View File

@ -1,6 +1,7 @@
import { isArray } from '@vue/shared'
import {
type VaporComponentInstance,
currentInstance,
isVaporComponent,
mountComponent,
unmountComponent,
@ -8,6 +9,8 @@ import {
import { createComment, createTextNode } from './dom/node'
import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
import { isHydrating } from './dom/hydration'
import { isKeepAlive } from 'vue'
import type { KeepAliveInstance } from './components/KeepAlive'
export type Block =
| Node
@ -50,8 +53,14 @@ export class DynamicFragment extends VaporFragment {
pauseTracking()
const parent = this.anchor.parentNode
const instance = currentInstance!
// teardown previous branch
if (this.scope) {
if (isKeepAlive(instance)) {
;(instance as KeepAliveInstance).process(
this.nodes as VaporComponentInstance,
)
}
this.scope.stop()
parent && remove(this.nodes, parent)
}
@ -59,6 +68,11 @@ export class DynamicFragment extends VaporFragment {
if (render) {
this.scope = new EffectScope()
this.nodes = this.scope.run(render) || []
if (isKeepAlive(instance)) {
;(instance as KeepAliveInstance).process(
this.nodes as VaporComponentInstance,
)
}
if (parent) insert(this.nodes, parent, this.anchor)
} else {
this.scope = undefined

View File

@ -15,7 +15,6 @@ import {
currentInstance,
endMeasure,
expose,
isKeepAlive,
nextUid,
popWarningContext,
pushWarningContext,
@ -36,7 +35,13 @@ import {
resetTracking,
unref,
} from '@vue/reactivity'
import { EMPTY_OBJ, invokeArrayFns, isFunction, isString } from '@vue/shared'
import {
EMPTY_OBJ,
ShapeFlags,
invokeArrayFns,
isFunction,
isString,
} from '@vue/shared'
import {
type DynamicPropsSource,
type RawProps,
@ -376,6 +381,7 @@ export class VaporComponentInstance implements GenericComponentInstance {
propsOptions?: NormalizedPropsOptions
emitsOptions?: ObjectEmitsOptions | null
isSingleRoot?: boolean
shapeFlag?: number
constructor(
comp: VaporComponent,
@ -498,13 +504,12 @@ export function mountComponent(
parentNode: ParentNode,
anchor?: Node | null | 0,
): void {
let parent
if (
(parent = instance.parent) &&
isKeepAlive(parent as any) &&
(parent as KeepAliveInstance).isKeptAlive(instance)
) {
;(parent as KeepAliveInstance).activate(instance, parentNode, anchor as any)
if (instance.shapeFlag! & ShapeFlags.COMPONENT_KEPT_ALIVE) {
;(instance.parent as KeepAliveInstance).activate(
instance,
parentNode,
anchor as any,
)
instance.isMounted = true
return
}
@ -516,9 +521,7 @@ export function mountComponent(
insert(instance.block, parentNode, anchor)
if (instance.m) queuePostFlushCb(() => invokeArrayFns(instance.m!))
if (
parent &&
isKeepAlive(parent as any) &&
(parent as KeepAliveInstance).shouldKeepAlive(instance) &&
instance.shapeFlag! & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE &&
instance.a
) {
queuePostFlushCb(instance.a!)
@ -533,13 +536,8 @@ export function unmountComponent(
instance: VaporComponentInstance,
parentNode?: ParentNode,
): void {
let parent
if (
(parent = instance.parent) &&
isKeepAlive(parent as any) &&
(parent as KeepAliveInstance).shouldKeepAlive(instance)
) {
;(parent as KeepAliveInstance).deactivate(instance)
if (instance.shapeFlag! & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
;(instance.parent as KeepAliveInstance).deactivate(instance)
return
}

View File

@ -3,25 +3,25 @@ import {
currentInstance,
devtoolsComponentAdded,
getComponentName,
invalidateMount,
isKeepAlive,
matches,
onBeforeUnmount,
onMounted,
onUpdated,
queuePostFlushCb,
resetShapeFlag,
warn,
watch,
} from '@vue/runtime-dom'
import { type Block, insert, isFragment, isValidBlock } from '../block'
import {
type ObjectVaporComponent,
type VaporComponent,
type VaporComponentInstance,
isVaporComponent,
unmountComponent,
} from '../component'
import { defineVaporComponent } from '../apiDefineComponent'
import { invokeArrayFns, isArray } from '@vue/shared'
import { ShapeFlags, invokeArrayFns, isArray } from '@vue/shared'
export interface KeepAliveInstance extends VaporComponentInstance {
activate: (
@ -30,15 +30,14 @@ export interface KeepAliveInstance extends VaporComponentInstance {
anchor: Node,
) => void
deactivate: (instance: VaporComponentInstance) => void
shouldKeepAlive: (instance: VaporComponentInstance) => boolean
isKeptAlive: (instance: VaporComponentInstance) => boolean
process: (instance: VaporComponentInstance) => void
}
type CacheKey = PropertyKey | VaporComponent
type Cache = Map<CacheKey, VaporComponentInstance>
type Keys = Set<CacheKey>
const VaporKeepAliveImpl = defineVaporComponent({
export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
name: 'VaporKeepAlive',
// @ts-expect-error
__isKeepAlive: true,
@ -57,7 +56,6 @@ const VaporKeepAliveImpl = defineVaporComponent({
const keys: Keys = new Set()
const storageContainer = document.createElement('div')
let current: VaporComponentInstance | undefined
let isUnmounting = false
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
;(keepAliveInstance as any).__v_cache = cache
@ -90,9 +88,10 @@ const VaporKeepAliveImpl = defineVaporComponent({
onMounted(cacheBlock)
onUpdated(cacheBlock)
onBeforeUnmount(() => {
isUnmounting = true
cache.forEach(cached => {
resetShapeFlag(cached)
cache.delete(cached.type)
// current instance will be unmounted as part of keep-alive's unmount
if (current && current.type === cached.type) {
@ -104,12 +103,20 @@ const VaporKeepAliveImpl = defineVaporComponent({
})
})
const children = slots.default()
if (isArray(children) && children.length > 1) {
if (__DEV__) {
warn(`KeepAlive should contain exactly one component child.`)
keepAliveInstance.process = (instance: VaporComponentInstance) => {
if (cache.has(instance.type)) {
instance.shapeFlag! |= ShapeFlags.COMPONENT_KEPT_ALIVE
}
const name = getComponentName(instance.type)
if (
!(
(include && (!name || !matches(include, name))) ||
(exclude && name && matches(exclude, name))
)
) {
instance.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
}
return children
}
keepAliveInstance.activate = (
@ -117,9 +124,6 @@ const VaporKeepAliveImpl = defineVaporComponent({
parentNode: ParentNode,
anchor: Node,
) => {
// invalidateMount(instance.m)
// invalidateMount(instance.a)
const cachedBlock = cache.get(instance.type)!
insert((instance.block = cachedBlock.block), parentNode, anchor)
queuePostFlushCb(() => {
@ -144,20 +148,12 @@ const VaporKeepAliveImpl = defineVaporComponent({
}
}
keepAliveInstance.shouldKeepAlive = (instance: VaporComponentInstance) => {
if (isUnmounting) return false
const name = getComponentName(instance.type)
if (
(include && (!name || !matches(include, name))) ||
(exclude && name && matches(exclude, name))
) {
return false
const children = slots.default()
if (isArray(children) && children.length > 1) {
if (__DEV__) {
warn(`KeepAlive should contain exactly one component child.`)
}
return true
}
keepAliveInstance.isKeptAlive = (instance: VaporComponentInstance) => {
return cache.has(instance.type)
return children
}
function pruneCache(filter: (name: string) => boolean) {