vue-to-quickapp-converter
v1.0.0
Published
A tool to convert Vue Single File Components to QuickApp UX components
Downloads
23
Maintainers
Readme
Vue to QuickApp Converter
将Vue单文件组件(.vue)转换为快应用组件(.ux)的工具。
功能特性
- 模板转换: 将Vue模板语法转换为快应用模板语法
- 脚本转换: 将Vue组件选项转换为快应用组件格式
- 样式转换: 处理CSS语法差异和快应用特有样式
- 批量转换: 支持单个文件或整个目录的批量转换
- 可配置: 支持自定义转换规则和选项
安装
npm install使用方法
命令行工具
# 转换单个文件
npm run convert -- input.vue output.ux
# 转换整个目录
npm run convert -- src/ dist/
# 查看帮助
npm run convert -- --help转换规则
Template 模板转换
Vue指令转换
| Vue指令 | 快应用指令 | 说明 |
|---------|------------|------|
| v-if | if="{{condition}}" | 条件渲染 |
| v-else-if | elif="{{condition}}" | 条件渲染 |
| v-else | else | 条件渲染 |
| v-show | show="{{condition}}" | 显示/隐藏 |
| v-for | for="{{items}}" | 列表渲染(需要添加tid) |
| v-model | value + @change | 双向绑定(特殊处理) |
| v-bind: | : | 属性绑定 |
| v-on: | @ | 事件绑定 |
HTML标签转换
| HTML标签 | 快应用组件 | 说明 |
|----------|------------|------|
| <div> | <div> | 容器组件(自动添加flex样式) |
| <span> | <span> | 行内容器 |
| <p>, <h1>-<h6> | <text> | 文本组件 |
| <img> | <image> | 图片组件 |
| <button> | <input> | 按钮组件 |
| <input> | <input> | 输入组件 |
| <textarea> | <textarea> | 多行输入 |
| <ul>, <ol> | <list> | 列表组件 |
| <li> | <list-item> | 列表项 |
事件转换
| Vue事件 | 快应用事件 | 说明 |
|---------|------------|------|
| @click | @click | 点击事件 |
| @input | @change | 输入变化 |
| @change | @change | 值变化 |
| @focus | @focus | 获得焦点 |
| @blur | @blur | 失去焦点 |
| @submit | @submit | 表单提交 |
特殊处理
- 动态绑定: 移除
:style、:class等的冒号前缀 - 文本包裹: 纯文本内容自动用
<text>标签包裹 - v-for循环: 添加
tid属性用于性能优化
Script 脚本转换
Vue组件选项转换
| Vue选项 | 快应用格式 | 说明 |
|---------|------------|------|
| data() | data: {} | 组件数据(函数转对象) |
| props | props: {} | 属性定义 |
| computed | computed: {} | 计算属性 |
| watch | watch: {} | 监听器 |
| methods | 平级方法 | 方法直接定义在组件中 |
| mounted | onReady() | 组件准备完成 |
| created | onInit() | 组件初始化 |
| beforeDestroy | onDestroy() | 组件销毁前 |
数据转换示例
// Vue格式
export default {
data() {
return {
count: 0,
message: 'Hello'
}
},
methods: {
increment() {
this.count++
}
}
}
// 快应用格式
export default {
data: {
count: 0,
message: 'Hello'
},
increment() {
this.count++
}
}Style 样式转换
不支持的CSS属性(自动移除)
/* 以下属性在快应用中不支持,转换时会自动移除 */
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
text-shadow: 1px 1px 1px #000;
transform: translateX(10px);
animation: fadeIn 0.3s ease;
transition: all 0.3s ease;
position: absolute;
z-index: 999;
gap: 10px;
overflow-x: auto;
cursor: pointer;不支持的伪类选择器(自动移除)
/* 以下伪类选择器会被完全移除 */
.input::placeholder { color: #999; }
.button:hover { background: #blue; }
.link:focus { outline: none; }
.item:first-child { margin-top: 0; }
.element::before { content: ""; }CSS简写属性转换
/* 转换前 */
.element {
background: #ffffff;
border: none;
border: 1px solid #ccc;
}
/* 转换后 */
.element {
background-color: #ffffff;
border-width: 0px;
border-style: none;
/* border简写会被移除,建议使用具体属性 */
}支持的CSS属性
/* 以下属性在快应用中完全支持 */
background-color: #ffffff;
color: #333333;
font-size: 16px;
font-weight: bold;
text-align: center;
line-height: 1.5;
margin: 10px;
padding: 20px;
border-radius: 4px;
width: 100px;
height: 50px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;自动添加的样式
- Flex布局: 所有
<div>标签自动添加display: flex; flex-direction: column; - 布局检测: 如果已有
display属性或class包含flex关键词,则不重复添加 - CSS类生成: 无class的div会自动添加
qa-flex-container类
快应用特有功能
快应用专有组件
<!-- 快应用提供的特有组件 -->
<list> <!-- 高性能列表 -->
<list-item> <!-- 列表项 -->
<swiper> <!-- 轮播组件 -->
<tabs> <!-- 标签页 -->
<tab-bar> <!-- 标签栏 -->
<tab-content> <!-- 标签内容 -->
<refresh> <!-- 下拉刷新 -->
<loading> <!-- 加载指示器 -->
<progress> <!-- 进度条 -->
<slider> <!-- 滑块 -->
<switch> <!-- 开关 -->
<picker> <!-- 选择器 -->
<canvas> <!-- 画布 -->
<web> <!-- 网页视图 -->
<map> <!-- 地图 -->快应用专有事件
<!-- 快应用特有的生命周期和交互事件 -->
<div @appear="onAppear"> <!-- 组件出现 -->
<div @disappear="onDisappear"> <!-- 组件消失 -->
<div @swipe="onSwipe"> <!-- 滑动手势 -->v-for循环优化
<!-- Vue写法 -->
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
<!-- 快应用写法(自动转换) -->
<list-item for="{{items}}" tid="$idx" :key="item.id">
{{ item.name }}
</list-item>使用示例
完整转换示例
Vue组件 (input.vue):
<template>
<div class="container">
<h1>{{ title }}</h1>
<div class="form">
<input v-model="message" placeholder="请输入内容" />
<button @click="submit" :disabled="!message">
提交
</button>
</div>
<ul v-if="items.length">
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
title: 'Hello QuickApp',
message: '',
items: []
}
},
methods: {
submit() {
if (this.message) {
this.items.push({
id: Date.now(),
name: this.message
})
this.message = ''
}
}
}
}
</script>
<style>
.container {
padding: 20px;
background: #f5f5f5;
}
.form {
margin: 20px 0;
gap: 10px;
}
button:hover {
background-color: #007AFF;
}
</style>转换后的快应用组件 (output.ux):
<template>
<div class="container" style="display: flex; flex-direction: column;">
<text>{{ title }}</text>
<div class="form" style="margin: 20px 0; display: flex; flex-direction: column;">
<input value="{{message}}" @change="handleInputChange" placeholder="请输入内容" />
<input @click="submit" disabled="{{!message}}">
<text>提交</text>
</input>
</div>
<list if="{{items.length}}">
<list-item for="{{items}}" tid="$idx" :key="item.id">
<text>{{ item.name }}</text>
</list-item>
</list>
</div>
</template>
<script>
export default {
data: {
title: 'Hello QuickApp',
message: '',
items: []
},
submit() {
if (this.message) {
this.items.push({
id: Date.now(),
name: this.message
})
this.message = ''
}
},
handleInputChange(evt) {
this.message = evt.value
}
}
</script>
<style>
.container {
padding: 20px;
background-color: #f5f5f5;
}
.form {
margin: 20px 0;
}
/* 自动生成的flex布局样式 */
.qa-flex-container {
display: flex;
flex-direction: column;
}
</style>项目结构
vue-ux/
├── src/ # 源代码目录
│ ├── converters/ # 转换器模块
│ │ ├── template-converter.js # 模板转换器
│ │ ├── script-converter.js # 脚本转换器
│ │ └── style-converter.js # 样式转换器
│ ├── utils/ # 工具模块
│ │ ├── acorn-parser.js # Acorn AST解析器
│ │ ├── ast-parser.js # 兜底AST解析器
│ │ ├── conversion-rules.js # 转换规则配置
│ │ ├── parser.js # Vue SFC解析器
│ │ ├── safe-parser.js # 安全解析器
│ │ └── template-ast.js # 模板AST工具
│ ├── converter.js # 主转换器
│ └── index.js # 入口文件
├── bin/ # 命令行工具
│ └── cli.js # CLI入口
├── examples/ # 示例文件
│ ├── vue/ # Vue组件示例
│ │ ├── Example.vue
│ │ ├── HelloWorld.vue
│ │ └── SimpleButton.vue
│ └── quickapp/ # 转换后的快应用组件
│ ├── Example.ux
│ ├── HelloWorld.ux
│ └── SimpleButton.ux
├── tests/ # 测试文件
│ ├── ast-parser.test.js # AST解析器测试
│ ├── converter.test.js # 转换器测试
│ ├── safe-parser.test.js # 安全解析器测试
│ └── setup.js # 测试配置
├── jest.config.js # Jest测试配置
├── package.json # 项目配置
├── package-lock.json # 依赖锁定文件
└── README.md # 项目说明核心模块说明
转换器模块 (src/converters/)
template-converter.js: 处理Vue模板到快应用模板的转换
- Vue指令转换 (
v-if→if) - HTML标签转换 (
<p>→<text>) - 动态绑定处理 (
:style→style) - 文本自动包裹 (
<text>) - Flex布局自动添加
- Vue指令转换 (
script-converter.js: 处理Vue脚本到快应用脚本的转换
data()函数转换为data对象methods转换为平级方法- 生命周期钩子转换
- v-model处理函数生成
style-converter.js: 处理CSS样式转换
- 移除不支持的CSS属性
- 移除不支持的伪类选择器
- 简写属性转换 (
background→background-color) - 警告信息收集
解析器模块 (src/utils/)
acorn-parser.js: 基于Acorn的AST解析器
- 安全的JavaScript代码解析
- 支持完整的ES2020语法
- 多层回退机制
ast-parser.js: 自定义AST解析器
- 轻量级JavaScript解析
- 基本语法支持
- 作为Acorn的备用方案
safe-parser.js: 安全字符串解析器
- 正则表达式解析
- 最后的回退方案
- 基础数据类型支持
conversion-rules.js: 转换规则配置
- 标签映射规则
- 事件映射规则
- 不支持属性列表
- 样式转换规则
parser.js: Vue单文件组件解析器
- 解析Vue SFC结构
- 提取template、script、style
- 生成快应用UX文件
主要功能特性
- 多层解析策略: Acorn → 自定义AST → 安全解析器
- 智能样式处理: 自动移除不支持属性,转换简写属性
- 完整的Vue语法支持: 指令、事件、双向绑定、生命周期
- 快应用优化: 自动添加flex布局,文本包裹,性能优化
- 详细的警告信息: 帮助开发者了解转换过程中的变化
注意事项
快应用限制
CSS限制
- 不支持
::placeholder、:hover等伪类选择器 - 不支持
background简写,需使用background-color - 不支持
border简写,需使用border-width、border-style、border-color - 不支持
gap、cursor、overflow-x等属性
- 不支持
JavaScript限制
- 不支持
v-model双向绑定,需手动处理value和@change - 动态样式绑定不能使用冒号前缀(
:style→style) - 所有文本内容必须用
<text>标签包裹
- 不支持
组件限制
<button>标签会转换为<input>组件<p>、<h1>-<h6>标签会转换为<text>组件<ul>、<ol>、<li>会转换为<list>和<list-item>
最佳实践
样式编写
/* ✅ 推荐写法 */ .button { background-color: #007AFF; border-width: 1px; border-style: solid; border-color: #007AFF; } /* ❌ 避免使用 */ .button { background: #007AFF; border: 1px solid #007AFF; }模板编写
<!-- ✅ 推荐写法 --> <div class="container"> <text>标题文本</text> <input value="{{inputValue}}" @change="handleChange" /> </div> <!-- ❌ 避免使用 --> <div :style="dynamicStyle"> 标题文本 <input v-model="inputValue" /> </div>脚本编写
// ✅ 推荐写法 export default { data: { count: 0 }, increment() { this.count++ } } // ❌ 避免使用 export default { data() { return { count: 0 } }, methods: { increment() { this.count++ } } }
开发
# 开发模式
npm run dev
# 运行测试
npm test
# 构建
npm run build