|
| 1 | +# @opentiny/vue-common 适配层开发规范 |
| 2 | + |
| 3 | +## 适用场景 |
| 4 | + |
| 5 | +- 理解 TinyVue 如何同时支持 Vue 2.6 / 2.7 / Vue 3 |
| 6 | +- 编写或调试组件 `setup`、`$setup`、跨端模板选择 |
| 7 | +- 使用 `defineComponent`、`hooks`、`directive`、`svg`、`mergeClass` 等适配 API |
| 8 | +- 排查「仅在某一 Vue 版本下复现」的问题 |
| 9 | + |
| 10 | +## 核心原则 |
| 11 | + |
| 12 | +### 1. 包定位 |
| 13 | + |
| 14 | +`packages/vue-common` 发布为 `@opentiny/vue-common`,是整个组件库的 **Vue 版本适配与运行时胶水层**: |
| 15 | + |
| 16 | +- 通过 `virtual:common/adapter/vue` 在构建时指向 `adapter/vue2` | `vue2.7` | `vue3` |
| 17 | +- 向 renderless 提供统一的 `hooks` 对象(即对应 Vue 版本的 API) |
| 18 | +- 提供 `$setup` / `setup`,连接模板与 renderless |
| 19 | +- 提供主题/模式解析、设计规范注入、图标 `svg` 包装等 |
| 20 | + |
| 21 | +**修改约束**:表结构变更、新 Vue 版本支持需充分评估;日常组件开发**优先改 renderless / vue 模板**,避免随意改动适配层行为。`SKILL.md` 中亦提示:非必要不修改适配层。 |
| 22 | + |
| 23 | +### 2. 目录结构 |
| 24 | + |
| 25 | +```text |
| 26 | +packages/vue-common/src/ |
| 27 | +├── index.ts # 对外主入口:$setup、setup、$prefix、svg 等 |
| 28 | +├── adapter/ |
| 29 | +│ ├── index.ts # 导出当前 Vue 版本 adapter |
| 30 | +│ ├── vue2/ |
| 31 | +│ ├── vue2.7/ |
| 32 | +│ └── vue3/ |
| 33 | +├── breakpoint.ts # useBreakpoint 响应式断点 |
| 34 | +├── csscls.ts # 类名序列化、去重 |
| 35 | +├── usedefer.ts # useDefer |
| 36 | +└── generateIcon.ts # 渐变图标 id 处理 |
| 37 | +``` |
| 38 | + |
| 39 | +### 3. 组件开发中最常用的 API |
| 40 | + |
| 41 | +| API | 用途 | |
| 42 | +| ---------------------------------- | ----------------------------------------------------------------- | |
| 43 | +| `$prefix` | 组件名前缀 `'Tiny'`,如 `TinyButton` | |
| 44 | +| `$props` / `props` | 框架保留 prop:`tiny_mode`、`tiny_renderless`、`tiny_template` 等 | |
| 45 | +| `defineComponent` | 定义组件(跨版本) | |
| 46 | +| `$setup` | 跨端父组件:根据 `tiny_mode` 选择 `pc` / `mobile-first` 模板 | |
| 47 | +| `setup` | 子模板中连接 renderless,返回模板绑定对象 | |
| 48 | +| `hooks` | 当前 Vue 版本的 API 集合,可传给 renderless 第二参数 | |
| 49 | +| `isVue2` / `isVue3` | 版本判断(renderless 中应尽量避免,优先用 utils/vm) | |
| 50 | +| `directive` | 统一 Vue2/3 指令钩子名 | |
| 51 | +| `svg` | 包装 `@opentiny/vue-theme` 的 svg 为图标组件 | |
| 52 | +| `mergeClass` | mobile-first 下合并 Tailwind 类名 | |
| 53 | +| `filterAttrs`(模板中 `a`) | 过滤 `$attrs` 绑定 | |
| 54 | +| `Teleport` / `KeepAlive` | 须从此包导入以保证兼容 | |
| 55 | +| `useBreakpoint` / `useDefer` | 布局与渲染优化 | |
| 56 | +| `useInstanceSlots` / `useRelation` | 已注入 `isVue2` 的 hooks 封装 | |
| 57 | + |
| 58 | +### 4. 双层组件:$setup + 子模板 setup |
| 59 | + |
| 60 | +**跨端组件**(`pc.vue` + `mobile-first.vue`): |
| 61 | + |
| 62 | +```typescript |
| 63 | +// packages/vue/src/alert/src/index.ts |
| 64 | +import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common' |
| 65 | +import template from 'virtual-template?pc|mobile-first' |
| 66 | + |
| 67 | +export default defineComponent({ |
| 68 | + name: $prefix + 'Alert', |
| 69 | + props: alertProps, |
| 70 | + setup(props, context) { |
| 71 | + return $setup({ props, context, template }) |
| 72 | + } |
| 73 | +}) |
| 74 | +``` |
| 75 | + |
| 76 | +`$setup` 会: |
| 77 | + |
| 78 | +1. `resolveMode` 解析 `tiny_mode`(`pc` | `mobile` | `mobile-first`) |
| 79 | +2. 通过 `virtual-template` 插件加载对应模板 |
| 80 | +3. `renderComponent` 渲染子组件,并合并设计规范 `designConfig` 中的默认 props |
| 81 | + |
| 82 | +**具体模板**(`pc.vue`): |
| 83 | + |
| 84 | +```typescript |
| 85 | +import { setup, defineComponent, props } from '@opentiny/vue-common' |
| 86 | +import { renderless, api } from '@opentiny/vue-renderless/alert/vue' |
| 87 | +import type { IAlertApi } from '@opentiny/vue-renderless/types/alert.type' |
| 88 | + |
| 89 | +export default defineComponent({ |
| 90 | + props: [...props, 'type', 'size' /* 其它组件 props */], |
| 91 | + setup(props, context) { |
| 92 | + return setup({ props, context, renderless, api }) as unknown as IAlertApi |
| 93 | + } |
| 94 | +}) |
| 95 | +``` |
| 96 | + |
| 97 | +### 5. setup 与 renderless 的协作 |
| 98 | + |
| 99 | +`setup` 内部流程概要: |
| 100 | + |
| 101 | +1. 选择 `props.tiny_renderless` 或入参 `renderless` |
| 102 | +2. 构造 `utils`(含 `vm`、`emit`、`t`、`designConfig`、`mode`、`mergeClass` 等) |
| 103 | +3. 调用 `render(props, hooks, utils, extendOptions)` 得到 `sdk` |
| 104 | +4. 按 `api` 数组将 `sdk` 上的方法/状态暴露给模板(`attrs`) |
| 105 | +5. 双层组件默认 `mono: false`,将 api 同步到父组件 ref;单层组件传 `mono: true` |
| 106 | + |
| 107 | +模板中常用简写: |
| 108 | + |
| 109 | +- `t('ui.xxx')` — 国际化 |
| 110 | +- `a($attrs, filters, include)` — `filterAttrs` |
| 111 | +- `m(...)` — `mergeClass` |
| 112 | +- `f` / `d` / `dp` — 过滤器与实例属性定义(见 renderless.skill.md) |
| 113 | + |
| 114 | +### 6. 模式与主题解析 |
| 115 | + |
| 116 | +- **模式** `resolveMode`:`tiny_mode` prop > inject `TinyMode` > 全局 config > 默认 `'pc'` |
| 117 | +- **主题** `resolveTheme`:`'tiny'` | `'saas'` |
| 118 | +- 根组件可设 `tiny_mode_root` 以 `provide('TinyMode', mode)` |
| 119 | + |
| 120 | +函数式组件(Modal、Loading、Notify)依赖全局 `tiny_mode`,需在应用级配置。 |
| 121 | + |
| 122 | +### 7. 设计规范 designConfig |
| 123 | + |
| 124 | +适配层负责注入与合并 `designConfig`,详细约定见 [design.skill.md](./design.skill.md)。 |
| 125 | + |
| 126 | +```typescript |
| 127 | +import { provideDesignConfig } from '@opentiny/vue-common' |
| 128 | + |
| 129 | +provideDesignConfig({ |
| 130 | + components: { |
| 131 | + Button: { props: { round: true }, api: [], renderless: fn } |
| 132 | + } |
| 133 | +}) |
| 134 | +``` |
| 135 | + |
| 136 | +`setup` 会合并 `designConfig.renderless` 与组件级 props 默认值。 |
| 137 | + |
| 138 | +### 8. 图标 svg 工厂 |
| 139 | + |
| 140 | +```typescript |
| 141 | +import { svg } from '@opentiny/vue-common' |
| 142 | +import IconX from '@opentiny/vue-theme/svgs/icon-x.svg' |
| 143 | + |
| 144 | +export default () => svg({ name: 'IconX', component: IconX, filledComponent: IconX })() |
| 145 | +``` |
| 146 | + |
| 147 | +详见 [icon.skill.md](./icon.skill.md)。 |
| 148 | + |
| 149 | +### 9. 版本判断与 hooks 使用 |
| 150 | + |
| 151 | +```typescript |
| 152 | +// ❌ renderless 中 |
| 153 | +if (process.env.VUE_VERSION === '3') { ... } |
| 154 | + |
| 155 | +// ✅ 模板/极少数适配代码 |
| 156 | +import { isVue2, hooks } from '@opentiny/vue-common' |
| 157 | + |
| 158 | +// ✅ renderless 接收第二参数 |
| 159 | +export const renderless = (props, { computed, reactive, watch }, utils) => { ... } |
| 160 | +``` |
| 161 | + |
| 162 | +### 10. 禁止事项 |
| 163 | + |
| 164 | +- ❌ 组件模板中不要 `import from 'vue'`(使用 `@opentiny/vue-common` 的 `defineComponent`、`hooks`、`Teleport` 等) |
| 165 | +- ❌ renderless 中不要依赖适配层实现细节(仅使用文档化的 `utils` / `vm` 字段) |
| 166 | +- ❌ 不要随意修改 `adapter/` 下生命周期映射逻辑 |
| 167 | +- ❌ 不要在 `.vue` 中写复杂业务逻辑,应下沉 renderless |
| 168 | + |
| 169 | +## 与其它规范的关系 |
| 170 | + |
| 171 | +| 模块 | 规范文件 | |
| 172 | +| ---------- | -------------------------------------------- | |
| 173 | +| 模板层 | [vue.skill.md](./vue.skill.md) | |
| 174 | +| 逻辑层 | [renderless.skill.md](./renderless.skill.md) | |
| 175 | +| 样式 | [theme.skill.md](./theme.skill.md) | |
| 176 | +| 设计规范 | [design.skill.md](./design.skill.md) | |
| 177 | +| 工具函数 | [utils.skill.md](./utils.skill.md) | |
| 178 | +| 组合式逻辑 | [hooks.skill.md](./hooks.skill.md) | |
| 179 | +| 指令 | [directive.skill.md](./directive.skill.md) | |
| 180 | + |
| 181 | +## 参考资源 |
| 182 | + |
| 183 | +- [主入口 setup / $setup](../../packages/vue-common/src/index.ts) |
| 184 | +- [Vue3 适配器](../../packages/vue-common/src/adapter/vue3/index.ts) |
| 185 | +- [Alert 跨端入口](../../packages/vue/src/alert/src/index.ts) |
| 186 | +- [Alert pc 模板 setup](../../packages/vue/src/alert/src/pc.vue) |
0 commit comments