html-to-vue-converter
v1.0.2
Published
A powerful tool to convert HTML/Vue files to Vue3 + TypeScript + Element Plus components, with CSS to SCSS conversion support
Maintainers
Readme
HTML to Vue Converter
📖 项目概述
html-to-vue-converter 是一个专业的代码转换工具包,旨在帮助开发者将传统的 HTML 页面或旧版 Vue 组件快速迁移到现代化的 Vue3 技术栈。
为什么需要这个工具?
- 降低迁移成本:手动转换大量 HTML 或 Vue2 代码耗时且容易出错
- 保持代码一致性:自动生成的代码遵循 Vue3 最佳实践
- 提升开发效率:批量处理文件,专注于业务逻辑而非重复劳动
- 学习参考:通过转换结果学习 Vue3 和 TypeScript 的最佳实践
目标用户
| 用户类型 | 使用场景 | |---------|---------| | 前端开发者 | 将旧项目迁移到 Vue3 | | 全栈工程师 | 快速原型转换为生产代码 | | 团队负责人 | 统一团队代码风格和规范 | | 学习者 | 了解 Vue3 与旧版本的差异 |
✨ 功能特性
核心功能
1. HTML 转 Vue3 单文件组件
输入: 标准 HTML 文件
输出: 完整的 .vue 单文件组件- 自动提取 HTML 结构转换为
<template> - 内联样式转换为
<style scoped> - 内联脚本转换为
<script setup> - 自动识别表单元素并添加
v-model绑定
2. Vue 文件升级转换
输入: Vue2 / 旧版 Vue3 组件
输出: Vue3 + TypeScript + Element Plus 组件- Options API → Composition API
- Vue2 生命周期钩子 → Vue3 钩子
- Element UI → Element Plus 组件映射
- 自动添加 TypeScript 类型定义
3. Element Plus 智能集成
| 原始元素 | 转换后组件 |
|---------|-----------|
| <input> | <el-input> |
| <select> | <el-select> |
| <button> | <el-button> |
| <form> | <el-form> |
| <table> | <el-table> |
4. TypeScript 类型生成
自动生成以下类型定义:
- Props 接口
- Emits 接口
- 响应式数据类型
5. CSS 转 SCSS 转换
将普通 CSS 转换为现代化的 SCSS 格式:
/* 原始 CSS */
.container { padding: 20px; }
.container .header { background: #409eff; }
.container .header .nav { display: flex; }
.button { background: #409eff; }
.button:hover { background: #66b1ff; }转换为 SCSS:
// 变量提取
$color-409eff: #409eff;
.container {
padding: 20px;
& .header {
background: $color-409eff;
& .nav {
display: flex;
}
}
}
.button {
background: $color-409eff;
&:hover {
background: #66b1ff;
}
}转换特性:
| 功能 | 说明 |
|------|------|
| 变量提取 | 自动提取重复的颜色、间距等值为 SCSS 变量 |
| 嵌套规则 | 将 .container .header 转换为嵌套结构 |
| 伪类嵌套 | .button:hover → .button { &:hover } |
| 媒体查询 | 支持在规则内嵌套媒体查询 |
| 注释保留 | 保留原有 CSS 注释 |
🚀 安装
系统要求
- Node.js >= 18.0.0
- npm >= 9.0.0 或 yarn >= 1.22.0
安装方式
方式一:全局安装(推荐)
# 使用 npm
npm install -g html-to-vue-converter
# 使用 yarn
yarn global add html-to-vue-converter方式二:项目本地安装
# 使用 npm
npm install --save-dev html-to-vue-converter
# 使用 yarn
yarn add --dev html-to-vue-converter方式三:使用 npx(无需安装)
npx html-to-vue-converter convert ./your-file.html验证安装
html2vue --version
# 输出: 1.0.0
html2vue --help
# 显示帮助信息🏃 快速开始
基础示例
转换 HTML 文件
假设有一个登录页面 login.html:
<!DOCTYPE html>
<html>
<head>
<title>登录</title>
<style>
.form { padding: 20px; }
.btn { background: #409eff; }
</style>
</head>
<body>
<form id="loginForm">
<input type="text" name="username" placeholder="用户名">
<input type="password" name="password" placeholder="密码">
<button type="submit" onclick="submitForm()">登录</button>
</form>
<script>
function submitForm() {
alert('提交成功');
}
</script>
</body>
</html>运行转换:
html2vue convert login.html -o ./components生成的 login.vue:
<template>
<el-form ref="loginForm" class="form">
<el-input
v-model="formData.username"
placeholder="用户名"
ref="username"
/>
<el-input
v-model="formData.password"
type="password"
placeholder="密码"
/>
<el-button type="primary" @click="submitForm">登录</el-button>
</el-form>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { ElForm, ElInput, ElButton } from 'element-plus';
const loginForm = ref();
const formData = reactive({
username: '',
password: ''
});
const submitForm = () => {
alert('提交成功');
};
</script>
<style scoped>
.form { padding: 20px; }
.btn { background: #409eff; }
</style>升级 Vue 文件
假设有一个 Vue2 组件:
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello'
}
},
mounted() {
console.log('mounted');
}
}
</script>运行升级:
html2vue upgrade old-component.vue -o ./new-components生成的 Vue3 组件:
<template>
<div>{{ message }}</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
const message = ref('Hello');
onMounted(() => {
console.log('mounted');
});
</script>📚 CLI 命令参考
全局选项
| 选项 | 简写 | 说明 |
|-----|-----|------|
| --version | -V | 显示版本号 |
| --help | -h | 显示帮助信息 |
convert 命令
转换 HTML 文件为 Vue3 单文件组件。
html2vue convert <input> [options]参数:
| 参数 | 必需 | 说明 |
|-----|-----|------|
| <input> | 是 | HTML 文件或目录路径 |
选项:
| 选项 | 简写 | 默认值 | 说明 |
|-----|-----|-------|------|
| --output | -o | ./output | 输出目录 |
| --typescript | -t | true | 使用 TypeScript |
| --no-typescript | - | - | 禁用 TypeScript |
| --element-plus | -e | true | 自动引入 Element Plus |
| --no-element-plus | - | - | 禁用 Element Plus |
| --prettier | -p | true | 格式化输出 |
| --scoped | -s | true | 使用 scoped 样式 |
| --scss | - | false | 将 CSS 转换为 SCSS 格式 |
| --scss-extract-colors | - | true | 提取颜色为 SCSS 变量 |
| --scss-extract-spacing | - | true | 提取间距为 SCSS 变量 |
| --overwrite | -w | false | 覆盖已存在的文件 |
| --verbose | -v | false | 显示详细日志 |
示例:
# 转换单个文件
html2vue convert page.html
# 转换到指定目录
html2vue convert page.html -o ./src/components
# 批量转换目录
html2vue convert ./pages -o ./src/views
# 禁用 Element Plus
html2vue convert page.html --no-element-plus
# 使用 SCSS 格式
html2vue convert page.html --scss
# 使用详细模式
html2vue convert page.html -vupgrade 命令
升级 Vue 文件到 Vue3 + TypeScript + Element Plus。
html2vue upgrade <input> [options]参数和选项与 convert 命令相同。
示例:
# 升级单个文件
html2vue upgrade old-component.vue
# 批量升级目录
html2vue upgrade ./src -o ./src-new
# 覆盖原文件
html2vue upgrade ./src --overwriteinfo 命令
分析 Vue 文件并显示详细信息。
html2vue info <file>输出示例:
📁 文件信息:
路径: ./component.vue
Vue 版本: 3
API 风格: Composition API
TypeScript: 是
🎨 样式:
[1] 语言: css, Scoped: 是
📦 导入:
- ref, reactive from 'vue'
- ElButton from 'element-plus'
🧩 组件:
- ElButtoncss-to-scss 命令
将 CSS 文件转换为 SCSS 格式。
html2vue css-to-scss <input> [options]参数:
| 参数 | 必需 | 说明 |
|-----|-----|------|
| <input> | 是 | CSS 文件或目录路径 |
选项:
| 选项 | 默认值 | 说明 |
|-----|-------|------|
| --output | -o | ./output | 输出目录 |
| --extract-variables | true | 提取重复值为变量 |
| --extract-colors | true | 提取颜色为变量 |
| --extract-spacing | true | 提取间距为变量 |
| --enable-nesting | true | 启用嵌套规则 |
| --overwrite | -w | false | 覆盖已存在的文件 |
| --verbose | -v | false | 显示详细日志 |
示例:
# 转换单个文件
html2vue css-to-scss ./styles.css
# 转换到指定目录
html2vue css-to-scss ./styles.css -o ./scss
# 批量转换目录
html2vue css-to-scss ./css-folder -o ./scss-folder
# 禁用嵌套
html2vue css-to-scss ./styles.css --no-enable-nesting转换效果:
输入 CSS:
.container { padding: 16px; }
.container .header { background: #409eff; }
.button { background: #409eff; }
.button:hover { background: #66b1ff; }输出 SCSS:
// Variables
$color-409eff: #409eff;
$spacing-16: 16px;
.container {
padding: $spacing-16;
& .header {
background: $color-409eff;
}
}
.button {
background: $color-409eff;
&:hover {
background: #66b1ff;
}
}init 命令
生成配置文件。
html2vue init [options]选项:
| 选项 | 默认值 | 说明 |
|-----|-------|------|
| --format | json | 配置文件格式 (json/js/ts) |
示例:
# 生成 JSON 配置
html2vue init
# 生成 TypeScript 配置
html2vue init --format ts⚙️ 配置
配置文件
在项目根目录创建 html2vue.config.json:
{
"outputDir": "./src/components",
"typescript": true,
"elementPlus": true,
"prettier": true,
"scoped": true,
"componentMappings": {
"button": "el-button",
"input": "el-input",
"select": "el-select"
}
}TypeScript 配置
// html2vue.config.ts
import type { HTMLToVueConfig } from 'html-to-vue-converter';
const config: HTMLToVueConfig = {
outputDir: './src/components',
typescript: true,
elementPlus: true,
prettier: true,
styleOptions: {
scoped: true,
cssModules: false,
preprocessLang: 'scss'
},
typescriptOptions: {
strict: true,
generateProps: true,
generateEmits: true
},
elementPlusOptions: {
autoImport: true,
resolveIcons: true
}
};
export default config;配置选项详解
基础选项
| 选项 | 类型 | 默认值 | 说明 |
|-----|------|-------|------|
| outputDir | string | ./output | 输出目录 |
| typescript | boolean | true | 是否使用 TypeScript |
| elementPlus | boolean | true | 是否自动引入 Element Plus |
| prettier | boolean | true | 是否格式化输出 |
| overwrite | boolean | false | 是否覆盖已存在文件 |
| verbose | boolean | false | 是否显示详细日志 |
样式选项 (styleOptions)
| 选项 | 类型 | 默认值 | 说明 |
|-----|------|-------|------|
| scoped | boolean | true | 使用 scoped 样式 |
| cssModules | boolean | false | 启用 CSS Modules |
| preprocessLang | string | - | 预处理器语言 (scss/less/stylus) |
| scss | boolean | false | 将 CSS 转换为 SCSS |
| extractVariables | boolean | true | 提取 CSS 变量 |
| enableNesting | boolean | true | 启用 SCSS 嵌套规则 |
| extractColors | boolean | true | 提取颜色为 SCSS 变量 |
| extractSpacing | boolean | true | 提取间距为 SCSS 变量 |
TypeScript 选项 (typescriptOptions)
| 选项 | 类型 | 默认值 | 说明 |
|-----|------|-------|------|
| strict | boolean | true | 严格模式 |
| generateProps | boolean | true | 生成 Props 类型 |
| generateEmits | boolean | true | 生成 Emits 类型 |
Element Plus 选项 (elementPlusOptions)
| 选项 | 类型 | 默认值 | 说明 |
|-----|------|-------|------|
| autoImport | boolean | true | 自动导入组件 |
| resolveIcons | boolean | true | 解析图标组件 |
自定义组件映射
{
"componentMappings": {
"my-button": "el-button",
"my-input": "el-input",
"custom-select": "el-select"
}
}📦 API 文档
编程方式使用
基础导入
import {
// 转换函数
convertHTML,
upgradeVue,
batchConvert,
batchUpgrade,
cssToSCSS,
// 类
HTMLToVueConverter,
VueUpgrader,
CSSToSCSSConverter,
HTMLParser,
VueParser,
ElementPlusGenerator,
TypeScriptGenerator,
// 工具函数
toPascalCase,
toKebabCase,
toCamelCase,
// 类型
type ConvertOptions,
type ConversionResult,
type BatchConversionResult,
type CSSToSCSSOptions,
type SCSSConversionResult,
type SCSSVariable
} from 'html-to-vue-converter';convertHTML
快速转换 HTML 内容。
import { convertHTML } from 'html-to-vue-converter';
const html = `
<form>
<input type="text" name="username">
<button type="submit">提交</button>
</form>
`;
const result = await convertHTML(html, 'LoginForm.vue', {
typescript: true,
elementPlus: true,
});
console.log(result.content); // Vue SFC 内容
console.log(result.warnings); // 警告信息
console.log(result.elementPlusComponents); // 使用的 Element Plus 组件upgradeVue
快速升级 Vue 文件。
import { upgradeVue } from 'html-to-vue-converter';
const vue2Code = `
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
data() {
return { message: 'Hello' }
}
}
</script>
`;
const result = await upgradeVue(vue2Code, 'MyComponent.vue', {
typescript: true,
elementPlus: true,
});cssToSCSS
快速转换 CSS 为 SCSS。
import { cssToSCSS } from 'html-to-vue-converter';
const css = `
.container { padding: 16px; }
.container .header { background: #409eff; }
.button { background: #409eff; }
.button:hover { background: #66b1ff; }
`;
const result = cssToSCSS(css, {
extractVariables: true,
extractColors: true,
extractSpacing: true,
enableNesting: true,
});
console.log(result.scss); // SCSS 内容
console.log(result.variables); // 提取的变量列表
// [{ name: 'color-409eff', value: '#409eff' }, { name: 'spacing-16', value: '16px' }]CSSToSCSSConverter 类
使用转换器实例进行更精细的控制。
import { CSSToSCSSConverter } from 'html-to-vue-converter';
const converter = new CSSToSCSSConverter({
extractVariables: true,
variableThreshold: 2, // 重复2次以上才提取为变量
enableNesting: true,
preserveComments: true,
sortProperties: false,
indentStyle: '2spaces', // 缩进风格
extractColors: true,
extractSpacing: true,
});
const result = converter.convert(cssContent);
// 获取 SCSS 内容
console.log(result.scss);
// 获取提取的变量
result.variables.forEach(v => {
console.log(`$${v.name}: ${v.value};`);
});批量处理
import { batchConvert, batchUpgrade } from 'html-to-vue-converter';
// 批量转换 HTML
const htmlFiles = [
{ path: 'page1.html', content: '<html>...</html>' },
{ path: 'page2.html', content: '<html>...</html>' },
];
const result = await batchConvert(htmlFiles, {
typescript: true,
elementPlus: true,
});
console.log(`成功: ${result.successful.length}`);
console.log(`失败: ${result.failed.length}`);
console.log(`耗时: ${result.duration}ms`);使用类实例
import { HTMLToVueConverter, VueUpgrader } from 'html-to-vue-converter';
// 创建转换器实例
const converter = new HTMLToVueConverter({
typescript: true,
elementPlus: true,
styleOptions: { scoped: true },
});
// 转换内容
const result = converter.convert(
htmlContent,
'MyComponent.html',
'output/MyComponent.vue'
);
// 创建升级器实例
const upgrader = new VueUpgrader({
typescript: true,
elementPlus: true,
});
// 升级内容
const upgradedResult = upgrader.upgrade(
vue2Content,
'OldComponent.vue',
'output/OldComponent.vue'
);解析器使用
import { HTMLParser, VueParser } from 'html-to-vue-converter';
// HTML 解析
const htmlParser = new HTMLParser();
const parsedHTML = htmlParser.parse(htmlContent);
console.log(parsedHTML.title);
console.log(parsedHTML.body);
console.log(parsedHTML.styles);
console.log(parsedHTML.scripts);
// 提取表单元素
const formElements = htmlParser.extractFormElements(htmlContent);
// 提取内联事件
const eventHandlers = htmlParser.extractInlineEventHandlers(htmlContent);
// Vue 解析
const vueParser = new VueParser();
const parsedVue = vueParser.parse(vueContent, 'Component.vue');
console.log(vueParser.isOptionsAPI(parsedVue)); // 是否 Options API
console.log(vueParser.isCompositionAPI(parsedVue)); // 是否 Composition API
console.log(vueParser.isTypeScript(parsedVue)); // 是否 TypeScript
console.log(vueParser.detectVueVersion(parsedVue)); // Vue 版本 (2 或 3)类型定义
ConvertOptions
interface ConvertOptions {
outputDir?: string;
typescript?: boolean;
elementPlus?: boolean;
componentMappings?: Record<string, string>;
prettier?: boolean;
preserveStructure?: boolean;
customRules?: ConversionRule[];
verbose?: boolean;
overwrite?: boolean;
encoding?: BufferEncoding;
styleOptions?: {
scoped?: boolean;
cssModules?: boolean;
preprocessLang?: 'scss' | 'less' | 'stylus';
scss?: boolean;
extractVariables?: boolean;
enableNesting?: boolean;
extractColors?: boolean;
extractSpacing?: boolean;
};
}CSSToSCSSOptions
interface CSSToSCSSOptions {
/** 提取重复值为变量 */
extractVariables?: boolean;
/** 最小重复次数阈值 */
variableThreshold?: number;
/** 启用嵌套规则 */
enableNesting?: boolean;
/** 保留注释 */
preserveComments?: boolean;
/** 按字母排序属性 */
sortProperties?: boolean;
/** 缩进风格 */
indentStyle?: '2spaces' | '4spaces' | 'tab';
/** 提取颜色为变量 */
extractColors?: boolean;
/** 提取间距为变量 */
extractSpacing?: boolean;
}SCSSConversionResult
interface SCSSConversionResult {
/** 生成的 SCSS 内容 */
scss: string;
/** 提取的变量列表 */
variables: SCSSVariable[];
}
interface SCSSVariable {
name: string;
value: string;
}ConversionResult
interface ConversionResult {
content: string; // 转换后的内容
outputPath: string; // 输出路径
elementPlusComponents: ElementPlusComponent[];
interfaces?: string; // 生成的接口
warnings: string[]; // 警告列表
errors: string[]; // 错误列表
}BatchConversionResult
interface BatchConversionResult {
successful: ConversionResult[];
failed: Array<{
inputPath: string;
error: string;
}>;
total: number;
duration: number;
}🔌 Claude Code 插件
可用命令
| 命令 | 说明 |
|------|------|
| /html2vue | 转换 HTML 内容或文件 |
| /vue-upgrade | 升级 Vue 文件 |
| /vue-analyze | 分析 Vue 文件结构 |
| /html2vue-help | 显示帮助信息 |
使用示例
# 转换 HTML 内容
/html2vue <div class="container"><button>Click</button></div>
# 转换文件
/html2vue ./page.html --output ./components
# 升级 Vue 文件
/vue-upgrade ./old-component.vue
# 分析组件
/vue-analyze ./component.vue
# 查看帮助
/html2vue-help /html2vue命令选项
--ts, --typescript 使用 TypeScript(默认开启)
--no-ts 禁用 TypeScript
--element-plus 自动引入 Element Plus(默认开启)
--no-element-plus 禁用 Element Plus
--output, -o <dir> 输出目录
--scoped 使用 scoped 样式
--prettier 格式化输出🛠️ 故障排除
常见问题
1. 命令未找到
问题: 运行 html2vue 提示命令未找到
解决方案:
# 确保全局安装
npm install -g html-to-vue-converter
# 或使用 npx
npx html-to-vue-converter convert ./file.html
# 检查 PATH 是否包含 npm 全局目录
npm config get prefix2. 转换后样式丢失
问题: 转换后部分样式未正确保留
解决方案:
# 使用详细模式查看警告
html2vue convert ./file.html -v
# 检查原始 HTML 中的内联样式和外部样式表
# 外部样式表需要手动迁移3. Element Plus 组件未正确识别
问题: 某些 HTML 元素未转换为 Element Plus 组件
解决方案:
// 在配置文件中添加自定义映射
{
"componentMappings": {
"my-custom-input": "el-input"
}
}4. TypeScript 类型生成错误
问题: 生成的类型定义与实际使用不符
解决方案:
// 手动调整生成的类型
interface MyComponentProps {
// 添加正确的类型
data: MyDataType;
}5. 批量转换时内存不足
问题: 处理大量文件时内存溢出
解决方案:
# 分批处理
html2vue convert ./batch1 -o ./output
html2vue convert ./batch2 -o ./output
# 或增加 Node.js 内存限制
NODE_OPTIONS="--max-old-space-size=4096" html2vue convert ./pages调试技巧
# 启用详细日志
html2vue convert ./file.html -v
# 分析源文件
html2vue info ./source.vue
# 检查转换结果
html2vue convert ./file.html -o ./test-output获取帮助
- 查看 GitHub Issues
- 提交 Bug 报告或功能请求
- 加入社区讨论
🤝 贡献指南
开发环境设置
# 克隆仓库
git clone https://github.com/your-repo/html-to-vue-converter.git
cd html-to-vue-converter
# 安装依赖
npm install
# 运行开发模式
npm run dev
# 运行测试
npm test
# 构建项目
npm run build项目结构
html-to-vue-converter/
├── src/
│ ├── parser/ # 解析器模块
│ │ ├── html-parser.ts
│ │ └── vue-parser.ts
│ ├── converter/ # 转换器模块
│ │ ├── html-to-vue.ts
│ │ ├── vue-upgrader.ts
│ │ └── css-to-scss.ts
│ ├── generator/ # 生成器模块
│ │ ├── element-plus.ts
│ │ └── typescript.ts
│ ├── cli/ # 命令行工具
│ ├── claude-plugin/ # Claude Code 插件
│ ├── utils/ # 工具函数
│ └── types/ # 类型定义
├── test/ # 测试文件
├── examples/ # 示例文件
└── dist/ # 构建输出提交规范
feat: 添加新功能
fix: 修复 Bug
docs: 文档更新
style: 代码格式调整
refactor: 重构代码
test: 测试相关
chore: 构建/工具相关代码风格
- 使用 TypeScript 编写
- 遵循 ESLint 规则
- 编写单元测试
- 保持测试覆盖率 >= 80%
📄 许可证
本项目采用 MIT 许可证。
MIT License
Copyright (c) 2024
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.📮 联系方式
- 问题反馈: GitHub Issues
- 功能建议: GitHub Discussions
- 安全问题: 请发送邮件至 [email protected]
