Freeze
Freeze 是组件缓存组件(替代 3.x 的 KeepAlive)。当组件切换时,Freeze 会将旧组件冻结(暂停响应式更新)并缓存起来,而不是销毁它。下次再切换回来时,直接从缓存中恢复,保留之前的操作状态。
基本用法
tsx
import { Freeze, shallowRef } from 'vitarx'
// 定义两个组件
function ComponentA() {
return <div>组件 A</div>
}
function ComponentB() {
return <div>组件 B</div>
}
function App() {
const current = shallowRef(ComponentA)
return (
<div>
<button
onClick={() => {
current.value = ComponentA
}}
>
显示 A
</button>
<button
onClick={() => {
current.value = ComponentB
}}
>
显示 B
</button>
<Freeze is={current} />
</div>
)
}切换到组件 B 时,组件 A 不会被销毁,而是被冻结并缓存。再切回组件 A 时,直接从缓存恢复,之前的状态(如表单输入、滚动位置等)都会保留。
属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
is | Component | null | undefined | false | 是 | 动态组件类型,传 null/undefined/false 则不渲染 |
key | unknown | 否 | 唯一标识,配合 is 使用,区分同一组件的不同实例 |
props | AnyProps | null | undefined | 否 | 传递给组件的属性对象 |
include | Component[] | 否 | 需要缓存的组件列表,只缓存列表中的组件 |
exclude | Component[] | 否 | 不需要缓存的组件列表,优先级高于 include |
max | number | 否 | 最大缓存数量,超出时采用 LRU 策略淘汰(默认 0 不限制) |
props — 传递属性
通过 props 属性可以向当前渲染的组件传递数据:
tsx
import { Freeze, shallowRef, reactive } from 'vitarx'
function DetailPage({ id }: { id: number }) {
return <div>详情页 ID: {id}</div>
}
function App() {
const current = shallowRef(DetailPage)
const pageProps = reactive({ id: 1 })
return (
<div>
<button
onClick={() => {
pageProps.id = 1
}}
>
ID 1
</button>
<button
onClick={() => {
pageProps.id = 2
}}
>
ID 2
</button>
<Freeze is={current} props={pageProps} />
</div>
)
}include / exclude — 控制缓存范围
默认情况下,Freeze 会缓存所有切换过的组件。你可以通过 include 和 exclude 来精确控制哪些组件需要缓存:
tsx
import { Freeze, shallowRef } from 'vitarx'
function Home() {
return <div>首页</div>
}
function Settings() {
return <div>设置</div>
}
function Profile() {
return <div>个人中心</div>
}
function App() {
const current = shallowRef(Home)
return (
// 只缓存 Home 和 Settings,不缓存 Profile
<Freeze is={current} include={[Home, Settings]} />
)
}exclude 的优先级高于 include,即如果一个组件同时出现在两个列表中,它不会被缓存:
tsx
// 缓存除 Settings 以外的所有组件
<Freeze is={current} exclude={[Settings]} />max — 限制缓存数量
当缓存组件过多时,会占用大量内存。通过 max 可以限制最大缓存数量,超出时采用 LRU(最近最少使用)策略淘汰最久未使用的组件:
tsx
<Freeze is={current} max={5} />key — 区分同一组件的不同实例
如果你需要让同一个组件根据不同参数缓存不同的实例,可以使用 key 属性:
tsx
import { Freeze, shallowRef, ref } from 'vitarx'
function DetailPage({ id }: { id: number }) {
return <div>详情页 ID: {id}</div>
}
function App() {
const currentId = ref(1)
const current = shallowRef(DetailPage)
return (
<div>
<button
onClick={() => {
currentId.value = 1
}}
>
详情 1
</button>
<button
onClick={() => {
currentId.value = 2
}}
>
详情 2
</button>
{/* key 不同,会缓存两个独立的实例 */}
<Freeze is={current} key={currentId} props={{ id: currentId }} />
</div>
)
}完整示例
下面是一个模拟 Tab 页签切换的完整示例,展示 Freeze 如何保留各 Tab 页的状态:
tsx
import { Freeze, shallowRef, reactive } from 'vitarx'
/** 表单组件 — 切换后保留输入内容 */
function FormTab() {
const form = reactive({ name: '', email: '' })
return (
<div>
<h3>表单</h3>
<div>
<label>姓名:</label>
<input
value={form.name}
onInput={(e) => {
form.name = (e.target as HTMLInputElement).value
}}
/>
</div>
<div>
<label>邮箱:</label>
<input
value={form.email}
onInput={(e) => {
form.email = (e.target as HTMLInputElement).value
}}
/>
</div>
</div>
)
}
/** 计数器组件 — 切换后保留计数 */
function CounterTab() {
const count = reactive({ value: 0 })
return (
<div>
<h3>计数器</h3>
<p>当前计数:{count.value}</p>
<button
onClick={() => {
count.value++
}}
>
+1
</button>
<button
onClick={() => {
count.value--
}}
>
-1
</button>
</div>
)
}
/** 设置组件 — 不缓存 */
function SettingsTab() {
return (
<div>
<h3>设置</h3>
<p>这是设置页面,切换后不会被缓存</p>
</div>
)
}
function App() {
const tabs = [
{ label: '表单', component: FormTab },
{ label: '计数器', component: CounterTab },
{ label: '设置', component: SettingsTab }
]
const current = shallowRef(FormTab)
return (
<div>
<div style="margin-bottom: 16px;">
{tabs.map((tab) => (
<button
key={tab.label}
onClick={() => {
current.value = tab.component
}}
style={{
marginRight: '8px',
fontWeight: current.value === tab.component ? 'bold' : 'normal'
}}
>
{tab.label}
</button>
))}
</div>
{/* 只缓存 FormTab 和 CounterTab,不缓存 SettingsTab */}
<Freeze is={current} include={[FormTab, CounterTab]} max={5} />
</div>
)
}试试在"表单"页输入内容,切换到"计数器"页点击几次,再切回"表单"页——你会发现之前的输入内容还在!
下一步
- Dynamic — 动态组件渲染
- Lazy 与 lazy — 懒加载组件
- Transition — 过渡动画