# plugin-vite
**Repository Path**: vitarx/plugin-vite
## Basic Information
- **Project Name**: plugin-vite
- **Description**: The official plugin for Vitarx support in Vite.
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-02-28
- **Last Updated**: 2026-05-11
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# @vitarx/plugin-vite
Vitarx 的 Vite 编译插件,提供 JSX 到 `createView` 的编译转换、指令支持和 HMR 热更新功能。
## 特性
- 🚀 **JSX 编译** - 将 JSX 语法编译为高效的 `createView` 调用
- 📦 **编译宏指令** - 支持 `v-if`、`v-else-if`、`v-else`、`v-model`、`v-show` 等指令
- 🧩 **编译宏组件** - 内置 `Switch`、`Match`、`IfBlock` 纯编译组件
- 🔥 **HMR 支持** - 开发模式下自动注入热更新代码,支持组件状态保留
- 📝 **TypeScript** - 完整的 TypeScript 类型支持
## 安装
```bash
npm install @vitarx/plugin-vite
# 或
pnpm add @vitarx/plugin-vite
# 或
yarn add @vitarx/plugin-vite
```
## 使用方法
### 配置 Vite
在 `vite.config.ts` 中配置插件:
```typescript
import { defineConfig } from 'vite'
import vitarx from '@vitarx/plugin-vite'
export default defineConfig({
plugins: [vitarx()]
})
```
### JSX 编译
插件会自动将 JSX 语法编译为 `createView` 调用:
```jsx
// 编译前
const App = () =>
Hello World
// 编译后
import { createView } from 'vitarx'
const App = () => /* @__PURE__ */createView('div', {
className: 'container',
children: 'Hello World'
})
```
## 编译宏指令
### v-if / v-else-if / v-else
条件渲染指令,编译为高效的 `branch` 调用:
```jsx
<>
显示内容
其他内容
默认内容
>
```
编译后:
```javascript
branch(
() => unref(show) ? 0 : unref(other) ? 1 : 2,
[
() => createView('div', { children: '显示内容' }),
() => createView('span', { children: '其他内容' }),
() => createView('p', { children: '默认内容' })
]
)
```
### v-model
双向绑定指令,自动生成 `modelValue` 和 `onUpdate:modelValue`:
```jsx
```
编译后:
```javascript
createView(Input, {
get modelValue() { return unref(value) },
'onUpdate:modelValue': v => { value.value = v }
})
```
### v-show
显示/隐藏指令:
```jsx
内容
```
编译后:
```javascript
withDirectives(
createView('div', { children: '内容' }),
[['show', { get value() { return unref(visible) } }]]
)
```
## 编译宏组件
### Switch / Match
条件分支组件,类似于 JavaScript 的 switch 语句:
```jsx
默认}>
加载中...
出错了
加载成功
```
编译后:
```javascript
branch(
() => status === 'loading' ? 0 : status === 'error' ? 1 : status === 'success' ? 2 : 3,
[
() => '加载中...',
() => '出错了',
() => '加载成功',
() => createView('div', { children: '默认' })
]
)
```
### IfBlock
用于包裹 v-if 链,确保类型正确:
```jsx
A
B
C
```
## Props 处理
### 响应式 Props
插件会自动处理响应式属性:
```jsx
// ref 变量自动使用 .value
const count = ref(0);
// 编译为: get count() { return count.value }
// 普通变量自动使用 unref
// 编译为: get className() { return unref(className) }
// 成员表达式直接访问
// 编译为: get value() { return props.value }
```
### v-bind 批量绑定
```jsx
// 或
```
## HMR 热更新
开发模式下,插件会自动为导出的组件注入 HMR 支持:
```jsx
// 编译前
export const App = () => {
const count = ref(0)
return {count}
}
// 编译后(HMR 模式)
import __$VITARX_HMR$__ from '@vitarx/plugin-vite/hmr-client'
import { createView as jsxDEV, getInstance } from 'vitarx'
export const App = () => {
const __$VITARX_HMR_VIEW_NODE$__ = getInstance()
__$VITARX_HMR$__.instance.register(__$VITARX_HMR_VIEW_NODE$__)
__$VITARX_HMR_VIEW_NODE$__ && Promise.resolve().then(() => {
__$VITARX_HMR_VIEW_NODE$__._$_VITARX_HMR_VIEW_STATE_$_ = {
get count() { return count }
}
})
const count = ref(0)
return jsxDEV('div', { children: count }, { fileName: '...', lineNumber: 5, columnNumber: 10 })
}
__$VITARX_HMR$__.instance.bindId(App, 'abc123')
import.meta.hot.accept(mod => {
__$VITARX_HMR$__.instance.update(mod)
})
```
### HMR 组件识别规则
只有满足以下条件的函数才会被注入 HMR 支持:
1. **函数名大写字母开头** - 符合组件命名规范
2. **被导出** - 使用 `export` 导出
3. **包含 JSX** - 函数体内包含 JSX 语法或返回编译宏组件
### HMR 代码分离
HMR 在检测到组件更新时,会将组件代码分离为 **UI 代码** 和 **逻辑代码** 两部分:
- **UI 代码**:`createView`、`branch`、`expr`、`accessor`、`dynamic`、`withDirectives` 等调用
- **逻辑代码**:其余所有代码
仅逻辑代码变化时,组件完全重新挂载;仅 UI 代码变化时,仅重建视图树。
### 开发模式位置信息
开发模式下,`createView` 调用会注入位置信息参数(用于调试),`branch`、`expr`、`accessor` 不注入:
```javascript
// dev 模式
createView("div", { children: "hello" }, { fileName: "App.tsx", lineNumber: 5, columnNumber: 10 })
expr(() => a && b) // 无位置信息
branch(() => unref(show) ? 0 : 1) // 无位置信息
accessor(props, "value") // 无位置信息
// 生产模式
createView("div", { children: "hello" }) // 无位置信息
```
## 子元素处理
### 响应式子元素
```jsx
// 标识符保持原样
{value}
// 成员表达式使用 accessor
{props.value}
// 编译为: accessor(props, 'value')
// 条件表达式使用 branch
{show ? 'yes' : 'no'}
// 编译为: branch(() => unref(show) ? 0 : 1, [...])
// 逻辑表达式使用 expr
{a && b}
// 编译为: expr(() => a && b)
// 二元表达式使用 expr
{count + 1}
// 编译为: expr(() => count + 1)
// 函数调用使用 expr
{render()}
// 编译为: expr(() => render())
```
### expr 与数组返回值
`expr` 底层由 `DynamicView` 渲染。当 `expr` 中的表达式返回数组时(如 `arr.map()`),运行时会自动检测并原样返回数组(仅渲染一次,不跟踪响应式)。推荐使用 `For` 组件实现动态数组的响应式渲染。
```jsx
// arr.map() 返回数组,expr 运行时原样返回(仅渲染一次)
{items.map(item => {item})}
// 编译为: expr(() => items.map(item => createView("span", { children: item })))
// 推荐:使用 For 组件实现响应式数组渲染
{item => {item}}
```
### 运行时 API 说明
| API | 用途 | 编译器注入 | HMR 识别为 UI 代码 |
|------------------|---------|-----------|---------------|
| `createView` | 创建视图节点 | ✅ | ✅ |
| `branch` | 条件分支渲染 | ✅ | ✅ |
| `expr` | 动态表达式包装 | ✅ | ✅ |
| `accessor` | 属性访问追踪 | ✅ | ✅ |
| `dynamic` | 动态值包装 | ❌(用户手动使用) | ✅ |
| `withDirectives` | 指令绑定 | ✅ | ✅ |
> **注意:** `expr` 是编译器插桩注入的辅助函数,用于包装逻辑/二元/调用表达式;`dynamic` 是运行时独立 API,供用户手动使用。两者在 HMR 代码分离时均被视为 UI 描述代码。
## API 参考
### 插件选项
```typescript
interface VitePluginVitarxOptions {
/**
* 是否将 className 属性转换为 class 属性
* 仅对原生 HTML 元素生效,组件不生效
* @default false
*/
transformClassNameToClass?: boolean
}
```
### className 转 class
启用 `transformClassNameToClass` 选项后,原生 HTML 元素的 `className` 属性会自动转换为 `class`:
```typescript
// vite.config.ts
import { defineConfig } from 'vite'
import vitarx from '@vitarx/plugin-vite'
export default defineConfig({
plugins: [vitarx({ transformClassNameToClass: true })]
})
```
```jsx
// 编译前
Hello
// 编译后
createView('div', { class: 'container', children: 'Hello' })
```
**注意事项:**
- 仅对原生 HTML 元素(小写字母开头)生效,组件不转换
- `class` 和 `className` 不能同时存在,否则会抛出 `E016` 错误
- `class` 属性优先级更高
## 目录结构
```
src/
├── constants/ # 常量定义
├── hmr-client/ # HMR 客户端运行时
├── passes/ # 编译转换处理
│ ├── components/ # 编译宏组件
│ ├── directives/ # 指令处理
│ ├── hmr/ # HMR 注入
│ ├── imports/ # 导入处理
│ ├── jsx/ # JSX 转换
│ └── props/ # 属性处理
├── utils/ # 公共工具函数
├── context.ts # 转换上下文
├── error.ts # 错误处理
├── transform.ts # 主转换入口
└── index.ts # 插件入口
```
## 错误码
| 错误码 | 描述 |
|------|-----------------------------|
| E001 | 无效的 JSX 属性值 |
| E002 | 无效的 v-model 值 |
| E003 | v-else 没有前置的 v-if |
| E004 | v-else-if 没有前置的 v-if |
| E005 | 无效的 v-if 值 |
| E006 | Switch 子元素必须是 Match 组件 |
| E007 | Match 组件缺少 when 属性 |
| E008 | IfBlock 子元素必须包含 v-if 指令 |
| E009 | v-model 与 modelValue 冲突 |
| E010 | v-model 值必须是标识符或成员表达式 |
| E011 | v-model Identifier 必须是 ref |
| E012 | Match 组件必须位于 Switch 组件内部 |
| E013 | Match 组件必须包含子元素 |
| E014 | IfBlock 组件必须包含子元素 |
| E015 | Switch 组件必须包含至少一个 Match 子元素 |
| E016 | class 和 className 不能同时存在 |
## License
MIT