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

原理思考

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

第一点,明确我们的目标是自动化生成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>
相关推荐
10年前端老司机27 分钟前
React无限级菜单:一个项目带你突破技术瓶颈
前端·javascript·react.js
阿芯爱编程5 小时前
2025前端面试题
前端·面试
wenzhangli75 小时前
低代码引擎核心技术:OneCode常用动作事件速查手册及注解驱动开发详解
人工智能·低代码·云原生
前端小趴菜056 小时前
React - createPortal
前端·vue.js·react.js
晓13136 小时前
JavaScript加强篇——第四章 日期对象与DOM节点(基础)
开发语言·前端·javascript
菜包eo6 小时前
如何设置直播间的观看门槛,让直播间安全有效地运行?
前端·安全·音视频
烛阴7 小时前
JavaScript函数参数完全指南:从基础到高级技巧,一网打尽!
前端·javascript
chao_7898 小时前
frame 与新窗口切换操作【selenium 】
前端·javascript·css·selenium·测试工具·自动化·html
天蓝色的鱼鱼8 小时前
从零实现浏览器摄像头控制与视频录制:基于原生 JavaScript 的完整指南
前端·javascript
三原8 小时前
7000块帮朋友做了2个小程序加一个后台管理系统,值不值?
前端·vue.js·微信小程序