Skip to content

Commit

Permalink
feat: support transform exitFn
Browse files Browse the repository at this point in the history
  • Loading branch information
cuixiaorui committed Jan 14, 2022
1 parent f48afde commit 565bbdc
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 61 deletions.
17 changes: 17 additions & 0 deletions src/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`element and interpolation 1`] = `
"
const { toDisplayString : _toDisplayString, createElementVNode : _createElementVNode} = Vue
return function render(_ctx) {return _createElementVNode('div', null, 'hi,' + _toDisplayString(_ctx.msg))}"
`;

exports[`interpolation module 1`] = `
"
const { toDisplayString : _toDisplayString} = Vue
return function render(_ctx) {return _toDisplayString(_ctx.hello)}"
`;
7 changes: 3 additions & 4 deletions src/compiler-core/__tests__/codegen.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@ test("interpolation module", () => {
});

const { code } = generate(ast);
console.log(code);
expect(code).toMatchSnapshot();
});

test("element and interpolation", () => {
const ast = baseParse("<div>hi,{{msg}}</div>");
transform(ast, {
nodeTransforms: [transformText,transformExpression, transformElement],
nodeTransforms: [transformElement, transformText, transformExpression],
});

const { code } = generate(ast);
console.log(code);
console.log(code.children[0])
expect(code).toMatchSnapshot();
});
2 changes: 1 addition & 1 deletion src/compiler-core/src/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function baseCompile(template, options) {
transform(
ast,
Object.assign(options, {
nodeTransforms: [transformText, transformExpression, transformElement],
nodeTransforms: [transformElement, transformText, transformExpression],
})
);

Expand Down
20 changes: 15 additions & 5 deletions src/compiler-core/src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ function traverseNode(node: any, context) {
// 把 node 给到 transform
// 用户可以对 node 做处理
const nodeTransforms = context.nodeTransforms;

const exitFns: any = [];
for (let i = 0; i < nodeTransforms.length; i++) {
const transform = nodeTransforms[i];
// TODO transform 可以返回一个函数, 作为 exit 的时候调用
transform(node, context);

const onExit = transform(node, context);
if (onExit) {
exitFns.push(onExit);
}
}

switch (type) {
Expand All @@ -36,15 +39,22 @@ function traverseNode(node: any, context) {

case NodeTypes.ROOT:
case NodeTypes.ELEMENT:
case NodeTypes.COMPOUND_EXPRESSION:
// TODO NodeTypes.COMPOUND_EXPRESSION 这里需要实现 exit 逻辑之后,把处理 text 的逻辑延迟到 exit 之后执行

traverseChildren(node, context);
break;

default:
break;
}



let i = exitFns.length;
// i-- 这个很巧妙
// 使用 while 是要比 for 快 (可以使用 https://jsbench.me/ 来测试一下)
while (i--) {
exitFns[i]();
}
}

function traverseChildren(parent: any, context: any) {
Expand Down
42 changes: 22 additions & 20 deletions src/compiler-core/src/transforms/transformElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,30 @@ import { createVNodeCall, NodeTypes } from "../ast";

export function transformElement(node, context) {
if (node.type === NodeTypes.ELEMENT) {
// 没有实现 block 所以这里直接创建 element
return () => {
// 没有实现 block 所以这里直接创建 element

// TODO
// 需要把之前的 props 和 children 等一系列的数据都处理
const vnodeTag = `'${node.tag}'`;
// TODO props 暂时不支持
const vnodeProps = null;
let vnodeChildren = null;
if (node.children.length > 0) {
if (node.children.length === 1) {
// 只有一个孩子节点 ,那么当生成 render 函数的时候就不用 [] 包裹
const child = node.children[0];
vnodeChildren = child;
// TODO
// 需要把之前的 props 和 children 等一系列的数据都处理
const vnodeTag = `'${node.tag}'`;
// TODO props 暂时不支持
const vnodeProps = null;
let vnodeChildren = null;
if (node.children.length > 0) {
if (node.children.length === 1) {
// 只有一个孩子节点 ,那么当生成 render 函数的时候就不用 [] 包裹
const child = node.children[0];
vnodeChildren = child;
}
}
}

// 创建一个新的 node 用于 codegen 的时候使用
node.codegenNode = createVNodeCall(
context,
vnodeTag,
vnodeProps,
vnodeChildren
);
// 创建一个新的 node 用于 codegen 的时候使用
node.codegenNode = createVNodeCall(
context,
vnodeTag,
vnodeProps,
vnodeChildren
);
};
}
}
67 changes: 36 additions & 31 deletions src/compiler-core/src/transforms/transformText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,49 @@ import { isText } from "../utils";

export function transformText(node, context) {
if (node.type === NodeTypes.ELEMENT) {
// hi,{{msg}}
// 上面的模块会生成2个节点,一个是 text 一个是 interpolation 的话
// 生成的 render 函数应该为 "hi," + _toDisplayString(_ctx.msg)
// 这里面就会涉及到添加一个 “+” 操作符
// 那这里的逻辑就是处理它
// 在 exit 的时期执行
// 下面的逻辑会改变 ast 树
// 有些逻辑是需要在改变之前做处理的
return () => {
// hi,{{msg}}
// 上面的模块会生成2个节点,一个是 text 一个是 interpolation 的话
// 生成的 render 函数应该为 "hi," + _toDisplayString(_ctx.msg)
// 这里面就会涉及到添加一个 “+” 操作符
// 那这里的逻辑就是处理它

// 检测下一个节点是不是 text 类型,如果是的话, 那么会创建一个 COMPOUND 类型
// COMPOUND 类型把 2个 text || interpolation 包裹(相当于是父级容器)
// 检测下一个节点是不是 text 类型,如果是的话, 那么会创建一个 COMPOUND 类型
// COMPOUND 类型把 2个 text || interpolation 包裹(相当于是父级容器)

const children = node.children;
let currentContainer;
const children = node.children;
let currentContainer;

for (let i = 0; i < children.length; i++) {
const child = children[i];
for (let i = 0; i < children.length; i++) {
const child = children[i];

if (isText(child)) {
// 看看下一个节点是不是 text 类
for (let j = i + 1; j < children.length; j++) {
const next = children[j];
if (isText(child)) {
// 看看下一个节点是不是 text 类
for (let j = i + 1; j < children.length; j++) {
const next = children[j];

// currentContainer 的目的是把相邻的节点都放到一个 容器内
if (!currentContainer) {
currentContainer = children[i] = {
type: NodeTypes.COMPOUND_EXPRESSION,
loc: child.loc,
children: [child],
};
}
// currentContainer 的目的是把相邻的节点都放到一个 容器内
if (!currentContainer) {
currentContainer = children[i] = {
type: NodeTypes.COMPOUND_EXPRESSION,
loc: child.loc,
children: [child],
};
}

currentContainer.children.push(` + `, next);
// 把当前的节点放到容器内, 然后删除掉j
children.splice(j, 1);
// 因为把 j 删除了,所以这里就少了一个元素,那么 j 需要 --
j--;
currentContainer.children.push(` + `, next);
// 把当前的节点放到容器内, 然后删除掉j
children.splice(j, 1);
// 因为把 j 删除了,所以这里就少了一个元素,那么 j 需要 --
j--;
}
} else {
currentContainer = undefined;
}
} else {
currentContainer = undefined;
}
}
};
}
}

0 comments on commit 565bbdc

Please sign in to comment.