42-mini-vue 实现 transform 功能

实现 transform 功能

  1. transform 功能
  • transform 就是对生成的整颗 ast 树,做增删改查的操作。
  1. 具体需求, 给 hi, 后面拼接 mini-vue
html 复制代码
  <div>hi,{{message}}</div>
  <!-- 过程 -->
  root---> element----> text
  `----> 插值
  1. 核心点
  • 找到 hi, 这块的 text 节点
    • 遍历一棵树
      • 广度优先搜索
      • 深度优先搜索 ----> 我们这里使用深度优先搜索进行遍历 ----> 使用递归来做处理
  • 修改 content 这块的值
  1. 先简单实现一个 happy path
js 复制代码
import {
    baseParse
} from "../src/parse"
import {
    transform
} from "../src/transform"

describe("transform", () => {
    it("happy path", () => {
        const ast = baseParse("<div>hi,{{message}}</div>")
        transform(ast)
        const nodeText = ast.children[0].children[0]
        expect(nodeText.content).toBe("hi,mini-vue")
    })
})
  1. 具体实现
js 复制代码
import {
    NodeTypes
} from "./ast";

export function transform(root) {
    // 1.遍历-深度优先搜索
    traverseNode(root)
    // 2.修改 text content
}

function traverseNode(node: any) {
    if (node.type === NodeTypes.TEXT) { // 这里处理修改
        node.content += 'mini-vue'
    }
    const children = node.children
    if (children) {
        for (let i = 0; i < children.length; i++) {
            const node = children[i]
            traverseNode(node)
        }
    }
}
  1. 测试通过,我们进行优化
  • 如果我们在拼接 mini-vue 以后,还要处理 element 节点

  • 还需要处理插值节点

  • 有的时候需要根据不同的环境做不同的处理, 有些环境不需要处理 text,有些则需要。

  • 这时候我们修改,拼接的内容,应该师动态的,也就是 mini-vue 是动态的

  • 还有递归的流程是固定的,我们需要抽离出变动点和稳定点的

  1. 如果处理呢?
  • 我们把动态拼接部分抽离出来,放到页面上
js 复制代码
describe("transform", () => {
    it("happy path", () => {
        const ast = baseParse("<div>hi,{{message}}</div>")
        const plugin = (node: any) => {
            if (node.type === NodeTypes.TEXT) { // 想实现什么类型写什么类型,高内聚,低耦合,程序的可测试性
                node.content += 'mini-vue'
            }
        }
        // 把要处理的逻辑传入过去,程序调用我们传入的函数,并把节点传回来,这样在外部 plugin 中处理内容区域,可以方便灵活的处理节点
        transform(ast, {
            nodeTransforms: [plugin]
        })
        const nodeText = ast.children[0].children[0]
        expect(nodeText.content).toBe("hi,mini-vue")
    })
})
js 复制代码
export function transform(root, options) {
    // 缓存传入的 plugin
    const context = createTransformContext(root, options)
    // 1.遍历-深度优先搜索
    traverseNode(root, context)
    // 2.修改 text content
}

function createTransformContext(root, options) {
    const context = {
        root,
        nodeTransforms: options.nodeTransforms || []
    }
    return context
}

function traverseNode(node: any, context) {
    // 这里就是外面传入的 plugin
    const nodeTransforms = context.nodeTransforms
    for (let i = 0; i < nodeTransforms.length; i++) {
        const transform = nodeTransforms[i]
        transform(node)
    }
    const children = node.children
    if (children) {
        for (let i = 0; i < children.length; i++) {
            const node = children[i]
            traverseNode(node, context)
        }
    }
}
  1. 重构递归函数
js 复制代码
function traverseNode(node: any, context) {
  const nodeTransforms = context.nodeTransforms
  for(let i = 0; i < nodeTransforms.length;i++) {
    const transform = nodeTransforms[i]
    transform(node)
  }
  traverseChildren(node, context) // 
}

function traverseChildren(node: any, context: any) {
  const children = node.children
  if (children) {
    for (let i = 0; i < children.length; i++) {
      const node = children[i]
      traverseNode(node, context)
    }
  }
}
相关推荐
无限大620 小时前
AI实战02:一个万能提示词模板,搞定90%的文案/设计/分析需求
前端·后端
朝阳58121 小时前
控制 Nuxt 页面的渲染模式:客户端 vs 服务端渲染
前端·javascript
发现一只大呆瓜21 小时前
Vue-Vue2与Vue3核心差异与进化
前端·vue.js·面试
sunny_21 小时前
熬夜通宵读完 VitePlus 全部源码,我后悔没早点看
前端·前端框架·前端工程化
发现一只大呆瓜21 小时前
Vue2:数组/对象操作避坑大全
前端·vue.js·面试
发现一只大呆瓜21 小时前
Vue3:ref 与 reactive 超全对比
前端·vue.js·面试
lzksword21 小时前
C++ Builder XE OpenDialog1打开多文件并显示xls与xlsx二种格式文件
java·前端·c++
陈随易1 天前
站在普通开发者的角度,我觉得 RollCode 更像是“把 H5 交付这件事重新捋顺了”
前端·后端·程序员
陈随易1 天前
RollCode:不只是在做页面,而是在缩短“从需求到上线”的整条链路
前端·后端
炽烈小老头1 天前
【每天学习一点算法 2026/03/17】括号生成
前端·学习·typescript