parse->AST->transform->gengeratejavascript
如何编译js->ASThtml
语法糖的polyfilljava
代码统一hacknode
依赖core-js,提供es*->es3的方法,只转化语法,不转换API(类Promise,WeakMap)git
babel的基础配置initgithub
利用pirate对require进行劫持,在hook中进行babel 原理见express
同时对babel后的code进行缓存,提升下次babel效率npm
function compile(code, filename) { ... let cacheKey = `${JSON.stringify(opts)}:${babel.version}`; const env = babel.getEnv(false); if (env) cacheKey += `:${env}`; //读取缓存 根据mtime判断是否须要从新babel if (cache) { const cached = cache[cacheKey]; if (cached && cached.mtime === mtime(filename)) { return cached.code; } } const result = babel.transform(code, { ...opts, sourceMaps: opts.sourceMaps === undefined ? "both" : opts.sourceMaps, ast: false, }); if (cache) { cache[cacheKey] = result; result.mtime = mtime(filename); } if (result.map) { if (Object.keys(maps).length === 0) { installSourceMapSupport(); } maps[filename] = result.map; } return result.code; } //hook中传入ext配置 function hookExtensions(exts) { if (piratesRevert) piratesRevert(); piratesRevert = addHook(compile, { exts, ignoreNodeModules: false }); } //入口函数 export default function register(opts?: Object = {}) { // Clone to avoid mutating the arguments object with the 'delete's below. opts = Object.assign({}, opts); if (opts.extensions) hookExtensions(opts.extensions); if (opts.cache === false && cache) { registerCache.clear(); cache = null; } else if (opts.cache !== false && !cache) { registerCache.load(); cache = registerCache.get(); } ... }
提供基础的transform方法json
babel-plugin实际上是对code转出的ast进行操做,segmentfault
ast的解构能够类比成一个树状或者json嵌套结构,他的每一层结构均可以叫作一个节点,以下图
babel提供一个visitor的方法,容许咱们在里面指定咱们想要访问的节点,而且能够在命中该节点时作出自定义的的操做
如今咱们有一个须要移除整个业务bundle包里全部console.log的需求
1.那咱们首先要知道console.log实际在ast是怎样的一个节点结构
形如
console.log('a')
实际ast的展示以下
对于各个节点具体含义,这里不作细讲,能够参考文末的babel手册
2.这里直接贴上代码讲吧
module.exports = function (babel) { const { types: t, template } = babel; const visitor = { //须要访问的节点名 //访问器默认会被注入两个参数 path(类比成dom),state ExpressionStatement(path, state) { const node = path.node; //延当前节点向内部访问,判断是否符合console解析出的ast的特征 const expressionNode = keyPathVisitor(node, ['expression']); const isCallExpression = expressionNode.type === 'CallExpression'; if (isCallExpression) { const objectName = keyPathVisitor(expressionNode, ['callee', 'object', 'name']); const prototypeName = keyPathVisitor(expressionNode, ['callee', 'property', 'name']); if (objectName === 'console' && prototypeName === 'log' && !MAC) { //若是符合上述条件,直接移除该节点 path.remove(); } } } }; return { visitor }; };
3.进阶版:若是咱们想在babel-plugin中新增代码呢
差很少有三种方法
A:手动添加节点(很恶心~相信你不会想去了解)
B:先生成ast,直接path.insertBefore
C:使用babel-template
例子: 移除autobind装饰器,并在constructor中自动bind this
1.由于babel判断是否babel是根据modify time,因此babel插件写完想实时生效,须要给当前的env加上 BABEL_DISABLE_CACHE
//babel-register/cache.js function load() { if (process.env.BABEL_DISABLE_CACHE) return; process.on("exit", save); process.nextTick(save); if (!_fs2.default.existsSync(FILENAME)) return; try { data = JSON.parse(_fs2.default.readFileSync(FILENAME)); } catch (err) { return; } }
2.babel插件写完后发布npm时,记得必定要加上babel-plugin-前缀,由于配置在babelrc中的插件名都会被babel在加载时统一加上babel-plugin前缀,而后在模块系统中去查找