Vue SFC:配置与宏
usingComponents:为什么脚本里不要 import
小程序组件的注册是 JSON 声明式 的:只认 usingComponents。因此在 SFC 里推荐:
- 优先在
defineAppJson/definePageJson/defineComponentJson里声明usingComponents - App/Page/Component SFC 能用对应 JSON 宏就不要再写
<json> - 模板里直接写组件标签(例如
<my-card />)
页面示例:
<script setup lang="ts">
definePageJson(() => ({
usingComponents: {
'my-card': '/components/MyCard/index',
},
}))
</script>
<template>
<my-card />
<!-- 也可以传 slot -->
<my-card>
<text>hello</text>
</my-card>
</template>常见误区
不要把小程序组件当成 Web Vue 组件来做“ESM import + components 注册”。即使你在脚本里写了 import,最终是否能工作也取决于产物如何生成 usingComponents。
提示:
tsconfig.app.json已预置"vueCompilerOptions.plugins": ["weapp-vite/volar"],配合 Volar 扩展即可获得 JSON 宏与模板提示。
必须设置 vueCompilerOptions.lib
若使用 Wevu 的 <script setup> 宏(defineProps/withDefaults/defineEmits 等),请务必在同一处设置 "vueCompilerOptions.lib": "wevu",否则宏的类型提示会退化为 any。
配置块模式对比
| 写法 | 作用 | 适合场景 |
|---|---|---|
| Script Setup 宏 | build-time 注入配置 | 默认方案(页面/组件/App 均推荐) |
<json> | JSONC + Schema 提示 | 兼容旧代码的静态配置 |
<json lang="jsonc"> | JSONC + Schema 提示 | 兼容旧代码的静态配置(显式标注) |
<json lang="ts/js"> | TS/JS + 类型检查 | 历史项目迁移期的动态/异步配置 |
示例(动态配置):
<script setup lang="ts">
definePageJson(async () => ({
navigationBarTitleText: process.env.APP_TITLE ?? '默认标题',
}))
</script>Script Setup JSON 宏(build-time)
weapp-vite 支持在 Vue SFC 的 <script setup> 中使用以下 JSON 宏,并在构建时把返回结果合并进最终的 page.json / component.json:
defineAppJsondefinePageJsondefineComponentJson
建议:
- App/Page/Component SFC 默认使用对应宏:
- App 用
defineAppJson - Page 用
definePageJson - Component 用
defineComponentJson
- App 用
<json>作为兼容模式保留,不建议在新增 SFC 里使用。
特点与限制:
- 必须是
<script setup>的顶层语句,且只能传 1 个参数。 - 同一个 SFC 内只能使用一种宏(例如只能用
definePageJson),但可以调用多次;多次返回会 deep merge(后者覆盖前者)。 - 支持
object/() => object/async () => object(也支持返回Promise)。 - 宏只在 构建期(Node.js) 执行:请保持幂等,避免依赖小程序运行时 API;推荐只做本地 import、常量拼装与轻量计算。
- 合并优先级最高:会覆盖
<json>块以及自动usingComponents的结果。
TypeScript 提示:确保项目的
vite-env.d.ts包含/// <reference types="weapp-vite/client" />(脚手架默认已配置),这样 IDE 才能识别这些全局宏。
组件示例:
<script setup lang="ts">
defineComponentJson(() => ({
styleIsolation: 'isolated',
options: { virtualHost: true },
externalClasses: ['macro-card-class'],
}))
</script>这里要注意字段分层:
virtualHost、multipleSlots、styleIsolation这类原生ComponentOptions字段,放在optionsexternalClasses、behaviors、relations这类组件顶层字段,不要塞进options
错误示例:
<script setup lang="ts">
defineOptions({
options: {
externalClasses: ['class'],
},
})
</script>正确示例:
<script setup lang="ts">
defineOptions({
externalClasses: ['class'],
options: {
virtualHost: true,
styleIsolation: 'apply-shared',
},
})
</script>如果你想把组件宿主节点虚拟化,这就是最直接的写法。若项目里大多数组件都需要 virtualHost: true,更推荐在 /config/wevu 里统一配置:
import { defineConfig } from 'weapp-vite/config'
export default defineConfig({
weapp: {
wevu: {
defaults: {
component: {
options: {
virtualHost: true,
},
},
},
},
},
})补充说明:
- 这个全局默认值只会作用于组件,不会把页面默认改成
virtualHost: true - 页面若确实需要开启,请在页面内显式声明
options.virtualHost - 局部组件自己写的
options.virtualHost优先级高于全局默认值
virtualHost 与样式透传边界
virtualHost: true 经常和 styleIsolation: 'apply-shared' 一起使用,但这两个配置解决的问题并不一样:
virtualHost: true:隐藏组件宿主节点styleIsolation: 'apply-shared':允许页面样式影响组件内部
它们不会自动提供 Vue Web 里的“根节点 class/style fallthrough”。
也就是说,下面这种写法并不意味着组件内部根节点一定会变红:
<MyCard class="text-red-500" />如果组件没有显式把外部类名或样式绑定到内部根节点,那么:
- 组件标签上的
class仍然只是挂在组件标签上 virtualHost: true时宿主节点不可见,这个class没有可见挂载点styleIsolation: 'apply-shared'也不会帮你自动把这个class合并进内部根节点
推荐写法
如果你希望组件支持外部样式,推荐显式设计 API,而不是依赖隐式透传。
写法 1:外部类名
<script setup lang="ts">
defineOptions({
externalClasses: ['custom-class'],
})
</script>
<template>
<view class="card custom-class">
<slot />
</view>
</template>父组件:
<MyCard custom-class="text-red-500 border border-red-500" />写法 2:显式样式 prop
<script setup lang="ts">
const props = defineProps<{
rootStyle?: string
}>()
</script>
<template>
<view class="card" :style="props.rootStyle">
<slot />
</view>
</template>父组件:
<MyCard root-style="color:#2563eb;background:#dbeafe;" />TIP
如果你确实想沿用组件标签上的 class,也可以声明 externalClasses: ['class'],但仍然要在内部根节点显式写出 class 占位。
页面示例:
<script setup lang="ts">
import { macroDemoNavBg, macroDemoNavTitle } from './macro.config'
definePageJson(() => ({
navigationBarTitleText: macroDemoNavTitle,
navigationBarBackgroundColor: macroDemoNavBg,
enablePullDownRefresh: true,
}))
</script>