From 565bbdc7a8ed5d69753710356d59a3481013e841 Mon Sep 17 00:00:00 2001 From: cuixiaorui Date: Fri, 14 Jan 2022 15:40:15 +0800 Subject: [PATCH] feat: support transform exitFn --- .../__snapshots__/codegen.spec.ts.snap | 17 +++++ src/compiler-core/__tests__/codegen.spec.ts | 7 +- src/compiler-core/src/compile.ts | 2 +- src/compiler-core/src/transform.ts | 20 ++++-- .../src/transforms/transformElement.ts | 42 ++++++------ .../src/transforms/transformText.ts | 67 ++++++++++--------- 6 files changed, 94 insertions(+), 61 deletions(-) create mode 100644 src/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap diff --git a/src/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap b/src/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap new file mode 100644 index 0000000..565ffa1 --- /dev/null +++ b/src/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap @@ -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)}" +`; diff --git a/src/compiler-core/__tests__/codegen.spec.ts b/src/compiler-core/__tests__/codegen.spec.ts index db171a6..88a612a 100644 --- a/src/compiler-core/__tests__/codegen.spec.ts +++ b/src/compiler-core/__tests__/codegen.spec.ts @@ -12,16 +12,15 @@ test("interpolation module", () => { }); const { code } = generate(ast); - console.log(code); + expect(code).toMatchSnapshot(); }); test("element and interpolation", () => { const ast = baseParse("
hi,{{msg}}
"); 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(); }); diff --git a/src/compiler-core/src/compile.ts b/src/compiler-core/src/compile.ts index f87002e..88e7dbe 100644 --- a/src/compiler-core/src/compile.ts +++ b/src/compiler-core/src/compile.ts @@ -12,7 +12,7 @@ export function baseCompile(template, options) { transform( ast, Object.assign(options, { - nodeTransforms: [transformText, transformExpression, transformElement], + nodeTransforms: [transformElement, transformText, transformExpression], }) ); diff --git a/src/compiler-core/src/transform.ts b/src/compiler-core/src/transform.ts index 274cdfe..5f24e60 100644 --- a/src/compiler-core/src/transform.ts +++ b/src/compiler-core/src/transform.ts @@ -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) { @@ -36,8 +39,6 @@ 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; @@ -45,6 +46,15 @@ function traverseNode(node: any, context) { default: break; } + + + + let i = exitFns.length; + // i-- 这个很巧妙 + // 使用 while 是要比 for 快 (可以使用 https://jsbench.me/ 来测试一下) + while (i--) { + exitFns[i](); + } } function traverseChildren(parent: any, context: any) { diff --git a/src/compiler-core/src/transforms/transformElement.ts b/src/compiler-core/src/transforms/transformElement.ts index 8c2fbb2..c0830df 100644 --- a/src/compiler-core/src/transforms/transformElement.ts +++ b/src/compiler-core/src/transforms/transformElement.ts @@ -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 + ); + }; } } diff --git a/src/compiler-core/src/transforms/transformText.ts b/src/compiler-core/src/transforms/transformText.ts index a2f1844..45efb5e 100644 --- a/src/compiler-core/src/transforms/transformText.ts +++ b/src/compiler-core/src/transforms/transformText.ts @@ -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; } - } + }; } }