--- url: /guide/directory-structure.md description: Weapp-vite 的目录结构总览页,按 Nuxt 式目录索引组织,左侧目录项为独立路由,右侧为每个目录或文件的详细说明。 --- # 目录结构 这一组文档按“目录即能力边界”的方式组织,交互形态参考 Nuxt 的 directory structure 页面。 * 左侧每一个目录或文件项,都是单独的文档路由 * 右侧页面只解释当前项的职责、默认行为和触发的能力 * 如果你只想先看全局,再决定点进哪个目录,从这页开始就够了 ## 一个典型项目 ```text . ├─ vite.config.ts / weapp-vite.config.ts ├─ project.config.json ├─ package.json ├─ public/ ├─ .weapp-vite/ │ ├─ typed-router.d.ts │ ├─ typed-components.d.ts │ └─ components.d.ts └─ / ├─ app.(js|ts) ├─ app.vue ├─ app.json(.js|.ts)? ├─ app.(css|scss|wxss|...) ├─ layouts/ ├─ custom-tab-bar/ ├─ app-bar/ ├─ pages/ ├─ components/ ├─ / │ ├─ pages/ │ └─ components/ ├─ shared/ ├─ utils/ ├─ workers/ ``` ## 先记住三条 1. `weapp-vite` 真正依赖的是 `srcRoot`,不是硬编码的 `src/` 2. 页面自动扫描默认只看 `srcRoot/pages/**` 和已声明分包 root 下的 `pages/**` 3. `layouts`、`custom-tab-bar`、`app-bar`、类型声明文件这些都属于带固定语义的保留位置 > \[!TIP] > 这一组文档里的 ``、`` 都是变量占位,不是固定目录名。它们分别代表你在 `vite.config.ts` / `weapp-vite.config.ts` 中声明的源码根目录和分包 root。 ## 从哪里开始看 * 想先建立全局认知:看 [📄 配置入口文件](/guide/directory-structure/vite-config)、[📁 `/`](/guide/directory-structure/src-root) * 想搞清楚页面 layout 放哪里:看 [📁 `/layouts/`](/guide/directory-structure/layouts) * 想搞清楚页面与分包:看 [📁 `/pages/`](/guide/directory-structure/pages)、[📁 `//`](/guide/directory-structure/subpackages) * 想搞清楚自动生成产物:看 [类型声明文件](/guide/directory-structure/generated-files) ## 默认能力速查 | 位置 | 作用 | | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ----------------------------------------------- | ----- | ------------------------------------------- | | `vite.config.ts` / `weapp-vite.config.ts` | 定义 `srcRoot`、自动路由、分包、自动导入组件等能力 | | `project.config.json` | 微信开发者工具配置 | | `public/` | 构建时原样复制的静态资源 | | `app.(js | ts)` | 应用脚本入口,承载生命周期和全局初始化 | | `app.vue` | Vue SFC 形式的应用入口,可组合脚本、JSON 宏与样式 | | `app.json(.js | .ts)?` | 应用配置入口,既支持原生 JSON,也支持脚本化生成 | | `app.(css | scss | wxss | ...)` | 全局样式入口,支持 CSS、WXSS 与常见预处理器 | | `layouts/` | 页面 layout 约定目录,承载默认布局和命名布局 | | `pages/` | 主包页面目录 | | `components/` | 主包组件目录,默认参与自动导入扫描 | | `/pages/` | 已声明分包 root 下的页面目录 | | `custom-tab-bar/` | `tabBar.custom === true` 时的固定入口 | | `app-bar/` | `appBar` 开启时的固定入口 | | `.weapp-vite/typed-router.d.ts` / `.weapp-vite/typed-components.d.ts` / `.weapp-vite/components.d.ts` | 自动生成的类型声明文件 | ## 相关文档 * [自动路由](/guide/auto-routes) * [页面 Layout 使用指南](/guide/layouts) * [自动导入组件](/guide/auto-import) * [分包指南](/guide/subpackage) * [手动集成](/guide/manual-integration) --- --- url: /guide/directory-structure/vite-config.md description: Weapp-vite 配置入口文件,负责定义 srcRoot、自动路由、分包、自动导入组件等行为。 --- # `vite.config.ts` / `weapp-vite.config.ts` `vite.config.ts` 或 `weapp-vite.config.ts` 是这组目录约定的真正入口。很多“目录为什么会生效”的答案,最终都在这里。 如果两个文件同时存在,`weapp-vite` 会优先读取并合并 `weapp-vite.config.*` 中的 `weapp` 配置;如果项目只保留其中一个,也可以正常工作。 ## 它决定什么 * `weapp.srcRoot`:源码根目录在哪里 * `weapp.autoRoutes`:哪些页面目录会被扫描 * `weapp.subPackages`:哪些目录被当成分包 root * `weapp.autoImportComponents`:哪些组件目录参与自动导入 ## 最小示例 ```ts import { defineConfig } from 'weapp-vite' export default defineConfig({ weapp: { srcRoot: 'src', autoRoutes: true, autoImportComponents: true, subPackages: { packageA: {}, }, }, }) ``` ## 什么时候先看它 * 页面没被扫描到 * 分包页面被识别错了 * 类型文件生成到了意料之外的位置 * 你把源码目录从 `src/` 改到了 `miniprogram/` 相关文档:[srcRoot](/guide/directory-structure/src-root) / [subPackages](/guide/directory-structure/subpackages) --- --- url: /guide/directory-structure/project-config.md description: 微信开发者工具项目配置文件,定义 miniprogramRoot、appid 等平台侧参数。 --- # `project.config.json` 这是微信开发者工具项目配置,不属于 `weapp-vite` 的目录约定能力本身,但它决定开发者工具怎样打开和编译你的项目。 ## 常见职责 * 指定 `miniprogramRoot` * 保存 `appid` * 控制开发者工具的编译选项 ## 与目录结构的关系 最常见的一点是:它通常需要把 `miniprogramRoot` 指向 `dist/`,因为 `weapp-vite` 的源码目录和最终小程序运行目录不是同一个位置。 ```json { "miniprogramRoot": "dist/", "compileType": "miniprogram" } ``` 如果你发现开发者工具打开后目录不对、页面不出现,先检查这里。 --- --- url: /guide/directory-structure/package-json.md description: 项目脚本与依赖入口,通常承载 dev、build、open 等 weapp-vite 命令。 --- # `package.json` `package.json` 不负责页面扫描,但它是项目工作流的入口。 ## 通常会放什么 * `dev` * `build` * `open` * `analyze` ```json { "scripts": { "dev": "wv dev", "build": "wv build", "open": "wv open" } } ``` ## 为什么目录结构页要提它 因为它和 `vite.config.ts` 一起构成了项目根目录的最小工程化入口。 如果别人接手你的项目,通常先看这两个文件。 --- --- url: /guide/directory-structure/public.md description: 构建时原样复制到产物目录的静态资源目录,适合放无需参与模块分析和编译转换的文件。 --- # `public/` `public/` 用来放不需要参与模块依赖分析的静态资源。 ## 适合放这里的内容 * 图标 * 纯静态配置 * 需要按原文件名复制的资源 ## 不适合放这里的内容 * 页面 * 组件 * 想通过 `import` 参与构建处理的资源 它不会被自动路由扫描,也不会被自动导入组件扫描。 --- --- url: /guide/directory-structure/src-root.md description: Weapp-vite 的源码根目录概念,所有 pages、components、分包、自动生成类型文件都基于它定位。 --- # `/` `/` 是这组目录文档里最重要的概念。 `weapp-vite` 真正依赖的是 `weapp.srcRoot`,不是某个固定叫 `src/` 的文件夹。 ## 默认值 大多数模板会把它设成: ```ts export default defineConfig({ weapp: { srcRoot: 'src', }, }) ``` 但你也可以改成: ```ts export default defineConfig({ weapp: { srcRoot: 'miniprogram', }, }) ``` ## 它会影响什么 * `pages/` 的扫描根目录 * `components/` 的扫描根目录 * `layouts/` 的扫描根目录 * `custom-tab-bar/`、`app-bar/` 的固定位置 * `.weapp-vite/typed-router.d.ts`、`.weapp-vite/typed-components.d.ts`、`.weapp-vite/components.d.ts` 的生成时机与引用方式 ## 一个简单判断 如果某条文档写的是 `/pages/**`,你应该先自动在脑中把它理解为: `/pages/**` --- --- url: /guide/directory-structure/app-ts.md description: 应用脚本入口,支持 JavaScript 与 TypeScript,承载 App 生命周期和全局初始化逻辑。 --- # `app.(js|ts)` `app.(js|ts)` 是应用脚本入口。 ## 适合放什么 * App 生命周期 * 全局启动逻辑 * 埋点初始化 * 和 `wevu`、router、全局状态相关的初始化 ## 支持哪些后缀 * `app.js` * `app.ts` 如果你项目使用的是原生小程序增强模式,这通常就是应用逻辑的主入口。 ## 它和页面目录的关系 它不参与自动路由扫描,但它通常是全局行为的起点。 目录结构上,它与 `app.json(.js|.ts)?`、`app.(css|scss|wxss|...)` 一起构成应用入口三件套。 --- --- url: /guide/directory-structure/app-vue.md description: Vue SFC 形式的应用入口,可在一个文件中组织脚本、defineAppJson 宏与样式。 --- # `app.vue` `app.vue` 是 Weapp-vite + Vue SFC 场景下非常常见的应用入口。 ## 它适合解决什么 * 希望把应用脚本、应用配置和样式放在一个 SFC 中维护 * 想直接在顶层使用 `defineAppJson` * 想在应用入口里直接创建 router、store 或其他全局运行时能力 ## 典型写法 ```vue ``` ## 它和 `app.(js|ts)` / `app.json(.js|.ts)?` / `app.(css|scss|wxss|...)` 的关系 可以把 `app.vue` 理解为一种“组合式入口”: * 脚本逻辑在 ` ``` ### 3.2 显式指定命名 layout ```vue ``` ### 3.3 指定 layout + props ```vue ``` ### 3.4 显式关闭 layout ```vue ``` 这类页面适合登录页、全屏页、沉浸式落地页。 ## 4. `definePageMeta({ layout })` 的约束 这部分需要特别注意,因为它是编译期静态分析的: | 支持的写法 | 是否支持 | | -------------------------------------------------- | -------- | | `layout: 'admin'` | ✅ | | `layout: false` | ✅ | | `layout: { name: 'admin', props: { title: 'A' } }` | ✅ | | `layout: someRef.value` | ❌ | | `layout: computed(() => 'admin')` | ❌ | | `props: dynamicObject` | ❌ | 也就是说: * `layout` 只支持静态字符串、`false`,或 `{ name, props }` 对象 * `props` 必须是对象字面量 * `props` 的键名必须是静态键名 > **提示**:如果你需要按状态切换 layout,不要把 `layout.name` 写成响应式值,而应该改用运行时 `setPageLayout()`。 ## 5. `routeRules` 怎么和 layout 配合 如果你不想每个页面都手写 `definePageMeta({ layout })`,可以在 `vite.config.ts` 或 `weapp-vite.config.ts` 里用 `weapp.routeRules` 批量声明默认布局。 ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { routeRules: { 'pages/dashboard/**': { appLayout: 'dashboard', }, 'pages/admin/**': { appLayout: { name: 'admin', props: { sidebar: true, title: 'Admin', }, }, }, }, }, }) ``` ### 5.1 优先级 优先级从高到低: 1. 页面源码里的 `definePageMeta({ layout })` 2. `weapp.routeRules` 3. `srcRoot/layouts/default.*` 如果页面显式写了: ```ts definePageMeta({ layout: false, }) ``` 那就会跳过默认 layout 包裹。 ## 6. 运行时怎么切换 layout 如果页面需要在运行过程中切换 layout,可以使用 `setPageLayout()`。 ### 6.1 Wevu 页面写法 ```vue ``` ### 6.2 原生 Page 写法 原生页面也可以切换 layout。当前 `setPageLayout` 也会从 `weapp-vite/runtime` 侧暴露给原生 Page 使用。 ```ts import { setPageLayout } from 'weapp-vite/runtime' Page({ onLoad() { setPageLayout('default') }, applyAdminLayout() { setPageLayout('admin', { title: 'Native Console', subtitle: '这个标题来自原生 Page 调用 setPageLayout()。', }) }, clearLayout() { setPageLayout(false) }, }) ``` ### 6.3 `usePageLayout()` 有什么用 `usePageLayout()` 用于读取当前页面的 layout 状态: ```ts import { usePageLayout } from 'wevu' const pageLayout = usePageLayout() if (pageLayout.name === 'admin') { console.log(pageLayout.props.title) } ``` 当项目里存在 layout 扫描结果时,`.weapp-vite/wevu-layouts.d.ts` 会自动增强 `WevuPageLayoutMap`,从而让 `layout.name` 与 `props` 拿到更严格的类型提示。 ## 7. page 和 layout 怎么通信 layout 不只是“包一层壳”,还经常要解决页面和壳子之间的数据流与能力协作。建议先把几种通信方式的边界分清楚: | 方式 | 方向 | 适合场景 | 推荐程度 | | ------------------------------------------------------------- | ------------------------ | -------------------------- | -------- | | `definePageMeta({ layout: { name, props } })` | page -> layout | 静态标题、模式、文案 | 高 | | `setPageLayout(name, props)` | page -> layout | 运行时切换 layout 与 props | 高 | | `usePageLayout()` | page 读取 layout 状态 | 页面感知当前壳子模式 | 高 | | store + page `watch` | store -> page -> layout | 统一管理布局状态与交互意图 | 高 | | `layout-host` / `resolveLayoutHost()` / `waitForLayoutHost()` | page/组件 -> layout 宿主 | toast、dialog、反馈节点 | 高 | | `provide()` / `inject()` | 当前实例或全局兜底 | 局部上下文共享 | 低 | ### 7.1 通过 layout props 通信 最直接的方式是由页面把布局需要的静态信息传给 layout,例如标题、副标题、页面模式、头部按钮文案。 编译期静态场景: ```vue ``` 运行时动态场景: ```vue ``` > **提示**:如果你的目标只是“页面告诉 layout 现在该显示什么”,优先用 `props`,不要一开始就引入额外的全局通信层。 ### 7.2 页面读取当前 layout 状态 页面也可以通过 `usePageLayout()` 读取当前命中的 layout 名称与 props。这在“页面自身也要根据 layout 模式调整局部 UI”时很有用。 ```vue ``` 适合这类场景: * 页面想知道自己当前是否处于 `admin` / `default` / `false` * 页面上的某个区块需要跟着 layout 模式切换 * 调试运行时布局切换结果 ### 7.3 通过 store 让页面协调 layout 当 layout 状态来自 store 时,推荐的边界是: * store 只保存“布局状态”和“交互意图” * 页面负责 `watch` store,并调用 `setPageLayout()` * layout 继续只关心自己的 props 和宿主能力 示例可以参考 e2e 用例里的 `e2e-apps/template-wevu-tdesign-regression/src/pages/layout-store/index.vue`。 ```vue ``` 这样做的好处是: * store 不需要直接依赖 page runtime hook * layout 切换逻辑仍然留在页面上下文,职责更清晰 * 后续替换 layout、增加平台差异处理时更容易收敛 ### 7.4 通过 `layout-host` 暴露 layout 内宿主能力 有些能力天然属于 layout,而不是页面内容本身,例如 toast、dialog、抽屉、全局反馈层。这时更推荐把它们放在 layout 中,再通过 `layout-host` 暴露给页面或子组件使用。 layout 侧: ```vue ``` 页面或组件侧: ```ts import { resolveLayoutHost, waitForLayoutHost } from 'wevu' const toast = resolveLayoutHost('layout-toast') toast?.show?.({ message: '操作成功' }) const dialog = await waitForLayoutHost('layout-dialog') dialog?.show?.({ title: '确认删除?' }) ``` 这类模式的重点不是“拿到 layout 实例”,而是“拿到 layout 暴露出来的宿主能力”。因此建议: * 页面/组件只调用业务 hook 或 `resolveLayoutHost()` / `waitForLayoutHost()` * 不直接手写 `selectComponent()` 去找 layout 内部节点 * layout 内部组件的选择器和实现细节由 layout 自己维护 > **提示**:如果你的项目已经封装了 `useToast()`、`useDialog()` 一类 hook,优先让这些 hook 内部对接 `layout-host`,而不是把 layout 结构细节散落到页面代码里。 TDesign Toast/Dialog 可以按下面的形态封装为模板级 recipe。核心约束是:页面只调用业务 hook,hook 内部再解析 layout 暴露的宿主实例。 ```ts import { getCurrentInstance, resolveLayoutHost } from 'wevu' interface ToastHost { show: (options: { message: string, theme?: string }) => void } export function showToast(message: string, theme = 'default') { const context = getCurrentInstance() const toast = resolveLayoutHost('layout-toast', { context }) toast?.show({ message, ...(theme === 'default' ? {} : { theme }), }) } ``` 这种封装不属于 `wevu` core 的 UI 能力:`wevu` 只负责 `layout-host` 的注册与解析,具体的 Toast/Dialog 参数、按钮行为和关闭逻辑仍然由 TDesign 或业务 hook 维护。 ### 7.5 不推荐直接把 layout 当成父组件做祖先注入 很多人会先想到 Vue Web 里的 `provide()` / `inject()`。但在当前 `wevu` 运行时语义下,这不是 page/layout 主通信手段。 原因是当前版本没有完整的祖先组件树查找语义,`inject()` 不会像 Web Vue 那样稳定地沿着“layout -> page -> 子组件”逐级向上查找。更准确地说: * `provide()` / `inject()` 优先作用于当前实例上下文 * 找不到时会回落到全局存储 * 它适合局部共享或全局兜底,不适合承担 page/layout 主通信链路 所以: * ✅ page -> layout 传参,优先 `definePageMeta(...props)` 或 `setPageLayout(...props)` * ✅ layout 内能力暴露,优先 `layout-host` * ✅ 跨页面稳定共享状态,优先 store * ❌ 不要默认假设 layout 可以像 Vue 父组件一样稳定向 page `inject()` ## 8. Vue layout 和原生 layout 怎么选 | 场景 | 更推荐的方式 | | -------------------------- | ----------------------------- | | 页面本身就是 Vue SFC 项目 | 优先 Vue layout | | 你需要纯原生小程序组件布局 | 原生 layout | | 团队里有大量原生 Page | 原生 layout 更容易渐进迁移 | | 需要和现有组件体系保持统一 | 优先与页面技术栈一致的 layout | ### 8.1 Vue layout 示例 ```vue ``` ### 8.2 原生 layout 示例 ```wxml {{title || '后台布局'}} ``` ```json { "component": true } ``` ## 9. layout 相关类型文件 启用 layout 后,常见会看到这些支持文件: | 文件 | 作用 | | ------------------------------- | -------------------------- | | `.weapp-vite/wevu-layouts.d.ts` | 增强 `WevuPageLayoutMap` | | `.weapp-vite/components.d.ts` | 补齐自动组件与布局组件类型 | 如果你在编辑器里没有拿到 layout 类型提示,优先检查: 1. 是否执行过 `wv prepare` 2. `tsconfig.json` 是否已经引用 `.weapp-vite/*` 3. `srcRoot/layouts/**` 是否位于当前项目真实源码根目录下 ## 10. 常见问题 **Q1: 为什么我写了 `layout: computed(() => 'admin')` 不生效?** 因为 `definePageMeta({ layout })` 是编译期静态分析,不会执行响应式表达式。需要动态切换时请改用 `setPageLayout()`。 **Q2: 为什么 `default` 布局会自动包裹页面?** 因为 `default` 是约定式默认 layout。页面没写 `definePageMeta({ layout })` 时,会把它作为最后一层回退。 **Q3: 为什么原生页面也能切 layout?** 因为当前 runtime 已经为页面实例补齐了 `setPageLayout()` 的调用链,原生 Page 也可以显式切换当前页面壳。 **Q4: page 和 layout 之间推荐怎么通信?** 优先级建议是:`layout props` 解决 page -> layout 传参,`usePageLayout()` 解决页面读状态,`layout-host` 解决 layout 内宿主能力,store 负责跨页面或较复杂的交互状态。 **Q5: `provide()` / `inject()` 能不能拿来做 layout 到 page 的主通信?** 不建议。当前 `wevu` 没有完整的祖先组件树注入语义,`inject()` 更适合当前实例上下文共享或全局兜底,不应替代 layout props、store 或 `layout-host`。 ## 11. 总结 layout 能力的核心不是“多一个配置项”,而是让“页面内容”和“页面外壳”真正分层: * `srcRoot/layouts/**` 负责定义壳 * `definePageMeta({ layout })` 负责页面声明 * `routeRules` 负责批量回退 * `setPageLayout()` 负责运行时切换 * `layout-host` 负责暴露 layout 内部宿主能力 如果你现在要落地这套能力,建议先从 `default` + 一个命名 layout 开始,再按需要引入运行时切换。 最后可以按下面的顺序选型: | 目标 | 更推荐的方式 | | ------------------------ | --------------------------------------------- | | 页面给 layout 传静态信息 | `definePageMeta({ layout: { name, props } })` | | 页面在运行时切换 layout | `setPageLayout()` | | 页面读取当前 layout | `usePageLayout()` | | 统一管理布局状态 | store + page `watch` | | 访问 layout 内反馈节点 | `layout-host` / `resolveLayoutHost()` | | 共享全局状态 | store | ## 12. 参考资源 | 主题 | 推荐入口 | | --------------------- | ----------------------------------------------------------------- | | Route Rules 与 Layout | [config/route-rules](/config/route-rules) | | `layouts/` 目录说明 | [directory-structure/layouts](/guide/directory-structure/layouts) | | Wevu 运行时 API | [wevu/api/setup-context](/wevu/api/setup-context) | | Wevu 运行时机制 | [wevu/runtime](/wevu/runtime) | | 目录结构总览 | [guide/directory-structure](/guide/directory-structure/) | --- --- url: /guide/auto-import.md description: >- weapp-vite 可以在构建阶段自动扫描并注册组件,让你在 WXML 或 Vue SFC 模板里直接写组件标签,而不必手动维护 usingComponents。 --- # 自动引入组件 `weapp-vite` 可以在构建阶段自动扫描并注册组件,让你在 WXML 或 Vue SFC 模板里直接写组件标签,而不需要手动维护 `usingComponents`。 你只需要告诉它两件事: * 组件放在哪些目录(`globs`) * 是否要接入第三方 UI 库(`resolvers`) 更细粒度的字段说明可参考 [配置文档 · 自动导入组件配置](/config/auto-import-components.md#weapp-autoimportcomponents)。 > \[!NOTE] > 自动导入默认开启:主包 `components/**/*.wxml` 与每个 `subPackages.` 下的 `components/**/*.wxml` 都会被自动扫描。仅当你需要: > > * 额外扩展扫描目录; > * 关闭自动导入(例如 `autoImportComponents: false` 或 `autoImportComponents: { globs: [] }`);或 > * 引入第三方 UI Resolver / 生成自定义输出 > > 时才需要手动配置 `autoImportComponents`。 > > 当你显式写成 `autoImportComponents: true` 时,除了扫描组件目录,还会默认开启 `.weapp-vite/auto-import-components.json`、`.weapp-vite/typed-components.d.ts`、`.weapp-vite/mini-program.html-data.json` 与 `vueComponents` 输出。 ## 适用场景 * 在模板里直接写 ``,而不是每次都去 JSON 里登记一次。 * 组件数量多、目录复杂,希望构建器帮忙维护 `usingComponents`。 * 同时使用 Vant、TDesign 等 UI 库,希望和本地组件一样开箱即用。 ## 快速上手:扫描项目组件 虽然无需额外配置即可生效,但你仍可以通过 [`weapp.autoImportComponents.globs`](/config/auto-import-components.md#weapp-autoimportcomponents) 表达更复杂的目录结构。满足“存在 `.wxml` + `.js/ts` + `.json` 且 `json.component === true`”的文件夹就会被自动注册: ::: code-group ```ts [vite.config.ts] export default { weapp: { autoImportComponents: { globs: [ 'components/**/*.wxml', 'shared/design-system/**/*.wxml', ], }, }, } ``` ::: 默认规则如下: * 组件名取决于文件名,且大小写敏感。`HelloWorld.{wxml,ts,json}` 会注册为 `HelloWorld`。 * 如果组件命名为 `index.*`,则使用父级目录名(`HelloWorld/index.wxml → HelloWorld`)。 * 当同一目录下既有 `index.*` 又有同名文件(如 `HelloWorld/index.ts` 与 `HelloWorld/HelloWorld.ts`)时,会优先选用 `index.*` 以保证产物路径稳定。 ::: warning 内置组件(`view`、`text`、[`navigation-bar`](https://developers.weixin.qq.com/miniprogram/dev/component/navigation-bar.html) 等)会被自动忽略,避免重复注册。请避免把自定义组件命名成内置组件的名字;忽略列表可在 [`builtin.auto.ts`](https://github.com/weapp-vite/weapp-vite/blob/main/packages/weapp-vite/src/auto-import-components/builtin.auto.ts) 查看。 ::: ## 第三方 UI 库自动引入 若项目使用 Vant、TDesign 等 UI 组件库,可以通过 **Resolver** 声明“标签名 → npm 包”之间的映射。`weapp-vite` 默认内置常见解析器,也支持自定义: ::: code-group ```ts [vite.config.ts] import { TDesignResolver, VantResolver } from 'weapp-vite/auto-import-components/resolvers' export default { weapp: { autoImportComponents: { globs: ['components/**/*.wxml'], resolvers: [ VantResolver(), TDesignResolver(), ], }, }, } ``` ::: 配置完成后,就能直接在 `wxml` 中书写 ``、`` 等标签,构建器会自动补全 `usingComponents`。 如果你希望接入自己的组件库(或对第三方库做二次封装),可以参考:[自定义 Resolver(自动导入组件)](/guide/auto-import-resolver)。 ## 自动生成的辅助文件 除了运行时的自动注册,`weapp-vite` 还会生成一些辅助产物,帮助你在 IDE 内掌控组件列表。 ### 组件清单 默认会在 `.weapp-vite/auto-import-components.json` 输出所有自动注册的组件: ```json { "HelloWorld": "/components/HelloWorld/index", "Navbar": "/components/Navbar/Navbar", "van-button": "@vant/weapp/button" } ``` 你可以通过 `autoImportComponents.output` 自定义保存位置,或传入 `false` 关闭输出: ```ts export default { weapp: { autoImportComponents: { globs: ['components/**/*.wxml'], output: 'dist/auto-import-components.json', }, }, } ``` ### 类型声明与 HTML 自定义数据 * `typedComponents`: 生成 `typed-components.d.ts`,提供 `componentProps`、`ComponentProp` 等类型,方便在脚本中推断组件属性。 * `htmlCustomData`: 生成 `mini-program.html-data.json`,供 VS Code、微信开发者工具读取,实现标签/属性智能提示。 * `vueComponents`: 生成 `.weapp-vite/components.d.ts`,为 Vue SFC 模板补齐全局组件声明。 ```ts export default { weapp: { autoImportComponents: { globs: ['components/**/*.wxml'], typedComponents: true, // 或 'types/typed-components.d.ts' vueComponents: true, htmlCustomData: 'dist/mini-program.html-data.json', }, }, } ``` 构建器会在组件扫描、resolver 匹配的同一流程中自动刷新这些文件,无需手动触发。 从 `weapp-vite 6.15.1` 开始,`components.d.ts` 在为“带源码跳转的原生组件”补齐 Vue 模板类型时,也会稳定合并小程序通用基础属性。这意味着像下面这样的写法不再被误报: ```vue ``` 如果你依赖组件库或业务组件在模板里接收 `class`、`style`、`id` 这类基础属性,建议保持 `vueComponents` 输出开启。 如果你需要在编辑器或 CI 预热阶段提前生成这些文件,可以执行: ```bash wv prepare ``` ## 常见疑问 * **为什么没自动注册?** 先检查组件 `json` 是否包含 `"component": true`,再确认路径是否命中了 `globs`。修改 `globs` 或新增组件后记得重启 `pnpm dev` 以刷新缓存。 * **Resolver 报错怎么办?** 请确认对应 UI 库的 npm 包已安装,并与 resolver 支持的版本匹配。只想扫描本地组件时,可以临时移除 `resolvers`。 * **如何禁用部分组件?** 结合 `include` / `exclude` 或自定义 resolver 即可实现选择性注册,详见 [自动导入组件配置](/config/auto-import-components.md#weapp-autoimportcomponents)。 --- --- url: /guide/auto-import-resolver.md description: >- weapp.autoImportComponents.resolvers 用来把 WXML 里的组件标签(例如 )解析成小程序 usingComponents 需要的 from 路径(例如 @vant/weapp/button)。 --- # 自定义 autoImportComponents Resolver `weapp.autoImportComponents.resolvers` 用来把 WXML 里的组件标签(例如 ``)解析成小程序 `usingComponents` 需要的 `from` 路径(例如 `@vant/weapp/button`)。 本文提供一个可直接落地的 Resolver 模板,并说明它会影响哪些能力: * 自动写入 `usingComponents` * 生成 `auto-import-components.json` * 生成 `typed-components.d.ts` / `mini-program.html-data.json` ## Resolver 是什么 Resolver 支持两种写法(推荐对象写法): ```ts // 函数写法:返回 { name, from } 或 void type ResolverFn = (componentName: string, baseName: string) => { name: string, from: string } | void // 对象写法:提供 components 映射表,或提供 resolve() 方法 interface ResolverObject { components?: Record resolve?: (componentName: string, baseName: string) => { name: string, from: string } | void resolveExternalMetadataCandidates?: (from: string) => { packageName: string dts: string[] js: string[] } | undefined } ``` > \[!TIP] > Weapp-vite 内置的 `VantResolver` / `TDesignResolver` / `WeuiResolver` 也是对象 resolver。自定义 resolver 推荐优先用对象写法:结构更清晰,也更方便提供 `components` / metadata 信息。 * `componentName`: 模板中的标签名(如 `van-button`、`t-tabs`、`HelloWorld`) * `baseName`: 当前处理的文件名(用于进阶场景:按页面/组件上下文做差异化映射) * 返回值:告诉 Weapp-vite 把该标签注册到哪个 `from` ## 推荐:对象写法(静态映射) 如果你已经有一份“标签名 → from”的静态表,推荐直接用对象写法(无需写函数逻辑): ```ts import type { Resolver } from 'weapp-vite/auto-import-components/resolvers' import { defineConfig } from 'weapp-vite/config' const resolver: Resolver = { components: { 'x-button': 'my-ui/button/button', 'x-dialog': 'my-ui/dialog/dialog', }, } export default defineConfig({ weapp: { autoImportComponents: { resolvers: [resolver], }, }, }) ``` > \[!TIP] > 这个实现已经足够让 Weapp-vite 在构建时自动补全 `usingComponents`,并且便于参与 `auto-import-components.json` / `typed-components.d.ts` 等产物生成。 ## 建议增强 1:暴露 `resolver.components`(用于“全量生成”) 当你开启 `typedComponents` / `htmlCustomData` 或希望输出的 `auto-import-components.json` 里包含第三方库组件时,建议维护 `resolver.components`: ```ts import type { Resolver } from 'weapp-vite/auto-import-components/resolvers' const components = ['button', 'tabs', 'dialog'] as const const map = Object.fromEntries( components.map(name => [`x-${name}`, `my-ui/${name}/${name}`]), ) export const MyUiResolver: Resolver = { components: Object.freeze({ ...map }), resolve(componentName) { const from = map[componentName] if (!from) { return } return { name: componentName, from } }, } ``` * `resolver.components` 会被 Weapp-vite 用来收集“该 resolver 支持哪些组件”。 * 如果你只写了动态解析逻辑但没有 `components` 映射,通常仍能自动注册 `usingComponents`,但“全量类型/补全文件”可能无法覆盖到这些组件。 ## 建议增强 2:支持第三方组件库 props 类型解析 如果你的 `from` 指向 npm 包(例如 `@vant/weapp/button`),并且希望 Weapp-vite 在生成 `typed-components.d.ts` 时能从第三方库读取 `.d.ts` / `.js` 里的 props 信息,可以实现: ```ts resolver.resolveExternalMetadataCandidates = (from) => { // 命中该 resolver 管理的包时返回候选路径 return { packageName: 'my-ui', dts: ['dist/button/index.d.ts'], js: ['dist/button/index.js'], } } ``` 完整示例(简化版): ```ts import type { Resolver } from 'weapp-vite/auto-import-components/resolvers' const resolver: Resolver = { components: { 'x-button': 'my-ui/button', }, resolve(componentName) { const from = componentName === 'x-button' ? 'my-ui/button' : undefined if (!from) { return } return { name: componentName, from } }, resolveExternalMetadataCandidates(from) { if (!from.startsWith('my-ui/')) { return } const component = from.slice('my-ui/'.length) if (!component) { return } return { packageName: 'my-ui', dts: [`dist/${component}/index.d.ts`], js: [`dist/${component}/index.js`], } }, } ``` > \[!NOTE] > `resolveExternalMetadataCandidates` 的目标不是“让组件能被解析”,而是“告诉 Weapp-vite 到第三方依赖包里去哪里找 metadata 文件”,用于类型与补全产物。 ## 进阶:函数写法(按需动态解析) 如果你需要按上下文动态解析(例如同一个标签在不同页面映射不同 `from`),可以使用函数写法: ```ts import type { Resolver } from 'weapp-vite/auto-import-components/resolvers' export function DynamicResolver(): Resolver { return (componentName, baseName) => { if (componentName !== 'x-button') { return } const from = baseName.includes('admin') ? 'my-ui-admin/button/button' : 'my-ui/button/button' return { name: componentName, from } } } ``` ## 参考实现 * `weapp-vite` 内置 Resolver:`VantResolver`、`TDesignResolver`、`WeuiResolver` * 配置入口:[`weapp.autoImportComponents`](/config/auto-import-components.md#weapp-autoimportcomponents) --- --- url: /guide/wxml.md description: WXML 增强,聚焦 guide / wxml 相关场景,覆盖 Weapp-vite 与 Wevu 的能力、配置和实践要点。 --- # WXML 增强 `weapp-vite` 对 WXML 做了两类增强: * **自动收集 WXML 依赖**:把 `import` / `include` 引到的模板文件自动带进产物 * **事件语法糖(可选)**:允许写 `@tap="fn"`,构建时自动转换成原生 `bind:tap` 本页介绍它们的工作方式,以及不需要时如何关闭。 ## 静态分析与额外文件 默认情况下,框架会扫描页面、组件、分包目录,解析 `import` / `include` 中的 `src`,将对应的 WXML 文件复制到产物目录,并保持路径一致。 > \[!IMPORTANT] > 该分析是静态的,无法推断运行时动态拼接的路径。当前版本的 `weapp.isAdditionalWxml` 仍为预留字段,**不会参与扫描**。如果必须走动态路径,请确保这些模板能通过某个固定的 `import` / `include` 被引用,或在构建流程中自行补充产物。 ## 事件绑定语法糖(可选) 开启 `weapp.wxml` 后,可以使用类似 Vue 的 `@` 语法,Weapp-vite 会在构建时转换为原生事件写法: ```html ``` 对应关系示例: | 写法 | 转换结果 | | ------------------------------------------- | ------------------- | | `@tap` | `bind:tap` | | `@tap.catch` | `catch:tap` | | `@tap.mut` | `mut-bind:tap` | | `@tap.capture` | `capture-bind:tap` | | `@tap.capture.catch` / `@tap.catch.capture` | `capture-catch:tap` | 更多事件类型可参考[官方事件分类](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html)。 > \[!WARNING] > 某些第三方 WXML 插件的格式化功能可能无法识别 `@tap` 等语法糖。当前版本暂无单独开关,建议直接使用原生 `bind:` 写法规避冲突。 ## 当前可配置的范围 目前 `weapp.wxml` 仅影响 **扫描阶段**(`excludeComponent` / `platform`),模板处理阶段的 `transformEvent` / `removeComment` 等选项尚未接入,详见 [WXML 配置](/config/wxml.md#weapp-wxml)。 --- --- url: /guide/wxss.md description: >- Weapp-vite 继承了 Vite 的样式处理能力:支持 .wxss、.css、.scss、.less、.sass、.styl 等格式,并输出为小程序可识别的 WXSS。 --- # WXSS 样式增强与注意点 `weapp-vite` 继承了 Vite 的样式处理能力:支持 `.wxss`、`.css`、`.scss`、`.less`、`.sass`、`.styl` 等格式,并输出为小程序可识别的 WXSS。 本页主要讲两件事: 1. Weapp-vite 会怎么收集/编译同名样式文件 2. 小程序场景里为什么有些 `@import` 不能被“提前内联”,以及怎么处理 ## 支持的样式格式 入口脚本(如 `pages/index/index.ts`)在构建时会按固定顺序注入同名样式文件: ```ts import './pages/index/index.wxss' import './pages/index/index.css' import './pages/index/index.scss' import './pages/index/index.less' import './pages/index/index.sass' import './pages/index/index.styl' ``` 只要安装了对应的预处理器依赖(`sass`、`less`、`stylus`),上述文件都会被 Rolldown 处理并转换成 WXSS。 ## Vite 的默认行为 在 Web 项目中,Vite 会解析样式里的 `@import`、`url()` 等语句,在构建阶段将其“内联”成一份完整的样式。这在小程序项目里同样适用: ```scss /* input.scss */ @import './base.scss'; .box { background: pink; } ``` ```css /* output.wxss */ /* from base.scss */ .base { background: pink; } .box { background: pink; } ``` > \[!NOTE] > 预处理器通常不会识别 `.wxss` 扩展名。如果需要在 SCSS 中引用 `.wxss` 文件,需要额外配置 resolver,或使用同名的 `.scss` 文件。 ## 小程序场景下的差异 小程序原生支持 `@import`,由运行时解析路径并加载对应文件。部分生态(如 TDesign 的主题切换)依赖这种机制,如果被 Vite 内联,就会破坏原有逻辑。 为此 Weapp-vite 提供了一个自定义指令 `@wv-keep-import`,用于跳过 Vite 的内联处理: ```diff - @import 'miniprogram_npm/tdesign-miniprogram/common/style/theme/_index.wxss'; + @wv-keep-import 'miniprogram_npm/tdesign-miniprogram/common/style/theme/_index.wxss'; ``` 编译后会恢复为原生写法: ```css @import 'miniprogram_npm/tdesign-miniprogram/common/style/theme/_index.wxss'; ``` 这样微信开发者工具就能在运行时继续处理该依赖。 ### 何时使用 `@wv-keep-import` * 需要把 `@import` 原封不动交给小程序运行时。 * 引用路径指向 `miniprogram_npm`、分包或其他构建后目录。 * 希望在 WXSS 里保留官方语法(尤其是 `_index.wxss`、`theme` 等主题相关文件)。 对于普通的样式拆分,仍建议使用默认的 `@import` 或 `@use`,让构建器提前合并,提升首屏效率。 ## 资源引用与公共样式 * 使用 `@/`、`./` 等路径导入图片时,Rolldown 会自动复制并生成正确的产物路径。 * 若资源位于 `public/`,请改用绝对路径 `/icons/logo.png`,该目录会被原样复制。 * 可结合 [`weapp.subPackages[].styles`](/config/subpackages.md#subpackages-styles) 在普通或独立分包中注入共享主题、变量。 ## 常见问题 * **样式顺序异常?** Weapp-vite 会按固定顺序注入不同后缀的文件,建议团队统一主力格式(例如全部使用 `.scss`)。 * **SCSS 引入 `.wxss` 报错?** 这是 Sass 的限制。可以将共享样式改为 `.scss`,或在编译后通过 `@wv-keep-import` 保留 WXSS 引入。 * **`@wv-keep-import` 不生效?** 请确认对应文件在构建产物中仍保留了 `@import`,若被其他插件处理,可尝试调整插件顺序或在 PostCSS 阶段配置 `exclude`。 --- --- url: /guide/json-intelli-sense.md description: 给 app.json、page.json 等文件加上 $schema 字段后,VS Code、微信开发者工具等编辑器就能提供: --- # JSON 配置文件的智能提示 给 `app.json`、`page.json` 等文件加上 `$schema` 字段后,VS Code、微信开发者工具等编辑器就能提供: * 字段补全 * 取值提示 * 错误校验 本页提供现成的 Schema 地址,复制到文件开头即可使用。 > \[!TIP] > 使用 Weapp-vite 的脚手架(`pnpm g`)生成的页面/组件,默认已经包含对应的 `$schema`,你只需确认编辑器支持 JSON Schema 即可。工作区已在 `.vscode/settings.json` 里把在线地址映射到本地 `node_modules/@weapp-core/schematics/schemas/*.json`,既保留短链接又支持离线补全。 ## 如何添加 `$schema` 在目标配置文件的开头写入对应的 `$schema` 字段,例如: ```jsonc { "$schema": "https://vite.icebreaker.top/page.json", "navigationBarTitleText": "Home" } ``` `$schema` 只在编辑器里生效;构建阶段会自动剥离,不会影响最终产物。 ## 常用 Schema 列表 | 文件 | `$schema` 地址 | | ---------------- | ----------------------------------------------- | | 组件 `component.json` | `"https://vite.icebreaker.top/component.json"` | | 页面 `page.json` | `"https://vite.icebreaker.top/page.json"` | | 应用 `app.json` | `"https://vite.icebreaker.top/app.json"` | | `sitemap.json` | `"https://vite.icebreaker.top/sitemap.json"` | | `theme.json` | `"https://vite.icebreaker.top/theme.json"` | 直接复制右侧的地址粘贴即可。如果编辑器支持“悬浮后复制按钮”,也可以将整行复制下来放入文件中。VS Code 用户无需修改 `$schema`,工作区设置会自动将在线地址重定向到本地 Schema 文件。 ## 效果展示 ![vscode-json-intel](/vscode-json-intel.png) ## 常见问题 * **需要联网吗?** 默认 `$schema` 仍是在线地址,但 VS Code 会把它映射到本地 `node_modules/@weapp-core/schematics/schemas/*.json`,离线也能补全。如果换用其他编辑器,可手动做类似映射或直接改成本地路径。 * **生成器能输出本地 `$schema` 吗?** 可以,通过环境变量 `WEAPP_SCHEMA_BASE=file:///<你的项目绝对路径>/node_modules/@weapp-core/schematics/schemas` 让 `@weapp-core/schematics` 生成时使用本地 Schema(非必需,仅想让文件自带本地路径时启用)。 * **脚手架已生成 `$schema`,但仍没有提示?** 请确认编辑器启用了 JSON Schema 支持:VS Code 需安装官方小程序扩展或开启原生 JSON 支持;微信开发者工具需升级到较新的版本。 * **和 `json.ts` / `json.js` 配合?** 可以。在脚本文件里同样可以导出 `$schema` 字段,Weapp-vite 在构建时会一并剥离。 --- --- url: /guide/json-enhance.md description: >- 小程序项目里有很多结构相似的 json 配置(页面/组件/App)。Weapp-vite 在兼容原生 json/jsonc 的基础上,允许你用 json.ts / json.js 生成最终配置,让配置也能享受模块化、类型提示和复用能力。 --- # 使用 TS/JS 生成 JSON 小程序项目里有很多结构相似的 `json` 配置(页面/组件/App)。`weapp-vite` 在兼容原生 `json/jsonc` 的基础上,允许你用 `json.ts` / `json.js` 生成最终配置,让配置也能享受模块化、类型提示和复用能力。 一个组件若名为 `custom`,框架会按以下优先级查找配置文件:`custom.jsonc` → `custom.json` → `custom.json.ts` → `custom.json.js`。因此你可以在需要时逐步升级为脚本驱动的配置。 ## 为什么要用脚本生成 JSON? * **复用与拆分**:直接 `import` 共享配置,避免复制粘贴。 * **类型安全**:借助 TypeScript 或 JSDoc 获得智能提示、错误提示。 * **动态拼装**:在同一文件中根据条件判断、合并配置,更易维护。 ## 快速示例 下面示例展示了同一个组件的三种写法。`defineComponentJson` 只是为了提供类型提示,不会修改你传入的内容。 > 如果你使用的是 Vue SFC(`.vue`)并希望把配置写在 ` ``` ### `definePageJson` 宏定义页面配置 ```html ``` ### 在 `.vue` 里直接用原生组件 ```html ``` ### `v-model` 表单双向绑定 ```html ``` 更多像 `slots`、`props/emits`、`app.vue` 配置以及编译行为说明,已放到原理文档统一说明:[`Weapp-vite@6 原理拆解`](/blog/release6-principles)。 ## 适用场景 ### 双模式并存才是 Weapp-vite 的杀手锏 Weapp-vite@6 最实用的一点就是"同仓双模式"。性能敏感的页面继续走原生,迭代快、业务重的页面丢到 Vue 模式里。迁移可以一个页面一个页面来,不用一口气重写整个项目。 ### 什么时候用 Vue 模式: * 你平时写 Vue 3,想用同样的写法搞小程序 * 团队本来就是 Vue 技术栈,想复用过来 * 想要热重载、TypeScript 这些现代开发体验 * 希望 Vue 代码后面还能往 Web 项目上搬 ### 什么时候用原生模式: * 对性能有洁癖,一点运行时开销都不想要 * 已经有一大堆原生代码,不想大动 * 团队对小程序原生 API 很熟 * 包体积卡得很死 ### 什么时候该选别的框架? * **Taro**:如果你真的要同时出微信、支付宝、百度、字节好几个平台的小程序,甚至还要编 H5 和 RN,那 Taro 确实是绕不开的。不过说真的,大部分项目真需要跨这么多端吗? * **uni-app**:如果你想要一个开箱即用的全家桶,而且已经习惯了 DCloud 那套生态(HBuilderX、uniCloud 之类的),uni-app 挺合适。就是它的 DSL 跟标准 Vue 还是有些差异。 * **mpx**:Vue 2.7 + webpack,技术栈偏老了。 ## 快速体验 1. **创建项目**: ```sh pnpm create weapp-vite@latest # 选择 Wevu 模板或者 Wevu + TDesign 模板 ``` 2. **开发**: ```sh pnpm dev ``` 3. **享受 Vue 带来的快乐**: ```html ``` ## 技术细节 原理和实现细节,如果大家有兴趣的话,我会另外写一篇专门的技术拆解文档。 ## 后面打算做什么 接下来主要推两条线:支持更多小程序平台,以及支持 Web 目标。 ### Android / iOS 原生方向 现在原生 Android / iOS 这边,很多场景还是得靠微信开发者工具的多端框架来转。这块后面会继续投入,目标是把链路做得更稳、接入成本更低。 ## 最后 Weapp-vite@6 这次就是想把选择权留给你:要性能就走原生,要开发体验就走 Vue 模式,混着来也行。背后靠的是 `vue/compiler-sfc` 的解析能力、`wevu` 的运行时设计,以及社区一路给的真实反馈。 感谢每一位提建议、报 bug、提 PR 的同学。 *** 如果 Weapp-vite 帮到了你,欢迎给项目点个 [Star](https://github.com/weapp-vite/weapp-vite)! Happy Coding! 🚀 --- --- url: /blog/release6-principles.md description: 这篇文档不是功能清单,而是 Weapp-vite@6 在实现 Vue SFC 支持时的一份技术复盘,重点记录编译链路、运行时更新路径和关键取舍。 --- ![Weapp-vite 6 顶部海报](/6/bg.jpg) # 重走 Vue 长征路 Weapp-vite:编译链路与 Wevu 运行时原理拆解 书接上篇 我当时在团队里做[《Vue 编译本质论》](https://deep-in-vue.icebreaker.top/)分享,正好把一些判断过程也整理了下来:为什么这么做,没选什么,以及这些取舍在小程序里到底值不值。 如果你更关心怎么上手,先看发布文会更顺:[`Weapp-vite:原生模式之外,多一种 Vue SFC 选择`](/blog/release6)。 ## 先把边界说清:Wevu 不是 Vue 3 的搬运工 Wevu 用起来确实很像 Vue 3,但骨子里不是一回事。 | 对比维度 | Vue 3 | Wevu | | :--------- | :------------------------- | :------------------------ | | 运行环境 | Web 浏览器 | 微信小程序 | | 响应式系统 | Proxy + effect | Proxy + effect(同源) | | 渲染目标 | DOM 节点 | 小程序页面/组件实例 | | 渲染方式 | Virtual DOM Diff → DOM API | Snapshot Diff → `setData` | | 数据模型 | VNode 树 | 纯 JS 对象快照 | | 更新机制 | 异步调度 + DOM 操作 | 异步调度 + `setData` | | 生命周期 | onMounted/onUpdated 等 | 映射到小程序生命周期 | | 事件系统 | DOM 事件 | 小程序 bind/catch 事件 | | SFC 编译 | @vitejs/plugin-vue | Weapp-vite 内置 | 说白了就一件事:**响应式 API 长得一样,但最后数据往哪送、怎么送,完全不同**。 ## API 为什么能"几乎同写法" `ref`、`computed`、`watch` 这些在 wevu 里跟 Vue 3 写法一模一样,没必要再造一套 DSL 出来。 ```ts import { computed, ref, watch } from 'wevu' const count = ref(0) const doubled = computed(() => count.value * 2) watch(count, (val) => { console.log('count changed:', val) }) ``` 很多团队迁过来之后第一反应不是"又要学新东西",而是"这不就是我平时写的吗,换了个宿主而已"。 ## 渲染链路才是真正不一样的地方 Vue 3 走的是这条路: ```text 状态变化 -> effect 触发 -> 组件更新 -> VNode Diff -> DOM 操作 ``` Wevu 走的是这条: ```text 状态变化 -> effect 触发 -> 快照 Diff -> setData -> 小程序渲染 ``` Wevu 干的事情说穿了就是把"算出哪些东西变了"这一步尽量提前做完,等到真正调 `setData` 的时候,payload 已经被压到最小了。这在小程序里特别关键——大家踩过坑的都知道,`setData` 传多了,页面就卡,尤其是列表页。 ## `.vue` 到四件套:编译阶段干了啥 一个 `MyComponent.vue` 最终会变成小程序四件套: ```text MyComponent.vue ├─> MyComponent.js ├─> MyComponent.wxml ├─> MyComponent.wxss └─> MyComponent.json ``` 中间的流程大概是这样:先把 SFC 拆成四块——` ``` 好处就是直接写在 ` ``` ### 2. 指定 layout + props ```vue ``` ### 3. 显式关闭 layout ```vue ``` > \[!NOTE] > `definePageMeta().layout` 只支持静态字符串、`false`,或 `{ name, props }` 对象。 > `props` 必须是对象字面量,键名必须是静态的。 ## 运行时动态切换 如果页面需要在运行过程中切换 layout,可以使用 `wevu` 提供的 API: ```ts import { setPageLayout, usePageLayout } from "wevu"; const currentLayout = usePageLayout(); function switchToAdmin() { setPageLayout("admin", { sidebar: true, title: "管理后台", }); } function switchToPlain() { setPageLayout(false); } ``` 当项目里存在 layout 扫描结果时,`.weapp-vite/wevu-layouts.d.ts` 会自动为这些 API 生成更严格的类型提示。 ## layout 相关类型文件 layout 能力会联动生成: * `.weapp-vite/wevu-layouts.d.ts` * `.weapp-vite/components.d.ts` 其中 `wevu-layouts.d.ts` 会增强 `WevuPageLayoutMap`,让 `setPageLayout()` 与 `usePageLayout()` 拿到 layout 名称和 props 类型。 ## 适用场景 * 按页面目录批量挂载同一套页面壳子 * 让业务页面复用导航栏、侧边栏、底部容器等布局结构 * 在 Vue layout 与原生 layout 间混合使用 * 在页面运行时按状态切换 layout ## 相关文档 * [页面 Layout 使用指南](/guide/layouts) * [layouts 目录说明](/guide/directory-structure/layouts) * [Vue SFC 配置](/config/vue) * [TypeScript 支持文件](/config/typescript) * [Wevu 概览](/wevu/) --- --- url: /config/subpackages.md description: >- weapp-vite 会读取 app.json.subPackages 生成分包产物;weapp.subPackages 则提供独立分包、分包级内联配置、自动导入覆盖与共享样式等增强能力。 --- # 分包配置 {#subpackages-config} `app.json.subPackages` 决定“小程序有哪些分包”,而 `weapp.subPackages` 决定“这些分包在构建阶段还需要哪些额外能力”。 这两者要一起看: * `app.json.subPackages`:声明分包本身 * `weapp.subPackages`:补充构建期增强 \[\[toc]] ## `weapp.subPackages` {#weapp-subpackages} * **类型**: ```ts Record autoImportComponents?: AutoImportComponents | boolean watchSharedStyles?: boolean styles?: SubPackageStyleConfigEntry | SubPackageStyleConfigEntry[] }> ``` * **默认值**:`undefined` ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { subPackages: { marketing: { independent: true, autoImportComponents: false, styles: [ 'styles/shared.wxss', { source: 'styles/pages.wxss', scope: 'pages', }, ], }, }, }, }) ``` > \[!NOTE] > 这里的 key 必须与 `app.json.subPackages[].root` 一致,否则不会生效。 ## 字段说明 ### `independent` * **类型**:`boolean` 表示该分包是否按独立分包上下文处理。 它通常应和 `app.json` 中对应分包的 `independent: true` 保持一致,避免配置语义分裂。 ### `inlineConfig` * **类型**:`Partial` 允许为单个分包追加 Vite 内联配置。 适用场景: * 某个分包需要额外 `define` * 某个分包要临时增加插件或局部构建参数 > \[!TIP] > `inlineConfig` 里写的是 **Vite 原生配置**。字段本身的语义请直接看 [Vite 中文官方配置文档](https://cn.vite.dev/config/)。 ### `autoImportComponents` * **类型**:`AutoImportComponents | boolean` 用于给单个分包覆盖全局组件自动导入策略,或直接关闭它。 常见场景: * 某个分包不希望自动补 `usingComponents` * 某个分包需要额外的 resolver 或组件扫描规则 ### `watchSharedStyles` * **类型**:`boolean` 控制该分包在开发时是否监听共享样式并触发重新生成。 适合: * 分包共享样式很多,且你明确想控制 dev 监听成本 ### `styles` * **类型**:`SubPackageStyleConfigEntry | SubPackageStyleConfigEntry[]` 用于声明分包共享样式入口。 ## `subPackages.*.styles` {#subpackages-styles} `SubPackageStyleConfigEntry` 支持两种形式: * 字符串 * 对象 对象结构为: ```ts { source: string scope?: 'all' | 'pages' | 'components' include?: string | string[] exclude?: string | string[] } ``` 示例: ```ts export default defineConfig({ weapp: { subPackages: { marketing: { styles: [ 'styles/shared.wxss', { source: 'styles/page-only.wxss', scope: 'pages', exclude: ['pages/legacy/**'], }, ], }, }, }, }) ``` 字段说明: * `source`:样式源文件路径,可相对分包 root、相对 `srcRoot`,也可直接用绝对路径 * `scope`: * `all`:分包内所有页面和组件 * `pages`:只作用于页面 * `components`:只作用于组件 * `include` / `exclude`:更精细的 glob 匹配 ## 与自动路由的关系 当启用 `weapp.autoRoutes` 时,`weapp.subPackages` 还有一个额外作用: * 告诉自动路由“哪些 root 应按分包页面目录处理” 如果你的分包页面不走默认 `root/pages/**` 约定,通常需要同时配置: * `weapp.subPackages` * `weapp.autoRoutes.include` ## 与 npm 分包落位的关系 `weapp.subPackages` 不负责 npm 包落位。 如果你要控制: * 哪些依赖进入主包 `miniprogram_npm` * 哪些依赖进入指定分包 `miniprogram_npm` 请使用: * `weapp.npm.mainPackage` * `weapp.npm.subPackages` 详见 [npm 配置](./npm.md)。 ## 常见建议 ### 什么时候要用 `inlineConfig` 只有当某个分包真的需要“局部构建差异”时再用。大多数分包项目先保持全局统一配置更稳。 ### 什么时候要把样式抽到 `styles` 当一套共享样式要稳定作用于整个分包,而不是单个组件手动导入时,这个字段价值最高。 ### 独立分包为什么还要单独关注共享 chunk 因为独立分包不参与普通分包那套共享分发模型。做分包优化时,要一起看: * [共享 Chunk 配置](./chunks.md) * [npm 配置](./npm.md) *** 如果你接下来要继续处理分包依赖落位,请看 [npm 配置](./npm.md)。如果你要继续处理共享模块落盘策略,请看 [共享 Chunk 配置](./chunks.md)。 --- --- url: /config/worker.md description: 当 app.json 配置了 workers 目录,Weapp-vite 可以帮助编译 Worker 入口脚本。 --- # Worker 配置 {#worker-config} 当 `app.json` 配置了 `workers` 目录,`weapp-vite` 可以帮助编译 Worker 入口脚本。 \[\[toc]] ## `weapp.worker` {#weapp-worker} * **类型**: ```ts { entry?: string | string[] } ``` * **默认值**:`undefined` ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { worker: { entry: ['calc.ts', 'image.ts'], }, }, }) ``` 说明: * `entry` 为 **相对于 `app.json.workers` 目录** 的路径。 * 若未写扩展名,会自动尝试 `.js/.ts`。 * Worker 构建会复用 TS/别名/依赖解析能力,并输出到同一 `dist/` 目录下的 `workers/` 目录。 常见问题: * **未指定 `entry` 会怎样?** 不会额外构建 Worker;你可以自行维护产物,但无法享受自动编译。 * **Worker 入口不存在?** 构建时会输出警告,并跳过该入口。 *** 更多调试手段请见 [共享配置 · weapp.debug](/config/shared.md#weapp-debug)。 --- --- url: /config/json.md description: >- Weapp-vite 支持原生 json/jsonc,并提供 **JSON 别名** 与 **JSON 合并策略**,方便在 app.json / page.json / component.json 中复用配置。 --- # JSON 配置 {#json-config} `weapp-vite` 支持原生 `json/jsonc`,并提供 **JSON 别名** 与 **JSON 合并策略**,方便在 `app.json / page.json / component.json` 中复用配置。 \[\[toc]] ## `weapp.jsonAlias` {#weapp-jsonalias} * **类型**:`false | { entries?: Record | { find: string | RegExp; replacement: string }[] }` * **默认值**:不启用,需要通过 `entries` 显式配置 * **作用范围**:**仅作用于 `usingComponents`**(其他字段保持原样)。 默认情况下,`compilerOptions.paths` 只会参与 JS/TS 模块解析,不会自动作用到 JSON / JSONC 的 `usingComponents`。如果源码页面 `src/pages/index/index.json` 里需要写 `@/components/foo` 这类路径,请显式配置 `weapp.jsonAlias.entries`。 如果需要完全关闭 JSON 别名,可以设置 `jsonAlias: false`: ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { jsonAlias: false, }, }) ``` ```ts import path from 'node:path' import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { jsonAlias: { entries: [ { find: '@/components/', replacement: path.resolve(import.meta.dirname, 'src/components/') }, { find: /^@icons\//, replacement: path.resolve(import.meta.dirname, 'src/assets/icons/') }, ], }, }, }) ``` JSON/JSONC 中可直接使用别名: ```jsonc { "usingComponents": { "nav-bar": "@/components/navigation-bar", "logo-icon": "@icons/logo" } } ``` 构建产物会转换为相对路径: ```json { "usingComponents": { "nav-bar": "../../components/navigation-bar", "logo-icon": "../../assets/icons/logo" } } ``` > \[!TIP] > `replacement` 推荐使用**绝对路径**,避免因工作目录变化导致解析失败。 ## `weapp.json.defaults` {#weapp-json-defaults} * **类型**:`{ app?: Record; page?: Record; component?: Record }` * **默认值**:`undefined` * **作用**:给 app/page/component JSON 注入统一默认值。 ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { json: { defaults: { app: { entryPagePath: 'pages/index/index', }, page: { navigationStyle: 'custom', }, component: { styleIsolation: 'apply-shared', }, }, }, }, }) ``` 说明: * 默认值会在生成 `.json` 产物时合并。 * 页面/组件自身的 JSON(或 SFC `` / 宏)会覆盖默认值。 ## `weapp.json.mergeStrategy` {#weapp-json-merge-strategy} * **类型**:`'deep' | 'assign' | 'replace' | (target, source, ctx) => Record | void` * **默认值**:`'deep'` ```ts export default defineConfig({ weapp: { json: { mergeStrategy: 'assign', }, }, }) ``` 函数策略会收到上下文: ```ts export default defineConfig({ weapp: { json: { mergeStrategy(target, source, ctx) { if (ctx.kind === 'page' && ctx.stage === 'defaults') { return { ...target, ...source } } return { ...source, ...target } }, }, }, }) ``` 常见 `ctx.stage`:`defaults` / `json-block` / `auto-using-components` / `component-generics` / `macro` / `emit` / `merge-existing`。 *** 需要配置脚本别名?请前往 [JS 配置](/config/js.md#weapp-tsconfigpaths)。 --- --- url: /config/js.md description: >- Weapp-vite 默认使用 Vite 8 原生的 resolve.tsconfigPaths 读取 tsconfig.json/jsconfig.json 的 paths/baseUrl,并在需要高级选项时兼容 vite-tsconfig-paths。 --- # JS 配置 {#js-config} `weapp-vite` 默认使用 Vite 8 原生的 `resolve.tsconfigPaths` 读取 `tsconfig.json/jsconfig.json` 的 `paths/baseUrl`,把别名映射到 Vite / Rolldown 流程中。JSON / JSONC 的 `usingComponents` 不会默认继承 `paths`,需要别名时请显式配置 `weapp.jsonAlias`。只有在你传入高级选项对象时,才会回退到 `vite-tsconfig-paths` 插件。 \[\[toc]] ## `weapp.tsconfigPaths` {#weapp-tsconfigpaths} * **类型**:`true | TsconfigPathsOptions | false` * **默认值**:`undefined`(按需自动启用) 启用规则: * 当 `tsconfig.json` 或 `jsconfig.json` **存在 `paths` 或 `baseUrl`** 时,会自动启用 Vite 原生 `resolve.tsconfigPaths`; * 传入 `true` 时,会强制启用原生 `resolve.tsconfigPaths`; * 传入对象时,会启用 `vite-tsconfig-paths` 插件以支持 `projects`、`exclude` 等高级选项; * 传入 `false` 可完全禁用(适合没有别名需求、追求更快启动的项目)。 推荐优先使用默认行为或 `true`,这样不会触发 Vite 8 对 `vite-tsconfig-paths` 的提示信息。 ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { tsconfigPaths: true, }, }) ``` ```ts import { defineConfig } from 'weapp-vite/config' import type { PluginOptions } from 'vite-tsconfig-paths' const tsconfigOptions: PluginOptions = { projects: ['./tsconfig.base.json'], extensions: ['.ts', '.js', '.vue'], exclude: ['**/__tests__/**'], } export default defineConfig({ weapp: { tsconfigPaths: tsconfigOptions, }, }) ``` ### 与 `resolve.alias` 的关系 * `weapp.tsconfigPaths` / `resolve.tsconfigPaths` 负责把 **tsconfig 的 paths/baseUrl** 转成 Vite alias。 * JSON / JSONC 的 `usingComponents` 不会默认继承 `compilerOptions.paths`;需要别名时请显式配置 `weapp.jsonAlias`。 * 你仍然可以在 `resolve.alias` 中补充或覆盖特定映射,两者可共存。 ```ts export default defineConfig({ resolve: { alias: { '@shared': '/packages/shared/src', }, }, weapp: { tsconfigPaths: { projects: ['./tsconfig.base.json'], }, }, }) ``` ### 常见问题 * **修改 `paths` 没生效?** 需要重启 `pnpm dev`,并确认 tsconfig 在 `projects` 列表内。 * **JSON 别名怎么配?** JSON 别名需要显式配置 `weapp.jsonAlias`;它不会默认继承 `compilerOptions.paths`(见 [JSON 配置](/config/json.md#weapp-jsonalias))。 ## `weapp.ast` {#weapp-ast} * **类型**:`{ engine?: 'babel' | 'oxc' }` * **默认值**:`{ engine: 'babel' }` `weapp.ast` 用来控制部分“静态分析链路”优先使用哪套 AST 引擎,例如: * 组件 props 元数据提取 * `usingComponents` / 自动导入相关分析 * `setData.pick` 模板 key 收集 * 一部分平台 API / require 快速判定 ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { ast: { engine: 'oxc', }, }, }) ``` 使用建议: * 默认保持 `babel` 即可,兼容性最稳。 * 当你明确想在“分析链路”上尝试更快的解析实现时,再切到 `oxc`。 * 这不是“所有编译流程全部切换引擎”的总开关,而是给已接入 AST 抽象层的分析能力提供统一入口。 *** 更多 alias 实战与疑难排查,请参考 [路径别名指南](/guide/alias)。 --- --- url: /config/vue.md description: Weapp-vite 内置 Vue SFC(.vue → WXML/WXSS/JS/JSON)编译链路。这里聚焦编译期可配置项。 --- # Vue SFC 配置 {#vue-config} `weapp-vite` 内置 Vue SFC(`.vue` → WXML/WXSS/JS/JSON)编译链路。这里聚焦编译期可配置项。 > \[!TIP] > 如果你在找页面级 `layout` 能力,请优先看 [Route Rules 与 Layout](/config/route-rules)。`layout` 本身不属于 `weapp.vue` 字段,而是通过 `definePageMeta()`、`weapp.routeRules` 与 `srcRoot/layouts/` 目录协同工作。 \[\[toc]] ## `weapp.vue.enable` {#weapp-vue-enable} * **类型**:`boolean` * **默认值**:`true` * **说明**:保留字段。当前版本会在检测到 `.vue` 时自动启用 SFC 支持,该字段不影响行为。 ## `weapp.vue.template` {#weapp-vue-template} * **类型**: ```ts { removeComments?: boolean simplifyWhitespace?: boolean formatWxml?: boolean | 'auto' htmlTagToWxml?: boolean | Record htmlTagToWxmlTagClass?: boolean scopedSlotsCompiler?: 'auto' | 'augmented' | 'off' scopedSlotsRequireProps?: boolean slotSingleRootNoWrapper?: boolean slotFallbackWrapperStrategy?: 'view' | 'virtual-host' slotFallbackWrapper?: string | { tag?: string attrs?: Record singleRootNoWrapper?: boolean rules?: Array<{ component?: string | RegExp | Array componentName?: string | RegExp | Array slot?: string | RegExp | Array tag?: string attrs?: Record singleRootNoWrapper?: boolean }> } slotMultipleInstance?: boolean classStyleRuntime?: 'auto' | 'wxs' | 'js' objectLiteralBindMode?: 'runtime' | 'inline' mustacheInterpolation?: 'compact' | 'spaced' classStyleWxsShared?: boolean functionPropNames?: Array } ``` 示例: ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { vue: { template: { htmlTagToWxml: true, htmlTagToWxmlTagClass: true, formatWxml: 'auto', scopedSlotsCompiler: 'auto', scopedSlotsRequireProps: false, slotSingleRootNoWrapper: false, slotFallbackWrapperStrategy: 'virtual-host', slotFallbackWrapper: { tag: 'view', attrs: { class: 'slot-wrapper', }, rules: [ { component: 'IssueCard', slot: 'header', tag: 'cover-view' }, { componentName: 'HelloWorld', slot: 'header', tag: 'cover-view' }, { component: 'IssueCard', slot: 'footer', attrs: { class: 'slot-footer', }, }, { component: /^Van/, slot: ['title', 'label'], tag: 'view' }, ], }, slotMultipleInstance: true, classStyleRuntime: 'js', objectLiteralBindMode: 'runtime', mustacheInterpolation: 'compact', classStyleWxsShared: true, functionPropNames: ['handler', /^on[A-Z]/], }, }, }, }) ``` 字段说明: * `htmlTagToWxml`:是否将 `.vue` 模板中的常见 HTML 标签映射为小程序内置标签。 * `true` 或省略:启用默认映射表(例如 `div -> view`、`span -> text`、`img -> image`、`a -> navigator`、`br -> view`、`hr -> view`)。 * `false`:关闭该能力,保留模板中的原始标签名。 * `Record`:在默认映射表基础上追加或覆盖自定义映射。 * `htmlTagToWxmlTagClass`:是否在发生 HTML 标签映射时,同时给转换后的节点追加“原标签名 class”。 * 默认 `true`。 * 启用后,`

