源起:
前段时间通过一篇关于开发者他介绍自己开发的 web 应用的文章知道了这名开发者。觉得他很厉害,要向他学习。所以呢,就学习一下他之前做过的项目。虽然是想认真研究完的,一来是代码没有什么注释,二来我认真看过的部分某些其实可参考意义不是很大。所以这个计划的执行就暂停了。不过呢,在其中我也找到了一个我想了解的方向,就是 AST(抽象语法树),这个其实会挺有用的。
原因我暂时想到的有两点:
一、通过解析代码,能够拿到代码结构化的信息,这样可以应用于一些代码自动化生成,解析模块依赖等(这种可以自动化的东西就是我辈所向往的)
二、单纯自己通过正则去实现这样的解析还是一个比较麻烦的工程,暂时非自己能力所及。
实践:
JS 的 AST 的解析主要会用到 Babel 这个库。其中需要了解到的主要有这三个个包@babel/parser(babylon)、@babel/traverse、@babel/type。
下面以官方的一个插件说明 AST 中的一些概念和使用流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
|
module.exports = function({ types: t }) { return { name: "transform-remove-console", visitor: { CallExpression(path, state) { const callee = path.get("callee");
if (!callee.isMemberExpression()) return;
if (isIncludedConsole(callee, state.opts.exclude)) { if (path.parentPath.isExpressionStatement()) { path.remove(); } else { path.replaceWith(createVoid0()); } } else if (isIncludedConsoleBind(callee, state.opts.exclude)) { path.replaceWith(createNoop()); } }, MemberExpression: { exit(path, state) { if ( isIncludedConsole(path, state.opts.exclude) && !path.parentPath.isMemberExpression() ) { if ( path.parentPath.isAssignmentExpression() && path.parentKey === "left" ) { path.parentPath.get("right").replaceWith(createNoop()); } else { path.replaceWith(createNoop()); } } } } } }; function isGlobalConsoleId(id) { const name = "console"; return ( id.isIdentifier({ name }) && !id.scope.getBinding(name) && id.scope.hasGlobal(name) ); }
|
结语:
我这里写的只是 AST 的冰山一粒,上述内容其实在 babel handbook 上都有,把自己心得体会记录下来,一是要是以后忘记了,看自己的文字更加容易唤醒自己的记忆,二是为自己的学习留下痕迹,再者有新的发现再到新文章中再叙吧。