从webpack-route-plugin来了解babel

564 阅读2分钟

从webpack-route-plugin来了解babel

一、 插件babel相关源码分析

插件地址: github.com/hxfdarling/…

webpack-route-plugin相对其他自动路由插件比较有意思,它的核心思路操作ast,从每个文件的ast中判断是否有 export const routes =[],如果有将ast抽离出来加入 componentNode, 生成route 数据写入文件中, 这里只展示与ast相关的逻辑,

parseFilesToNodes.ts

function getRouterNode({ code, file, baseDir, routeFile }) {
  // 获取router的值 -- 生成的是每个文件的路径
  const routeFunction = arrowFunctionExpression(
    [],
    callExpression(identifier('import'), [      stringLiteral('.' + path.sep + path.relative(baseDir, file)),    ]),
  );
  //生成componentast
  const componentNode = objectProperty(identifier('component'), routeFunction);
  ...
  ...
  // 判断是否有 export const routes =[]
  const routesDeclaration = body.find(node => {
    if (
      isExportNamedDeclaration(node) &&
      isVariableDeclaration(node.declaration) &&
      isVariableDeclarator(node.declaration.declarations[0]) &&
      [ROUTES_KEY, ROUTE_KEY].indexOf(
        (node.declaration.declarations[0].id as Identifier).name,
      ) >= 0 &&
      isArrayExpression(node.declaration.declarations[0].init)
    ) {
      return true;
    }
    return false;
  });
  ...
  ....
  return toArray(router)
    .map(node => {
      if (isObjectExpression(node)) {
        if (!isRedirect(node.properties)) {
         ...
         ...
         ...
         //将生成的componentNode添加到每个抽离的router ast中
          node.properties.push(componentNode);
        }
        return node;
      } else {
        console.warn('route item must is pure object:', node);
      }
    })
    .filter(Boolean);
}

genCode.ts
 ...
 ...
 //生成 const routes = [] 的整个路由数据
function genRoutesCode(nodes, outputFile) {
  const ast = file(
    program([
      variableDeclaration('const', [
        variableDeclarator(identifier(ROUTES_KEY), arrayExpression(nodes)),
      ]),
    ]),
    '',
    '',
  );
  return transformFromAstSync(ast, '', { filename: outputFile }).code;
}

genFile.ts
...
...
//将生成的路由数据写入文件
export default (options: RouteOptions | BootstrapOptions) => {
  fs.writeFileSync(options.outputFile, genBootstrapCode(options));
  return options.outputFile;
};

二、babel入门

cheogo.github.io/learn-javas…

juejin.cn/post/684490…

segmentfault.com/a/119000002…

www.babeljs.cn/docs/plugin…

zhuanlan.zhihu.com/p/72995336

developer.mozilla.org/zh-CN/docs/…

juejin.cn/post/684490…

cheogo.github.io/learn-javas…

上面的链接各位大牛都写得相当详细,就不用在码字了,简单写写我的学习过程:

esprima.org/demo/parse.… 这个地址是javascript在线转化工具,刚开始接触ast比较蒙,无从下手,通过在线转化可以直观的看到javascript转化成每个节点对应什么,比如:

js
var answer = 6 * 7;

ast
{
  "type": "Program",
  "body": [
    {
      "type": "VariableDeclaration",
      "declarations": [
        {
          "type": "VariableDeclarator",
          "id": {
            "type": "Identifier",
            "name": "answer"
          },
          "init": {
            "type": "BinaryExpression",
            "operator": "*",
            "left": {
              "type": "Literal",
              "value": 6,
              "raw": "6"
            },
            "right": {
              "type": "Literal",
              "value": 7,
              "raw": "7"
            }
          }
        }
      ],
      "kind": "var"
    }
  ],
  "sourceType": "script"
}

比如"type": "VariableDeclaration",

developer.mozilla.org/zh-CN/docs/…

可以查询到 相关的节点类型的具体用处

variableDeclaration(kind, dtors[, loc])
  kind: "const" | "let" | "var"
  dtors: [ CustomDeclarator ]
  loc: SourceLocation
返回: CustomDeclaration

www.babeljs.cn/docs/babel-…

可以查询到babel types使用api那些api可以生成相对应的ast

variableDeclaration
  t.variableDeclaration(kind, declarations)
   See also t.isVariableDeclaration(node, opts) and t.assertVariableDeclaration(node, opts).

Aliases: Statement, Declaration
  kind: "var" | "let" | "const" (required)
  declarations: Array<VariableDeclarator> (required)
  declare: boolean (default: null)

上述流程对应到webpack-route-plugin的逻辑中就是下面声明router的ast代码

function genRoutesCode(nodes, outputFile) {
  const ast = file(
    program([
      variableDeclaration('const', [
        variableDeclarator(identifier(ROUTES_KEY), arrayExpression(nodes)),
      ]),
    ]),
    '',
    '',
  );
  return transformFromAstSync(ast, '', { filename: outputFile }).code;
}

也许这个办法比较笨,但是我确实找不到好的有效的办法,通过分析javascript生成的ast节点,多写写,多看看,后面在看ast就相对来说好一点