前言
初识babel,是在webpack打包里面的babel-loader里面,作用是将es6转译成es5。
webpack,babel,loader之间的关系:
babel 是编译工具,把js高级语言转换成浏览器能识别的js语言。
webpack 是打包工具,定义入口文件,将所有模块引入整理后,通过 loader 和 plugin 处理后,打包输出。
loader让webpack也能够去处理那些非js文件的模块**(如ES6新增的API,webpack本身是能处理简单的js文件)
webpack 通过 babel-loader 使用 babel 。
一、工作原理
babel是一个转译器,感觉相对于编译器compiler,叫转译器transpiler更准确,因为它只是把同种语言的高版本规则翻译成低版本规则,而不像编译器那样,输出的是另一种更低级的语言代码。
但是和编译器类似,babel的转译过程也分为三个阶段:
parsing、transforming、generating
[解析、转译、生成],以ES6代码转译为ES5代码为例,babel转译的具体过程如下:
babel编译器。被拆分三个模块:@babel/parser,@babel/traverse,@babel/generator。
@babel/parser: 接受源码,进行词法分析、语法分析,生成AST。
@babel/traverse:接受一个AST,并对其遍历,根据preset、plugin进行逻辑处理,进行替换、删除、添加节点。
@babel/generator:接受最终生成的AST,并将其转换为代码字符串,同时此过程也可以创建source map。
ES6代码输入 ==》 babylon进行解析 ==》 得到AST
==》 plugin用babel-traverse对AST树进行遍历转译 ==》 得到新的AST树
==》 用babel-generator通过AST树生成ES5代码
Babel默认只转换新的javascript语法,而不转换新的API,比如 Iterator, Generator, Set, Maps, Proxy, Reflect,Symbol,Promise 等全局对象。以及一些在全局对象上的方法(比如 Object.assign)都不会转码。比如说,ES6在Array对象上新增了Array.form方法,Babel就不会转码这个方法,如果想让这个方法运行,必须使用 babel-polyfill来转换等。
babel-polyfill与babel-runtime
就是为了解决这些新的API的
如何编译
babel工作时会去项目根目录下寻找.babelrc或者babel.config.js配置文件,
根据配置
文件里的配置信息来对js语法进行编译。
配置文件的查找
babel会从当前转译的文件所在目录下查找配置文件,如果没有找到,就顺着文档目录树一层层往上查找,一直到.babelrc文件存在或者带babel字段的package.json文件存在为止。
presets中的配置项:(预设的插件集)
modules:将ES6模块语法转换为另一种模块类型。将该设置为false就不会转换模块。默认为 ‘commonjs’.
presets :预设
babel通过插件来告诉它如何编译和要编译那些js语言,比如我们想要编译let关键字,就需要引入let相关的插件,要编译箭头函数,就要引入箭头函数相关的插件,由于一个一个引入配置比较麻烦,babel官方就这些常用的插件封装成了一组plugin集合,这样我们就只需配置一次了。
{
"plugins": [
"check-es2015-constants",
"es2015-arrow-functions",
"es2015-block-scoped-functions",
// ...
]
}
plugins:(插件应用于babel的转译过程,尤其是第二个阶段transforming,如果这个阶段不使用任何插件,那么babel会原样输出代码。我们主要关注transforming阶段使用的插件,因为transform插件会自动使用对应的词法插件,所以parsing阶段的插件不需要配置。)
有一些插件在presets集合里没有包含,这时候我们还得通过插件来配置
比如:bable-polyfill与babel-runtime
babel-polyfill与babel-runtime比较:
babel-polyfill:当前运行环境不支持新的API时,babel-polyfill向全局对象或内置对象的原型上
添加属性
来实现,
缺点是可能造成全局变量的污染
。通过 Polyfill 方式在目标环境中添加缺失的特性 (通过 @babel/polyfill 模块)
babel-runtime:它是将es6编译成es5去执行。我们使用es6的语法来编写,最终会通过babel-runtime编译成es5.也就是说,不管浏览器是否支持ES6,只要是ES6的语法,它都会进行转码成ES5.所以就有很多冗余的代码。这时我们就是通过import来按需导入需要兼容的API,但是如果我现在有100个文件甚至更多的话,难道我们需要一个个文件加import
Promise from ‘babel-runtime/core-js/promise’ 吗?那这样肯定是不行的,因此这个时候出来一个 叫
babel-plugin-transform-runtime
。
babel-plugin-transform-runtime:babel-plugin-transform-runtime会自动帮我们polyfill当前环境不支持的API,不需要我们一个一个去导入,polyfill包就在在babel-runtime这个包里。
配置项:
helpers: 默认值为true,表示是否开启内联的babel helpers(即babel或者环境本来存在的某些对象方法函数)如:extends,etc这样的
在调用模块名字时将被替换名字。
polyfill:默认值为true,表示是否把内置的东西(Promise, Set, Map)等转换成非全局污染的。
regenerator:默认值为true,是否开启generator函数转换成使用regenerator runtime来避免污染全局域。
moduleName:默认值为 babel-runtime,当调用辅助 设置模块(module)名字/路径.
二、babel的包构成
1.核心包
-
babel-core:babel转译器本身,提供了babel的转译API,如
babel.transform
等,用于对代码进行转译。像webpack的
babel-loader
就是调用这些API来完成转译过程的。 - babylon:js的词法解析器,解析成AST树
- babel-traverse:用于对AST(抽象语法树,想了解的请自行查询编译原理)的遍历,主要给plugin用
- babel-generator:根据AST生成代码
2.功能包
- babel-types:用于检验、构建和改变AST树的节点
- babel-template:辅助函数,用于从字符串形式的代码来构建AST树节点
- babel-helpers:一系列预制的babel-template函数,用于提供给一些plugins使用
- babel-code-frames:用于生成错误信息,打印出错误点源代码帧以及指出出错位置
- babel-plugin-xxx:babel转译过程中使用到的插件,其中babel-plugin-transform-xxx是transform步骤使用的
- babel-preset-xxx:transform阶段使用到的一系列的plugin
-
babel-polyfill:JS标准新增的原生对象和API的shim,
实现上仅仅是core-js和regenerator-runtime
两个包的封装 -
babel-runtime:功能类似babel-polyfill,一般用于library或plugin中,因为它不会污染全局作用域
3.工具包
- babel-cli:babel的命令行工具,通过命令行对js代码进行转译
- babel-register:通过绑定node.js的require来自动转译require引用的js代码文件
babel官网的一些总结
1.Babel 可以删除类型注释!
务必牢记 Babel 不做类型检查
,你仍然需要安装 Flow 或 TypeScript 来执行类型检查的工作。
2.可调式:由于 Babel 支持 Source map,因此你可以轻松调试编译后的代码。
3.为了支持 Promises 功能,你必须引入 Babel polyfill。(前面讲到polyfill的功能)
官网链接
后续补充。