实现 transform 功能
- transform 功能
- transform 就是对生成的整颗 ast 树,做增删改查的操作。
- 具体需求, 给 hi, 后面拼接 mini-vue
html
<div>hi,{{message}}</div>
<!-- 过程 -->
root---> element----> text
`----> 插值
- 核心点
- 找到 hi, 这块的 text 节点
- 遍历一棵树
- 广度优先搜索
- 深度优先搜索 ----> 我们这里使用深度优先搜索进行遍历 ----> 使用递归来做处理
- 遍历一棵树
- 修改 content 这块的值
- 先简单实现一个 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")
})
})
- 具体实现
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)
}
}
}
- 测试通过,我们进行优化
-
如果我们在拼接 mini-vue 以后,还要处理 element 节点
-
还需要处理插值节点
-
有的时候需要根据不同的环境做不同的处理, 有些环境不需要处理 text,有些则需要。
-
这时候我们修改,拼接的内容,应该师动态的,也就是 mini-vue 是动态的
-
还有递归的流程是固定的,我们需要抽离出变动点和稳定点的
- 如果处理呢?
- 我们把动态拼接部分抽离出来,放到页面上
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)
}
}
}
- 重构递归函数
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)
}
}
}