Open jyzwf opened 5 years ago
对于babel,相信没有哪个前端不知,它是当下前端开发的标配,可以让我们提早使用es6/7/8等新的js特性。它将使用最新标准编写的js代码向下编译使之能在各个浏览器中运行,实现源码到源码的编译。 本文主要介绍一些babel的基础,毕竟有了基础,才能熟练的使用或者编写出babel的插件,同时编写一个很是easy的babel的插件。
babel
源码到源码
easy
它允许用户在命令行中编译js代码,可以直接运行 babel index.js,来编译js文件:
babel index.js
--out-file
-o
--watch
-w
--source-maps
sourceMap
presets
plugins
.babelrc
package.json
babel的核心功能就在这个包中,例如 transform 方法,这样我们就能在代码中直接使用编程的方式来转化js代码,实现形如组件库中代码的组件代码的预览功能。
transform
babel的解析器,生成babel的ast树:
const parser = require('@babel/parser'); const code = `function square(n) { return n * n; }`; parser.parse(code);
它也接受一些配置项,如:
true
super
jsx
用于遍历ast树,同时负责替换,移除或者添加节点,可以根据各个节点的不同类型,做一些不同的事
babel的工具库,可以帮助我们了解ast的构造变换或者验证,编写babel插件免不了与他打交道。 该模块拥有每一个单一类型节点的定义,包括节点包含哪些属性,什么是合法值,如何构建节点、遍历节点,以及节点的别名等信息。babel Definitions
将ast 转化为代码并生成sourceMap:
const generate = require('@babel/generator').default; const ast = { type: 'Program', start: 0, end: 38, body: [ { type: 'FunctionDeclaration', start: 0, end: 38, id: { type: 'Identifier', start: 9, end: 15, name: 'square', }, expression: false, generator: false, params: [ { type: 'Identifier', start: 16, end: 17, name: 'n', }, ], body: { type: 'BlockStatement', start: 19, end: 38, body: [ { type: 'ReturnStatement', start: 23, end: 36, argument: { type: 'BinaryExpression', start: 30, end: 35, left: { type: 'Identifier', start: 30, end: 31, name: 'n', }, operator: '*', right: { type: 'Identifier', start: 34, end: 35, name: 'n', }, }, }, ], }, }, ], sourceType: 'module', }; const { code } = generate(ast); console.log(code); // output: /* function square(n) { return n * n; } */
这个类似于模板替换,将模板中的字符串转化为指定值:
const template = require('@babel/template').de; const generate = require('@babel/generator').default; const t = require('@babel/types'); const buildRequire = template(` var IMPORT_NAME = require(SOURCE); `); const ast = buildRequire({ IMPORT_NAME: t.identifier('myModule'), SOURCE: t.stringLiteral('my-module'), }); console.log(generate(ast).code); // var myModule = require("my-module");
相当于一个帮你封装了plugins以及另外的presets的简便操作,省的你要将babel的配置到处重写一遍,它是从后往前执行的,区别于plugin的从前往后执行,简单看下babel-preset-react中的配置:
从后往前
plugin
从前往后
babel-preset-react
import { declare } from "@babel/helper-plugin-utils"; import transformReactJSX from "@babel/plugin-transform-react-jsx"; import transformReactDisplayName from "@babel/plugin-transform-react-display-name"; import transformReactJSXSource from "@babel/plugin-transform-react-jsx-source"; import transformReactJSXSelf from "@babel/plugin-transform-react-jsx-self"; export default declare((api, opts) => { api.assertVersion(7); const pragma = opts.pragma || "React.createElement"; // 当编译jsx表达式的时候使用的替换函数 const pragmaFrag = opts.pragmaFrag || "React.Fragment"; // 当编译 JSX fragments 时使用的替换组件 const throwIfNamespace = opts.throwIfNamespace === undefined ? true : !!opts.throwIfNamespace; const development = !!opts.development; const useBuiltIns = !!opts.useBuiltIns; if (typeof development !== "boolean") { throw new Error( "@babel/preset-react 'development' option must be a boolean.", ); } return { plugins: [ [ transformReactJSX, { pragma, pragmaFrag, throwIfNamespace, useBuiltIns }, ], transformReactDisplayName, development && transformReactJSXSource, development && transformReactJSXSelf, ].filter(Boolean), }; });
可以看出,该preset内置了plugin-transform-react-jsx,plugin-transform-react-display-name插件,同时在开发环境中内置了 plugin-transform-react-jsx-source 和 plugin-transform-react-jsx-self
plugin-transform-react-jsx
plugin-transform-react-display-name
plugin-transform-react-jsx-source
plugin-transform-react-jsx-self
好了介绍了一些基础知识,就该来编写一个插件了。 之前在项目开发的时候,经常要在代码中写debugger来打断点,但写着写着,很容易忘记把这些debugger给注释或者删除,所以我们来写一个删除debugger的babel plugin
debugger
我们先来看看,有debugger时的代码其ast树是如何的:
可以看见,在ast中存在一个 DebuggerStatement类型的节点,当我们把该debugger注释或者删除之后,看下其ast类型是如何的: 所以,该插件只要把该节点给删除了就好了,下面是直接加一个文件中演示的结果,可以看见,它返回结果是把debugger给删除了:
DebuggerStatement
看起来OK,接着我们将其在实际项目中试试,babel的插件必须以 babel-plugin-* 开头,同时由于该插件是转化了代码,所以我们就将其命名为 babel-plugin-transform-remove-debugger:
babel-plugin-*
babel-plugin-transform-remove-debugger
module.exports = function(babel){ return { visitor:{ DebuggerStatement(path, state) { // 用户的配置项可以通过state来获取 path.remove(); }, } } }
我们直接在create-react-app中看看效果:
create-react-app
class App extends Component { clickImage = () => { debugger; console.log('点击了'); }; render() { return ( <div className="App"> <button onClick={this.clickImage}>点我</button> </div> ); } }
将我们的插件配置其中: 在点击按钮的时候可以看见,并没有出现断点,所以我们的插件是ok的,至此我们就完成了一个插件,是不是很简单??后面当我想把该插件发布的时候,发先npm上已经有一个了,这就勾起了我的好奇心,想知道实现上有何不同,果然实现思路都是一样,就多了一个命名与严格模式,哈哈。
关于babel,自己还会继续深入学习,看看其他著名的插件是如何实现的,并会做记录。
下面的 Babel 用户手册和 Babel 插件手册是干货,读完之后你会更加深刻学习到babel的内部,同时如何更好的实现一个plugin
Babel 用户手册
Babel 插件手册
Babel 用户手册 Babel 插件手册 从零开始编写一个babel插件
对于
babel
,相信没有哪个前端不知,它是当下前端开发的标配,可以让我们提早使用es6/7/8等新的js特性。它将使用最新标准编写的js代码向下编译使之能在各个浏览器中运行,实现源码到源码
的编译。 本文主要介绍一些babel的基础,毕竟有了基础,才能熟练的使用或者编写出babel
的插件,同时编写一个很是easy
的babel的插件。babel的一些常用包简介
babel-cli
它允许用户在命令行中编译js代码,可以直接运行
babel index.js
,来编译js文件:--out-file
或者-o
来指定编译后的输出文件--watch
或者-w
来进行实时编译--source-maps
来生成sourceMap
此外还能将presets
,plugins
等写在命令行中,但并不推荐,最好使用.babelrc
或者在package.json
中的babel字段babel-core
babel的核心功能就在这个包中,例如
transform
方法,这样我们就能在代码中直接使用编程的方式来转化js代码,实现形如组件库中代码的组件代码的预览功能。babel-parser
babel的解析器,生成babel的ast树:
它也接受一些配置项,如:
true
,那么你可以写在任何一个允许声明的地方super
jsx
语法编译babel-traverse
用于遍历ast树,同时负责替换,移除或者添加节点,可以根据各个节点的不同类型,做一些不同的事
babel-types
babel的工具库,可以帮助我们了解ast的构造变换或者验证,编写babel插件免不了与他打交道。 该模块拥有每一个单一类型节点的定义,包括节点包含哪些属性,什么是合法值,如何构建节点、遍历节点,以及节点的别名等信息。babel Definitions
babel-generator
将ast 转化为代码并生成sourceMap:
babel-template
这个类似于模板替换,将模板中的字符串转化为指定值:
presets与plugins
presets
相当于一个帮你封装了
plugins
以及另外的presets
的简便操作,省的你要将babel的配置到处重写一遍,它是从后往前
执行的,区别于plugin
的从前往后
执行,简单看下babel-preset-react
中的配置:可以看出,该preset内置了
plugin-transform-react-jsx
,plugin-transform-react-display-name
插件,同时在开发环境中内置了plugin-transform-react-jsx-source
和plugin-transform-react-jsx-self
好了介绍了一些基础知识,就该来编写一个插件了。 之前在项目开发的时候,经常要在代码中写
debugger
来打断点,但写着写着,很容易忘记把这些debugger
给注释或者删除,所以我们来写一个删除debugger的babel plugin我们先来看看,有
debugger
时的代码其ast树是如何的:可以看见,在ast中存在一个
DebuggerStatement
类型的节点,当我们把该debugger注释或者删除之后,看下其ast类型是如何的: 所以,该插件只要把该节点给删除了就好了,下面是直接加一个文件中演示的结果,可以看见,它返回结果是把debugger
给删除了:看起来OK,接着我们将其在实际项目中试试,babel的插件必须以
babel-plugin-*
开头,同时由于该插件是转化了代码,所以我们就将其命名为babel-plugin-transform-remove-debugger
:我们直接在
create-react-app
中看看效果:将我们的插件配置其中: 在点击按钮的时候可以看见,并没有出现断点,所以我们的插件是ok的,至此我们就完成了一个插件,是不是很简单??后面当我想把该插件发布的时候,发先npm上已经有一个了,这就勾起了我的好奇心,想知道实现上有何不同,果然实现思路都是一样,就多了一个命名与严格模式,哈哈。
关于babel,自己还会继续深入学习,看看其他著名的插件是如何实现的,并会做记录。
参考资料
Babel 用户手册 Babel 插件手册 从零开始编写一个babel插件