【转】babel那些事儿

从前,一提到新东西,我的反应就是兼容性好不好,如果不能满足产品经理的需求,就还是用保守的方式实现吧。毕竟前端开发是一件很灵活的事,怎么写都行,至于为何会用某种方法,一定是综合考虑兼容性,性能,用户体验,开发成本等因素后再说。兼容性和新事物有时就像鱼和熊掌不可兼得,必须权衡利弊,做一个决断。但是ECMAScript 2015不一定要等所有浏览器都支持后才可以用,于是你会想到一个工具babel,它可以助ECMAScript 2015一臂之力,让处于某个stage的且尚未纳入es规范的特性提前支持,因为babel能够把不同stage的es,转成浏览器识别的js。
在学babel前,我有一个这样的疑问,为啥说到babel,一般会提到es5+?他俩有啥关系?babel是伴随ECMAScript 2015出现的吗?如果不是,它比ECMAScript 2015出生早,还是晚呢?感觉问题很多,就像破案一样,疑点重重,越走越深,但是主方向不会忘。我有时会问自己,我是搞清它的历史背景呢,还是赶紧敲命令行,立刻做项目?内心的躁动告诉我,反正有时间研究,索性我就挖地三尺,弄清babel的祖坟在哪儿。
看了babel的官方文档后,我恍然大悟,原来babel不是TC39的作品啊!!!如果说ECMAScript的爹娘是TC39,那么babel的爹是james kyle,一个澳大利亚工程师,曾就职于Facebook。总之,babel和ECMAScript 2015没有什么血缘关系,babel也不是ECMAScript 2015出生的附属品,babel5.0发布的比ECMAScript 2015早。

一、babel版本有哪些?
2015年1月12日,6to5和esnext开源;
2015年2月15日,6to5重命名为babel;
2015年3月31日,babel 5.0发布;
2015年10月30日,babel 6.0发布。

二、babel的用途
babel是一个JavaScript编译器。
1、ES6转成ES5
默认情况下,babel自带了一组ES2015语法转化器,这些转化器能把ES2015转为ES5,让你现在就使用最新的JavaScript语法,而不用等待浏览器提供支持。在babel中,默认启用stage-2及以上的proposal。如果有人说“不同stage的babel”,这种说法是错误的,因为babel没有所谓的stage,babel就是一个编译器。至于babel的配置文件参数中为什么会牵扯到stage,是由于babel要知道把哪个stage的es5+转换成浏览器能识别的js,而TC39 Process会告诉babel是哪个stage。
2、jsx文件转成js文件
babel可以把React的JSX语法转成普通JavaScript语法。
3、babel由插件组成,可组合拼装。
4、babel支持source map,帮助调试。

三、babel的工作原理
1、解析
用babylon对源代码(比如ES6)进行词法解析,得到AST;
2、转换
用babel-traverse对AST树进行遍历转换,得到新的AST树;
3、生成
用babel-generator通过AST树生成目标代码(比如ES5)。

四、babel的配置
babel的配置文件是.babelrc,存放在项目的根目录下。使用babel的第一步,就是配置这个文件。什么是.babelrc文件呢?熟悉linux的同学一定知道,rc结尾的文件通常代表运行时自动加载的文件,配置等等,类似bashrc,zshrc。同样babelrc在这里也是有同样的作用,而且在babel6中,这个文件必不可少。
该文件用来设置转码规则和插件,基本格式如下。

{
  "presets": [],
  "plugins": []
}

1、转码规则
presets字段设定转码规则,转码规则可以告诉babel去处理什么语法。

A、babel-preset-es2015
这是ES2015(最新版本的JavaScript标准,也叫做ES6)的转码规则。使用它后,babel可以将ES6语法转码为普通 JavaScript(即ES5)语法。

# 安装ES2015转码规则
$ npm install --save-dev babel-preset-es2015

