weapp-vite
Version:
weapp-vite 一个现代化的小程序打包工具
296 lines (232 loc) • 7.64 kB
Markdown
# Vue SFC
这个文档只覆盖 `weapp-vite` 下小程序 Vue SFC 的高频规则。
## 推荐基线
- 优先使用 `<script setup lang="ts">`
- 页面用 `definePageJson`
- 组件用 `defineComponentJson`
- 页面元信息用 `definePageMeta`
## 宏的职责划分
- `defineAppJson`:应用级 JSON
- `definePageJson`:页面级 JSON
- `defineComponentJson`:组件级 JSON
- `definePageMeta`:页面元信息,例如 layout
`definePageJson` 和 `definePageMeta` 可以同时存在,但职责不同。
## `v-model`
小程序编译场景下,`v-model` 目标应是可赋值表达式。
可行:
```vue
<input v-model="form.name" />
```
不可行:
```vue
<input v-model="x + y" />
```
## `usingComponents`
当你在页面或组件里需要显式注册原生小程序组件时,优先明确当前文件使用的 JSON 宏与配置来源,避免多个入口互相覆盖。
## HTML 标签迁移辅助
如果你把偏 Web 风格的模板迁到 `.vue`,可以优先关注这两个配置:
- `weapp.vue.template.htmlTagToWxml`
把常见 HTML 标签映射为小程序内置标签。
- `weapp.vue.template.htmlTagToWxmlTagClass`
默认开启。映射发生时,会再补一个原标签名 class,方便你自己用 CSS 恢复默认外观。
- `weapp.vue.template.formatWxml`
默认 `auto`。开发态会格式化生成的 WXML,生产构建保持紧凑输出;也可以显式设置 `true` 或 `false`。
例如:
```vue
<template>
<h3 class="title">
标题
</h3>
<br>
</template>
```
启用默认行为后,会得到类似:
```wxml
<view class="h3 title">标题</view>
<view class="br" />
```
## 具名插槽透传 wrapper
当你把当前组件的 `<slot />` 转发到子组件的具名插槽时:
```vue
<IssueCard>
<template #header>
<slot />
</template>
</IssueCard>
```
编译器会使用真实节点 wrapper,默认类似:
```wxml
<view slot="header">
<slot />
</view>
```
不要改成 `<block slot="header"><slot /></block>`。真实 WeChat DevTools 运行时里,这种写法会出现宿主 header,但转发内容不会渲染。
如果需要自定义 wrapper,可以在组件使用处写静态属性。组件内配置推荐使用普通 kebab-case 静态属性,避免和 Vue 指令参数语法混淆。
`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>
```
产物中 `header` 使用 `slot-wrapper="cover-view"`,`footer` 在对应的 `<template #footer>` 上覆盖:
```wxml
<IssueCard>
<cover-view slot="header">
<slot />
</cover-view>
<view slot="footer">
<slot name="footer" />
</view>
</IssueCard>
```
只覆盖单个 slot 时,更推荐把配置写在对应的 `<template #xxx>` 上。这个写法最靠近 slot 内容,优先级高于父组件标签上的默认值和 `slot-wrapper-<slotName>`:
```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>
```
产物:
```wxml
<IssueCard>
<text slot="header" class="slot-header">
<slot />
</text>
<cover-view slot="footer">
<slot name="footer" />
</cover-view>
</IssueCard>
```
在 `<template #header>` 上配置时,属性仍写 `slot-wrapper` / `slot-wrapper-class` / `slot-wrapper-style` / `slot-single-root-no-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"`,以及 `<template #footer :slot-wrapper-class="footerClass">` 这种单 slot 就近覆盖。
`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 />`。微信平台默认会使用内部 `virtualHost` wrapper:
```vue
<template>
<IssueCard slot-single-root-no-wrapper-header>
<template #header>
<slot />
</template>
</IssueCard>
</template>
```
```wxml
<IssueCard>
<weapp-slot-wrapper slot="header">
<slot />
</weapp-slot-wrapper>
</IssueCard>
```
如果需要回到旧版 `view` wrapper,可配置 `weapp.vue.template.slotFallbackWrapperStrategy: 'view'`,或显式配置 `slotFallbackWrapper: 'view'`。
默认策略不会使用 `block`。如果显式配置 `slotFallbackWrapper: 'block'`,编译器会按原样输出:
```wxml
<IssueCard>
<block slot="header">
<slot />
</block>
</IssueCard>
```
注意:`block` 在转发 `<slot />` 的部分 WeChat DevTools 运行时场景中会丢失内容,因此不作为默认值。显式启用时需要自行确认目标运行时和具体插槽内容可用。
你选择的 wrapper 必须能承载实际内容。比如下面的写法会让 `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>
```
也可以通过 `weapp.vue.template.slotFallbackWrapper` 全局配置,按组件和具名插槽匹配。显式配置 `slotFallbackWrapper` 后会优先于默认 `virtualHost` 策略。`rules[].component` 匹配使用处模板标签名;`rules[].componentName` 匹配子组件里的静态 `defineOptions({ name: 'HelloWorld' })`。`componentName` 需要编译器能解析到被引用的 Vue SFC,原生小程序组件继续用 `component`。
## 何时继续看其他文档
- 需要更完整的编辑器提示说明:[`../volar.md`](../volar.md)
- 需要运行时页面/组件/store 约束:[`wevu-authoring.md`](./wevu-authoring.md)
- 需要项目级 `weapp` 配置:[`weapp-config.md`](./weapp-config.md)