` 会编译为带有稳定语义 class 的节点,静态 class 会拼合成 `h3 title`,动态 `:class` 保持原样。 * 适合低成本还原 `h1/h2/h3/ul/ol/li/p/br/hr` 等 HTML 标签的默认外观。 * 设为 `false` 时,只做标签名映射,不追加这层 class。 * `formatWxml`:是否格式化 `.vue` / JSX 编译生成的 WXML。 * `auto` 或省略:开发态默认开启,生产构建默认关闭。 * `true`:始终输出带缩进和换行的 WXML,便于在开发者工具中调试。 * `false`:始终保持紧凑输出,适合对包体更敏感的场景。 * 当前只做标签层级缩进;含文本内容的元素会保持单行,避免重排文本空白语义。 * `scopedSlotsCompiler`:作用域插槽编译策略。 * `auto`:自动选择最小可用方案(默认)。 * `augmented`:强制使用增强方案。 * `off`:关闭 scoped slot(仅保留原生 slot,不支持 slot props)。 * `scopedSlotsRequireProps`:仅在 slot 传递作用域参数时才生成 scoped slot 组件。默认 `false`,普通插槽内容也会走增强 scoped slot 组件,以便 slot 投影下的运行时父子关系可被 `provide()` / `inject()` 正确解析;设为 `true` 可保留普通插槽的原生 slot 输出。 * `slotSingleRootNoWrapper`:普通具名插槽内容只有一个可投影根节点时,是否把 `slot="..."` 直接下推到该根节点,避免额外生成 wrapper。 * 默认 `false`,保持稳定的真实节点 wrapper。 * 开启后只影响“单个可投影根节点”;多节点、空内容、转发 `` 等场景仍会保留真实 wrapper。 * `slotFallbackWrapperStrategy`:配置普通具名插槽 fallback wrapper 的默认策略。 * 微信平台默认 `virtual-host`,会自动生成内部 `virtualHost` 组件作为 wrapper,减少 `view` 带来的布局影响。 * 其他平台默认 `view`。 * 如果需要回到旧行为,可显式配置为 `view`,或继续显式配置 `slotFallbackWrapper: 'view'`。 * `slotFallbackWrapper`:配置普通具名插槽 fallback wrapper 的真实标签。 * 微信平台默认由 `slotFallbackWrapperStrategy: 'virtual-host'` 生成内部 `virtualHost` wrapper;其他平台默认 `view`。 * 字符串形式等价于 `{ tag: '...' }`。 * 显式配置该字段后,会优先使用这里指定的真实标签,并保持旧版 `view` / 自定义标签行为。 * 对象形式支持全局默认 `tag`、全局默认 `attrs`、全局默认 `singleRootNoWrapper`,以及按组件 / 插槽匹配的 `rules`。 * `rules[].component` 匹配模板里的组件标签名,例如 `` 对应 `IssueCard`,`` 对应 `issue-card`。 * `rules[].componentName` 匹配被引用 Vue SFC 的组件名,也就是子组件里静态 `defineOptions({ name: 'HelloWorld' })` 的 `name`。这个字段需要编译器能解析到该子组件的 `.vue` 文件;原生组件或第三方小程序组件通常没有这个信息,应继续用 `component`。 * `rules[].component`、`rules[].componentName` 与 `rules[].slot` 都支持字符串、正则或数组;同一条规则里写了多个匹配条件时需要同时命中。规则按顺序匹配,后匹配到的字段可覆盖前面的字段。 * `attrs` 是追加到 fallback wrapper 上的静态属性,适合项目级 class、style 或 `data-*`;组件使用处可用 `slot-wrapper-class` / `slot-wrapper-style` 继续覆盖。 * `block` 不能作为 wrapper。`` 在转发 slot 场景会在真实 DevTools 运行时丢内容;如果配置成 `block`,编译器会回退到 `view` 并输出 warning。 * `slotMultipleInstance`:`v-for` 下 scoped slot 多实例模式(默认 `true`)。 * `classStyleRuntime`:class/style 绑定运行时。 * `js`:强制 JS(默认)。 * `auto`:平台支持 WXS 时优先 WXS,否则回退 JS。 * `wxs`:强制 WXS,不支持时回退 JS 并告警。 * `objectLiteralBindMode`:对象字面量 `v-bind` 的输出方式。 * `runtime`:默认,借助运行时中间变量输出。 * `inline`:直接内联对象字面量到模板插值。 * `mustacheInterpolation`:Mustache 输出风格。 * `compact`:默认,输出 `{{expr}}`。 * `spaced`:输出 `{{ expr }}`,更便于调试阅读。 * `classStyleWxsShared`:是否复用 class/style 的 WXS helper(主包与非独立分包共享,独立分包各自生成)。 * `functionPropNames`:显式声明需要按函数 prop 传递的组件 prop 名称。 * 默认值为空,不内置 `callback`、`handler`、`on-*`、`change` 等名称猜测。 * 字符串按 prop 名称精确匹配;正则表达式按 prop 名称测试。 * 例如 `functionPropNames: ['handler', /^on[A-Z]/]` 会让 `<Comp :handler="callbacks[id]" />` 生成运行时绑定;普通值绑定如 `<Comp :selected="data.userId" />` 仍直接输出 `selected` 对 `data.userId` 的 Mustache 绑定,不会生成 `__wv_bind_*`。 示例:关闭默认映射 ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { vue: { template: { htmlTagToWxml: false, }, }, }, }) ``` 示例:覆盖部分标签映射 ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { vue: { template: { htmlTagToWxml: { section: 'view', article: 'view', a: 'navigator', }, }, }, }, }) ``` 示例:关闭自动追加原标签 class ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { vue: { template: { htmlTagToWxml: true, htmlTagToWxmlTagClass: false, }, }, }, }) ``` 示例:默认开启时的模板效果 ```vue ``` 会编译出类似结果: ```wxml 标题 ``` > \[!NOTE] > `htmlTagToWxml` 只作用于 Vue SFC 模板编译,不影响原生 `.wxml` 文件。 > > `htmlTagToWxmlTagClass` 只在“标签名确实发生了 HTML -> WXML 映射”时生效;像 `button -> button` 这类未改名场景,不会额外注入 `.button`。 > > `removeComments` / `simplifyWhitespace` 当前仍是兼容性预留位,尚未接入实际编译流程;其余字段已经参与模板编译输出。 ### 自定义具名插槽 wrapper {#slot-fallback-wrapper} 当普通具名插槽内容是转发的 `` 时,小程序不能直接接收 ``,也不能稳定接收 ``。微信平台默认会生成一个内部 `virtualHost` 组件作为 wrapper: ```wxml ``` 同时会在当前入口 JSON 中自动注入内部组件引用。若需要回到旧版 `view` 行为,可配置 `slotFallbackWrapperStrategy: 'view'`,或显式配置 `slotFallbackWrapper: 'view'`。 如果某些组件的某些具名插槽需要用其他真实节点承载,可以用 `slotFallbackWrapper` 全局配置: ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { vue: { template: { slotFallbackWrapper: { tag: 'view', attrs: { class: 'slot-wrapper', }, rules: [ { component: 'IssueCard', slot: 'header', tag: 'cover-view' }, { componentName: 'HelloWorld', slot: 'header', tag: 'cover-view' }, { component: 'IssueCard', slot: 'footer', attrs: { class: 'slot-footer-global', }, }, { component: /^Van/, slot: ['title', 'label'], tag: 'view' }, ], }, }, }, }, }) ``` `component` 匹配的是使用处模板标签名,不是子组件声明名。比如下面这个模板里,`component: 'issue-card'` 会命中,`component: 'HelloWorld'` 不会命中: ```vue ``` 如果你希望按子组件自己的名字匹配,需要让子组件声明静态 `defineOptions({ name })`,然后在规则里使用 `componentName`: ```vue ``` ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { vue: { template: { slotFallbackWrapper: { rules: [ { componentName: 'HelloWorld', slot: 'header', tag: 'cover-view' }, ], }, }, }, }, }) ``` `componentName` 只在编译器能解析到被引用的 Vue SFC 时可用,包括 ` ``` ### 方式 2:` ``` ### 方式 3:`defineComponent({ options })` 适合非 ` ``` ### `definePageMeta()` {#definepagemeta} **类型签名:** `typeof import('wevu')['definePageMeta']` **运行时说明:** 这是 ` ``` ### `defineAppSetup()` {#defineappsetup} **类型签名:** `typeof import('wevu')['defineAppSetup']` **运行时说明:** 这是 ` ``` ### `use()` {#use} **类型签名:** `typeof import('wevu')['use']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/core#example-core-macros)。 * 类型入口:`WevuPlugin` * 用途:安装 Wevu 插件。 * 说明:可在 App setup 或受控初始化逻辑中使用;插件如果需要注册 hook,仍必须保持同步注册。 ### `mergeModels()` {#mergemodels} **类型签名:** `typeof import('wevu')['mergeModels']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/core#example-core-macros)。 * 类型入口:`ModelBindingPayload` * 用途:合并多路 model 绑定结果。 ### `useModel()` {#usemodel} **类型签名:** `typeof import('wevu')['useModel']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/core#example-core-macros)。 * 类型入口:`ModelBinding` * 用途:运行时读取/写入某个 model。 ### 本组示例 {#example-core-macros} 相关宏可以在一个组件中共同声明类型、默认值、事件和 model。 ```vue ``` ## 模板工具 ### `useAttrs()` {#useattrs} **类型签名:** `typeof import('wevu')['useAttrs']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/core#example-core-template)。 * 类型入口:`SetupContext` * 用途:获取透传属性 attrs。 ### `useSlots()` {#useslots} **类型签名:** `typeof import('wevu')['useSlots']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/core#example-core-template)。 * 类型入口:`TemplateRefs` * 用途:读取 slots。 ### `useTemplateRef()` {#usetemplateref} **类型签名:** `typeof import('wevu')['useTemplateRef']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/core#example-core-template)。 * 类型入口:`TemplateRef` / `TemplateRefValue` * 用途:读取模板 ref 对应实例。 ### `useNativeInstance()` {#usenativeinstance} * 类型入口:`SetupContextNativeInstance` * 用途:在 setup 中访问原生页面/组件实例。 ### `useNativeRouter()` {#usenativerouter} * 类型入口:`WechatMiniprogram.Component.Router` * 用途:获取组件路径语义的路由器对象。 * 说明:优先命中实例 `router`,然后回退 `pageRouter`,低版本基础库再降级到全局 `wx/my/tt` 路由方法。 ### `useNativePageRouter()` {#usenativepagerouter} * 类型入口:`WechatMiniprogram.Component.Router` * 用途:获取页面路径语义的路由器对象。 * 说明:优先命中实例 `pageRouter`,然后回退 `router`,低版本基础库再降级到全局 `wx/my/tt` 路由方法。 ### `normalizeClass()` {#normalizeclass} **类型签名:** `typeof import('wevu')['normalizeClass']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/core#example-core-template)。 * 类型入口:`string` * 用途:归一化 class 输入(对象/数组/字符串)。 ### `normalizeStyle()` {#normalizestyle} **类型签名:** `typeof import('wevu')['normalizeStyle']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/core#example-core-template)。 * 类型入口:`string` * 用途:归一化 style 输入。 ### 本组示例 {#example-core-template} 原生实例和模板 ref 只在方法中使用,不要把它们返回为模板可序列化状态。 ```vue ``` --- --- url: /wevu/api/options-api.md description: Wevu 支持的 Vue 风格与小程序原生 Options API 清单,以及迁移时需要注意的语义差异。 --- # Options API Wevu 接受 Vue 风格选项,同时保留小程序 `Component` 的宿主选项。名称相同不代表运行时语义完全相同:组件注册、生命周期时机、props 规范化和更新最终都受小程序宿主约束。 ## Vue 风格选项 ### `props` {#props} **类型签名:** `DefineComponentOptions['props']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **Vue 差异:** 名称沿用 Vue Options API,但 Wevu 最终生成小程序 `Component()` 配置,没有 DOM、Virtual DOM 或浏览器组件实例;生命周期应使用 Wevu/宿主对应项。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-vue)。 支持数组、对象和类型声明,并在注册阶段转换为小程序 `properties`。 ### `emits` {#emits} **类型签名:** `DefineComponentOptions['emits']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **Vue 差异:** 名称沿用 Vue Options API,但 Wevu 最终生成小程序 `Component()` 配置,没有 DOM、Virtual DOM 或浏览器组件实例;生命周期应使用 Wevu/宿主对应项。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-vue)。 用于声明组件事件;实际派发通过小程序组件事件系统完成。 ### `data` {#data} **类型签名:** `DefineComponentOptions['data']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **Vue 差异:** 名称沿用 Vue Options API,但 Wevu 最终生成小程序 `Component()` 配置,没有 DOM、Virtual DOM 或浏览器组件实例;生命周期应使用 Wevu/宿主对应项。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-vue)。 支持对象或返回初始状态的函数,推荐使用函数形式。 ### `setup` {#setup} **类型签名:** `DefineComponentOptions['setup']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **Vue 差异:** 名称沿用 Vue Options API,但 Wevu 最终生成小程序 `Component()` 配置,没有 DOM、Virtual DOM 或浏览器组件实例;生命周期应使用 Wevu/宿主对应项。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-vue)。 在 Wevu setup 上下文中执行。默认在组件 `attached` 阶段运行,与 Vue 组件创建时机存在差异。 ### `computed` {#computed} **类型签名:** `DefineComponentOptions['computed']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **Vue 差异:** 名称沿用 Vue Options API,但 Wevu 最终生成小程序 `Component()` 配置,没有 DOM、Virtual DOM 或浏览器组件实例;生命周期应使用 Wevu/宿主对应项。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-vue)。 参与 Wevu 响应式快照和 `setData` 差量更新,不是 Vue DOM 渲染器的 computed 调度链路。 ### `methods` {#methods} **类型签名:** `DefineComponentOptions['methods']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **Vue 差异:** 名称沿用 Vue Options API,但 Wevu 最终生成小程序 `Component()` 配置,没有 DOM、Virtual DOM 或浏览器组件实例;生命周期应使用 Wevu/宿主对应项。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-vue)。 方法会绑定到小程序公开实例,并可供模板事件处理器调用。 ### `watch` {#watch} **类型签名:** `DefineComponentOptions['watch']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **Vue 差异:** 名称沿用 Vue Options API,但 Wevu 最终生成小程序 `Component()` 配置,没有 DOM、Virtual DOM 或浏览器组件实例;生命周期应使用 Wevu/宿主对应项。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-vue)。 监听 data、props 或 computed 路径;调度和深度监听能力以 [Reactivity API](/wevu/api/reactivity#watch) 为准。 ### 本组示例 {#example-options-vue} Vue 风格选项最终仍由小程序 `Component()` 注册和调度。 ```ts import { defineComponent } from 'wevu' export default defineComponent({ props: { initial: { type: Number, default: 0 } }, data() { return { count: this.initial } }, computed: { doubled() { return this.count * 2 } }, methods: { increment() { this.count += 1 } }, watch: { count(value) { console.log('count', value) } }, }) ``` ## 小程序宿主选项 ### `properties` {#properties} **类型签名:** `DefineComponentOptions['properties']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-host)。 原生小程序属性声明。新代码优先使用 `props`,需要宿主级 observer 或原生类型行为时使用本选项。 ### `behaviors` {#behaviors} **类型签名:** `DefineComponentOptions['behaviors']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-host)。 透传小程序 behaviors 配置。 ### `lifetimes` {#lifetimes} **类型签名:** `DefineComponentOptions['lifetimes']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-host)。 透传组件生命周期;对应 Hook 映射见 [Lifecycle API](/wevu/api/lifecycle#小程序原生生命周期映射说明)。 ### `pageLifetimes` {#pagelifetimes} **类型签名:** `DefineComponentOptions['pageLifetimes']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-host)。 透传组件所在页面的生命周期。 ### `externalClasses` {#externalclasses} **类型签名:** `DefineComponentOptions['externalClasses']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-host)。 声明组件可接收的外部样式类。 ### `options` {#options} **类型签名:** `DefineComponentOptions['options']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-host)。 透传小程序组件选项,例如 `virtualHost`。 ### `observers` {#observers} **类型签名:** `DefineComponentOptions['observers']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-host)。 透传小程序数据字段监听器。 ### `relations` {#relations} **类型签名:** `DefineComponentOptions['relations']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-host)。 透传小程序组件关系定义。 ### `features` {#features} **类型签名:** `DefineComponentOptions['features']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-host)。 按需开启页面事件桥接,避免为未使用的页面事件生成处理函数。 ### `setData` {#setdata} **类型签名:** `DefineComponentOptions['setData']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-host)。 配置快照、diff 和 `setData` payload 策略。 ### `setupLifecycle` {#setuplifecycle} **类型签名:** `DefineComponentOptions['setupLifecycle']` **运行时说明:** 该选项最终注册到小程序 `Component()` 定义;页面与组件共享注册模型,但宿主只会调用当前实例支持的字段。 **示例:** 见 [本组示例](/wevu/api/options-api#example-options-host)。 选择 `setup` 在 `created` 或 `attached` 阶段执行,默认值为 `attached`。 ### 本组示例 {#example-options-host} 宿主选项直接对应 `Component()` 字段,不要按 Vue Web 选项解释。 ```ts import { defineComponent } from 'wevu' export default defineComponent({ properties: { status: { type: String, value: 'idle' } }, options: { multipleSlots: true, styleIsolation: 'apply-shared' }, lifetimes: { attached() { console.log('attached') } }, pageLifetimes: { show() { console.log('page show') } }, externalClasses: ['custom-class'], }) ``` --- --- url: /wevu/api/reactivity.md description: 本页覆盖 Wevu 响应式层的全部公开函数,包括状态创建、监听副作用、工具函数与调度能力。 --- # Reactivity API(响应式与调度) 本页按「一个 API 一个小节」组织,结构参考 Vue 官方 API 文档。每个小节都包含用途、类型入口与使用建议,便于快速定位与深入阅读。 ## 状态创建与派生 ### `ref()` {#ref} **类型签名:** `typeof import('wevu')['ref']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-state)。 * 类型入口:`Ref` * 用途:创建最常用的基础响应式引用,适合原始值或独立状态。 * 说明:读取使用 `ref.value`;在模板中会自动解包。 ### `customRef()` {#customref} **类型签名:** `typeof import('wevu')['customRef']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-state)。 * 类型入口:`CustomRefFactory` / `CustomRefOptions` * 用途:自定义 `track/trigger` 行为。 * 说明:适合防抖输入、手动控制触发时机等高级场景。 ### `reactive()` {#reactive} **类型签名:** `typeof import('wevu')['reactive']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-state)。 * 类型入口:`object` * 用途:创建深层响应式对象,适合聚合状态。 * 说明:对嵌套对象也会做代理;适合表单、复杂页面状态。 ### `shallowRef()` {#shallowref} **类型签名:** `typeof import('wevu')['shallowRef']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-state)。 * 类型入口:`ShallowRef` * 用途:只追踪 `.value` 本身是否变更,不做深层代理。 * 说明:用于大对象/第三方实例,减少深层响应式开销。 ### `shallowReactive()` {#shallowreactive} **类型签名:** `typeof import('wevu')['shallowReactive']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-state)。 * 类型入口:`object` * 用途:仅顶层属性响应式,内部对象保持原值。 * 说明:常用于“只关心顶层替换”的场景。 ### `readonly()` {#readonly} **类型签名:** `typeof import('wevu')['readonly']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-state)。 * 类型入口:`Readonly` * 用途:创建只读代理,防止误修改。 * 说明:常用于向下游暴露状态快照。 ### `shallowReadonly()` {#shallowreadonly} **类型签名:** `typeof import('wevu')['shallowReadonly']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-state)。 * 类型入口:`Readonly` * 用途:创建浅层只读代理。 * 说明:仅保护顶层字段;嵌套对象仍保持原始语义。 ### `computed()` {#computed} **类型签名:** `typeof import('wevu')['computed']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-state)。 * 类型入口:`ComputedRef` / `WritableComputedRef` * 用途:声明派生状态,自动缓存并按依赖更新。 * 说明:支持只读与可写两种形式;优先用于“纯函数派生”。 ### 本组示例 {#example-reactivity-state} 组合深层状态、独立 Ref 和派生值时,只把可序列化数据交给模板。 ```vue ``` ## 监听与副作用 ### `watch()` {#watch} **类型签名:** `typeof import('wevu')['watch']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-effects)。 * 类型入口:`WatchOptions` / `WatchStopHandle` * 用途:侦听 source 变化并执行回调。 * 说明:适合精确观察某个字段、computed 或 getter。 ### `watchEffect()` {#watcheffect} **类型签名:** `typeof import('wevu')['watchEffect']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-effects)。 * 类型入口:`WatchStopHandle` * 用途:自动收集依赖并立即执行副作用。 * 说明:适合快速联动逻辑与调试输出。 ### `watchPostEffect()` {#watchposteffect} **类型签名:** `typeof import('wevu')['watchPostEffect']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-effects)。 * 类型入口:`WatchStopHandle` * 用途:以 post flush 语义注册自动依赖副作用。 * 说明:用于希望等待当前更新批次之后再执行的副作用;业务侧仍要避免在高频页面滚动里写重逻辑。 ### `watchSyncEffect()` {#watchsynceffect} **类型签名:** `typeof import('wevu')['watchSyncEffect']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-effects)。 * 类型入口:`WatchStopHandle` * 用途:以 sync flush 语义注册自动依赖副作用。 * 说明:适合极少数需要同步响应状态变化的组合式工具;普通业务优先使用 `watchEffect()`。 ### `effect()` {#effect} **类型签名:** `typeof import('wevu')['effect']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-effects)。 * 类型入口:`WatchStopHandle` * 用途:底层副作用 API,通常框架层/高级场景使用。 * 说明:业务代码优先使用 `watchEffect`。 ### `effectScope()` {#effectscope} **类型签名:** `typeof import('wevu')['effectScope']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-effects)。 * 类型入口:`EffectScope` * 用途:把一组 effect/watch 放在同一作用域统一管理。 * 说明:在组件外组合逻辑、插件场景很有用。 ### `getCurrentScope()` {#getcurrentscope} **类型签名:** `typeof import('wevu')['getCurrentScope']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-effects)。 * 类型入口:`EffectScope | undefined` * 用途:获取当前激活的 effect scope。 * 说明:常与 `onScopeDispose` 配合使用。 ### `onScopeDispose()` {#onscopedispose} **类型签名:** `typeof import('wevu')['onScopeDispose']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-effects)。 * 类型入口:`() => void` * 用途:在 scope 销毁时执行清理逻辑。 * 说明:适合解绑事件、释放资源。 ### `stop()` {#stop} **类型签名:** `typeof import('wevu')['stop']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-effects)。 * 类型入口:`void` * 用途:停止指定 effect/watch。 * 说明:用于手动中断监听链路。 ### 本组示例 {#example-reactivity-effects} 把相关监听放进同一 scope,可以在页面卸载或业务结束时统一停止。 ```ts import { effectScope, onScopeDispose, ref, watch, watchEffect } from 'wevu' const count = ref(0) const scope = effectScope() scope.run(() => { const stopWatch = watch(count, value => console.log(value)) watchEffect(() => documentTitle(count.value)) onScopeDispose(stopWatch) }) scope.stop() ``` ## Ref / Proxy 工具 ### `toRef()` {#toref} **类型签名:** `typeof import('wevu')['toRef']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-ref-tools)。 * 类型入口:`Ref` * 用途:把对象某个属性映射为 ref。 * 说明:常用于解构后保持响应式引用。 ### `toRefs()` {#torefs} **类型签名:** `typeof import('wevu')['toRefs']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-ref-tools)。 * 类型入口:`ToRefs` * 用途:批量把对象属性转换为 ref。 * 说明:适合从 `reactive` 安全解构。 ### `unref()` {#unref} **类型签名:** `typeof import('wevu')['unref']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-ref-tools)。 * 类型入口:`T` * 用途:统一读取 `ref.value` 或普通值。 * 说明:减少“值/Ref 双形态”判断分支。 ### `toValue()` {#tovalue} **类型签名:** `typeof import('wevu')['toValue']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-ref-tools)。 * 类型入口:`MaybeRefOrGetter` * 用途:统一展开普通值、Ref 或 getter。 * 说明:编写可复用工具函数时很常见。 ### `triggerRef()` {#triggerref} **类型签名:** `typeof import('wevu')['triggerRef']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-ref-tools)。 * 类型入口:`void` * 用途:手动触发 `shallowRef` 依赖更新。 * 说明:用于“对象内部变更但引用未变”场景。 ### `toRaw()` {#toraw} **类型签名:** `typeof import('wevu')['toRaw']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-ref-tools)。 * 类型入口:`T` * 用途:拿到代理前的原始对象。 * 说明:调试或与第三方库交互时使用。 ### `markRaw()` {#markraw} **类型签名:** `typeof import('wevu')['markRaw']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-ref-tools)。 * 类型入口:`T` * 用途:标记对象跳过响应式代理。 * 说明:适用于大型类实例、SDK 对象。 ### 本组示例 {#example-reactivity-ref-tools} 工具函数让组合式函数同时接受普通值、Ref 或 getter。 ```ts import { reactive, shallowRef, toRaw, toRef, toRefs, toValue, triggerRef } from 'wevu' const state = reactive({ count: 0, name: 'Ada' }) const count = toRef(state, 'count') const fields = toRefs(state) const config = shallowRef({ enabled: true }) config.value.enabled = false triggerRef(config) console.log(toValue(count), fields.name.value, toRaw(state)) ``` ## 响应式判定 ### `isRef()` {#isref} **类型签名:** `typeof import('wevu')['isRef']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-checks)。 * 类型入口:type predicate * 用途:判断某值是否为 Ref。 ### `isReactive()` {#isreactive} **类型签名:** `typeof import('wevu')['isReactive']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-checks)。 * 类型入口:type predicate * 用途:判断对象是否是 `reactive` 代理。 ### `isShallowRef()` {#isshallowref} **类型签名:** `typeof import('wevu')['isShallowRef']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-checks)。 * 类型入口:type predicate * 用途:判断是否为 `shallowRef`。 ### `isShallowReactive()` {#isshallowreactive} **类型签名:** `typeof import('wevu')['isShallowReactive']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-checks)。 * 类型入口:type predicate * 用途:判断是否为 `shallowReactive`。 ### `isRaw()` {#israw} **类型签名:** `typeof import('wevu')['isRaw']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-checks)。 * 类型入口:`boolean` * 用途:判断对象是否被 `markRaw` 标记。 ### `isReadonly()` {#isreadonly} **类型签名:** `typeof import('wevu')['isReadonly']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-checks)。 * 类型入口:`boolean` * 用途:判断值是否来自 `readonly()` 或 `shallowReadonly()`。 ### `isProxy()` {#isproxy} **类型签名:** `typeof import('wevu')['isProxy']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-checks)。 * 类型入口:`boolean` * 用途:判断值是否为 Wevu 响应式/只读代理。 ### 本组示例 {#example-reactivity-checks} 判定函数适合库边界和调试,不应代替清晰的数据建模。 ```ts import { isProxy, isRaw, isReactive, isReadonly, isRef, markRaw, reactive, readonly, ref } from 'wevu' const count = ref(0) const state = reactive({ count: 0 }) const locked = readonly(state) const sdk = markRaw({ version: 1 }) console.log(isRef(count), isReactive(state), isReadonly(locked)) console.log(isProxy(state), isRaw(sdk)) ``` ## 批处理与调度 ### `nextTick()` {#nexttick} **类型签名:** `typeof import('wevu')['nextTick']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-scheduling)。 * 类型入口:`Promise` * 用途:等待当前批次响应式更新完成。 * 说明:常用于更新后读取最新渲染状态。 ### `batch()` {#batch} **类型签名:** `typeof import('wevu')['batch']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-scheduling)。 * 类型入口:`() => void` * 用途:在同一批处理中执行一组状态改动。 * 说明:减少中间态触发的无效计算与更新。 ### `startBatch()` {#startbatch} **类型签名:** `typeof import('wevu')['startBatch']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-scheduling)。 * 类型入口:`void` * 用途:手动开启批处理。 * 说明:需要与 `endBatch` 成对调用。 ### `endBatch()` {#endbatch} **类型签名:** `typeof import('wevu')['endBatch']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-scheduling)。 * 类型入口:`void` * 用途:手动结束批处理并触发提交。 ### `traverse()` {#traverse} **类型签名:** `typeof import('wevu')['traverse']` **运行时说明:** 依赖变化进入 Wevu 调度队列,并最终收敛为小程序 `setData` 更新;模板状态必须保持可序列化。 **示例:** 见 [本组示例](/wevu/api/reactivity#example-reactivity-scheduling)。 * 类型入口:`unknown` * 用途:显式遍历响应式值及其嵌套成员,建立深层依赖。 * 说明:主要用于组合式工具和调试能力;普通业务的深层监听优先使用 `watch()` 的 `deep` 选项。 ### 本组示例 {#example-reactivity-scheduling} 业务代码优先使用 `batch()`;手动批处理必须用 `finally` 保证配对结束。 ```ts import { batch, endBatch, nextTick, ref, startBatch } from 'wevu' const count = ref(0) batch(() => { count.value += 1 count.value += 1 }) await nextTick() startBatch() try { count.value += 1 } finally { endBatch() } ``` ## 内部能力说明 响应式内部调度与调试函数(如 reactive 树预链接、深度策略切换)属于框架内部能力,文档页不再展开展示。若你在业务侧需要类似能力,建议优先使用本页已列出的公开 API 组合实现。 --- --- url: /wevu/api/lifecycle.md description: >- 本页仅覆盖 wevu 实际导出的生命周期 Hook(源码:runtime/hooks.ts),并补充与小程序 lifetimes/pageLifetimes 的映射说明。 --- # Lifecycle API(生命周期) 以下条目严格对应 `packages-runtime/wevu/src/runtime/hooks.ts` 的导出函数。所有 Hook 都要求在 `setup()` 同步阶段调用。 ## App 生命周期 Hook ### `onLaunch()` {#onlaunch} **类型签名:** `typeof import('wevu')['onLaunch']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-app)。 * 作用域:`App` * 源码行为:注册到 `onLaunch`。 ### `onShow()` {#onshow} **类型签名:** `typeof import('wevu')['onShow']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-app)。 * 作用域:`App / Page / Component` * 源码行为:统一注册到 `onShow`(App 与页面/组件共用函数名)。 ### `onHide()` {#onhide} **类型签名:** `typeof import('wevu')['onHide']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-app)。 * 作用域:`App / Page / Component` * 源码行为:统一注册到 `onHide`。 ### `onError()` {#onerror} **类型签名:** `typeof import('wevu')['onError']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-app)。 * 作用域:`App / Component` * 源码行为:注册到 `onError`。 ### `onPageNotFound()` {#onpagenotfound} **类型签名:** `typeof import('wevu')['onPageNotFound']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-app)。 * 作用域:`App` * 源码行为:注册到 `onPageNotFound`。 ### `onUnhandledRejection()` {#onunhandledrejection} **类型签名:** `typeof import('wevu')['onUnhandledRejection']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-app)。 * 作用域:`App` * 源码行为:注册到 `onUnhandledRejection`。 ### `onThemeChange()` {#onthemechange} **类型签名:** `typeof import('wevu')['onThemeChange']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-app)。 * 作用域:`App` * 源码行为:注册到 `onThemeChange`。 ### `onMemoryWarning()` {#onmemorywarning} **类型签名:** `typeof import('wevu')['onMemoryWarning']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-app)。 * 作用域:`App` * 源码行为:通过 `wx.onMemoryWarning` 注册监听,并在重复绑定时自动调用 `wx.offMemoryWarning` 清理旧监听。 * 建议:回调内优先释放大缓存、长列表临时数据与不必要的订阅/定时器。 ### 本组示例 {#example-lifecycle-app} App hook 同样必须在同步 `setup()` 中注册。 ```ts import { createApp, onError, onLaunch, onMemoryWarning, onShow } from 'wevu' createApp({ setup() { onLaunch(options => console.log('launch', options)) onShow(options => console.log('show', options)) onError(error => console.error(error)) onMemoryWarning(({ level }) => console.warn('memory', level)) }, }) ``` ## 页面生命周期 Hook ### `onLoad()` {#onload} **类型签名:** `typeof import('wevu')['onLoad']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-page)。 * 作用域:`Page` * 源码行为:注册到页面 `onLoad`。 ### `onReady()` {#onready} **类型签名:** `typeof import('wevu')['onReady']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-page)。 * 作用域:`Page / Component` * 源码行为:注册到 `onReady`;组件通过 `lifetimes.ready` 触发。 ### `onUnload()` {#onunload} **类型签名:** `typeof import('wevu')['onUnload']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-page)。 * 作用域:`Page / Component` * 源码行为:在页面 `onUnload` 或组件 teardown 时统一触发。 ### 本组示例 {#example-lifecycle-page} 先同步注册 hook,再在回调内部执行异步工作。 ```vue ``` ## 页面事件 Hook ### `onPullDownRefresh()` {#onpulldownrefresh} **类型签名:** `typeof import('wevu')['onPullDownRefresh']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-events)。 * 作用域:`Page` * 源码行为:注册到页面 `onPullDownRefresh`。 ### `onReachBottom()` {#onreachbottom} **类型签名:** `typeof import('wevu')['onReachBottom']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-events)。 * 作用域:`Page` * 源码行为:注册到页面 `onReachBottom`。 ### `onPageScroll()` {#onpagescroll} **类型签名:** `typeof import('wevu')['onPageScroll']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-events)。 * 作用域:`Page` * 源码行为:注册到页面 `onPageScroll`。 ### `onRouteDone()` {#onroutedone} **类型签名:** `typeof import('wevu')['onRouteDone']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-events)。 * 作用域:`Page / Component` * 源码行为:注册到 `onRouteDone`;组件通过 `pageLifetimes.routeDone` 桥接触发。 ### `onTabItemTap()` {#ontabitemtap} **类型签名:** `typeof import('wevu')['onTabItemTap']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-events)。 * 作用域:`Page` * 源码行为:注册到页面 `onTabItemTap`。 ### `onResize()` {#onresize} **类型签名:** `typeof import('wevu')['onResize']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-events)。 * 作用域:`Page / Component` * 源码行为:注册到 `onResize`;组件通过 `pageLifetimes.resize` 桥接触发。 ### 本组示例 {#example-lifecycle-events} 高频滚动回调只更新必要状态,耗时请求放在低频事件中。 ```vue ``` ## 返回值型页面 Hook ### `onShareAppMessage()` {#onshareappmessage} **类型签名:** `typeof import('wevu')['onShareAppMessage']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-return)。 * 作用域:`Page` * 源码行为:单实例 Hook(`single: true`),返回值用于分享配置。 ### `onShareTimeline()` {#onsharetimeline} **类型签名:** `typeof import('wevu')['onShareTimeline']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-return)。 * 作用域:`Page` * 源码行为:单实例 Hook(`single: true`),返回值用于朋友圈分享配置。 ### `onAddToFavorites()` {#onaddtofavorites} **类型签名:** `typeof import('wevu')['onAddToFavorites']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-return)。 * 作用域:`Page` * 源码行为:单实例 Hook(`single: true`),返回值用于收藏配置。 ### `onSaveExitState()` {#onsaveexitstate} **类型签名:** `typeof import('wevu')['onSaveExitState']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-return)。 * 作用域:`Page` * 源码行为:单实例 Hook(`single: true`),返回值用于退出状态保存。 ### 本组示例 {#example-lifecycle-return} 返回值直接交给小程序宿主,字段结构应遵循对应平台契约。 ```vue ``` ## 组件扩展 Hook ### `onAttached()` {#onattached} **类型签名:** `typeof import('wevu')['onAttached']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-component)。 * 作用域:`Component` * 源码行为:在组件 `lifetimes.attached` 阶段触发。 ### `onDetached()` {#ondetached} **类型签名:** `typeof import('wevu')['onDetached']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-component)。 * 作用域:`Component` * 源码行为:在组件 `lifetimes.detached` 阶段触发。 ### `onMoved()` {#onmoved} **类型签名:** `typeof import('wevu')['onMoved']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-component)。 * 作用域:`Component` * 源码行为:注册到 `lifetimes.moved`。 ### 本组示例 {#example-lifecycle-component} 组件资源在 attached 后创建,并在 detached 中释放。 ```vue ``` ## Vue 语义对齐 Hook ### `onBeforeMount()` {#onbeforemount} **类型签名:** `typeof import('wevu')['onBeforeMount']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **Vue 差异:** 没有 DOM mount 阶段;该回调发生在小程序实例已创建、首次宿主渲染提交之前。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-vue)。 * 对齐语义:Vue `beforeMount` * 源码行为:在 `setup()` 内同步立即执行。 ### `onMounted()` {#onmounted} **类型签名:** `typeof import('wevu')['onMounted']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **Vue 差异:** 对应宿主 `ready`,不是浏览器 DOM 插入完成;此时才适合执行节点查询。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-vue)。 * 对齐语义:Vue `mounted` * 源码行为:映射到 `onReady`。 ### `onBeforeUpdate()` {#onbeforeupdate} **类型签名:** `typeof import('wevu')['onBeforeUpdate']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **Vue 差异:** 围绕 Wevu 即将提交的 `setData` 批次触发,而不是 Virtual DOM patch。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-vue)。 * 对齐语义:Vue `beforeUpdate` * 源码行为:映射到内部 `__wevuOnBeforeUpdate`。 ### `onUpdated()` {#onupdated} **类型签名:** `typeof import('wevu')['onUpdated']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **Vue 差异:** 在当前 `setData` 更新批次完成后触发,不能据此假设浏览器布局或 `requestAnimationFrame` 语义。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-vue)。 * 对齐语义:Vue `updated` * 源码行为:映射到内部 `__wevuOnUpdated`。 ### `onBeforeUnmount()` {#onbeforeunmount} **类型签名:** `typeof import('wevu')['onBeforeUnmount']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **Vue 差异:** 对应页面卸载或组件移除前的清理阶段,不涉及 DOM unmount。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-vue)。 * 对齐语义:Vue `beforeUnmount` * 源码行为:在 `setup()` 内同步立即执行(小程序无对应原生 before-unmount)。 ### `onUnmounted()` {#onunmounted} **类型签名:** `typeof import('wevu')['onUnmounted']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **Vue 差异:** 对应宿主 `detached`/页面卸载后的销毁阶段。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-vue)。 * 对齐语义:Vue `unmounted` * 源码行为:映射到 `onUnload`。 ### `onActivated()` {#onactivated} **类型签名:** `typeof import('wevu')['onActivated']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **Vue 差异:** 映射到页面或组件重新可见的宿主生命周期,不依赖 Vue ``。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-vue)。 * 对齐语义:Vue `activated` * 源码行为:映射到 `onShow`。 ### `onDeactivated()` {#ondeactivated} **类型签名:** `typeof import('wevu')['onDeactivated']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **Vue 差异:** 映射到宿主隐藏阶段,不表示 `` 缓存树被停用。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-vue)。 * 对齐语义:Vue `deactivated` * 源码行为:映射到 `onHide`。 ### `onErrorCaptured()` {#onerrorcaptured} **类型签名:** `typeof import('wevu')['onErrorCaptured']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **Vue 差异:** 捕获 Wevu setup、渲染和宿主生命周期链路中的错误,不具备完整 Vue 组件树错误传播语义。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-vue)。 * 对齐语义:Vue `errorCaptured` * 源码行为:映射到 `onError` 包装调用。 ### `onServerPrefetch()` {#onserverprefetch} **类型签名:** `typeof import('wevu')['onServerPrefetch']` **运行时说明:** 必须在同步 `setup()` 阶段注册,运行时才能把回调绑定到当前 App、页面或组件实例;不要在 `await` 之后注册。 **Vue 差异:** 小程序没有 SSR;为迁移兼容保留该 hook,但运行时不会执行预取。 **示例:** 见 [本组示例](/wevu/api/lifecycle#example-lifecycle-vue)。 * 对齐语义:Vue `serverPrefetch` * 源码行为:保留 API 形态,仅做调用时机校验,不执行实际逻辑。 ### 本组示例 {#example-lifecycle-vue} 这些名称接近 Vue,但时机围绕宿主 ready、setData 和 detached。 ```vue ``` ## 小程序原生生命周期映射(说明) 以下是桥接关系说明,不是 `wevu` 直接导出的 Hook API。 ### `lifetimes.created` {#lifetimes-created} * 映射:组件初始化桥接(无独立 `onCreated` 导出)。 ### `lifetimes.attached` {#lifetimes-attached} * 映射:组件挂载流程(可使用 `onAttached`;`onMounted` 仍映射 `onReady`)。 ### `lifetimes.ready` {#lifetimes-ready} * 映射:`onReady`。 ### `lifetimes.moved` {#lifetimes-moved} * 映射:`onMoved`。 ### `lifetimes.detached` {#lifetimes-detached} * 映射:组件 teardown(可使用 `onDetached`)+ `onUnload` 钩子链(含 `onUnmounted`)。 ### `lifetimes.error` {#lifetimes-error} * 映射:`onError`。 ### `pageLifetimes.show` {#pagelifetimes-show} * 映射:`onShow`。 ### `pageLifetimes.hide` {#pagelifetimes-hide} * 映射:`onHide`。 ### `pageLifetimes.resize` {#pagelifetimes-resize} * 映射:`onResize`。 ### `pageLifetimes.routeDone` {#pagelifetimes-routedone} * 映射:`onRouteDone`。 --- --- url: /wevu/api/setup-context.md description: >- 本页严格对应 wevu 源码中的 setup 上下文相关导出(runtime/hooks.ts、runtime/provide.ts、runtime/register.ts、runtime/vueCompat.ts)。 --- # Setup Context API(setup 上下文) `setup(props, ctx)` 的字段语义来自 `SetupContext` 类型;本页重点列出可直接导入调用的公开 API。 `ctx` 常见字段(类型定义语义): * `ctx.runtime`:当前 `RuntimeInstance` * `ctx.instance`:原生小程序实例 * `ctx.emit`:事件派发函数 ## 实例与上下文访问 API ### `getCurrentInstance()` {#getcurrentinstance} **类型签名:** `typeof import('wevu')['getCurrentInstance']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-instance)。 * 用途:获取当前运行时实例。 * 源码:`runtime/hooks.ts`。 ### `getCurrentSetupContext()` {#getcurrentsetupcontext} **类型签名:** `typeof import('wevu')['getCurrentSetupContext']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-instance)。 * 用途:获取当前 setup context。 * 源码:`runtime/hooks.ts`。 ### 本组示例 {#example-setup-instance} Wevu 上下文可参与组合逻辑,原生实例只在命令式方法里使用。 ```vue ``` ## 依赖注入 API ### `provide()` {#provide} **类型签名:** `typeof import('wevu')['provide']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-provide)。 * 用途:在当前组件树提供依赖。 * 源码:`runtime/provide.ts`。 ### `inject()` {#inject} **类型签名:** `typeof import('wevu')['inject']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-provide)。 * 用途:从上层读取依赖。 * 源码:`runtime/provide.ts`。 ### `provideGlobal()` / `injectGlobal()`(Deprecated) {#provideglobal} **类型签名:** `typeof import('wevu')['provideGlobal']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-provide)。 * 用途:已弃用的全局 provide/inject 兼容入口。 * 说明:当前更推荐优先使用 `provide()` / `inject()`;无实例上下文时,它们本身就会回退到全局存储。 * 源码:`runtime/provide.ts`。 > \[!WARNING] > 这组 API 仅为兼容旧代码而保留,不建议在新代码中继续使用。 > 优先使用 `provide()` / `inject()`;需要稳定的跨页面全局共享时,优先考虑 store。 ### 本组示例 {#example-setup-provide} 页面或上层组件提供响应式依赖,下层组件按同一个 key 注入。 ```ts import type { InjectionKey, Ref } from 'wevu' import { inject, provide, ref } from 'wevu' const ThemeKey: InjectionKey> = Symbol('theme') // 上层同步提供。 provide(ThemeKey, ref('light')) // 下层读取;缺失时使用显式默认值。 const theme = inject(ThemeKey, ref('light')) ``` ## Setup 兼容工具 API ### `useNativeInstance()` {#usenativeinstance} **类型签名:** `typeof import('wevu')['useNativeInstance']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:获取当前 setup 对应的原生实例。 * 源码:`runtime/vueCompat.ts`。 ### `useNativeRouter()` {#usenativerouter} **类型签名:** `typeof import('wevu')['useNativeRouter']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:获取当前组件路径语义的 Router 对象。 * 行为:优先使用 `this.router`;不可用时回退 `this.pageRouter`;低版本基础库(`< 2.16.1`)再回退全局路由方法(`wx`/`my`/`tt`)。 * 类型:可通过声明合并 `WevuTypedRouterRouteMap.entries` 收窄 `url` 字面量;可选 `tabBarEntries` 进一步收窄 `switchTab`(`weapp-vite autoRoutes` 会自动注入 `entries`)。 * 源码:`runtime/vueCompat.ts`。 ### `useNativePageRouter()` {#usenativepagerouter} **类型签名:** `typeof import('wevu')['useNativePageRouter']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:获取当前页面路径语义的 Router 对象。 * 行为:优先使用 `this.pageRouter`;不可用时回退 `this.router`;低版本基础库(`< 2.16.1`)再回退全局路由方法(`wx`/`my`/`tt`)。 * 类型:与 `useNativeRouter()` 共享 `WevuTypedRouterRouteMap.entries / tabBarEntries` 收窄能力。 * 源码:`runtime/vueCompat.ts`。 ### Router 语义与兼容建议 `Router` 能力在微信基础库 `2.16.1+` 才有原生对象。`wevu` 在低版本会自动降级到全局路由方法,所以建议你明确区分“路径语义”与“兼容语义”。 | 场景 | 推荐 API | 相对路径基准 | | -------------- | ----------------------- | ------------------------------------------ | | 页面内跳转 | `useNativePageRouter()` | 当前页面路径(更稳定) | | 组件内组件路径 | `useNativeRouter()` | 当前组件路径 | | 组件内页面路径 | `useNativePageRouter()` | 组件所在页面路径 | | 低版本降级路径 | 自动回退 | 回退为全局 `wx/my/tt` 语义(按当前激活页) | **实践建议:** * 需要跨版本稳定时,优先使用 `useNativePageRouter()`(尤其是页面方法被延迟调用、跨页调用时)。 * 当你明确要“相对组件目录”导航时,使用 `useNativeRouter()`。 * 对低版本必须严格一致的跳转,优先使用绝对路径(如 `/pages/foo/index`),避免依赖相对路径基准。 * 若开启 `autoRoutes`,可结合路由联合类型减少拼写错误(见 `/guide/auto-routes`)。 ### `useBindModel()` {#usebindmodel} **类型签名:** `typeof import('wevu')['useBindModel']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:创建绑定 payload(`value + onXxx`)辅助函数。 * 源码:`runtime/vueCompat.ts`。 ### `useChangeModel()` {#usechangemodel} **类型签名:** `typeof import('wevu')['useChangeModel']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:创建默认使用 `change` 事件的 model 绑定辅助函数。 * 适合:TDesign、Vant 等第三方小程序组件库中大量使用 `change` 事件的表单场景。 * 行为:等价于 `useBindModel({ event: 'change' }).model(...)` 的收敛写法。 * 源码:`runtime/vueCompat.ts`。 示例: ```vue ``` ### `useIntersectionObserver()` {#useintersectionobserver} **类型签名:** `typeof import('wevu')['useIntersectionObserver']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:在 `setup()` 中创建 `IntersectionObserver`,并在卸载时自动 `disconnect()`。 * 建议:优先用它替代 `onPageScroll + setData` 的可见性轮询逻辑。 * 源码:`runtime/intersectionObserver.ts`。 ### `useElementIntersectionObserver()` {#useelementintersectionobserver} **类型签名:** `typeof import('wevu')['useElementIntersectionObserver']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:声明式观察当前页面/组件内某个 selector 的可见性,并在卸载时自动断开。 * 适合:商品卡片曝光、懒加载、可见区域统计。 * 说明:`selector`、`enabled`、`observerOptions` 支持普通值、Ref 或 getter;当 selector 或 enabled 变化时会自动重新 observe。 * 源码:`runtime/elementIntersectionObserver.ts`。 示例: ```vue ``` ### `useSelectorQuery()` {#useselectorquery} **类型签名:** `typeof import('wevu')['useSelectorQuery']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:创建绑定当前原生实例的 `SelectorQuery` 工厂。 * 适合:需要直接调用小程序 `select()` / `selectAll()` / `exec()` 的高级节点查询。 * 源码:`runtime/selectorQuery.ts`。 ### `useBoundingClientRect()` {#useboundingclientrect} **类型签名:** `typeof import('wevu')['useBoundingClientRect']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:创建节点布局查询函数。 * 说明:默认查询单个节点;传入 `{ all: true }` 时返回节点数组。 * 源码:`runtime/selectorQuery.ts`。 ### `useSelectorFields()` {#useselectorfields} **类型签名:** `typeof import('wevu')['useSelectorFields']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:按小程序 `fields()` 语义读取节点字段。 * 说明:必须显式传入 `fields`,例如 `{ size: true, dataset: true }`。 * 源码:`runtime/selectorQuery.ts`。 ### `useScrollOffset()` {#usescrolloffset} **类型签名:** `typeof import('wevu')['useScrollOffset']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:读取 scroll-view 等可滚动节点的滚动位置。 * 说明:默认查询单个节点;传入 `{ all: true }` 时返回节点数组。 * 源码:`runtime/selectorQuery.ts`。 ### `useDisposables()` {#usedisposables} **类型签名:** `typeof import('wevu')['useDisposables']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:创建一个自动随页面/组件卸载执行的清理袋。 * 适合:定时器、请求任务、事件退订函数、`disconnect()` / `abort()` / `stop()` 一类资源清理。 * 源码:`runtime/disposables.ts`。 ### `usePageLayout()` / `setPageLayout()` {#usepagelayout} **类型签名:** `typeof import('wevu')['usePageLayout']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:读取或切换当前页面的 layout 状态。 * 说明:这是 `wevu` 根入口的页面运行时辅助能力,常与 Weapp-vite 的 `routeRules.layout`、`definePageMeta({ layout })` 配合。 * 源码:`runtime/pageLayout.ts`。 ### `usePageStack()` / `getCurrentPageStackSnapshot()` {#usepagestack} **类型签名:** `typeof import('wevu')['usePageStack']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:读取当前小程序页面栈快照。 * 返回:`currentRoute`、`stackLength`、`canGoBack` 与 `refresh()`。 * 适合:自定义导航栏返回按钮、页面层级提示、页面栈相关 UI。 * 说明:`getCurrentPageStackSnapshot()` 可在非响应式场景读取一次性快照;`usePageStack()` 必须在 `setup()` 同步阶段调用。 * 源码:`runtime/pageEnvironment.ts`。 ### `useNavigationBarMetrics()` / `getNavigationBarMetrics()` {#usenavigationbarmetrics} **类型签名:** `typeof import('wevu')['useNavigationBarMetrics']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:计算自定义导航栏需要的状态栏、导航栏和总高度。 * 返回:`statusBarHeight`、`navigationBarHeight`、`navigationHeight` 与 `refresh()`。 * 适合:自定义导航栏布局、沉浸式页面顶部安全区。 * 说明:读取 `getSystemInfoSync()` 与 `getMenuButtonBoundingClientRect()`;不可用时使用默认高度兜底。 * 源码:`runtime/pageEnvironment.ts`。 ### `usePageScrollThrottle()` {#usepagescrollthrottle} **类型签名:** `typeof import('wevu')['usePageScrollThrottle']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:在 `onPageScroll()` 基础上提供节流包装,并在卸载时自动清理。 * 适合:吸顶状态、滚动进度、轻量联动,而不是每次滚动都直接 `setData`。 * 源码:`runtime/pageScroll.ts`。 ### `useUpdatePerformanceListener()` {#useupdateperformancelistener} **类型签名:** `typeof import('wevu')['useUpdatePerformanceListener']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:注册原生 `setUpdatePerformanceListener` 监听,并在卸载时自动移除。 * 适合:排查页面/组件更新耗时与更新结果。 * 源码:`runtime/updatePerformance.ts`。 ### `useAsyncPullDownRefresh()` {#useasyncpulldownrefresh} **类型签名:** `typeof import('wevu')['useAsyncPullDownRefresh']` **运行时说明:** 返回值或回调直接连接当前小程序宿主实例,不应把原生实例、查询器或观察器暴露为模板可序列化状态。 **示例:** 见 [本组示例](/wevu/api/setup-context#example-setup-host-tools)。 * 用途:注册异步下拉刷新回调,并在回调结束后自动停止宿主下拉刷新状态。 * 适合:页面刷新逻辑需要 `await` 请求、错误处理和统一 `stopPullDownRefresh()` 的场景。 * 说明:默认调用 `wpi.stopPullDownRefresh()`;可通过 `stopPullDownRefresh` 注入自定义停止函数,便于测试或平台差异适配。 * 源码:`runtime/pullDownRefresh.ts`。 示例: ```vue ``` ### 本组示例 {#example-setup-host-tools} 节点查询、页面栈和自动清理工具可以在同一 setup 中组合。 ```vue ``` --- --- url: /wevu/api/store.md description: 本页覆盖 wevu/store 的入口函数、Store 实例 API、Manager、Options Store 配置及公开类型。 --- # Store API(状态管理) 以下条目来源于 `packages-runtime/wevu/src/store/index.ts` 的模块导出,以及 `defineStore()` 返回实例和 `createStore()` 返回 Manager 的公共契约。 > Wevu Store 对齐 Pinia 的主要使用心智,但不是 Pinia 的完整实现。`createStore()`、Manager 安装行为、订阅时机和小程序响应式更新均以本页契约为准。 ## 核心函数 ### `defineStore()` {#definestore} **类型签名:** `typeof import('wevu/store')['defineStore']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** API 心智接近 Pinia,但实现使用 Wevu 响应式与小程序实例作用域;不包含 Pinia devtools、SSR hydration 和完整插件生态。 **示例:** 见 [本组示例](/wevu/api/store#example-store-entry)。 * 类型入口:`DefineStoreOptions` * 用途:定义 store(setup 风格或 option 风格)。 ### `createStore()` {#createstore} **类型签名:** `typeof import('wevu/store')['createStore']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **示例:** 见 [本组示例](/wevu/api/store#example-store-entry)。 * 类型入口:`StoreManager` * 用途:创建 store 管理器(可用于作用域隔离和测试隔离)。 ### `storeToRefs()` {#storetorefs} **类型签名:** `typeof import('wevu/store')['storeToRefs']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** API 心智接近 Pinia,但实现使用 Wevu 响应式与小程序实例作用域;不包含 Pinia devtools、SSR hydration 和完整插件生态。 **示例:** 见 [本组示例](/wevu/api/store#example-store-entry)。 * 类型入口:`StoreToRefsResult` * 用途:将 store 状态转换为 refs,避免解构丢失响应性。 ### 本组示例 {#example-store-entry} Setup Store 优先返回 Ref、computed 和 Action;状态解构使用 `storeToRefs()`。 ```ts import { computed, createStore, defineStore, ref, storeToRefs } from 'wevu' const manager = createStore() const useCounter = defineStore('counter', () => { const count = ref(0) const doubled = computed(() => count.value * 2) const increment = () => count.value += 1 return { count, doubled, increment } }) const counter = useCounter(manager) const { count, doubled } = storeToRefs(counter) ``` ## Store 实例 API ### `$id` {#store-id} **类型签名:** `ReturnType>['$id']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** API 心智接近 Pinia,但实现使用 Wevu 响应式与小程序实例作用域;不包含 Pinia devtools、SSR hydration 和完整插件生态。 **示例:** 见 [本组示例](/wevu/api/store#example-store-instance)。 * 用途:读取 `defineStore()` 声明的 Store 标识。 * 适用:Setup Store 与 Options Store。 ### `$state` {#store-state} **类型签名:** `ReturnType>['$state']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** API 心智接近 Pinia,但实现使用 Wevu 响应式与小程序实例作用域;不包含 Pinia devtools、SSR hydration 和完整插件生态。 **示例:** 见 [本组示例](/wevu/api/store#example-store-instance)。 * 用途:读取或浅合并替换 Options Store 的响应式 state。 * 适用:仅 Options Store 的公共类型包含 `$state`;Setup Store 应直接使用 setup 返回的 state/ref。 ### `$patch()` {#store-patch} **类型签名:** `ReturnType>['$patch']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** API 心智接近 Pinia,但实现使用 Wevu 响应式与小程序实例作用域;不包含 Pinia devtools、SSR hydration 和完整插件生态。 **示例:** 见 [本组示例](/wevu/api/store#example-store-instance)。 * 用途:通过部分对象或回调函数批量修改状态。 * 订阅类型:分别触发 `patch object` 或 `patch function`。 ### `$reset()` {#store-reset} **类型签名:** `ReturnType>['$reset']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** API 心智接近 Pinia,但实现使用 Wevu 响应式与小程序实例作用域;不包含 Pinia devtools、SSR hydration 和完整插件生态。 **示例:** 见 [本组示例](/wevu/api/store#example-store-instance)。 * 用途:恢复 Store 创建时保存的初始状态快照。 * 适用:Setup Store 与 Options Store;Setup Store 中不可写的 computed/readonly ref 会被跳过。 ### `$subscribe()` {#store-subscribe} **类型签名:** `ReturnType>['$subscribe']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** API 心智接近 Pinia,但实现使用 Wevu 响应式与小程序实例作用域;不包含 Pinia devtools、SSR hydration 和完整插件生态。 **示例:** 见 [本组示例](/wevu/api/store#example-store-instance)。 * 用途:订阅 Store 状态变化,回调接收 mutation 信息和当前状态。 * 返回值:取消订阅函数。 * 选项:支持 `{ detached: true }`,用于跨页面生命周期保留订阅。 ### `$onAction()` {#store-onaction} **类型签名:** `ReturnType>['$onAction']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** API 心智接近 Pinia,但实现使用 Wevu 响应式与小程序实例作用域;不包含 Pinia devtools、SSR hydration 和完整插件生态。 **示例:** 见 [本组示例](/wevu/api/store#example-store-instance)。 * 用途:订阅 Action 调用,可通过 `after()` 和 `onError()` 监听成功结果或错误。 * 返回值:取消订阅函数。 ### 本组示例 {#example-store-instance} 实例 API 可以批量更新、重置并观察 mutation 与 Action 结果。 ```ts const stopState = counter.$subscribe((mutation, state) => { console.log(mutation.type, state.count) }) const stopAction = counter.$onAction(({ name, after, onError }) => { after(result => console.log(name, result)) onError(error => console.error(name, error)) }) counter.$patch({ count: 2 }) counter.$patch((state) => { state.count += 1 }) counter.$reset() stopState() stopAction() ``` ## Store Manager API ### `manager.install()` {#storemanager-install} **类型签名:** `StoreManager['install']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** Pinia 通过 `app.use(pinia)` 注入 Vue App;Wevu 小程序没有同等插件挂载阶段,`install()` 仅保留兼容入口。 **示例:** 见 [本组示例](/wevu/api/store#example-store-manager)。 * 用途:保留与插件安装心智一致的接口。 * 差异:小程序环境不需要注册全局插件入口,当前实现不执行额外逻辑。 ### `manager.use()` {#storemanager-use} **类型签名:** `StoreManager['use']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** API 心智接近 Pinia,但实现使用 Wevu 响应式与小程序实例作用域;不包含 Pinia devtools、SSR hydration 和完整插件生态。 **示例:** 见 [本组示例](/wevu/api/store#example-store-manager)。 * 用途:注册 Store 插件;每个新建 Store 会调用插件并传入 `{ store }`。 * 返回值:当前 `StoreManager`,支持链式调用。 ### 本组示例 {#example-store-manager} Manager 插件只影响之后创建的 Store,适合隔离测试和注入横切能力。 ```ts import { createStore, defineStore } from 'wevu' const manager = createStore() manager.use(({ store }) => { console.log('created store', store.$id) }) manager.install() const useSession = defineStore('session', { state: () => ({ token: '' }) }) const session = useSession(manager) ``` ## Options Store 配置 ### `state` {#options-state} **类型签名:** `DefineStoreOptions['state']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** API 心智接近 Pinia,但实现使用 Wevu 响应式与小程序实例作用域;不包含 Pinia devtools、SSR hydration 和完整插件生态。 **示例:** 见 [本组示例](/wevu/api/store#example-store-options)。 * 类型:`() => Record`。 * 用途:返回每个 Store 的初始响应式状态。 ### `getters` {#options-getters} **类型签名:** `DefineStoreOptions['getters']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** API 心智接近 Pinia,但实现使用 Wevu 响应式与小程序实例作用域;不包含 Pinia devtools、SSR hydration 和完整插件生态。 **示例:** 见 [本组示例](/wevu/api/store#example-store-options)。 * 用途:声明派生状态;getter 可接收 state,并可通过 `this` 访问 state、其他 getters 和 actions。 ### `actions` {#options-actions} **类型签名:** `DefineStoreOptions['actions']` **运行时说明:** 状态由 Wevu 响应式系统追踪,并随所属页面或组件的渲染批次同步;解构 state/getter 时必须使用 `storeToRefs()`。 **Vue/Pinia 差异:** API 心智接近 Pinia,但实现使用 Wevu 响应式与小程序实例作用域;不包含 Pinia devtools、SSR hydration 和完整插件生态。 **示例:** 见 [本组示例](/wevu/api/store#example-store-options)。 * 用途:声明 Store 方法;Action 内的 `this` 指向 Store 实例,并会触发 `$onAction()` 订阅。 ### 本组示例 {#example-store-options} Options Store 的 getter 和 Action 可通过 `this` 访问同一个 Store。 ```ts import { defineStore } from 'wevu' export const useCart = defineStore('cart', { state: () => ({ count: 0, price: 20 }), getters: { total: state => state.count * state.price }, actions: { add(quantity = 1) { this.count += quantity }, clear() { this.count = 0 }, }, }) ``` ## Store 类型 ### `StoreManager` {#storemanager} **类型签名:** ```ts interface StoreManager { install: (app: any) => void _stores: Map use: (plugin: (context: { store: any }) => void) => StoreManager _plugins: Array<(context: { store: any }) => void> } ``` **运行时说明:** 该类型用于约束 Store 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/store` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/store#example-store-types)。 * 用途:store 根管理器类型。 ### `DefineStoreOptions` {#definestoreoptions} **类型签名:** ```ts interface DefineStoreOptions< S extends Record, G extends GetterTree, A extends Record, > { state: () => S getters?: G & Record any> & ThisType & A> actions?: A & ThisType & A> } ``` **运行时说明:** 该类型用于约束 Store 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/store` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/store#example-store-types)。 * 用途:定义 option 风格 store 的类型约束。 ### `StoreToRefsResult` {#storetorefsresult} **类型签名:** ```ts type StoreToRefsResult> = { [K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : T[K] extends Ref ? Ref : Ref } ``` **运行时说明:** 该类型用于约束 Store 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/store` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/store#example-store-types)。 * 用途:`storeToRefs()` 返回结果类型。 ### `ActionContext` {#actioncontext} **类型签名:** ```ts interface ActionContext { name: string store: TStore args: any[] after: (cb: (result: any) => void) => void onError: (cb: (error: any) => void) => void } ``` **运行时说明:** 该类型用于约束 Store 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/store` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/store#example-store-types)。 * 用途:`$onAction` 回调上下文。 ### `ActionSubscriber` {#actionsubscriber} **类型签名:** ```ts interface ActionSubscriber { (context: ActionContext): void } ``` **运行时说明:** 该类型用于约束 Store 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/store` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/store#example-store-types)。 * 用途:action 订阅回调签名。 ### `SubscriptionCallback` {#subscriptioncallback} **类型签名:** ```ts interface SubscriptionCallback { (mutation: { type: MutationType, storeId: string }, state: S): void } ``` **运行时说明:** 该类型用于约束 Store 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/store` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/store#example-store-types)。 * 用途:状态变更订阅回调签名。 ### `StoreSubscribeOptions` {#storesubscribeoptions} **类型签名:** ```ts interface StoreSubscribeOptions { /** * @description 是否在卸载后仍保留订阅(适用于跨页面生命周期的订阅) */ detached?: boolean } ``` **运行时说明:** 该类型用于约束 Store 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/store` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/store#example-store-types)。 * 用途:`$subscribe` 的订阅选项类型。 ### `MutationType` {#mutationtype} **类型签名:** ```ts type MutationType = 'patch object' | 'patch function' | 'direct' ``` **运行时说明:** 该类型用于约束 Store 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/store` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/store#example-store-types)。 * 用途:store mutation 类型(`'patch object' | 'patch function' | 'direct'`)。 ### 本组示例 {#example-store-types} 公开类型适合约束插件、订阅器和 Action 观察工具。 ```ts import type { ActionSubscriber, StoreManager, SubscriptionCallback } from 'wevu' const logMutation: SubscriptionCallback<{ count: number }> = (mutation, state) => { console.log(mutation.storeId, mutation.type, state.count) } const logAction: ActionSubscriber = ({ name, args }) => console.log(name, args) declare const manager: StoreManager manager.use(({ store }) => { store.$subscribe(logMutation) store.$onAction(logAction) }) ``` --- --- url: /wevu/api/runtime-bridge.md description: 本页仅展示面向业务与配置层的 Wevu 运行时 API。框架内部桥接函数不会在文档中展开。 --- # Runtime Bridge API(运行时配置) 本页聚焦可在业务工程中直接使用的运行时能力。内部注册与调度函数已从文档目录中移除。 ## 全局默认值 ### `setWevuDefaults()` {#setwevudefaults} **类型签名:** `typeof import('wevu')['setWevuDefaults']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/runtime-bridge#example-runtime-defaults)。 * 类型入口:`WevuDefaults` * 用途:配置 `createApp/defineComponent` 的默认行为。 ### `resetWevuDefaults()` {#resetwevudefaults} **类型签名:** `typeof import('wevu')['resetWevuDefaults']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/runtime-bridge#example-runtime-defaults)。 * 类型入口:`void` * 用途:重置默认配置。 * 场景:测试隔离、开发调试。 ### 本组示例 {#example-runtime-defaults} 全局默认值按 App 和 Component 分开配置,测试后应重置。 ```ts import { resetWevuDefaults, setWevuDefaults } from 'wevu' setWevuDefaults({ component: { setData: { autoSetDataPick: true }, options: { multipleSlots: true }, }, }) afterEach(() => resetWevuDefaults()) ``` ## setData 排除标记 ### `markNoSetData()` {#marknosetdata} **类型签名:** `typeof import('wevu')['markNoSetData']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/runtime-bridge#example-runtime-no-setdata)。 * 类型入口:`(value: T) => T` * 用途:标记对象不参与 `setData` 快照同步。 ### `isNoSetData()` {#isnosetdata} **类型签名:** `typeof import('wevu')['isNoSetData']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/runtime-bridge#example-runtime-no-setdata)。 * 类型入口:`boolean` * 用途:判断对象是否被标记为 no-setData。 ### 本组示例 {#example-runtime-no-setdata} 原生实例和 SDK 对象不应进入模板快照,可以显式排除。 ```ts import { isNoSetData, markNoSetData, reactive } from 'wevu' const player = markNoSetData(wx.createVideoContext('player')) const state = reactive({ title: '视频详情', player }) console.log(isNoSetData(state.player)) ``` ## 调试记录 ### `addMutationRecorder()` {#addmutationrecorder} **类型签名:** `typeof import('wevu')['addMutationRecorder']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/runtime-bridge#example-runtime-mutations)。 * 类型入口:`MutationRecord` * 用途:注册状态 mutation 记录器。 ### `removeMutationRecorder()` {#removemutationrecorder} **类型签名:** `typeof import('wevu')['removeMutationRecorder']` **运行时说明:** 该 API 运行在 Wevu 的组件作用域内;涉及 hook 或实例上下文时,应在同步 `setup()` 中调用。 **示例:** 见 [本组示例](/wevu/api/runtime-bridge#example-runtime-mutations)。 * 类型入口:`MutationRecord` * 用途:移除状态 mutation 记录器。 ### 本组示例 {#example-runtime-mutations} Recorder 用同一个函数引用注册和移除,避免调试监听残留。 ```ts import type { MutationRecord } from 'wevu' import { addMutationRecorder, removeMutationRecorder } from 'wevu' const recorder = (record: MutationRecord) => console.log(record) addMutationRecorder(recorder) // 调试结束或实例卸载时清理。 removeMutationRecorder(recorder) ``` ## 页面布局桥接 ### `setPageLayout()` / `usePageLayout()` {#pagelayout} * 类型入口:`PageLayoutState` / `WevuPageLayoutMap` * 用途:在运行时读取或切换页面 layout。 * 说明:通常由 Weapp-vite 的 `definePageMeta({ layout })`、`routeRules.layout` 先确定初始 layout;业务侧只在需要运行时切换页面壳时调用。 ### `registerPageLayoutBridge()` / `unregisterPageLayoutBridge()` {#pagelayoutbridge} * 类型入口:`LayoutBridgeInstance` * 用途:注册页面 layout 桥接实例。 * 说明:主要给 Weapp-vite layout 运行时与框架集成使用,业务工程通常不需要直接调用。 ### `registerRuntimeLayoutHosts()` / `unregisterRuntimeLayoutHosts()` {#layouthosts} * 类型入口:`LayoutHostBinding` * 用途:注册 layout host 映射,供页面运行时定位当前页面壳。 * 说明:属于框架集成层 API;业务侧优先使用 `setPageLayout()`。 ### `useLayoutBridge()` / `useLayoutHosts()` {#uselayoutbridge} * 类型入口:`LayoutBridgeInstance` / `LayoutHostBinding` * 用途:读取当前 layout bridge 或 host 绑定。 * 说明:用于 layout 组件、调试页和框架扩展,不建议普通页面直接依赖。 ### `resolveLayoutBridge()` {#resolvelayoutbridge} * 类型入口:`LayoutBridgeInstance` * 用途:解析当前页面已注册的 layout bridge。 * 适合:需要在业务 hook 中统一访问 layout 内部宿主能力时,先定位承载宿主组件的 layout 上下文。 * 说明:普通业务页面优先使用更直接的 `resolveLayoutHost()`;只有需要自己调用 `selectComponent()` 或做兼容兜底时再使用 bridge。 ### `resolveLayoutHost()` / `waitForLayoutHost()` {#resolvelayouthost} * 类型入口:`LayoutHostResolveOptions` * 用途:按 `layout-host` key 解析 layout 内暴露的宿主组件实例。 * 适合:Toast、Dialog、全局反馈层、抽屉等天然属于 layout 的组件。 * 说明:`resolveLayoutHost()` 同步返回当前可用实例;`waitForLayoutHost()` 会按短间隔重试,适合页面初次进入时等待 layout 宿主完成挂载。 示例: ```vue ``` ## 子路径边界 * `wevu/compiler` 不是 `wevu` 根入口的一部分,主要给编译工具使用。 * `wevu/router` 也不是运行时桥接页的一部分;若你需要 `createRouter()` / `useRouter()`,请直接看 [/wevu/router](/wevu/router)。 * `wevu/fetch` 与 `wevu/web-apis` 是网络/Web API 兼容入口;分别看 [/wevu/fetch](/wevu/fetch) 与 Web runtime 相关说明。 --- --- url: /wevu/api/router.md description: wevu/router 完整 API 参考,覆盖入口函数、Router 实例、导航守卫、动态路由和公开类型。 --- # Wevu Router API 本页对应 `wevu/router` 的公开导出,以及 `createRouter()` 返回实例的公共契约。Wevu Router 对齐 Vue Router 的主要使用心智,但最终导航仍受小程序页面栈、tabBar 和宿主 API 约束。 ```ts import { createRouter, useRoute, useRouter } from 'wevu/router' ``` ## Router 入口 ### `createRouter()` {#createrouter} **类型签名:** `typeof import('wevu/router')['createRouter']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 不选择 Web History 实现;配置重点是路由记录、tabBar、params 策略和宿主导航失败处理。 **示例:** 见 [本组示例](/wevu/api/router#example-router-entry)。 创建并注册默认 Router。选项支持路由记录、tabBar 路径、params 模式、重定向上限、query codec 和导航失败策略。 ### `useRouter()` {#userouter} **类型签名:** `typeof import('wevu/router')['useRouter']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-entry)。 读取当前已创建的 Router;调用前必须先执行 `createRouter()`。 ### `useRoute()` {#useroute} **类型签名:** `typeof import('wevu/router')['useRoute']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 返回值随小程序页面生命周期和导航完成事件同步,不依赖浏览器 URL/history 监听。 **示例:** 见 [本组示例](/wevu/api/router#example-router-entry)。 在 `setup()` 同步阶段读取只读的当前路由状态,并随页面生命周期和导航完成事件更新。 ### 本组示例 {#example-router-entry} App 初始化阶段只创建一个 Router,页面在同步 setup 中读取它。 ```ts import { createRouter, useRoute, useRouter } from 'wevu/router' createRouter({ routes: [{ name: 'home', path: '/pages/home/index' }], tabBarEntries: ['/pages/home/index'], }) const router = useRouter() const route = useRoute() console.log(router.currentRoute, route.fullPath) ``` ## 原生 Router ### `useNativeRouter()` {#usenativerouter} **类型签名:** `typeof import('wevu/router')['useNativeRouter']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **示例:** 见 [本组示例](/wevu/api/router#example-router-native)。 获取当前组件路径语义的原生 Router,直接暴露小程序导航能力。 ### `useNativePageRouter()` {#usenativepagerouter} **类型签名:** `typeof import('wevu/router')['useNativePageRouter']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **示例:** 见 [本组示例](/wevu/api/router#example-router-native)。 获取当前页面路径语义的原生 Router,适合页面级相对导航。 ### 本组示例 {#example-router-native} 原生 Router 适合直接调用宿主能力;高层业务导航优先使用 `wevu/router`。 ```ts import { useNativePageRouter, useNativeRouter } from 'wevu/router' const componentRouter = useNativeRouter() const pageRouter = useNativePageRouter() componentRouter.navigateTo({ url: '../detail/index?id=42' }) pageRouter.redirectTo({ url: '/pages/home/index' }) ``` ## 解析与导航失败 ### `resolveRouteLocation()` {#resolveroutelocation} **类型签名:** `typeof import('wevu/router')['resolveRouteLocation']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **示例:** 见 [本组示例](/wevu/api/router#example-router-resolution)。 将字符串或位置对象解析为标准路由位置,可传入当前路径处理相对地址。 ### `parseQuery()` {#parsequery} **类型签名:** `typeof import('wevu/router')['parseQuery']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **示例:** 见 [本组示例](/wevu/api/router#example-router-resolution)。 把 query 字符串解析为 `LocationQuery`。 ### `stringifyQuery()` {#stringifyquery} **类型签名:** `typeof import('wevu/router')['stringifyQuery']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **示例:** 见 [本组示例](/wevu/api/router#example-router-resolution)。 把 `LocationQueryRaw` 序列化为 query 字符串。 ### `createNavigationFailure()` {#createnavigationfailure} **类型签名:** `typeof import('wevu/router')['createNavigationFailure']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **示例:** 见 [本组示例](/wevu/api/router#example-router-resolution)。 创建带失败类型、目标位置、来源位置和原始原因的导航失败对象。 ### `isNavigationFailure()` {#isnavigationfailure} **类型签名:** `typeof import('wevu/router')['isNavigationFailure']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-resolution)。 判断异常或导航结果是否为 Wevu 导航失败,也可按失败类型过滤。 ### `NavigationFailureType` {#navigationfailuretype} **类型签名:** `typeof import('wevu/router')['NavigationFailureType']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-resolution)。 运行时失败类型常量,包含 `unknown`、`aborted`、`cancelled` 和 `duplicated`。 ### 本组示例 {#example-router-resolution} 解析与失败判断可以脱离实际跳转,用于日志、预览和统一错误处理。 ```ts import { isNavigationFailure, parseQuery, resolveRouteLocation, stringifyQuery } from 'wevu/router' const query = parseQuery('?tag=wevu&page=2') const search = stringifyQuery({ tag: 'wevu', page: 2 }) const target = resolveRouteLocation({ path: '/pages/list/index', query }) try { await router.push(target) } catch (error) { if (isNavigationFailure(error)) { report(error.type) } } console.log(search) ``` ## Router 实例 ### `router.nativeRouter` {#router-nativerouter} **类型签名:** `RouterNavigation['nativeRouter']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-instance)。 Router 内部使用的原生 `SetupContextRouter`。 ### `router.options` {#router-options} **类型签名:** `RouterNavigation['options']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-instance)。 创建 Router 时使用的只读、规范化选项快照。 ### `router.currentRoute` {#router-currentroute} **类型签名:** `RouterNavigation['currentRoute']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-instance)。 当前只读路由位置,包含 path、query、params、matched 和 redirectedFrom 等信息。 ### `router.install()` {#router-install} **类型签名:** `RouterNavigation['install']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** Vue Router 安装组件与全局属性;Wevu 只注册默认 Router,并在宿主对象支持时写入 `$router`。 **示例:** 见 [本组示例](/wevu/api/router#example-router-instance)。 注册当前 Router,并在 App 支持 `globalProperties` 时写入 `$router`。 ### `router.resolve()` {#router-resolve} **类型签名:** `RouterNavigation['resolve']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-instance)。 基于当前路由和 Router 配置解析目标位置,不执行实际跳转。 ### `router.isReady()` {#router-isready} **类型签名:** `RouterNavigation['isReady']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-instance)。 返回 Router 就绪 Promise;当前小程序实现创建后即可就绪。 ### 本组示例 {#example-router-instance} 先 `resolve()` 检查目标,再根据当前页面栈决定是否执行导航。 ```ts import { useRouter } from 'wevu/router' const router = useRouter() await router.isReady() const target = router.resolve({ name: 'detail', params: { id: 42 } }) console.log(router.options, router.currentRoute, target.href) router.install() ``` ## 导航方法 ### `router.push()` {#router-push} **类型签名:** `RouterNavigation['push']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-navigation)。 执行前进导航,通常映射到 `navigateTo`,也会处理 tabBar、守卫、重定向和失败分类。 ### `router.replace()` {#router-replace} **类型签名:** `RouterNavigation['replace']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-navigation)。 替换当前页面,通常映射到 `redirectTo`。 ### `router.back()` {#router-back} **类型签名:** `RouterNavigation['back']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 返回操作受当前小程序页面栈深度限制,不存在浏览器跨站 history。 **示例:** 见 [本组示例](/wevu/api/router#example-router-navigation)。 按指定层数返回;默认返回一层。 ### `router.go()` {#router-go} **类型签名:** `RouterNavigation['go']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** Vue Router 可正向或反向移动 history;Wevu 只能把负数映射为 `navigateBack`,正数无法前进。 **示例:** 见 [本组示例](/wevu/api/router#example-router-navigation)。 使用相对层数操作页面栈;小程序环境只支持可映射的返回语义。 ### `router.forward()` {#router-forward} **类型签名:** `RouterNavigation['forward']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 浏览器可沿 history 前进;小程序没有 forward 栈,该方法会返回 `aborted` 导航失败。 **示例:** 见 [本组示例](/wevu/api/router#example-router-navigation)。 保留 Vue Router 心智的前进入口;小程序没有浏览器 forward 栈,通常返回 `aborted` 失败。 ### 本组示例 {#example-router-navigation} 小程序没有浏览器 forward 栈,正向导航和返回需要使用不同方法。 ```ts import { useRouter } from 'wevu/router' const router = useRouter() await router.push({ name: 'detail', params: { id: 42 } }) await router.replace('/pages/detail/index?id=43') await router.back() // go(-2) 可映射为 navigateBack;forward() 会返回 aborted 失败。 await router.go(-2) ``` ## 动态路由 ### `router.hasRoute()` {#router-hasroute} **类型签名:** `RouterNavigation['hasRoute']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-dynamic)。 按名称判断路由记录是否存在。 ### `router.getRoutes()` {#router-getroutes} **类型签名:** `RouterNavigation['getRoutes']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-dynamic)。 返回当前规范化路由记录列表。 ### `router.addRoute()` {#router-addroute} **类型签名:** `RouterNavigation['addRoute']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-dynamic)。 新增顶层或子路由记录,返回移除该记录的函数。 ### `router.removeRoute()` {#router-removeroute} **类型签名:** `RouterNavigation['removeRoute']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-dynamic)。 按名称移除路由记录。 ### `router.clearRoutes()` {#router-clearroutes} **类型签名:** `RouterNavigation['clearRoutes']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-dynamic)。 清空动态路由注册表。 ### 本组示例 {#example-router-dynamic} 动态注册返回清理函数,组件卸载时应主动移除临时路由。 ```ts import { onUnmounted } from 'wevu' import { useRouter } from 'wevu/router' const router = useRouter() const remove = router.addRoute({ name: 'preview', path: '/pages/preview/index' }) console.log(router.hasRoute('preview'), router.getRoutes()) onUnmounted(remove) ``` ## 导航守卫 ### `router.beforeEach()` {#router-beforeeach} **类型签名:** `RouterNavigation['beforeEach']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-guards)。 注册全局前置守卫,返回取消注册函数。 ### `router.beforeResolve()` {#router-beforeresolve} **类型签名:** `RouterNavigation['beforeResolve']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-guards)。 注册导航确认前守卫,运行在 `beforeEach` 和路由记录 `beforeEnter` 之后。 ### `router.afterEach()` {#router-aftereach} **类型签名:** `RouterNavigation['afterEach']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-guards)。 注册导航完成回调,可读取失败对象和小程序导航上下文。 ### `router.onError()` {#router-onerror} **类型签名:** `RouterNavigation['onError']` **运行时说明:** 解析和守卫在 JavaScript 层执行,真正跳转仍由小程序 Router 完成,因此页面栈、tabBar 和宿主失败回调是最终边界。 **Vue Router 差异:** 调用形式接近 Vue Router,但导航最终映射到 `navigateTo`、`redirectTo`、`switchTab`、`reLaunch` 或 `navigateBack`,受页面栈和 tabBar 约束。 **示例:** 见 [本组示例](/wevu/api/router#example-router-guards)。 注册异常型导航失败处理器,返回取消注册函数。 ### 本组示例 {#example-router-guards} 守卫按 beforeEach、路由 beforeEnter、beforeResolve 的顺序运行。 ```ts import { onUnmounted } from 'wevu' import { useRouter } from 'wevu/router' const router = useRouter() const removeGuard = router.beforeEach((to) => { if (to?.meta?.requiresLogin) { return { name: 'login' } } }) const removeAfter = router.afterEach((to, from, failure) => report({ to, from, failure })) const removeError = router.onError((error, context) => report({ error, context })) onUnmounted(() => { removeGuard() removeAfter() removeError() }) ``` ## 兼容边界 * `forward()`、hash-only 导航和页面栈行为受小程序宿主限制。 * `useRoute()` 必须在 `setup()` 同步阶段调用,不能放在 `await` 之后。 * `useNativeRouter()` / `useNativePageRouter()` 直接面向宿主能力;高阶导航优先使用 `createRouter()` / `useRouter()`。 * 所有公开类型见 [Wevu Router 类型参考](/wevu/api/router-types)。 * 详细示例和迁移建议见 [wevu/router 使用指南](/wevu/router)。 --- --- url: /wevu/api/router-types.md description: wevu/router 的公开 TypeScript 类型,覆盖位置、参数、守卫、失败、路由记录和小程序宿主 Router。 --- # Wevu Router 类型 以下类型均从 `wevu/router` 导出。运行时函数和 Router 实例方法见 [Wevu Router API](/wevu/api/router)。 ## 位置与参数类型 ### `RouterNavigation` {#type-routernavigation} **类型签名:** ```ts interface RouterNavigation { readonly nativeRouter: SetupContextRouter readonly options: Readonly readonly currentRoute: Readonly install: (app?: unknown) => void resolve: (to: RouteLocationRaw) => RouteLocationNormalizedLoaded isReady: () => Promise push: (to: RouteLocationRaw) => Promise replace: (to: RouteLocationRaw) => Promise back: (delta?: number) => Promise go: (delta: number) => Promise forward: () => Promise hasRoute: (name: string) => boolean getRoutes: () => readonly RouteRecordRaw[] addRoute: AddRoute removeRoute: (name: string) => void clearRoutes: () => void beforeEach: (guard: NavigationGuard) => () => void beforeResolve: (guard: NavigationGuard) => () => void afterEach: (hook: NavigationAfterEach) => () => void onError: (handler: NavigationErrorHandler) => () => void } ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 `createRouter()` 返回的完整 Router 实例类型。 ### `UseRouterOptions` {#type-userouteroptions} **类型签名:** ```ts interface UseRouterOptions { tabBarEntries?: readonly (TypedRouterTabBarUrl | string)[] /** * Vue Router 对齐入口:推荐使用 `routes` */ routes?: readonly RouteRecordInput[] /** * 兼容入口:支持对象 map 或路由记录数组 */ namedRoutes?: NamedRoutes paramsMode?: RouteParamsMode maxRedirects?: number parseQuery?: RouteQueryParser stringifyQuery?: RouteQueryStringifier /** * 异常型导航失败时是否以 Promise reject 抛出失败对象。 * * - `true`:更贴近 Vue Router 心智(默认) * - `false`:始终以返回值形式携带失败对象 */ rejectOnError?: boolean } ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 Router 创建选项,包含 routes、tabBar、params、query codec 和失败策略。 ### `AddRoute` {#type-addroute} **类型签名:** ```ts interface AddRoute { (route: RouteRecordRaw): () => void (parentName: string, route: RouteRecordRaw): () => void } ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 `router.addRoute()` 的重载函数类型。 ### `RouteLocationRaw` {#type-routelocationraw} **类型签名:** ```ts type RouteLocationRaw = string | { path?: string fullPath?: string query?: LocationQueryRaw hash?: string name?: string params?: RouteParamsRaw } ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 导航方法接受的字符串或未规范化位置对象。 ### `RouteLocationNormalizedLoaded` {#type-routelocationnormalizedloaded} **类型签名:** ```ts interface RouteLocationNormalizedLoaded { path: string fullPath: string query: LocationQuery hash: string name?: string meta?: RouteMeta href?: string matched?: readonly RouteRecordMatched[] redirectedFrom?: RouteLocationRedirectedFrom params: RouteParams } ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 已解析并加载的当前路由位置。 ### `RouteLocationRedirectedFrom` {#type-routelocationredirectedfrom} **类型签名:** ```ts interface RouteLocationRedirectedFrom { path: string fullPath: string query: LocationQuery hash: string name?: string meta?: RouteMeta href?: string matched?: readonly RouteRecordMatched[] params: RouteParams } ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 重定向前原始位置的只读快照结构。 ### `LocationQuery` {#type-locationquery} **类型签名:** ```ts type LocationQuery = Record ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 规范化后的 query 对象。 ### `LocationQueryRaw` {#type-locationqueryraw} **类型签名:** ```ts type LocationQueryRaw = Record ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 导航输入可接受的原始 query 对象。 ### `LocationQueryValue` {#type-locationqueryvalue} **类型签名:** ```ts type LocationQueryValue = string | null ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 规范化 query 的单值类型。 ### `LocationQueryValueRaw` {#type-locationqueryvalueraw} **类型签名:** ```ts type LocationQueryValueRaw = LocationQueryValue | number | boolean | undefined ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 原始 query 可接受的单值类型。 ### `RouteParams` {#type-routeparams} **类型签名:** ```ts type RouteParams = Record ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 规范化后的命名路由 params 对象。 ### `RouteParamsRaw` {#type-routeparamsraw} **类型签名:** ```ts type RouteParamsRaw = Record ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 命名路由输入可接受的原始 params 对象。 ### `RouteParamValue` {#type-routeparamvalue} **类型签名:** ```ts type RouteParamValue = string ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 规范化 params 的字符串值类型。 ### `RouteParamValueRaw` {#type-routeparamvalueraw} **类型签名:** ```ts type RouteParamValueRaw = RouteParamValue | number | boolean | null | undefined ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 原始 params 可接受的单值类型。 ### `RouteParamsMode` {#type-routeparamsmode} **类型签名:** ```ts type RouteParamsMode = 'loose' | 'strict' ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 params 缺失或多余时采用的 `loose` 或 `strict` 策略。 ### `RouteQueryParser` {#type-routequeryparser} **类型签名:** ```ts type RouteQueryParser = (search: string) => LocationQueryRaw | LocationQuery ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 自定义 query 解析函数签名。 ### `RouteQueryStringifier` {#type-routequerystringifier} **类型签名:** ```ts type RouteQueryStringifier = (query: LocationQueryRaw | LocationQuery) => string ``` **运行时说明:** 该类型用于约束 位置与参数类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-location)。 自定义 query 序列化函数签名。 ### 本组示例 {#example-router-types-location} 用公开类型约束业务导航函数,避免手写字符串结构。 ```ts import type { RouteLocationRaw, RouteParamsRaw, UseRouterOptions } from 'wevu/router' const params: RouteParamsRaw = { id: 42 } const target: RouteLocationRaw = { name: 'detail', params } const options: UseRouterOptions = { routes: [{ name: 'detail', path: '/pages/detail/:id' }], } ``` ## 守卫与失败类型 ### `NavigationFailure` {#type-navigationfailure} **类型签名:** ```ts interface NavigationFailure extends Error { readonly __wevuNavigationFailure: true readonly type: NavigationFailureTypeValue readonly to?: RouteLocationNormalizedLoaded readonly from?: RouteLocationNormalizedLoaded readonly cause?: unknown } ``` **运行时说明:** 该类型用于约束 守卫与失败类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-guards)。 带类型、目标、来源和 cause 的导航失败对象。 ### `NavigationFailureTypeValue` {#type-navigationfailuretypevalue} **类型签名:** ```ts type NavigationFailureTypeValue = (typeof NavigationFailureType)[keyof typeof NavigationFailureType] ``` **运行时说明:** 该类型用于约束 守卫与失败类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-guards)。 `NavigationFailureType` 所有运行时值的联合类型。 ### `NavigationMode` {#type-navigationmode} **类型签名:** ```ts type NavigationMode = 'push' | 'replace' | 'back' ``` **运行时说明:** 该类型用于约束 守卫与失败类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-guards)。 导航执行模式:`push`、`replace` 或 `back`。 ### `NavigationRedirect` {#type-navigationredirect} **类型签名:** ```ts interface NavigationRedirect { to: RouteLocationRaw replace?: boolean } ``` **运行时说明:** 该类型用于约束 守卫与失败类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-guards)。 守卫或路由记录返回的重定向描述。 ### `NavigationGuard` {#type-navigationguard} **类型签名:** ```ts type NavigationGuard = ( to: RouteLocationNormalizedLoaded | undefined, from: RouteLocationNormalizedLoaded, context?: NavigationGuardContext, ) => NavigationGuardResult | Promise ``` **运行时说明:** 该类型用于约束 守卫与失败类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-guards)。 前置守卫函数签名。 ### `NavigationGuardResult` {#type-navigationguardresult} **类型签名:** ```ts type NavigationGuardResult = void | boolean | NavigationFailure | RouteLocationRaw | NavigationRedirect ``` **运行时说明:** 该类型用于约束 守卫与失败类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-guards)。 守卫可返回的继续、取消、失败位置或重定向结果。 ### `NavigationGuardContext` {#type-navigationguardcontext} **类型签名:** ```ts interface NavigationGuardContext { readonly mode: NavigationMode readonly to?: RouteLocationNormalizedLoaded readonly from: RouteLocationNormalizedLoaded readonly nativeRouter: SetupContextRouter } ``` **运行时说明:** 该类型用于约束 守卫与失败类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-guards)。 前置守卫获得的小程序导航上下文。 ### `NavigationAfterEach` {#type-navigationaftereach} **类型签名:** ```ts type NavigationAfterEach = ( to: RouteLocationNormalizedLoaded | undefined, from: RouteLocationNormalizedLoaded, failure?: NavigationFailure, context?: NavigationAfterEachContext, ) => void | Promise ``` **运行时说明:** 该类型用于约束 守卫与失败类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-guards)。 导航完成回调签名。 ### `NavigationAfterEachContext` {#type-navigationaftereachcontext} **类型签名:** ```ts interface NavigationAfterEachContext { readonly mode: NavigationMode readonly to?: RouteLocationNormalizedLoaded readonly from: RouteLocationNormalizedLoaded readonly nativeRouter: SetupContextRouter readonly failure?: NavigationFailure } ``` **运行时说明:** 该类型用于约束 守卫与失败类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-guards)。 导航完成时的模式、位置、原生 Router 和失败信息。 ### `NavigationErrorHandler` {#type-navigationerrorhandler} **类型签名:** ```ts type NavigationErrorHandler = ( error: unknown, context: NavigationErrorContext, ) => void | Promise ``` **运行时说明:** 该类型用于约束 守卫与失败类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-guards)。 异常型导航失败处理函数签名。 ### `NavigationErrorContext` {#type-navigationerrorcontext} **类型签名:** ```ts interface NavigationErrorContext { readonly mode: NavigationMode readonly to?: RouteLocationNormalizedLoaded readonly from: RouteLocationNormalizedLoaded readonly nativeRouter: SetupContextRouter readonly failure: NavigationFailure } ``` **运行时说明:** 该类型用于约束 守卫与失败类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-guards)。 错误处理器获得的导航上下文。 ### 本组示例 {#example-router-types-guards} 守卫类型会同时约束目标、来源、上下文和返回值。 ```ts import type { NavigationAfterEach, NavigationGuard, NavigationGuardContext } from 'wevu/router' const guard: NavigationGuard = (to, from, context?: NavigationGuardContext) => { if (context?.mode === 'back') { return true } return to?.meta?.disabled ? false : undefined } const afterEach: NavigationAfterEach = (to, from, failure) => { console.log(to, from, failure) } ``` ## 路由记录类型 ### `NamedRouteRecord` {#type-namedrouterecord} **类型签名:** ```ts interface NamedRouteRecord { name: string path: string } ``` **运行时说明:** 该类型用于约束 路由记录类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-records)。 最小命名路由记录,包含 name 和 path。 ### `NamedRoutes` {#type-namedroutes} **类型签名:** ```ts type NamedRoutes = Readonly> | readonly RouteRecordRaw[] ``` **运行时说明:** 该类型用于约束 路由记录类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-records)。 命名路由对象 map 或路由记录数组。 ### `RouteMeta` {#type-routemeta} **类型签名:** ```ts type RouteMeta = Record ``` **运行时说明:** 该类型用于约束 路由记录类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-records)。 路由记录自定义元信息。 ### `RouteRecordInput` {#type-routerecordinput} **类型签名:** ```ts interface RouteRecordInput { name?: string path: string meta?: RouteMeta alias?: string | readonly string[] children?: readonly RouteRecordInput[] beforeEnter?: NavigationGuard | readonly NavigationGuard[] redirect?: RouteRecordRedirect } ``` **运行时说明:** 该类型用于约束 路由记录类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-records)。 创建 Router 或添加动态路由时接受的记录结构。 ### `RouteRecordRaw` {#type-routerecordraw} **类型签名:** ```ts interface RouteRecordRaw extends Omit, NamedRouteRecord { children?: readonly RouteRecordRaw[] } ``` **运行时说明:** 该类型用于约束 路由记录类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-records)。 具有必填 name 的规范化公开路由记录。 ### `RouteRecordMatched` {#type-routerecordmatched} **类型签名:** ```ts interface RouteRecordMatched { name: string path: string aliasPath?: string meta?: RouteMeta } ``` **运行时说明:** 该类型用于约束 路由记录类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-records)。 当前路由 `matched` 中的匹配记录快照。 ### `RouteRecordRedirect` {#type-routerecordredirect} **类型签名:** ```ts type RouteRecordRedirect = RouteLocationRaw | NavigationRedirect | (( to: RouteLocationNormalizedLoaded, from: RouteLocationNormalizedLoaded, ) => RouteLocationRaw | NavigationRedirect | Promise) ``` **运行时说明:** 该类型用于约束 路由记录类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **Vue Router 差异:** 类型名称对齐 Vue Router 的常用概念,但字段只表达小程序路径、query、params、页面栈和原生 Router 能力,不包含浏览器 history/hash 状态。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-records)。 路由记录的静态或函数式重定向类型。 ### 本组示例 {#example-router-types-records} 路由记录可以组合 meta、alias、children、守卫和重定向。 ```ts import type { RouteRecordInput } from 'wevu/router' const routes: RouteRecordInput[] = [{ name: 'profile', path: '/pages/profile/:id', alias: '/profile/:id', meta: { requiresLogin: true }, children: [{ name: 'profile-settings', path: 'settings' }], }] ``` ## 小程序 Router 类型 ### `SetupContextRouter` {#type-setupcontextrouter} **类型签名:** ```ts interface SetupContextRouter { switchTab: (option: MiniProgramRouterSwitchTabOption) => ReturnType reLaunch: (option: MiniProgramRouterReLaunchOption) => ReturnType redirectTo: (option: MiniProgramRouterRedirectToOption) => ReturnType navigateTo: (option: MiniProgramRouterNavigateToOption) => ReturnType navigateBack: MiniProgramRouter['navigateBack'] } ``` **运行时说明:** 该类型用于约束 小程序 Router 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-native)。 Wevu `setup()` 上下文提供的原生小程序 Router。 ### `RouterNavigateToOption` {#type-routernavigatetooption} **类型签名:** ```ts type RouterNavigateToOption = MiniProgramRouterNavigateToOption ``` **运行时说明:** 该类型用于约束 小程序 Router 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-native)。 类型安全的 `navigateTo` 选项。 ### `RouterRedirectToOption` {#type-routerredirecttooption} **类型签名:** ```ts type RouterRedirectToOption = MiniProgramRouterRedirectToOption ``` **运行时说明:** 该类型用于约束 小程序 Router 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-native)。 类型安全的 `redirectTo` 选项。 ### `RouterReLaunchOption` {#type-routerrelaunchoption} **类型签名:** ```ts type RouterReLaunchOption = MiniProgramRouterReLaunchOption ``` **运行时说明:** 该类型用于约束 小程序 Router 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-native)。 类型安全的 `reLaunch` 选项。 ### `RouterSwitchTabOption` {#type-routerswitchtaboption} **类型签名:** ```ts type RouterSwitchTabOption = MiniProgramRouterSwitchTabOption ``` **运行时说明:** 该类型用于约束 小程序 Router 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-native)。 类型安全的 `switchTab` 选项。 ### `TypedRouterUrl` {#type-typedrouterurl} **类型签名:** ```ts type TypedRouterUrl = RouterUrl ``` **运行时说明:** 该类型用于约束 小程序 Router 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-native)。 由项目路由类型映射推导出的页面 URL。 ### `TypedRouterTabBarUrl` {#type-typedroutertabbarurl} **类型签名:** ```ts type TypedRouterTabBarUrl = RouterPathUrl ``` **运行时说明:** 该类型用于约束 小程序 Router 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-native)。 由项目路由类型映射推导出的 tabBar URL。 ### `WevuTypedRouterRouteMap` {#type-wevutypedrouterroutemap} **类型签名:** ```ts interface WevuTypedRouterRouteMap {} ``` **运行时说明:** 该类型用于约束 小程序 Router 类型 的公开契约,不会在运行时产生额外对象;应从 `wevu/router` 以 `import type` 导入。 **示例:** 见 [本组示例](/wevu/api/router-types#example-router-types-native)。 供项目声明合并扩展的类型路由映射。 ### 本组示例 {#example-router-types-native} 项目可以通过声明合并收窄原生 Router 接受的 URL。 ```ts import type { TypedRouterUrl, WevuTypedRouterRouteMap } from 'wevu/router' declare module 'wevu/router' { interface WevuTypedRouterRouteMap { entries: '/pages/home/index' | '/pages/detail/index' tabBarEntries: '/pages/home/index' } } const url: TypedRouterUrl = '/pages/detail/index' ``` --- --- url: /wevu/api/types.md description: 本页仅保留业务开发最常用的公开类型。内部运行时类型不会在文档中展开。 --- # Type Reference(类型总览) 本页提供最常用的公开类型速查,避免把内部实现类型暴露为常规 API。 ## 组件与应用 ### `RuntimeApp` {#runtimeapp} `createApp()` 的返回类型,公开 `mount/use/provide/onUnmount/unmount/config/version`。它不是 Vue DOM App 的完整类型,不包含 `component/directive/mixin/runWithContext`。 ### `AppConfig` {#appconfig} Wevu 应用配置类型,当前公开 `globalProperties: Record`。 ### `WevuPlugin` {#wevuplugin} 插件可以是 `(app, ...options) => any` 函数,也可以是带 `install(app, ...options)` 的对象。 ### `CreateAppOptions` {#createappoptions} `createApp()` 的参数类型。 ### `DefineComponentOptions` {#definecomponentoptions} `defineComponent()` 的参数类型。 ### `ComponentDefinition` {#componentdefinition} `defineComponent()` 返回定义结构类型。 ### `SetupContext` {#setupcontext} `setup(props, ctx)` 中 `ctx` 的类型。 ### `RuntimeInstance` {#runtimeinstance} 页面/组件运行时实例类型。 ### `MiniProgram*` / `HostMiniProgram*` {#miniprogram-types} 小程序宿主中立类型与底层宿主类型别名,例如 `MiniProgramRouter`、`MiniProgramSelectorQuery`、`MiniProgramIntersectionObserver`、`MiniProgramBoundingClientRectResult`、`HostMiniProgramPageScrollOption`。 推荐业务代码优先使用 `MiniProgram*` 命名;只有在明确需要表达底层宿主来源时再使用 `HostMiniProgram*`。 ## 响应式与监听 ### `PropType` {#proptype} 用于给运行时 props 构造器附加 TypeScript 值类型,语义与 Vue `PropType` 对齐。 ### `MaybeRef` {#mayberef} 表示普通值、`Ref`、`ShallowRef` 或可写计算值,适合声明可接受响应式输入的组合式函数。 ### `Ref` {#ref-type} 基础响应式引用类型。 ### `ShallowRef` {#shallowref-type} 浅层响应式引用类型。 ### `WatchOptions` {#watchoptions} `watch/watchEffect` 配置类型。 ### `WatchStopHandle` {#watchstophandle} watch 停止句柄类型。 ### `MaybeRefOrGetter` {#maybereforgetter} 可接收值、Ref 或 getter 的联合类型。 ### `ExtractPropTypes` {#extractproptypes} 从 Wevu `ComponentPropsOptions` 推导组件内部可见的 props 类型。 ### `ExtractPublicPropTypes` {#extractpublicproptypes} 从 Wevu props 配置推导父组件可传入的公开 props 类型。 ### `ComponentCustomProps` {#componentcustomprops} 沿用 Vue 的组件自定义 props 扩展类型,可通过模块增强补充跨组件属性。 ## Store ### `StoreManager` {#storemanager} store 根管理器类型。 ### `DefineStoreOptions` {#definestoreoptions} defineStore 选项类型。 ### `StoreToRefsResult` {#storetorefsresult} `storeToRefs()` 返回类型。 ### `MutationType` {#mutationtype} store mutation 类型。 ## 运行时配置 ### `WevuDefaults` {#wevudefaults} `setWevuDefaults()` 配置类型。 ### `ModelBinding` {#modelbinding} `defineModel/useModel/useBindModel` 相关绑定类型。 ### `ModelBindingOptions` / `ModelBindingPayload` {#modelbindingpayload} `useBindModel()`、`useChangeModel()` 生成 value + handler payload 时使用的参数与返回类型。 ### `TriggerEventOptions` {#triggereventoptions} 事件触发选项类型。 ### `UseElementIntersectionObserverOptions` {#useelementintersectionobserveroptions} `useElementIntersectionObserver()` 的参数类型。 ### `UseBoundingClientRectOptions` / `UseSelectorFieldsOptions` / `UseScrollOffsetOptions` {#selector-query-options} 节点查询相关组合式 API 的参数类型。 ### `PageStackSnapshot` / `UsePageStackOptions` {#pagestack-types} 页面栈快照和 `usePageStack()` 配置类型。 ### `NavigationBarMetrics` / `UseNavigationBarMetricsOptions` {#navigationbar-types} 自定义导航栏尺寸快照和 `useNavigationBarMetrics()` 配置类型。 ### `UseAsyncPullDownRefreshOptions` {#async-pulldown-types} `useAsyncPullDownRefresh()` 的错误处理和停止刷新函数配置类型。 ## 说明 更多底层与内部类型仍可在类型声明文件中找到,但不属于推荐直接依赖的公共 API 文档范围。 --- --- url: /wevu.md description: >- Wevu 是面向小程序的轻量运行时,为 weapp-vite 的 Vue SFC 与组合式开发提供响应式、生命周期、Store 与最小化 setData 更新能力。 --- # Wevu 概览 `wevu` 是一个面向小程序(以微信小程序为主)的轻量运行时。可以把它看作“把 Vue 3 的响应式心智模型带到小程序里”,但不引入 Virtual DOM,而是用快照 diff 来尽量减少 `setData` 的更新量。 :::warning 安装方式 `wevu` 在 `weapp-vite` 项目里通常建议安装在 `devDependencies` 中: ```sh pnpm add -D wevu ``` 这样更符合当前 `weapp-vite` 的产物策略与模板默认值。若你是在非 `weapp-vite` 场景单独消费 `wevu`,再根据自己的发布方式决定依赖落位。 ::: ## 1. Wevu 主要提供什么 它主要提供: * Vue 3 风格的响应式(`ref` / `reactive` / `computed` / `watch`) * 基于快照 diff 的最小化 `setData` 更新 * 类 Pinia 的 Store(状态管理) Wevu 不改变小程序“数据驱动 + 模板渲染”的基本模型:你仍然写 WXML/WXSS(或配合 Weapp-vite 用 Vue SFC 编写模板/样式/配置),但业务逻辑可以用熟悉的 Composition API 组织起来。 ## 2. Wevu 在整套体系里的位置 如果你同时使用 `weapp-vite` 的 Vue SFC: * **Weapp-vite(编译期)**:把 `.vue` 编译成 WXML/WXSS/JS/JSON,并做模板语法转换。 * **Wevu(运行期)**:负责响应式、生命周期 hooks、快照 diff 与最小化 `setData`。 因此遇到问题时可以快速分层定位: * “模板/指令/usingComponents/v-model 怎么编译?” → 先看 `/wevu/vue-sfc` * “状态为什么不更新 / hooks 为什么不触发?” → 先看 `/wevu/runtime` 与 `/wevu/compatibility` * “项目应该先看哪一层文档?” → 先看 `/guide/` 和 `/config/`;那里解决的是编译与工程问题,不是运行时问题。 如果你是在 `weapp-vite` 项目里让 AI 协助修改 Wevu 代码,建议再加一个前置顺序: 1. 先读项目根目录 `AGENTS.md` 2. 再读 `node_modules/weapp-vite/dist/docs/wevu-authoring.md` 3. 最后才进入具体的 `pages/components/stores` 这样 AI 更容易先遵守小程序运行时约束,而不是直接套用 Vue Web 的默认写法。 ## 3. Wevu 不是什么 * 它不是浏览器 DOM 运行时,也不是把 Vue 3 完整搬进小程序。 * 它不依赖 Virtual DOM,而是围绕小程序的 `setData` 模型做响应式与快照 diff。 * 它也不替代 `weapp-vite` 的编译能力。SFC、WXML、JSON、WXSS 的转换仍然由编译侧负责。 ## 4. 诞生的小故事 这段背景不影响你上手,但能帮助你理解 Wevu 为什么会长成现在这样: * 最初想叫 `wevue`,但 npm 包名已被占用,后来才收敛成现在的 `wevu`。 * 在给 `weapp-vite` 补齐 Vue SFC 支持时,调研过社区现有方案,但无论编译链还是运行时语义,都很难直接贴合小程序场景。 * 最后选择围绕小程序本身的运行模型重新组织这套能力:借鉴 Vue 3 的响应式心智,但不照搬浏览器渲染栈,而是把重点放在 `setData`、页面生命周期和小程序平台约束上。 如果你把 Wevu 理解成“面向小程序约束重新取舍过的一套 Vue 风格运行时”,通常会比把它理解成“Vue 3 的缩小版”更准确。 ## 5. 你会用到的能力 * **响应式与调度**:与 Vue 3 相同心智的 `ref` / `reactive` / `computed` / `watch` / `watchEffect`,更新通过微任务批量调度(`nextTick`)。 * **页面/组件注册**:`defineComponent()` 统一通过小程序 `Component()` 注册;`createApp()` 可在存在全局 `App()` 时自动注册应用;`createWevuComponent()` 供 Weapp-vite 编译产物调用。 * **最小化 setData**:运行时把 state + computed 转为 plain snapshot,diff 后只把变化路径传给 `setData`。 * **双向绑定辅助**:`bindModel(path)` 生成适配小程序事件的数据/事件绑定对象。 * **Store(状态管理)**:`defineStore` / `storeToRefs` / `createStore`(可选插件入口)。 :::tip 导入约定 运行时基础 API 默认从 `wevu` 主入口导入;高阶导航从 `wevu/router` 导入;`wevu/compiler` 仅供 Weapp-vite 等编译侧工具使用(非稳定业务 API)。 ::: ## 6. 其他子路径导出 除了 `wevu` 主入口外,当前还提供几组按能力拆分的子路径导出: * [wevu/api](/wevu/api-package):透传 `@wevu/api`,用于统一多端小程序 API 调用 * [wevu/fetch](/wevu/fetch):基于 `wpi.request` 的 Fetch 风格接口 * [wevu/router](/wevu/router):更接近 Vue Router 心智的路由入口 * [wevu/jsx-runtime](/wevu/jsx-runtime):给 TSX / JSX 类型系统使用的入口 ## 7. 开发产物与源码调试 `wevu` 默认入口指向压缩后的生产产物,用来降低小程序包体积。发布包里也会同时包含一份未压缩并带 sourcemap 的开发产物: * 支持 `development` export condition 的构建器会在开发模式下优先解析到 `dist/dev/*`。 * 需要手动切换时,可以把 `wevu` alias 到 `wevu/dev`,也可以把 `wevu/router` alias 到 `wevu/dev/router` 等同名开发入口。 * 生产构建应继续使用默认入口,避免 `dist/dev/*` 进入正式小程序包。 例如只在本地排查 Wevu 运行时源码时,可以临时改成: ```ts import { defineConfig } from 'vite' export default defineConfig({ resolve: { alias: { 'wevu/router': 'wevu/dev/router', 'wevu/store': 'wevu/dev/store', 'wevu': 'wevu/dev', }, }, }) ``` 排查结束后移除 alias,让正式构建回到默认压缩产物。 ## 8. 已弃用 API 提示 当前源码里已明确标记为弃用、但仍保留导出的公开 API 主要是: * `provideGlobal()` / `injectGlobal()` 它们仅为兼容旧代码保留。新代码请优先使用: * `provide()` / `inject()`:局部依赖注入 * `defineStore()` / `createStore()`:稳定全局共享状态 > **注意**:`useRouter()` 不属于 `wevu` 根入口;如果你要使用高阶导航,请从 [`wevu/router`](/wevu/router) 导入。 ## 9. 编译侧桥接(wevu/compiler) `wevu/compiler` 用来承载 Wevu 与编译工具之间的共享常量,避免在多个项目里重复写字符串: * `WE_VU_MODULE_ID`:运行时入口模块名(`wevu`)。 * `WE_VU_RUNTIME_APIS`:运行时 API 名称集合(如 `createApp` / `defineComponent` / `createWevuComponent`)。 * `WE_VU_PAGE_HOOK_TO_FEATURE`:页面 hook 与 features 的映射表。 这些导出面向编译工具(例如 Weapp-vite),应用代码不要依赖它们作为稳定 API。 ## 10. 推荐学习顺序(按“最短上手 → 深入理解”) 1. [快速上手](/wevu/quick-start):先跑通一个页面/组件(含 store) 2. [运行时与生命周期](/wevu/runtime):理解 `setup(props, ctx)`、生命周期 hooks、`bindModel`、watch 策略 3. [defineComponent(组件)](/wevu/component):掌握组件字段透传、`properties/props`、`emit/expose` 等细节 4. [Store](/wevu/store):落地状态管理(订阅、补丁、插件) 5. [兼容性与注意事项](/wevu/compatibility):了解限制与边界(尤其是 provide/inject、页面事件按需派发) 接下来可以按顺序阅读: * [快速上手](/wevu/quick-start) * [运行时与生命周期](/wevu/runtime) * [defineComponent(组件)](/wevu/component) * [Store](/wevu/store) * [API 参考总览](/wevu/api/) * [wevu/api](/wevu/api-package) * [wevu/fetch](/wevu/fetch) * [wevu/router](/wevu/router) * [wevu/jsx-runtime](/wevu/jsx-runtime) * [兼容性与注意事项](/wevu/compatibility) * [Vue 3 兼容性说明(完整)](/wevu/vue3-compat) * [Wevu vs Vue 3(核心差异)](/wevu/vue3-vs-wevu) ## 10. 速查表 | 主题 | 结论 | | --------------- | -------------------------------------------- | | `wevu` 是什么 | 小程序运行时层 | | `wevu` 不是什么 | 浏览器 DOM Runtime / 完整 Vue Web Runtime | | API 从哪导入 | 基础 API 从 `wevu`,高阶路由从 `wevu/router` | | 当前已弃用 API | `provideGlobal()` / `injectGlobal()` | ## 11. 扩展阅读 * [为什么没有使用 @vue/runtime-core 的 createRenderer 来实现](/wevu/why-not-runtime-core-create-renderer) * [Wevu 中的 setData 什么时候触发?](/wevu/when-setdata-triggers) ## 12. 参考资源 | 主题 | 推荐入口 | | ---------- | ----------------------------------------- | | API 首页 | [Wevu API](/wevu/api/) | | 高阶导航 | [wevu/router](/wevu/router) | | Vue SFC | [wevu/vue-sfc](/wevu/vue-sfc/) | | 兼容性说明 | [wevu/compatibility](/wevu/compatibility) | --- --- url: /wevu/quick-start.md description: >- 这一页只做一件事:帮你最快把 Wevu 跑起来。按“装包 -> 写一个页面/组件 ->(可选)接入 Store”走一遍即可。示例以 Weapp-vite + Vue SFC 为主;如果你不使用 SFC,也可以直接参考运行时 API 的部分。 --- # 快速上手 Wevu 这一页只做一件事:帮你最快把 Wevu 跑起来。按“装包 -> 写一个页面/组件 ->(可选)接入 Store”走一遍即可。示例以 Weapp-vite + Vue SFC 为主;如果你不使用 SFC,也可以直接参考运行时 API 的部分。 ## 1. 安装 ::: code-group ```sh [pnpm] pnpm add -D wevu ``` ```sh [yarn] yarn add -D wevu ``` ```sh [npm] npm i -D wevu ``` ```sh [bun] bun add -D wevu ``` ::: :::warning 版本一致性 如果你同时安装 `weapp-vite` 与 `wevu`,请保持两者版本号一致(例如 `weapp-vite@x.y.z` 与 `wevu@x.y.z`),这样可以避免编译期与运行期组合不一致。 ::: :::warning 安装位置 在 `weapp-vite` 项目里,`wevu` 通常建议安装在 `devDependencies` 中,因此上面的示例使用了 `-D` / `--save-dev`。 这是当前 `weapp-vite + wevu` 模板与常见工程实践的推荐组合;如果你是在非 `weapp-vite` 场景单独消费 `wevu`,应按自己的发布方式决定依赖落位。 ::: :::tip 运行时 API 均从 `wevu` 主入口导入;`wevu/compiler` 仅供 Weapp-vite 等编译侧工具使用(非稳定用户 API)。 ::: ## 2. (可选)启用 Volar 插件 如果你正在使用 Weapp-vite + Vue SFC 开发小程序,可以在 `tsconfig.app.json`(或项目主 `tsconfig.json`)里启用 Weapp-vite 的 Volar 插件,以获得模板侧的更好类型推导: ```json { "vueCompilerOptions": { "plugins": ["weapp-vite/volar"], "lib": "wevu" } } ``` :::warning 必须设置 lib `"vueCompilerOptions.lib": "wevu"` 用于告诉 Volar 从 Wevu 的类型声明里解析 `defineProps/withDefaults/defineEmits` 等脚本宏。若不设置,Volar 会按 Vue 默认宏处理,最终只剩 `any` 类型提示。 ::: :::info Wevu@1.2.0 起的 vue 依赖说明 从 Wevu@1.2.0 开始,`wevu` 会依赖 `vue`,但只用于获取其 `dts` 类型定义,不会引入任何 Vue 运行时代码。这样做是为了让 Volar 在 ` ``` ## 4. 引入自定义组件(小程序规则) 推荐使用 Script Setup JSON 宏声明 `usingComponents`;脚本里无需(也不推荐)`import` 子组件: ```vue ``` 模板中直接 `` 使用即可。 ## 5. 组件 props / emit(运行时约定) `setup` 与 Vue 3 对齐,仅支持 `setup(props, ctx)`。 若不需要 `props`,可写 `setup(_, ctx)`;若不需要 `ctx`,可只写 `setup(props)`。 `ctx.props` 来自小程序 `properties`;`ctx.emit(event, ...args)` 会调用小程序 `triggerEvent` 触发自定义事件。 ```vue ``` ```vue ``` ## 6. 接入 Store(可选但常用) ```ts // stores/counter.ts import { computed, defineStore, ref } from 'wevu' export const useCounter = defineStore('counter', () => { const count = ref(0) const doubled = computed(() => count.value * 2) const inc = () => count.value++ return { count, doubled, inc } }) ``` ```ts // pages/counter/index.ts import { defineComponent, storeToRefs } from 'wevu' import { useCounter } from '@/stores/counter' export default defineComponent({ setup() { const counter = useCounter() const { count, doubled } = storeToRefs(counter) return { count, doubled, inc: counter.inc } }, }) ``` 推荐继续阅读: * [`defineComponent` / 生命周期 / `bindModel`](/wevu/runtime) * [Store API](/wevu/store) --- --- url: /wevu/runtime.md description: 介绍 Wevu 运行时的桥接机制、生命周期映射与 setData 更新策略,并给出定位“未触发/未更新”问题的实操方法。 --- # 运行时与生命周期 本页主要说明 Wevu 运行时做了什么、哪些生命周期可用,以及常见的“为什么没触发/为什么没更新”的定位思路。 Wevu 运行时的核心职责是: * 把 `data/computed/methods/setup/watch` 等选项桥接到小程序 `Component() / App()`; * 将 state + computed 生成快照(plain object),diff 后只下发变化路径到 `setData()`; * 提供生命周期钩子与 `bindModel()` 等小程序友好能力。 :::tip 导入约定 所有 API 都从 `wevu` 主入口导入。 ::: ## 更新链路:为什么 Wevu 不需要 Virtual DOM Wevu 的渲染心智模型更接近“小程序原生”: 1. 你在 `setup()` 中创建响应式 state(`ref/reactive`)与 `computed` 2. 运行时把 **state + computed** 转成 **plain snapshot**(可序列化的普通对象) 3. 每次调度时对比“上一次 snapshot vs 新 snapshot” 4. 只把变化路径组装成 `setData({ 'a.b.c': next })` 的形式下发 这也是为什么你会看到一些“小程序语义”对行为有硬性影响: * 小程序 `created` 阶段不能调用 `setData`:Wevu 会缓冲由响应式更新产生的 `setData`,并在首次安全时机(组件 `attached` / 页面 `onLoad`)统一 flush(细节见 `/wevu/component`)。 * 小程序模板只能消费 JSON 友好的数据:`undefined` 会被归一化(通常变成 `null`),不要依赖“模板里区分 undefined 与缺失字段”的行为(见 `/wevu/compatibility`)。 ## defineComponent:注册页面/组件 `defineComponent(options)` 会直接调用全局 `Component()` 完成注册(页面和组件都走 `Component()`,这是 Wevu 的统一模型)。 ```ts import { defineComponent, onShow, ref } from 'wevu' export default defineComponent({ // 原生小程序字段保持原样(properties、options、lifetimes、pageLifetimes...) properties: { initial: { type: Number, value: 0 } }, setup(props) { const count = ref(props.initial ?? 0) onShow(() => console.log('show')) return { count, inc: () => count.value++ } }, }) ``` `defineComponent` 的 `data` 必须是函数(与 Vue 3 一致,和小程序原生对象写法不同)。原生小程序会在实例化时拷贝 `data` 对象以隔离实例;Wevu 需要为每个实例创建独立的响应式 state/代理与快照 diff,因此要求返回新对象。 :::warning 运行环境 `defineComponent()` 依赖小程序运行时提供的全局 `Component()`;在 Node/Vitest 等环境运行时请自行 stub。 ::: ### props / properties Wevu 同时支持两种 props 定义方式: * 小程序原生 `properties`:完全按小程序规范书写,`setup(props, ctx)` 通过 `props`/`ctx.props` 读取。 * Vue 风格 `props`:会被转换为小程序 `properties`(支持 `type`、`optionalTypes`、`observer` 与 `default` / `value`)。 两者的处理路径可以简单理解为: * `props`:先由 Wevu 归一化,再生成最终注册给原生 `Component()` 的 `properties` * `properties`:作为原生字段直接保留参与注册,不再按 Vue 风格 `props` 规则重新转换 如果同时声明了 `props` 和 `properties`,当前实现会优先保留显式传入的 `properties`。 SFC 中静态可识别的组件函数 prop 绑定默认会进入 `setData` 快照,例如声明 `props: { callback: Function }` 后通过 `:callback="callback"` 传递。动态绑定或手写组件无法被编译器精确标记时,可使用组件级 `allowFunctionProps: true` 全量放行;需要恢复严格过滤时,可设置 `allowFunctionProps: false`。 如果你使用 Weapp-vite 的 SFC 编译产物,通常会走 `createWevuComponent(options)`(见下节),并直接携带小程序 `properties`。 #### defineProps 泛型到 properties 的映射 ` ``` > 关键点:必须是**顶层语句**(不要放进 `setup()`/hook 里),这样才能早于 `createApp()` 执行。 > \[!TIP] > 使用 Weapp-vite 时,可以通过 `weapp.wevu.defaults` 在编译期自动注入 `setWevuDefaults()`(见 `/config/wevu#weapp-wevu-defaults`)。 ## setup:签名与上下文 `setup` 与 Vue 3 对齐,仅支持 `setup(props, ctx)` 签名。 若不需要 `props`,可使用 `setup(_, ctx)`;若不需要 `ctx`,可只写 `setup(props)`。 其中 `props` / `ctx.props` 来自小程序实例的 `properties`(页面通常为空对象)。 `ctx`(关键字段): * `ctx.runtime`:运行时实例(暴露 `bindModel` / `watch` / `snapshot` / `unmount` 等) * `ctx.state`:响应式 state(包含 `data()` 与 `setup()` 返回的非函数值) * `ctx.proxy`:公开实例代理(也是 `methods/computed` 的 `this`) * `ctx.emit(event, ...args)`:触发自定义事件(内部调用 `triggerEvent`) * `ctx.bindModel(path, options?)`:创建模型绑定(见下文) * `ctx.watch(source, cb, options?)`:等价于 `ctx.runtime.watch` * `ctx.instance`:小程序原生实例(高级/调试用途) :::warning 同步调用约束 生命周期钩子必须在 `setup()` **同步执行阶段**调用,否则会抛错。 ::: ## 生命周期钩子 ### 通用钩子(页面/组件) * `onShow` / `onHide` / `onReady` / `onUnload` ### 组件钩子(来自 lifetimes/pageLifetimes) * `onMoved`(`lifetimes.moved`) * `onError`(`lifetimes.error`) * `onResize`(`pageLifetimes.resize`,组件场景) ### 页面钩子(Page 事件) * `onPullDownRefresh` * `onReachBottom` * `onPageScroll` * `onRouteDone` * `onTabItemTap` * `onResize`(页面场景) * `onShareAppMessage` * `onShareTimeline` * `onAddToFavorites` 注意:分享/朋友圈/收藏是否触发由微信官方机制决定(例如右上角菜单/`open-type="share"`;朋友圈通常需配合 `wx.showShareMenu()` 开启菜单项)。 此外,小程序会对部分页面事件做“按需派发”:只有定义了对应页面方法,事件才会从渲染层派发到逻辑层;Wevu 也仅在你定义了这些页面方法时才桥接 `setup()` 中注册的同名 hooks。 如果你使用 Weapp-vite 构建,默认会在编译阶段根据你是否调用 `onPageScroll/onShareAppMessage/...` 自动补齐对应 `features.enableOnXxx = true`,以降低手动配置成本。 ### 返回值型钩子(单实例) 以下钩子按“单实例”注册(后注册覆盖先注册),并允许返回值: * `onSaveExitState` * `onShareAppMessage` * `onShareTimeline` * `onAddToFavorites` ### Vue 风格别名(语义对齐为主) * `onMounted` → `onReady` * `onUnmounted` → `onUnload` * `onActivated` → `onShow` * `onDeactivated` → `onHide` * `onErrorCaptured` → `onError` * `onBeforeMount` / `onBeforeUnmount`:在 `setup()` 同步阶段立即执行(小程序无精确对应时机) * `onBeforeUpdate` / `onUpdated`:在每次 `setData` 前/后触发(小程序没有“更新生命周期”,Wevu 通过在更新链路里补齐语义) ## bindModel:模型绑定 `ctx.bindModel(path, options?)` 返回一个 `ModelBinding`: * `binding.value` / `binding.update(value)`:读取或更新目标路径 * `binding.model(modelOptions?)`:生成事件 handler / value 字段(默认 `value` + `onInput`) ```ts // setup(props, ctx) 内 const { model } = ctx.bindModel('form.price', { event: 'blur', formatter: v => Number(v) || 0, }) const onPriceBlur = model().onBlur ``` ```vue ``` 在 ` ``` ### 写法 2:` ``` 补充说明: * 如果 `behaviors` 里是内建 behavior(例如 `wx://component-export`),直接写字符串数组即可。 * 如果 `behaviors` 来自原生 `Behavior()` 返回值,`defineOptions` 会在编译阶段保留该表达式并继续编译,不会因为构建环境没有全局 `Behavior` 而中断。 * `definitionFilter`、`observers`、`lifetimes` 的执行顺序与覆盖关系,仍以微信官方 `behaviors` 规则为准。 ## lifetimes / pageLifetimes 对应的 hooks > 说明:Wevu 的 `onXXX()` 必须在 `setup()` **同步阶段**注册;由于 Wevu 会在 `lifetimes.created` 内执行 `setup()`,因此你可以在 `setup()` 里注册所有 Wevu hooks(包括 `onBeforeMount` 等)。 ### lifetimes(组件生命周期) | 小程序字段 | 回调名 | 对应 Wevu hook | 说明 | | -------------------- | ---------- | -------------------------- | ------------------------------------------------------------------------------ | | `lifetimes.created` | `created` | `setup()` | Wevu 在此阶段 mount 并执行 `setup()`(`setData` 会被延迟到 `attached/onLoad`) | | `lifetimes.attached` | `attached` | - | 组件进入节点树;Wevu 会在此阶段 flush `created` 阶段缓冲的 `setData` | | `lifetimes.ready` | `ready` | `onReady` / `onMounted` | 组件就绪(内部做了重复触发去重) | | `lifetimes.moved` | `moved` | `onMoved` | 组件移动(例如在节点树中被移动) | | `lifetimes.detached` | `detached` | `onUnload` / `onUnmounted` | detached 时 teardown,并触发 `onUnload` | | `lifetimes.error` | `error` | `onError` | 组件错误(参数透传原生回调) | ### pageLifetimes(页面对组件的影响) | 小程序字段 | 回调名 | 对应 Wevu hook | 说明 | | ------------------------- | ----------- | -------------------------- | ---------------------------------------- | | `pageLifetimes.show` | `show` | `onShow` / `onActivated` | 所在页面显示 | | `pageLifetimes.hide` | `hide` | `onHide` / `onDeactivated` | 所在页面隐藏 | | `pageLifetimes.resize` | `resize` | `onResize` | 所在页面尺寸变化(参数透传原生回调) | | `pageLifetimes.routeDone` | `routeDone` | `onRouteDone` | 所在页面路由动画完成(基础库 `2.31.2+`) | --- --- url: /wevu/store.md description: Store(状态管理),聚焦 Wevu / store 相关场景,覆盖 Weapp-vite 与 Wevu 的能力、配置和实践要点。 --- # Store(状态管理) Wevu 内置了类 Pinia 的 Store: * 用 `defineStore()` 定义 Store * 用 `useXxx()` 获取**单例**实例 * 用 `storeToRefs()` 解构 state/getter,避免丢失响应式 :::tip 导入约定 运行时 API 均从 `wevu` 主入口导入;`wevu/compiler` 仅供 Weapp-vite 等编译侧工具使用(非稳定用户 API)。 ::: ## 导入与核心 API * `defineStore(id, setup | options)`:定义 Store,返回 `useXxx()` 获取单例实例。 * `storeToRefs(store)`:将 state/getter 包装为 `ref`,函数保持原样,解构不丢失响应式。 * `createStore()`:可选的插件入口;只有需要插件时才调用(见下文)。 ## Setup Store 示例(推荐) ```ts // stores/counter.ts import { computed, defineStore, ref } from 'wevu' export const useCounter = defineStore('counter', () => { const count = ref(0) const doubled = computed(() => count.value * 2) const inc = () => count.value++ return { count, doubled, inc } }) ``` 特点: * 你可以返回任意字段;函数会被当作 action(除 `$` 开头的保留字段)。 * `$patch/$subscribe/$onAction` 等基础 API 会自动合并进返回对象。 ## Options Store 示例 ```ts // stores/user.ts import { defineStore } from 'wevu' export const useUser = defineStore('user', { state: () => ({ name: '', age: 0 }), getters: { label(state) { return `${state.name}:${state.age}` }, canVote() { return this.age >= 18 }, }, actions: { grow() { this.age += 1 }, }, }) ``` ## 在页面/组件中使用 ```ts // pages/counter/index.ts import { defineComponent, storeToRefs } from 'wevu' import { useCounter } from '@/stores/counter' export default defineComponent({ setup() { const counter = useCounter() const { count, doubled } = storeToRefs(counter) counter.$subscribe((mutation) => { console.log('[counter]', mutation.type, mutation.storeId) }) return { count, doubled, inc: counter.inc } }, }) ``` 要点: * Store 是单例,在页面/组件 `setup` 里调用 `useXxx()` 即可复用。 * 解构 state/getter 请使用 `storeToRefs`,actions 可以直接解构。 ## 插件与订阅 * 默认无需插件即可使用;只有当你需要统一扩展所有 Store 时再调用 `createStore()` 并注册插件。 * 插件需在第一次 `useXxx()` 之前注册。`createStore()` 会记录为全局单例,之后创建的 Store 会自动应用插件(插件参数为 `{ store }`)。 ```ts import { createStore, defineStore } from 'wevu' const manager = createStore() manager.use(({ store }) => { store.$onAction((ctx) => { ctx.after(res => console.log('[after]', ctx.name, res)) ctx.onError(err => console.error('[error]', ctx.name, err)) }) store.$subscribe((mutation, state) => { console.log(`[${store.$id}]`, mutation.type, state) }) }) // 之后定义的任何 store 都会自动套用上述插件 export const useCart = defineStore('cart', { state: () => ({ items: [] as Array<{ id: string, count: number }> }), actions: { add(id: string, count = 1) { const found = this.items.find(i => i.id === id) if (found) { found.count += count } else { this.items.push({ id, count }) } }, }, }) ``` ## 持久化(storage)与初始化顺序 Store 是单例,适合承载登录态、用户偏好、缓存索引等“跨页面共享”的状态。常见诉求是把部分 state 持久化到本地存储(`wx.setStorageSync` / `wx.setStorage`)。 推荐做法: * 只持久化必要字段(避免把大对象/列表直接塞进 storage) * 统一在插件中处理(避免每个 store 手写重复逻辑) * 注意初始化顺序:在第一次 `useXxx()` 之前完成“读取 → 回填” 示例(简化版,仅展示形态): ```ts import { createStore, defineStore } from 'wevu' const manager = createStore() manager.use(({ store }) => { const key = `wevu:${store.$id}` try { const raw = wx.getStorageSync(key) if (raw) { store.$patch(JSON.parse(raw)) } } catch {} store.$subscribe((_mutation, state) => { try { wx.setStorageSync(key, JSON.stringify(state)) } catch {} }) }) export const usePrefs = defineStore('prefs', { state: () => ({ theme: 'light' as 'light' | 'dark' }), }) ``` :::warning 注意 上面示例直接读取/写入 `wx` 存储,必须在小程序运行时执行;如果你在 Node/Vitest 环境跑测试,需要 stub `wx` 或把持久化逻辑封装到可替换的 adapter。 ::: ## Store 实例 API * `$id`:当前 Store 的唯一标识。 * `$state`(Options Store):读取/替换整个 state;赋值会做浅合并并触发 `patch object`。 * `$patch(patch | fn)`:批量修改;Setup/Options Store 均可用,支持对象合并或回调方式。 * `$reset()`(Options Store):将 state 重置为初始值。 * `$subscribe((mutation, state) => void)`:订阅变更,返回取消订阅函数;`mutation.type` 为 `patch object` 或 `patch function`。 * `$onAction(({ name, store, args, after, onError }) => void)`:订阅 action 调用,支持成功/失败回调。 * `storeToRefs(store)`:将所有非函数字段转换为可写 `ref`,函数保持原样,避免解构丢失响应式。 ## TypeScript 与最佳实践 * Setup Store 会自动推导返回对象的类型;Options Store 可通过泛型精确声明 `state/getters/actions`。 * Store 文件按功能域组织(例如 `stores/user.ts`、`stores/cart.ts`),Store ID 使用小写单数。 * 避免直接解构 state:使用 `storeToRefs`;actions 可以直接解构。 * SSR/HMR/Devtools:Wevu 面向小程序运行环境,暂未提供这些 Web 专属能力。 ## 常见问题 * 所有运行时 API 均从 `wevu` 主入口导入。 * 不需要为了使用 store 先调用 `createStore()`;仅在使用插件时才需要。 --- --- url: /wevu/vue-sfc.md description: >- Weapp-vite 内置了 Vue SFC 编译链路,配合 Wevu 运行时即可用 Vue 风格开发小程序页面/组件,同时保持小程序能力(页面特性、分享、性能优化)。 --- # 在 Weapp-vite 中使用 Vue SFC Weapp-vite 内置了 Vue SFC 编译链路,配合 `wevu` 运行时即可用 Vue 风格开发小程序页面/组件,同时保持小程序能力(页面特性、分享、性能优化)。 > 适用版本:Vue SFC 仅在 `weapp-vite@6.x` 及以上可用,请先升级到 6 大版本。 > > 若项目同时使用 `weapp-vite` 与 `wevu`,请保持两者版本号一致(例如 `weapp-vite@x.y.z` 与 `wevu@x.y.z`)。 ## 快速开始 * 需要安装 `wevu`: ::: code-group ```sh [pnpm] pnpm add -D wevu ``` ```sh [yarn] yarn add -D wevu ``` ```sh [npm] npm i -D wevu ``` ```sh [bun] bun add -D wevu ``` ::: * 官方模板已默认带上,手动集成时请先装依赖再继续。 ## 心智模型 Vue SFC 在小程序里建议拆成两段: * **编译期(Weapp-vite)**:负责把 `.vue` 拆解/编译为小程序产物(WXML/WXSS/JS/JSON),并做模板语法(如 `v-if/v-for/v-model`)到 WXML 的转换。 * **运行期(Wevu)**:负责响应式、生命周期 hooks、快照 diff 与最小化 `setData`,让你用 Vue 3 风格的 Composition API 写业务逻辑。 ```mermaid flowchart LR A[Vue SFC
.vue] --> B[编译期
weapp-vite] B --> C[小程序产物
WXML / WXSS / JS / JSON] C --> D[运行期
wevu] D --> E[小程序逻辑层
响应式 / hooks / diff + setData] E --> F[渲染层更新
UI] ``` ## 章节导航 * [基础与组成](/wevu/vue-sfc/basics):SFC 各块作用、宏/指令的编译时与运行时、页面与组件区分等 * [配置与宏](/wevu/vue-sfc/config):`usingComponents` 规则、`` 与 Script Setup JSON 宏 * [模板与指令](/wevu/vue-sfc/template):页面事件触发机制、`v-model` 支持范围与限制 * [class/style 绑定](/wevu/vue-sfc/class-style):对齐 Vue 3 的 class/style 语法与运行时模式 * [示例](/wevu/vue-sfc/examples):页面示例与组件 `v-model` 示例 * [调试与排错](/wevu/vue-sfc/troubleshoot):常见问题定位与建议 * [Vue 3 写法对比](/wevu/vue-sfc/vue3-vs-weapp-sfc):与 Vue 3 SFC 写法的相同点/不同点 迁移相关内容已独立成章节,请阅读:[从原生小程序迁移到 Weapp-vite / Wevu(详细指南)](/wevu/migration/from-native-to-vue-sfc)。如果你只想先保留原生页面并接入 `weapp-vite` 工具链,也从这篇开始判断路线。 --- --- url: /wevu/vue-sfc/basics.md description: Vue SFC:基础与组成,聚焦 Wevu / vue-sfc 相关场景,覆盖 Weapp-vite 与 Wevu 的能力、配置和实践要点。 --- # Vue SFC:基础与组成 ## Vue SFC = 编译期(Weapp-vite)+ 运行期(Wevu) 在小程序里写 Vue SFC,建议把心智模型拆成两段: * **编译期(Weapp-vite)**:负责把 `.vue` 拆解/编译为小程序产物(WXML/WXSS/JS/JSON),并做模板语法(如 `v-if/v-for/v-model`)到 WXML 的转换。 * **运行期(Wevu)**:负责响应式、生命周期 hooks、快照 diff 与最小化 `setData`,让你用 Vue 3 风格的 Composition API 写业务逻辑。 ```mermaid flowchart TB subgraph 编译期[编译期(weapp-vite)] SFC[.vue] SFC --> T[template 编译
v-if/v-for/v-model → WXML] SFC --> S[script 编译
SFC compiler + 转换 → JS] SFC --> C[style 编译
lang/scoped/modules → WXSS] SFC --> J[json 合并
+ 宏 + auto usingComponents → JSON] end subgraph 运行期[运行期(wevu)] R[响应式 / hooks / diff] R --> SD[最小化 setData] end T --> OUT[产物:WXML/WXSS/JS/JSON] S --> OUT C --> OUT J --> OUT OUT --> R ``` 因此: * “模板能不能写”看编译器规则(本章主要讲这个)。 * “状态为什么不更新 / hooks 为什么不触发”通常是运行时使用方式问题(请对照 `/wevu/runtime` 与 `/wevu/compatibility`)。 ## SFC 组成速查 在 Weapp-vite 里,一个 `.vue` 最终会被拆成小程序的 `wxml/wxss/js/json` 四件套。各个块大致对应如下: | SFC 块 | 你写什么 | 主要发生阶段 | 对应小程序产物 | | ----------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------ | --------------------------------------------------------- | | `