B、babel-preset-react
这是react的转码规则。使用它后,babel可以将react语法转码为普通JavaScript语法。

# 安装react转码规则
$ npm install --save-dev babel-preset-react

C、babel-preset-stage-x
这是ES7不同阶段语法提案的转码规则。使用它后,babel可以将ES7不同阶段语法转码为普通JavaScript语法。

# 安装ES7不同阶段语法提案的转码规则(共有4个阶段),选装一个
$ npm install --save-dev babel-preset-stage-0
$ npm install --save-dev babel-preset-stage-1
$ npm install --save-dev babel-preset-stage-2
$ npm install --save-dev babel-preset-stage-3

以上每种预设都依赖于紧随的后期阶段预设。例如,babel-preset-stage-1依赖babel-preset-stage-2,后者又依赖babel-preset-stage-3。
最终,在.babelrc文件这样写。

{
  "presets": [
    "es2015",
    "stage-0"
  ],
  "plugins": []
}

D、babel-preset-env
这是最新的预设插件,包含es2015,es2016,es2017和latest等,不包含react,polyfill。

$ npm install --save-dev babel-preset-env

最终,在.babelrc文件这样写,是不是比上面简洁了很多?

{
  "presets": ["env"],
  "plugins": []
}

如果与webpack一起使用,需要再配置下webpack.config.js文件。

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader'
      }
    }
  ]
}

2、插件
A、babel-plugin-transform-runtime
babel-polyfill可以控制你自己写的文件,但是不能很好的处理第三方library,因为polyfill会污染原来的全局环境,容易发生冲突,这时候babel-runtime闪电登场。
babel-runtime是一个提供了regenerator、core-js和helpers的运行时库。
babel-plugin-transform-runtime插件依赖babel-runtime,babel-runtime是真正提供runtime环境的包,也就是说transform-runtime插件是把js代码中使用到的新原生对象和静态方法转换成对runtime实现包的引用。
a.1、利弊
可以用babel-runtime/core-js导出的对象和方法替代ES6引入的新原生对象和静态方法,避免全局污染;
可以用babel-runtime/regenerator导出的函数取代generators或async函数;
可以把多余代码提取到babel-runtime/helps中,减少体积。
由于runtime不会污染全局空间,所以实例方法是无法工作的,这就需要polyfill啦。

a.2、安装

# babel-plugin-transform-runtime用于开发环境
$ npm install --save-dev babel-plugin-transform-runtime

# babel-runtime用于生产环境
$ npm install --save babel-runtime

a.3、配置.babelrc文件

{
  "plugins": [
    ["transform-runtime", {
      "helpers": false,
      "polyfill": false,
      "regenerator": true,
      "moduleName": "babel-runtime"
    }]
  ]
}

a.4、使用

// ES6代码
var sym = Symbol();
// 通过transform-runtime转换后的ES5+runtime代码 
var _symbol = require("babel-runtime/core-js/symbol");
var sym = (0, _symbol.default)();

五、babel-core,新语法转码
1、安装

$ npm install --save-dev babel-core 

2、使用

var babel = require("babel-core");

A、如果是字符串形式的JavaScript代码,可以使用transform编译。
babel.transform("code();", options);  // { code, map, ast }

B、如果是文件,异步编译使用transformFile。
babel.transformFile("filename.js", options, function(err, result) {
  result; // { code, map, ast }
});
C、如果是文件,同步编译使用transformFileSync。
babel.transformFileSync("filename.js", options);  // { code, map, ast }

D、要是已经有一个babel AST(抽象语法树)了就可以直接从AST进行转换。
babel.transformFromAst(ast, code, options); // { code, map, ast }

六、babel-polyfill,新Api转码
babel默认只转换新的JavaScript句法,而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。babel-polyfill正好可以转码新的API,polyfill和runtime都是对core-js和regenerator的再封装,方便使用。

插播一段,一直好奇polyfill是个啥东西,简单查了下,代码填充,也可译作兼容性补丁,类似软垫片。

