# testvite1 **Repository Path**: alamhubb/testvite1 ## Basic Information - **Project Name**: testvite1 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-01-13 - **Last Updated**: 2026-01-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Vue 3 H函数嵌套组件状态保持方案 本项目演示了如何在 Vue 3 的 H函数写法中实现**子组件数据不被父级渲染影响**的技术方案。 ## 问题背景 在使用 H函数 + `reactive` 数组动态渲染子组件时,子组件的状态会在父组件重新渲染时丢失: ```ts // ❌ 错误示例:子组件状态会丢失 reactiveList(() => items.map((_, i) => h('div', { key: i }, [ (() => { const childState = reactive([{}]) // 每次 map 都重新创建 return h('div', ...) })() ]) )) ``` **原因**:`.map()` 每次执行都会重新运行 IIFE,导致 `childState` 被重新初始化。 ## 解决方案 ### 核心思路 **将子组件的组件定义(`defineComponent`)存储在父级数组的元素中**,使用 `markRaw` 标记避免响应式代理。 ```ts // ✅ 正确示例:子组件状态保持 const items = reactive([{ comp: markRaw(defineComponent(() => { const childState = reactive([{}]) // 只初始化一次 return () => h('div', ...) })) }]) // 添加子节点时 items.push({ comp: markRaw(defineComponent(() => { ... })) }) // 渲染时使用存储的组件定义 items.map((item, i) => h('div', { key: i }, [h(item.comp)])) ``` ### 关键点 1. **组件定义存储在数组元素中**:每个元素的 `comp` 属性持有唯一的组件定义 2. **使用 `markRaw`**:告诉 Vue 不要将组件对象转为响应式,避免性能开销和警告 3. **组件内部状态独立**:每个组件实例有自己的 `setup` 作用域,状态不会共享或丢失 4. **支持无限嵌套**:子组件内部可以用同样的模式继续嵌套 ## 工具函数 ```ts // 响应式文本 - 自动追踪依赖 const reactiveText = (getter: () => string) => h(defineComponent(() => getter)) // 响应式列表 - 动态渲染数组 const reactiveList = (getter: () => any[]) => h(defineComponent(() => () => h(Fragment, getter()))) ``` ## 与 Template 写法的对比 | 特性 | Template `v-for` | H函数 `.map()` | |------|-----------------|----------------| | 组件实例复用 | ✅ Vue 自动处理 | ❌ 需要手动存储组件定义 | | 状态保持 | ✅ 内置支持 | ✅ 需要 `markRaw` + 存储模式 | | 代码简洁度 | ✅ 简洁 | ⚠️ 嵌套较深 | ## 项目结构 - `src/components/HelloWorld.vue` - H函数嵌套组件示例 - `src/components/HelloWorldTemplate.vue` - Template 对照组 - `src/App.vue` - 组件切换演示 ## 运行 ```bash npm install npm run dev ```