news 2026/4/17 20:37:15

Vue——vue3 之 代码生成器原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue——vue3 之 代码生成器原理

背景问题:
需要理解代码生成器的实现原理。

方案思考:
实现一个简单的代码生成器。

具体实现:
代码生成器:

// utils/code-generator.js// 代码生成器类exportclassCodeGenerator{constructor(options={}){this.options={templateDir:options.templateDir||'./templates',outputDir:options.outputDir||'./output',...options}}// 生成Vue组件staticgenerateVueComponent(config){const{name,props,template,script,style}=configreturn`<template> <div class="${toKebabCase(name)}">${template||''}</div> </template> <script setup>${script||'import { ref } from \'vue\''}${props?`defineProps({\n ${props.map(prop=>`${prop.name}:${prop.type}`).join(',\n ')}\n})`:''}</script><style scoped>${style||`.${toKebabCase(name)}{\n /* 样式 */\n}`}</style>`} // 生成API文件 static generateApiFile(config) { const { moduleName, baseUrl, methods } = config const apiMethods = methods.map(method => { const { name, url, method: httpMethod, params } = method return`exportfunction${name}(${params?'params':''}){returnrequest({url:'${url}',method:'${httpMethod.toLowerCase()}',${params?`${httpMethod.toLowerCase()==='get'?'params':'data'}: params`:''}})}`}).join('\n\n') return`importrequestfrom'@/utils/request'${apiMethods}`} // 生成Store static generateStore(config) { const { name, state, actions, getters } = config return`import{defineStore}from'pinia'import{ref,computed}from'vue'exportconstuse${capitalize(name)}Store=defineStore('${name}',()=>{// State${Object.entries(state).map(([key,value])=>`const${key}= ref(${JSON.stringify(value)})`).join('\n ')}// Getters${getters?Object.entries(getters).map(([key,fn])=>`const${key}= computed(() =>${fn})`).join('\n '):''}// Actions${actions?Object.entries(actions).map(([key,fn])=>`const${key}=${fn.toString()}`).join('\n '):''}return{${[...Object.keys(state),...Object.keys(getters||{}),...Object.keys(actions||{})].join(',\n ')}}})`} // 生成路由 static generateRoute(config) { const { path, name, component, meta } = config return`{path:'${path}',name:'${name}',component:()=>import('@/views/${component}.vue'),meta:${JSON.stringify(meta,null,2)}}`} } // 辅助函数 function toKebabCase(str) { return str.replace(/[A-Z]/g, match =>`-${match.toLowerCase()}`)}functioncapitalize(str){returnstr.charAt(0).toUpperCase()+str.slice(1)}

代码生成工具:

