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)
    }
  }
}
相关推荐
小码哥_常1 小时前
一套自己部署的 IM,客户端是同一份 Flutter
前端·后端
蜗牛攻城狮4 小时前
微前端四大方案选型对比与最佳实践:qiankun、micro-app、wujie、Module Federation
前端·微前端·qiankun·前端架构·micro-app·wujie
她说可以呀5 小时前
Vue插槽
前端·javascript·vue.js
qq_448011165 小时前
Vue + Jinja2两套模版与法冲突造成undefinedError
前端·javascript·vue.js
aixingpan6 小时前
aixingpan.cn API开发文档:前端查询夏令时和时区接口指南
前端·php
2601_958352906 小时前
23.5×19mm 换掉一整条音频链?|A-68 集成了什么:ADC / DAC / 24.576MHz 时钟 / AEC / 波束降噪,主控仅需 6 根线
前端·音视频·ai降噪·回声消除·语音模块
志尊宝7 小时前
AI原生前端开发(039):AI辅助的Nuxt 4全栈开发
前端·vue.js·ai-native
FRing038 小时前
JavaScript入门到精通三十二(深拷贝、浅拷贝、递归函数)
开发语言·前端·javascript
excel8 小时前
双 Token 与前端使用 Crypto 公私钥鉴权换 Token,哪种方案更好?
前端·后端
外收内放8 小时前
Python基础语法练习题(73)
java·前端·python