1、安装

$ npm install --save-dev babel-polyfill

2、使用

//方案A,在your.js中引入
import 'babel-polyfill';

//方案B,在node.js中引入
require('babel-polyfill');

//方案C,设置webpack.config.js
module.exports = {
  entry: ["babel-polyfill", "./app/js"]
};

七、babel-cli,命令行转码
1、安装

$ npm install --save-dev babel-cli

2、命令行

# 转码结果输出到标准输出
$ babel example.js

# 转码结果写入一个文件
# --out-file 或 -o 参数指定输出文件
$ babel example.js --out-file compiled.js
# 或者
$ babel example.js -o compiled.js

# 整个目录转码
# --out-dir 或 -d 参数指定输出目录
$ babel src --out-dir lib
# 或者
$ babel src -d lib

# -s 参数生成source map文件
$ babel src -d lib -s

3、命令行优化
可以像上面一样直接输入命令完成转码,也可以修改package.json文件,然后执行npm run build。

"scripts": {
  "build": "babel src -d lib"
}

八、babel-register,require实时转码
通过绑定node.js的require来自动转译require引用的js代码文件。
babel-register模块改写require命令,为它加上一个钩子。此后,每当使用require加载.js、.jsx、.es和.es6后缀名的文件,就会先用babel进行转码。
1、安装

$ npm install --save-dev babel-register

2、使用

//必须先加载babel-register
require("babel-register");
require("./index.js");

3、利弊
这种方式不需要手动对index.js转码。
babel-register只会对require命令加载的文件转码,而不会对当前文件转码。另外,由于它是实时转码,所以只适合在开发环境使用。

九、sublime插件转码es6

1、安装插件
在sublime中,安装babel插件。
2、全局安装babel-cli包

cnpm install -g babel-cli

3、配置sublime中的babel插件

preferences -> Package Settings -> Babel -> Settings-Default,打开文件,按照下面的参数配置。
{
  "debug": false,
  "use_local_babel": true,
  "node_modules": {
    "windows": "C:/Users/Administrator/AppData/Roaming/npm/node_modules/babel-cli/node_modules",
    "linux": "/usr/lib/node_modules",
    "osx": "/usr/local/lib/node_modules"
  },
  "options": {}
}

4、配置.babelrc文件
在当前目录配置好.babelrc文件,定义转码规则,在当前目录放置好依赖包。

{
  "presets": ["es2015"],
  "plugins": []
}

5、执行转换
写一个es6.js文件,按下快捷键Ctrl+Shift+P,输入babel,选择babel transform执行,会自动生成一个匿名文件。很诡异的是,这个文件自动保存了,但不是在当前目录,也不是在sublime的包目录里,具体在哪,我也不清楚,我只知道这不是我想要的结果。我希望能够在保存es6.js文件时,自动生成期望目录的es5.js文件,而且名称一致,文件实际存在,不是无名文件。后面找到优化方案,再回来补充

tools -> Babel -> Babel Transform,也可以执行转换。

十、在线转码
babel提供一个repl在线编译器,可以在线将ES6代码转为ES5代码。转换后的代码,可以直接作为ES5代码插入网页运行。

十一、总结
看了上面的介绍,你会用babel了吗?如果还有点迷惑,搞不清东南西北,看这里就对啦。
1、声明转换规则
babel的配置文件.babelrc列举了一些要用到的东西。比如babel要用env,就把env写进去,声明要处理哪些语法。
2、进行转换操作
幕后高人则是babel-core,babel-polyfill或者babel-cli,他们负责把源文件转换成目标文件。
安装了babel-core和babel-polyfill,并在你的源文件中引用,页面加载的时候,就会自动实现转换。或者安装babel-cli,在命令行输入命令,手动转换。

posted @ 2022-03-19 09:50  威震天1  阅读(89)  评论(0编辑  收藏  举报