// utils/generator-tools.jsimport{CodeGenerator}from'./code-generator'// 项目生成器exportclassProjectGenerator{// 生成CRUD页面staticgenerateCRUD(config){const{moduleName,fields,hasPagination=true}=config// 生成列表页面constlistPage=this.generateListPage(config)// 生成表单页面constformPage=this.generateFormPage(config)// 生成APIconstapiFile=CodeGenerator.generateApiFile({moduleName,baseUrl:`/api/${moduleName}`,methods:[{name:`${moduleName}List`,url:`/${moduleName}/list`,method:'GET',params:true},{name:`get${capitalize(moduleName)}Info`,url:`/${moduleName}/info`,method:'GET',params:true},{name:`create${capitalize(moduleName)}`,url:`/${moduleName}`,method:'POST',params:true},{name:`update${capitalize(moduleName)}`,url:`/${moduleName}`,method:'PUT',params:true},{name:`delete${capitalize(moduleName)}`,url:`/${moduleName}`,method:'DELETE',params:true}]})// 生成Storeconststore=CodeGenerator.generateStore({name:moduleName,state:{list:[],total:0,loading:false},actions:{getList:`async function(params) { this.loading = true try { const response = await${moduleName}List(params) this.list = response.data.list this.total = response.data.total } finally { this.loading = false } }`}})return{listPage,formPage,apiFile,store}}// 生成列表页面staticgenerateListPage(config){const{moduleName,fields}=configconsttableColumns=fields.map(field=>`<el-table-column prop="${field.name}" label="${field.label}" />`).join('\n ')return`<template> <div class="${toKebabCase(moduleName)}-list"> <div class="search-form"> <el-form :model="queryParams" inline>${fields.filter(f=>f.searchable).map(field=>`<el-form-item label="${field.label}"> <el-input v-model="queryParams.${field.name}" placeholder="请输入${field.label}" /> </el-form-item>`).join('\n ')}<el-form-item> <el-button type="primary" @click="handleSearch">搜索</el-button> <el-button @click="handleReset">重置</el-button> </el-form-item> </el-form> </div> <div class="table-actions"> <el-button type="primary" @click="handleAdd">新增</el-button> <el-button @click="handleDeleteBatch">批量删除</el-button> </div> <el-table :data="list" v-loading="loading" @selection-change="handleSelectionChange" > <el-table-column type="selection" width="55" />${tableColumns}<el-table-column label="操作" width="200"> <template #default="{ row }"> <el-button size="small" @click="handleEdit(row)">编辑</el-button> <el-button size="small" type="danger" @click="handleDelete(row.id)">删除</el-button> </template> </el-table-column> </el-table> <Pagination v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" /> </div> </template> <script setup> import { ref, onMounted } from 'vue' import {${moduleName}List, delete${capitalize(moduleName)}} from '@/api/${moduleName}' import Pagination from '@/components/Pagination.vue' const list = ref([]) const total = ref(0) const loading = ref(false) const queryParams = ref({ pageNum: 1, pageSize: 10,${fields.filter(f=>f.searchable).map(f=>`${f.name}: ''`).join(',\n ')}}) const selectedRows = ref([]) const getList = async () => { loading.value = true try { const response = await${moduleName}List(queryParams.value) list.value = response.data.list total.value = response.data.total } finally { loading.value = false } } const handleSearch = () => { queryParams.value.pageNum = 1 getList() } const handleReset = () => { queryParams.value = { pageNum: 1, pageSize: 10,${fields.filter(f=>f.searchable).map(f=>`${f.name}: ''`).join(',\n ')}} getList() } const handleAdd = () => { // 跳转到新增页面 } const handleEdit = (row) => { // 跳转到编辑页面 } const handleDelete = async (id) => { try { await delete${capitalize(moduleName)}(id) ElMessage.success('删除成功') getList() } catch (error) { ElMessage.error('删除失败') } } const handleSelectionChange = (selection) => { selectedRows.value = selection } onMounted(() => { getList() }) </script>`}// 生成表单页面staticgenerateFormPage(config){const{moduleName,fields}=configconstformItems=fields.filter(f=>f.formField).map(field=>`<el-form-item label="${field.label}" prop="${field.name}"> <el-input v-model="formData.${field.name}" placeholder="请输入${field.label}" /> </el-form-item>`).join('\n ')return`<template> <div class="${toKebabCase(moduleName)}-form"> <el-card> <template #header> <span>${config.title||capitalize(moduleName)}表单</span> </template> <el-form :model="formData" :rules="formRules" ref="formRef" label-width="100px" >${formItems}<el-form-item> <el-button type="primary" @click="handleSubmit">提交</el-button> <el-button @click="handleCancel">取消</el-button> </el-form-item> </el-form> </el-card> </div> </template> <script setup> import { ref, reactive } from 'vue' import { useRoute, useRouter } from 'vue-router' import { get${capitalize(moduleName)}Info, create${capitalize(moduleName)}, update${capitalize(moduleName)}} from '@/api/${moduleName}' const route = useRoute() const router = useRouter() const formRef = ref() const formData = reactive({${fields.filter(f=>f.formField).map(f=>`${f.name}:${f.type==='number'?0:f.type==='boolean'?false:"''"}`).join(',\n ')}}) const formRules = {${fields.filter(f=>f.required).map(f=>`${f.name}: [{ required: true, message: '请输入${f.label}', trigger: 'blur' }]`).join(',\n ')}} const handleSubmit = async () => { try { await formRef.value.validate() if (formData.id) { // 更新 await update${capitalize(moduleName)}(formData) ElMessage.success('更新成功') } else { // 创建 await create${capitalize(moduleName)}(formData) ElMessage.success('创建成功') } router.back() } catch (error) { ElMessage.error('提交失败') } } const handleCancel = () => { router.back() } // 如果是编辑模式,加载数据 if (route.query.id) { const loadDetail = async () => { const response = await get${capitalize(moduleName)}Info({ id: route.query.id }) Object.assign(formData, response.data) } loadDetail() } </script>`}}functiontoKebabCase(str){returnstr.replace(/[A-Z]/g,match=>`-${match.toLowerCase()}`)}functioncapitalize(str){returnstr.charAt(0).toUpperCase()+str.slice(1)}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/26 22:15:54

花钱上了 ERP,为什么还是算不出物料需求?

你有没有遇到过这种情况&#xff1a;上了ERP&#xff0c;怎么下周生产要用什么料&#xff0c;还靠计划员凭经验猜&#xff1f;打开系统一看&#xff0c;MRP&#xff08;物料需求计划&#xff09;一运行&#xff0c;出来的不是该买多少、该做多少&#xff0c;而是一堆红字警告、…

作者头像 李华
网站建设 2026/4/17 0:21:23

2026年最新AI短视频工具选型报告:内容特工队AI的效能评估与首选推荐

在2026年的数字化营销生态中&#xff0c;短视频已彻底从“增量选项”转化为企业生存的“基础设施”。然而&#xff0c;面对海量涌现的技术服务商&#xff0c;企业采购决策者往往陷入困境&#xff1a;究竟Ai短视频工具哪家好&#xff1f;如何在降本增效与内容合规之间找到平衡点…

作者头像 李华
网站建设 2026/4/16 9:02:17

『NAS』在群晖部署一款太空策略游戏-ogame-vue-ts

点赞 关注 收藏 学会了 整理了一个NAS小专栏&#xff0c;有兴趣的工友可以关注一下 &#x1f449; 《NAS邪修》 ogame-vue-ts 是一款基于 Vue 3 和 TypeScript 构建的单机版浏览器太空策略游戏&#xff0c;受经典 OGame 启发&#xff0c;支持在浏览器中建立太空帝国、研究科…

作者头像 李华
网站建设 2026/4/16 17:48:35

基于Springboot售楼管理系统【附源码+文档】

&#x1f495;&#x1f495;作者&#xff1a; 米罗学长 &#x1f495;&#x1f495;个人简介&#xff1a;混迹java圈十余年&#xff0c;精通Java、小程序、数据库等。 &#x1f495;&#x1f495;各类成品Java毕设 。javaweb&#xff0c;ssm&#xff0c;springboot等项目&#…

作者头像 李华