前言
transform
是 compiler
中很重要的一个环境,当我们修改content或者dom时,都会触发 transform
用例
import { baseParse } from "../src/parse";
import { transform } from "../src/transform";
describe("Compiler: transform", () => {
test("should change text content", () => {
const ast = baseParse("<div>hi</div>");
transform(ast);
expect(ast.children[0].children[0].content).toEqual("hi mini-vue");
});
});
实现
用例上需要把 hi
替换成 hi mini-vue
,我们可以做深度查询,遍历ast树,找到对应的text节点,修改它的content即可。
/*
* @Author: Lin ZeFan
* @Date: 2022-04-16 22:33:45
* @LastEditTime: 2022-04-16 22:41:37
* @LastEditors: Lin ZeFan
* @Description:
* @FilePath: \mini-vue3\src\compiler-core\src\transform.ts
*
*/
import { NodeType } from "./ast";
export function transform(root) {
traverseNode(root);
}
function traverseNode(node) {
let { children, type } = node;
if (type === NodeType.TEXT) {
node.content = "hi mini-vue";
}
if (children) {
for (let i = 0; i < children.length; i++) {
traverseNode(children[i]);
}
}
}
上面简单实现了,很多地方不够完善,例如判断的类型,只做了text的判断,并且赋值是固定的,不是动态的,逻辑内部的耦合性也高,transform
内部程序都按规定流程走。
但是我们设计程序是肯定要通用性最佳的,不可能要将特定的处理写在程序中。
所以我们就可以换一种思路,通过外部提供处理程序,内部再调用外部传入的处理程序。我们称之为插件(plugin)。
设计为plugin
修改用例
// 改写测试
test('should change text content', () => {
const ast = baseParse('<div>hi</div>')
// 外部提供处理
const transformText = node => {
if (node.type === NodeType.TEXT) {
node.content += ' mini-vue'
}
}
// 通过 options 传入内部,内部再调用
transform(ast, {
nodeTransforms: [transformText],
})
expect(ast.children[0].children[0].content).toEqual('hi mini-vue')
})
修改transfrom
/*
* @Author: Lin ZeFan
* @Date: 2022-04-16 22:33:45
* @LastEditTime: 2022-04-16 23:08:13
* @LastEditors: Lin ZeFan
* @Description:
* @FilePath: \mini-vue3\src\compiler-core\src\transform.ts
*
*/
export function transform(root, options) {
const context = createTransformContext(root, options);
traverseNode(root, context);
}
function createTransformContext(root: any, options: any) {
const { nodeTransforms = [] } = options;
const context = {
root,
nodeTransforms: nodeTransforms || [],
};
return context;
}
function traverseNode(node, context) {
const { nodeTransforms } = context;
for (let index = 0; index < nodeTransforms.length; index++) {
const transform = nodeTransforms[index];
transform(node);
}
traverseChildren(node, context);
}
function traverseChildren(node: any, context: any) {
const { children } = node;
if (children) {
for (let i = 0; i < children.length; i++) {
traverseNode(children[i], context);
}
}
}