核心:使用模板引擎渲染出 Vue 代码
🔧第一步:安装所需依赖
npm install handlebars
并在index.html文件中引入以下文件:
用于美化代码
<script src="https://unpkg.com/prettier@2.8.8/standalone.js"></script>
<script src="https://unpkg.com/prettier@2.8.8/parser-html.js"></script>
<script src="https://unpkg.com/prettier@2.8.8/parser-babel.js"></script>
📁第二步:创建模板字符串(可在 JS 中定义)
定义结构化 Vue 模板
// templates/component-template.js
const componentTemplate = `
<template>
<div class="{{kebabCase name}}-container">
{{#each elements}}
<{{this.tag}}
{{#if this.props}}
{{#each this.props}}
{{@key}}="{{this}}"
{{/each}}
{{/if}}>
{{#if this.children}}
{{#each this.children}}
{{{this}}}
{{/each}}
{{else}}
{{this.text}}
{{/if}}
</{{this.tag}}>
{{/each}}
</div>
</template>
<script setup>
// Auto-generated component: {{name}}
</script>
<style scoped>
.{{kebabCase name}}-container {
padding: 20px;
}
</style>
`
export default componentTemplate
📄 第三步:编写 JSON 数据源
{
"name": "UserCard",
"elements": [
{
"tag": "h2",
"text": "用户名:张三"
},
{
"tag": "p",
"text": "年龄:25岁"
},
{
"tag": "button",
"props": {
"onClick": "alert('点击了按钮')",
"title": "点我"
},
"text": "查看详情"
}
]
}
⚙️ 第四步:编写转换逻辑(前端 Vue 组件内部)
<template>
<div>
<h2>JSON 转 Vue 代码演示</h2>
<!-- JSON 输入区域 -->
<textarea v-model="jsonInput" rows="10" cols="60" placeholder="请输入 JSON 数据"></textarea>
<br />
<button @click="generateCode">生成 Vue 代码</button>
<!-- 显示生成的代码 -->
<h3>生成的 Vue 代码:</h3>
<pre v-if="generatedCode"><code>{{ generatedCode }}</code></pre>
<!-- 实时预览生成的组件 -->
<h3>实时预览:</h3>
<Suspense>
<component :is="dynamicComponent" v-if="dynamicComponent" />
</Suspense>
</div>
</template>
<script setup>
import { ref, shallowRef, h } from 'vue';
import Handlebars from 'handlebars';
import componentTemplate from '../../templates/component-template.js';
// 注册辅助函数
Handlebars.registerHelper('kebabCase', str =>
str.replace(/([a-z])([A-Z])/g, '\$1-\$2').toLowerCase()
);
const jsonInput = ref(`{
"name": "UserCard",
"elements": [
{
"tag": "h2",
"text": "用户名:张三"
},
{
"tag": "p",
"text": "年龄:25岁"
},
{
"tag": "button",
"props": {
"onClick": "alert('点击了按钮')",
"title": "点我"
},
"text": "查看详情"
}
]
}`);
const generatedCode = ref('');
const dynamicComponent = shallowRef(null);
const generateCode = () => {
try {
const jsonData = JSON.parse(jsonInput.value);
// 将数据填入模板
const template = Handlebars.compile(componentTemplate);
const fullCode = template(jsonData); // 包含 <template>...</template> 的完整代码
const prettyCode = formatVueCode(fullCode)
generatedCode.value = prettyCode
// 提取 template 内容
const templateContent = prettyCode.match(/<template>([\s\S]*)<\/template>/)?.[1]?.trim();
console.log(templateContent)
if (!templateContent) {
throw new Error("无法提取有效模板内容");
}
dynamicComponent.value = {
name: jsonData.name || "DynamicComponent",
setup() {
return () => h('div', { innerHTML: templateContent }); // 使用 render 函数
}
}
// // 安全渲染方案:使用 VNodes 替代字符串模板
// dynamicComponent.value = {
// name: jsonData.name || "DynamicComponent",
// setup() {
// // 这里可以添加更精细的 VNode 构造逻辑
// return () => h('div', [
// h('h1', jsonData.title),
// jsonData.items?.map(item => h('p', item))
// ]);
// }
// };
} catch (e) {
console.error('JSON 解析或模板编译失败:', e)
alert('JSON 格式错误或模板编译异常')
}
}
const formatVueCode = (code) => {
return window.prettier.format(code, {
parser: 'vue',
plugins: window.prettierPlugins,
semi: false,
singleQuote: true
})
}
</script>
<style scoped>
pre {
background-color: #f4f4f4;
padding: 10px;
border-radius: 4px;
white-space: pre-wrap;
}
</style>