UNPKG

weapp-vite

Version:

weapp-vite 一个现代化的小程序打包工具

532 lines (404 loc) 20.3 kB
# Weapp Config ## 入口位置 `weapp-vite` 的小程序配置通常放在: ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { srcRoot: 'src', }, }) ``` ## 高频配置项 ### `srcRoot` 源码根目录。排查输出缺页、找不到入口、自动路由异常时先确认它。 ### `autoRoutes` 适合希望用约定生成页面路由的项目。启用后要保持 pages 目录与输出约定稳定。 ### `buildScope` 用于只构建主包和指定分包。常用在大项目里只调试某几个业务分包: ```bash wv dev --scope main,packages/order wv build --scope packages/order ``` 也可以写在配置里: ```ts export default defineConfig({ weapp: { buildScope: { includeMainPackage: true, include: ['packages/order'], }, }, }) ``` `main` 表示主包,`packages/order` 匹配 `app.json.subPackages[].root`。启用后,产物 `app.json.subPackages` 只保留参与 scope 的分包,`preloadRule``tabBar``entryPagePath`、自动路由和 typed router 也会按同一注册图裁剪。`preloadRule.packages` 支持分包 `root``name` 和主包标记 `__APP__``tabBar.list` 不足微信要求的 2 项时会删除整个 `tabBar`。发布前建议再跑不带 scope 的完整构建。 ### `chunks.preserveModules``srcRoot` 相对路径匹配源码模块,并为命中的模块保留独立输出文件和目录边界: ```ts export default defineConfig({ weapp: { chunks: { preserveModules: ['utils/**', 'services/**'], }, }, }) ``` 例如 `src/utils/request.ts` 会输出到 `utils/request.js`,引用方会保留对该文件的引用;barrel 模块的静态依赖也会保持独立。该配置用于调试定位和产物审计,不保证减少总包体积或提升冷启动;构建会自动选择兼容的 entry signature。 例如一个已经手动分包、包含大量 `utils``services` 模块的原生项目,可以这样配置: ```ts import { defineConfig } from 'weapp-vite' export default defineConfig({ weapp: { srcRoot: 'src', chunks: { preserveModules: [ 'utils/**', 'services/**', ], }, }, }) ``` 规则相对于 `srcRoot`,因此不要添加 `src/` 前缀。构建后,已进入依赖图的 `src/utils/request.ts``src/services/user.ts` 会分别输出为 `utils/request.js``services/user.js`;页面保留对这些文件的引用。CJSESM 均支持该配置。 `preserveModules` 保留的是构建后的文件和目录边界。源码仍会经过模块解析、TypeScript 转换并由构建器写入,不会被原样复制;未被入口引用的文件也不会仅因匹配规则而输出。如果只需要改变共享模块的输出位置,不要求单次引用模块保持独立,请使用 `chunks.sharedMode: 'path'`。 需要保留 `srcRoot` 下所有已引用源码模块时,使用 `**````ts export default defineConfig({ weapp: { srcRoot: 'src', chunks: { preserveModules: ['**'], }, }, }) ``` `**` 同时匹配 `src/helper.ts` 这样的顶层模块和 `src/utils/request.ts` 这样的嵌套模块。不要使用 `*/**` 代替,因为它不会匹配 `srcRoot` 顶层文件。该配置已通过 CJSESM 构建回归,顶层模块和嵌套模块都会按相对路径生成独立文件,引用方保留对应文件引用。 页面、组件等逻辑入口仍走各自的入口构建流程,`srcRoot` 外部依赖和未引用文件不会被强制输出。全量保留会增加文件数量,建议结合 `wv analyze` 检查实际包体积;只需要固定目录时优先使用更具体的 glob。 ### 分包异步模块 跨分包 JS 使用微信官方 callback 或 Promise API```ts require('../../packages/order/modules/price', onLoaded, onError) const moduleExport = await require.async('../../packages/order/modules/price') ``` `weapp-vite` 会把 callback 写法规范化为 `void require.async(path).then(onLoaded, onError)`,并确保静态路径目标作为异步 chunk 输出,避免被当成同步 CommonJS 依赖提升到主包。源码路径带 `.ts` 等扩展名时,调用参数会同步改为实际 `.js` 产物路径。路径必须是静态相对字面量,目标 root 也必须存在于最终 `app.json.subPackages`;使用自动路由的自定义 root 时,同时声明 `weapp.subPackages.<root>`。 希望保留标准 `import()` 写法时,可以选择微信原生分包模式: ```ts export default defineConfig({ weapp: { chunks: { dynamicImports: 'native', }, }, }) const moduleExport = await import('../../packages/order/modules/price.ts') ``` `native` 只转换微信构建中跨入已声明普通分包的静态相对导入,并将路径规范化为 `.js`。动态表达式、裸模块、同包导入、独立分包目标以及非微信构建继续保留 bundler 动态导入。默认的 `preserve` 不做转换;历史 `inline` 已废弃,当前会回退为 `preserve` 并输出一次警告。 跨包自定义组件不走这套 JS API。组件继续使用 `usingComponents``componentPlaceholder`,由微信基础库负责下载后的占位替换。 ### `autoImportComponents` 适合用目录扫描自动注册组件的项目。组件重名时要先解决命名冲突,不要让自动引入规则长期处于歧义状态。 ### `i18n` 微信项目可以通过 `weapp.i18n` 启用由 `@weapp-vite/i18n` 提供核心语义的 locale 编译与运行时切换: ```ts export default defineConfig({ weapp: { i18n: { defaultLocale: 'zh-CN', fallbackLocale: 'en-US', }, }, }) ``` 默认扫描 `**/i18n/*.json`Component Page、传统 Page、分包实例边界、原生无 Vite 用法和 v1 占位符限制见 `i18n.md`### `styles` 用于生成主包独立样式入口,并按规则向主包与普通分包的样式注入相对 `@import`。默认不会修改 `app.wxss`;对象配置的 `include` 显式匹配 `app.vue` 等应用入口时,可以向 `app.wxss` 注入: ```ts export default defineConfig({ weapp: { styles: [ { source: 'styles/theme.scss', include: ['pages/**', 'components/**', 'packages/*/**'], }, { source: 'styles/manual.less', inject: false, }, { source: 'styles/app-theme.scss', include: 'app.vue', }, ], }, }) ``` `inject: false` 只生成目标平台样式文件,适合由源码手动 `@import`。未显式配置 `include` 时仍默认排除 app,避免意外把共享样式提升为全局样式。独立分包不能依赖主包资源,不会收到 `weapp.styles` 的自动注入;需要在 `weapp.subPackages.<root>.styles` 中声明分包自己的入口。 ### `tailwindcss` 内置的 `weapp-tailwindcss` 集成支持显式配置和 Tailwind CSS v4 自动检测。显式配置优先级最高:设置为 `false` 会完全关闭(包括自动检测),设置为 `true` 或对象会按显式选项启用。未配置时,项目解析到 Tailwind CSS v4 且 CSS 模块实际包含 `@import "tailwindcss"`(也支持 `source(...)` 等合法参数)才会自动启用;Tailwind CSS v3、未安装或未引入该模块时不会生成 Tailwind CSS。 启用后,`weapp-vite` 使用 `weapp-tailwindcss@5.5.2``core` compiler 处理 WXSSWXMLJavaScript,通过 `compiler.generate()` 生成 Tailwind CSS,并将结果写入正常的样式产物。WXSS 最终化由 core 统一完成,Tailwind 构建阶段的 `@plugin``@source` 等指令不会泄漏到小程序产物: ```ts import { defineConfig } from 'weapp-vite/config' export default defineConfig({ weapp: { tailwindcss: { cssEntries: ['src/app.css'], rem2rpx: true, compiler: { maxRoots: 32, onRootEvicted(id) { console.log('Tailwind root evicted:', id) }, }, }, }, }) ``` 也可以直接写 `tailwindcss: true`,此时默认使用 `src/app.css` 作为入口。入口文件仍必须被项目实际引入,例如在 `app.vue` 中使用 `<style src="./app.css"></style>``cssEntries` 只声明 compiler 的入口集合,不能替代模块图导入。 `tailwindcss` 对象会透传 `weapp-tailwindcss/core` 支持的 options。`compiler.maxRoots` 用于限制长期 watch 中保留的 root 数量,`compiler.onRootEvicted` 会在 root 被淘汰时收到对应 id。HMR 会把真实变更文件交给 compiler,由 compiler 根据 `@source` glob 精确失效关联 root。 一次构建只应使用这一套内置集成,不要再额外注册 `WeappTailwindcss()` Vite 插件。`weapp-vite@6.24.0` 起,preflight 会移除所有 `weapp-tailwindcss:*` 外部插件并输出一次中文迁移警告;请删除对应的 import 和 `plugins` 注册代码。 ### `routeRules` 用于给页面路由追加规则,例如 layout、微信分包预下载等。它属于项目级编排,而不是组件内部语义。 ```ts export default defineConfig({ weapp: { routeRules: { 'pages/home/**': { preload: { packages: ['packages/order'], network: 'wifi', }, }, }, }, }) ``` 微信构建会把 `preload` 合成为 `app.json.preloadRule`;手写的同一路由规则优先,其他平台不会生成微信专属字段。多条 glob 命中时选择具体程度最高的一条。需要检查静态跨分包跳转时,运行 `wv analyze --preload`;它只输出建议,不修改源码,并按触发页所属包汇总实际分包体积与共享的 2 MB 额度。 ### `vue.template.htmlTagToWxml` 适合从 Web/Vue 模板迁移到小程序 `.vue` 的项目。开启后,会把常见 HTML 标签映射成小程序内置标签,例如 `div -> view``span -> text``img -> image``a -> navigator`,也包含 `br/hr` 这类容易在迁移时“消失”的标签。 ### `vue.template.htmlTagToWxmlTagClass` 默认开启。仅当 `htmlTagToWxml` 发生标签映射时,为输出节点追加原标签名 class,例如 `h3 -> <view class="h3">``br -> <view class="br" />`。 如果你的迁移策略是“先跑通,再用 CSS 逐步恢复默认外观”,这个开关很有价值;如果不希望产物里自动带这层语义 class,可以显式设为 `false`### `vue.template.formatWxml` 控制 `.vue` / JSX 编译生成的 WXML 是否格式化。默认 `auto`:开发态开启,生产构建关闭。显式设为 `true` 可始终输出带缩进和换行的 WXML,显式设为 `false` 可始终保持紧凑输出。 格式化只处理标签层级缩进,含文本内容的元素会保持单行,避免重排文本空白语义。 ### `vue.template.slotFallbackWrapper` 用于配置普通具名插槽 fallback 的真实 wrapper。微信平台默认使用内部 `virtualHost` 组件;需要回到旧版 `view` 行为时,可配置 `vue.template.slotFallbackWrapperStrategy: 'view'` 或显式 `slotFallbackWrapper: 'view'`。 当组件把自己的默认 `<slot />` 继续透传到子组件的具名插槽时,编译器不能生成 `<slot slot="header" />`,也不能稳定使用 `<block slot="header"><slot /></block>`。真实 WeChat DevTools 运行时中,`block` 路径会丢失转发内容。因此微信平台默认产物是: ```wxml <weapp-slot-wrapper slot="header"> <slot /> </weapp-slot-wrapper> ``` 全局配置示例: ```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' } }, { component: /^Van/, slot: ['title', 'label'], tag: 'view' }, ], }, }, }, }, }) ``` `component` 匹配使用处模板标签名,例如 `<IssueCard>` 对应 `IssueCard``<issue-card>` 对应 `issue-card`。如果要按子组件自己的名字匹配,让子组件写静态 `defineOptions({ name: 'HelloWorld' })`,然后使用 `componentName: 'HelloWorld'``componentName` 需要编译器能解析到被引用的 Vue SFC;原生小程序组件或第三方小程序组件继续用 `component`。 组件内也可以用静态属性覆盖。`slot-wrapper` 是当前组件所有普通具名插槽的默认 wrapper: ```vue <template> <IssueCard slot-wrapper="cover-view"> <template #header> <slot /> </template> <template #footer> <slot name="footer" /> </template> </IssueCard> </template> ``` 产物: ```wxml <IssueCard> <cover-view slot="header"> <slot /> </cover-view> <cover-view slot="footer"> <slot name="footer" /> </cover-view> </IssueCard> ``` `slot-wrapper-<slotName>` 覆盖指定具名插槽。单个 slot 的覆盖更推荐直接写在对应的 `<template #xxx>` 上: ```vue <template> <IssueCard slot-wrapper="cover-view"> <template #header> <slot /> </template> <template #footer slot-wrapper="view"> <slot name="footer" /> </template> </IssueCard> </template> ``` 产物: ```wxml <IssueCard> <cover-view slot="header"> <slot /> </cover-view> <view slot="footer"> <slot name="footer" /> </view> </IssueCard> ``` 也可以把单个 slot 的覆盖配置写在对应的 `<template #xxx>` 上。这个写法最靠近 slot 内容,也更适合单个 slot 的局部策略: ```vue <template> <IssueCard slot-wrapper="cover-view"> <template #header slot-wrapper="text" slot-wrapper-class="slot-header"> <slot /> </template> <template #footer> <slot name="footer" /> </template> </IssueCard> </template> ``` `<template #header>` 上的 `slot-wrapper` / `slot-wrapper-class` / `slot-wrapper-style` / `slot-single-root-no-wrapper` 是该 slot 的就近覆盖,优先级高于父组件标签上的默认值和 `slot-wrapper-header`。 组件内还可以把 class/style 加到生成的 wrapper 上: ```vue <template> <IssueCard slot-wrapper="cover-view"> <template #header slot-wrapper="cover-view" slot-wrapper-class="slot-default" slot-wrapper-style="padding: 8px"> <slot /> </template> <template #footer slot-wrapper="view" slot-wrapper-class="slot-footer" slot-wrapper-style="margin-top: 12px" > <slot name="footer" /> </template> </IssueCard> </template> ``` ```wxml <IssueCard> <cover-view slot="header" class="slot-default" style="padding: 8px"> <slot /> </cover-view> <view slot="footer" class="slot-footer" style="margin-top: 12px"> <slot name="footer" /> </view> </IssueCard> ``` 也支持 `:slot-wrapper-class="headerClass"` / `:slot-wrapper-style="headerStyle"` 这类动态绑定;单个 slot 更推荐写成 `<template #footer :slot-wrapper-class="footerClass">` 这种就近覆盖。参数名必须是静态的。 `slot-single-root-no-wrapper-<slotName>` 可以让指定插槽在单根真实节点场景下尽量下推 `slot="..."````vue <template> <IssueCard slot-single-root-no-wrapper-icon> <template #icon> <image src="/assets/icon.png" /> </template> </IssueCard> </template> ``` 产物: ```wxml <IssueCard> <image slot="icon" src="/assets/icon.png" /> </IssueCard> ``` 如果插槽内容是转发 `<slot />`,即使配置了 `slot-single-root-no-wrapper-header`,仍会保留 wrapper: ```wxml <IssueCard> <view slot="header"> <slot /> </view> </IssueCard> ``` `block` 不允许作为 wrapper,会回退到 `view` 并输出 warning。自定义 wrapper 必须是目标小程序运行时可渲染、并且能承载当前 slot 内容的真实节点或组件。例如下面的写法会生成 `text` 包裹 `view`,这不适合真实运行时: ```vue <template> <IssueCard> <template #header slot-wrapper="text"> <view>Header</view> </template> </IssueCard> </template> ``` ```wxml <IssueCard> <text slot="header"> <view>Header</view> </text> </IssueCard> ``` ### `layout` 页面 layout 既可能来自项目级规则,也可能来自页面侧 `definePageMeta`。排查时先确认是哪一层生效。 ### `chunks.sharedStrategy` 常见策略: - `duplicate`:偏向分包首开性能 - `hoist`:偏向共享抽取与包体控制 不要在 `srcRoot`、路由来源、分包边界都没确认前就先调 chunk 策略。 ### `hmr.runtime` 默认值为 `auto`。微信项目会在 `wv dev` 启动时根据开发者工具设置自动选择 HMR 运行时,也可显式锁定状态保持型热更新: ```ts export default defineConfig({ weapp: { hmr: { runtime: 'stateful-experimental', }, }, }) ``` 安全的 JavaScript/Vue 更新会保留当前 Page/Component 实例、route/query、输入和可序列化 data/setup ref,并替换原生 Page、原生 Component 与 wevu 方法。CSS、资源、JSON/配置、不兼容模块图或补丁失败会回退完整构建与当前路由重载。 `auto``project.private.config.json``setting.compileHotReLoad` 严格为 `true` 时选择 `stateful-experimental`,否则选择 `classic`;非微信平台也会回退到 `classic`。该判断只在启动时执行,修改开发者工具设置后需要重启 `wv dev`。启动日志会以 `HMR 模式``HMR 切换` 两行显示最终模式、选择来源,以及通过 DevTools 热重载开关或 `weapp.hmr.runtime` 切换模式的方法。显式配置通常优先,但 Skyline 兼容降级不受显式配置覆盖。 状态保持模式目前只支持微信小程序 WebView,需要微信开发者工具开启服务端口和热重载。微信开发者工具暂不支持 Skyline 热重载;首次编译检测到任意生成的应用或页面 JSON 使用 `renderer: 'skyline'` 时,`wv dev` 会输出带官方兼容文档链接的警告,将当前项目私有配置中的 `setting.compileHotReLoad` 持久化为 `false`,并强制使用 `classic`,包括显式配置 `stateful-experimental` 的场景。其他私有配置字段不会改变,切回 WebView 后也不会自动重新开启热重载。需要既有写盘/刷新行为时显式配置 `classic`### `hmr.touchAppWxss` 默认值为 `auto`,仅在非内置 Tailwind 集成发生真实内容失效时,额外更新已有全局样式的时间戳。内置 `weapp.tailwindcss` 使用 compiler 与 Vite/Rolldown 原生输出作为唯一刷新来源;页面、组件和 layout 的局部样式更新不会额外触碰 `app.wxss`,也不会因为祖先目录能解析到 Tailwind 依赖而触发全局重载。 `true` 保留每次增量构建后的额外全局刷新,可能使微信开发者工具重载 AppService、重置页面状态。`false` 仅关闭额外刷新,不会关闭 Tailwind 内容扫描、样式编译或正常产物更新。该选项只更新已有产物的时间戳,不创建缺失文件、不改写文件内容;除文件不存在外的刷新错误会输出到开发日志。 ### `hmr.logLevel` / `hmr.profileJson` 排查开发态热更新慢、共享 chunk 回退或 DevTools 热重载不稳定时,可以临时打开: ```ts export default defineConfig({ weapp: { hmr: { logLevel: 'concise', profileJson: '.tmp/weapp-vite-hmr-profile.jsonl', }, }, }) ``` - `logLevel: 'default' | 'concise' | 'verbose'` 控制终端诊断详细程度。 - `profileJson: boolean | string` 控制是否输出 JSONL profile,字符串表示自定义输出路径。 ### `mcp` `weapp.mcp` 默认启用,但默认不自动启动服务。AI 客户端接入优先走 CLI```bash wv mcp init codex wv mcp print codex wv mcp doctor codex ``` ## CLI 与 IDE 命令 `weapp-vite` 原生命令优先,IDE 相关命令通过 `weapp-ide-cli` 透传。 例如: ```bash weapp-vite build weapp-vite preview --project ./dist/build/mp-weixin weapp-vite ide preview --project ./dist/build/mp-weixin ``` ## 继续阅读 - 项目结构与 `AGENTS.md`:[`project-structure.md`](./project-structure.md) - wevu 运行时写法:[`wevu-authoring.md`](./wevu-authoring.md) - Vue SFC 宏与模板:[`vue-sfc.md`](./vue-sfc.md)