递归式生成前端代码:从原理到实践

原理思考

在设计递归式代码生成函数时,我们主要关注了几个核心原则。

第一点,明确我们的目标是自动化生成HTML代码,这需要我们深入理解代码的结构和语法。

第二点,考虑到代码的可读性和可维护性,我们尽量保持代码简洁,并使用字符串模板进行代码拼接。

第三点,我们根据属性前缀的不同格式化属性,并根据子节点的存在与否进行递归处理。

数据结构设计

可以设计一个组件节点ComponentNode的数据结构,包含name、props、children字段,其中children再嵌套子组件节点。

js 复制代码
class ComponentNode {
  constructor(name, props) {
    this.name = name
    this.props = props
    this.children = [] 
  }
}

递归算法设计

递归地处理节点和其子节点的属性及结构

js 复制代码
function generateCode(node) {

  // 拼接开始标签
  let code = `<${node.name}`
  
  // 拼接属性
Object.keys(node.props).forEach(key => {

  let value = node.props[key]

  if (key.startsWith('@')) {
    code += ` @${key.slice(1)}=${value}`
  } else if (key.startsWith(':')) {
    code += ` :${key.slice(1)}=${value}` 
  } else if (key.startsWith('v-')) {
    code += ` ${key}="${value}"`
  } else {
    code += ` ${key}="${value}"`
  }

})
  
  // 拼接子组件代码
  if (node.children.length) {
    code += '>'
    node.children.forEach(child => {
      code += generateCode(child) 
    })
    code += `</${node.name}>`
  } else {
    code += ' />'
  }

  return code
}

设计解析

  1. 开始标签的拼接

    • 首先,函数通过 node.name 获取节点名称,并将其与 < 符号拼接,从而得到开始标签。
  2. 属性拼接

    • 使用 Object.keys(node.props).forEach 遍历节点的属性对象 node.props

    • 根据属性的键前缀(例如 '@', ':', 'v-'),对属性进行不同的处理:

      • 如果属性键以 '@' 开头,则在代码中添加 @ 前缀和对应的属性值。
      • 如果属性键以 ':' 开头,则在代码中添加 : 前缀和对应的属性值。
      • 如果属性键以 'v-' 开头,则在代码中添加空格和属性键,以及等号和对应的属性值。
      • 对于其他属性键,直接添加空格、属性键和等号、属性值。
  3. 子组件代码的拼接

    • 检查节点是否有子节点。如果有,将 > 添加到代码中,然后对每个子节点递归调用 generateCode 函数,并将结果拼接到代码中。最后,添加 </${node.name}> 作为结束标签。
    • 如果没有子节点,则直接添加 /> 作为结束标签。
  4. 返回结果

    • 最后,返回生成的代码字符串。

入参示例

js 复制代码
// 组件节点
const node = new ComponentNode('el-table', {
  data: tableData, // 动态数据
  class: 'table-border', // 普通的 class 属性
  ':style': {color: 'red'}, // 动态样式
  '@click': onClick, // 点击事件
  'v-loading': loading, // v-指令
  'ref': 'tableRef', // ref 属性
  slot: 'slotContent', // 具名 slot
  is: 'component' // is 属性
})

node.children = [
  new ComponentNode('el-table-column', {
    prop: 'name',
    label: '姓名'
  }),
  new ComponentNode('el-table-column', { 
    prop: 'address',
    label: '地址' 
  }) 
]

生成结果

html 复制代码
<el-table data=tableData class="table-border" :style={color: 'red'} @click=onClick v-loading=loading ref="tableRef" slot="slotContent" is="component">
  <el-table-column prop="name" label="姓名" />
  <el-table-column prop="address" label="地址" /> 
</el-table>
相关推荐
人工智能训练师1 天前
Ubuntu22.04如何安装新版本的Node.js和npm
linux·运维·前端·人工智能·ubuntu·npm·node.js
Seveny071 天前
pnpm相对于npm,yarn的优势
前端·npm·node.js
yddddddy1 天前
css的基本知识
前端·css
昔人'1 天前
css `lh`单位
前端·css
Nan_Shu_6141 天前
Web前端面试题(2)
前端
知识分享小能手1 天前
React学习教程,从入门到精通,React 组件核心语法知识点详解(类组件体系)(19)
前端·javascript·vue.js·学习·react.js·react·anti-design-vue
蚂蚁RichLab前端团队1 天前
🚀🚀🚀 RichLab - 花呗前端团队招贤纳士 - 【转岗/内推/社招】
前端·javascript·人工智能
孩子 你要相信光1 天前
css之一个元素可以同时应用多个动画效果
前端·css
huangql5201 天前
npm 发布流程——从创建组件到发布到 npm 仓库
前端·npm·node.js
Days20501 天前
LeaferJS好用的 Canvas 引擎
前端·开源