介绍
Babel是一个工具链,主要用于将ECMAScript 2015+版本代码向后兼容 Javascript 语法,以便可以运行到旧版本浏览器或其他环境中。
作为一种语言,JavaScript在不断发展,新的标准/提案和新的特性层出不穷。在得到广泛普及之前,Babel能够让你提前(甚至数年)使用他们。
Babel 的三个主要处理步骤分别是: 解析(parse),转换(transform),生成(generate)。
- 解析
将代码解析成抽象语法树(AST),每个js引擎(比如Chrome浏览器中的V8引擎)都有自己的AST解析器,而Babel是通过Babylon实现的。在解析过程中有两个阶段:词法分析和语法分析,词法分析阶段把字符串形式的代码转换为令牌(tokens)流,令牌类似于AST中节点;而语法分析阶段则会把一个令牌流转换成 AST的形式,同时这个阶段会把令牌中的信息转换成AST的表述结构。
- 转换
在这个阶段,Babel接受得到AST并通过babel-traverse对其进行深度优先遍历,在此过程中对节点进行添加、更新及移除操作。这部分也是Babel插件介入工作的部分。
- 生成 将经过转换的AST通过babel-generator再转换成js代码,过程就是深度优先遍历整个AST,然后构建可以表示转换后代码的字符串。
例如,Babel能够将新的ES2015箭头函数语法:
const square = n => n * n;复制代码
转译为:
const square = function square(n) { return n * n;}复制代码
babel各个模块介绍
1.babel-core
babel的核心模块,包括一些核心api例如:transform。
/* * @param {string} code 要转译的代码字符串 * @param {object} options 可选,配置项 * @return {object} */babel.transform(code: string, options?: Object) //返回一个对象(主要包括三个部分):{ generated code, //生成码 sources map, //源映射 AST //即abstract syntax tree,抽象语法树}复制代码
更多知识点
一些使用babel插件的打包或构建工具都有使用到这个方法,下面是一些引入babel插件中的源码:
//gulp-babelconst babel = require('babel-core');/*some codes...*/module.exports = function (opts) { opts = opts || {}; return through.obj(function (file, enc, cb) { try { const fileOpts = Object.assign({}, opts, { filename: file.path, filenameRelative: file.relative, sourceMap: Boolean(file.sourceMap), sourceFileName: file.relative, sourceMapTarget: file.relative }); const res = babel.transform(file.contents.toString(), fileOpts); if (res !== null) { //some codes } } catch (err) { //some codes } }}//babel-loadervar babel = require("babel-core");/*some codes...*/var transpile = function transpile(source, options) { //some code try { result = babel.transform(source, options); } catch (error) { //some codes } //some codes}//rollup-pugin-babelimport { buildExternalHelpers, transform } from 'babel-core';/*some codes...*/export default function babel ( options ) { //some codes return { // some methods transform ( code, id ) { const transformed = transform( code, localOpts ); //some codes return { code: transformed.code, map: transformed.map }; } }}复制代码
2.babel-cli
Babel的CLI是一种在命令行下使用Babel编译文件的简单方法。主要用于文件的输入输出。
全局安装
npm install -g babel-cli复制代码
我们可以这样编译我们第一个文件:
babel test.js//编译后的文件输出在终端复制代码
babel test.js -o test-out.js//编译后的文件输出在test-out.js文件中复制代码
在项目内运行 Babel CLI
尽管可以把Babel CLI全局安装在你的机器上,但是按项目逐个安装在本地会更好。 有两个主要的原因。
- 1.在同一台机器上的不同项目或许会依赖不同版本的Babel并允许你有选择的更新。
- 2.意味着对工作环境没有隐式依赖,让项目有很好的移植性并易于安装
将Babel CLI安装到本地可以运行:
npm install --save-dev babel-cli复制代码
现在可以不直接在命令行运行Babel了,取而代之我们将命令写在package.json的script里。
只需将"scirpts"字段添加到你的package.json文件内。
{ "scripts":{ "build": "babel src -d lib", ... }, ...}复制代码
现在可以在终端里运行:
npm run build复制代码
这将以与之前同样的方式运行Babel。
3.babel-node
babel-node是随babel-cli一起安装的,只要安装了babel-cli就会自带babel-node。 在命令行输入babel-node会启动一个REPL(Read-Eval-Print-Loop),这是一个支持ES6的js执行环境。
4.babel-register
babel-register字面意思能看出来,这是babel的一个注册器,它在底层改写了node的require方法,引入babel-register之后所有require并以.es6, .es, .jsx 和 .js为后缀的模块都会经过babel的转译。
//test.jsconst name = 'test';module.exports = () => { const json = {name}; return json;};//main.jsrequire('babel-register');var test = require('./test.js'); //test.js中的es6语法将被转译成es5console.log(test.toString()); //通过toString方法,看看控制台输出的函数是否被转译/* function () { var json = { name: name }; return json; }*/复制代码
5.babel-polyfill
babel-polyfill在代码中的作用主要是用已经存在的语法和api实现一些浏览器还没有实现的api,对浏览器的一些缺陷做一些修补。例如Array新增了includes方法,我想使用,但是低版本的浏览器上没有,引入babel-polyfill则帮我们添加了这些方法。
项目使用
1. .babelrc
babel所有的操作基本都会来读取这个配置文件,除了一些在回调函数中设置options参数的,如果没有这个配置文件,会从package.json文件的babel属性中读取配置。
2.plugins
babel中的插件,通过配置不同的插件才能告诉babel,我们的代码中有哪些是需要转译的。
3.presets
预设就是一系列插件的集合,就好像修图一样,把上次修图的一些参数保存为一个预设,下次就能直接使用。
// cnpm install -D babel-preset -env{ "presets": [ ["env", { "targets": { //指定要转译到哪个环境 //浏览器环境 "browsers": ["last 2 versions", "safari >= 7"], //node环境 "node": "6.10", //"current" 使用当前版本的node }, //是否将ES6的模块化语法转译成其他类型 //参数:"amd" | "umd" | "systemjs" | "commonjs" | false,默认为'commonjs' "modules": 'commonjs', //是否进行debug操作,会在控制台打印出所有插件中的log,已经插件的版本 "debug": false, //强制开启某些模块,默认为[] "include": ["transform-es2015-arrow-functions"], //禁用某些模块,默认为[] "exclude": ["transform-es2015-for-of"], //是否自动引入polyfill,开启此选项必须保证已经安装了babel-polyfill //参数:Boolean,默认为false. "useBuiltIns": false }] ]}复制代码
关于最后一个参数useBuiltIns,有两点必须要注意:
- 1.如果useBuiltIns为true,项目中必须引入babel-polyfill。
- 2.babel-polyfill只能被引入一次,如果多次引入会造成全局作用域的冲突。