babel实践及配置
# 简介
将 ECMAScript 2015 及其版本以后的 javascript 代码转为旧版本浏览器或者是环境中向后兼容版本的 javascript 代码。【官方介绍】
简而言之,就是把不兼容的 JavaScript 代码转为可兼容,可以执行的 JavaScript 代码。可以理解为 babel是javascript语法的编译器。
# 功能
- 语法转换
- 添加一些兼容性的 polyfill 功能
- 源码转换等
# 配置方式
Babel是可以配置!许多其他工具具有类似的配置:ESLint (.eslintrc), Prettier (.prettierrc)。
允许使用所有Babel API选项 (opens new window)。然而,如果该选项需要JavaScript,您可能需要使用Javascript配置文件 (opens new window)。
# .babelrc【常用】
在项目的根目录下(package.json的位置)创建一个名为.babelrc的文件,其中包含以下内容:
{
"presets": [...],
"plugins": [...]
}
2
3
4
查看.babelrc文档 (opens new window),以查看更多配置选项。
示例:bdp-x转换
{
"presets": [
[
"env",
{
"targets": {
"node": "current"
}
}
]
// ["minify"]
],
"comments": false,
"minified":true
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"dependencies": {
"babel-env": "^2.4.1"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-minify": "^0.5.1"
}
2
3
4
5
6
7
# package.json
或者,您可以选择在package.json中,使用babel键值指定.babelrc配置,如下所示:
{
"name": "my-package",
"version": "1.0.0",
"babel": {
"presets": [ ... ],
"plugins": [ ... ],
}
}
2
3
4
5
6
7
8
示例:
"scripts": {
"build": "babel src -d lib --comments=true"
},
"babel":{
"presets":["es2015"],
"comments":false
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-loader": "^7.1.1",
"babel-preset-env": "^1.6.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"webpack": "^3.2.0"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"babel-env": "^2.4.1"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-minify": "^0.5.1"
},
"babel": {
"presets": [
[
"env",
{
"targets": {
"node": "current"
}
}
],
[
"minify"
]
],
"comments": false
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
设置production环境下压缩;
"babel": {
"presets": [
[
"env",
{
"targets": {
"node": "current"
},
"production": {
"presets": [
"minify"
]
}
}
]
],
"comments": false
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# babel.config.js
在项目的根目录下(package.json的位置)创建一个名为babel.config.js的文件,其中包含以下内容:
module.exports = function (api) {
api.cache(true);
const presets = [ ... ];
const plugins = [ ... ];
return {
presets,
plugins
};
}
2
3
4
5
6
7
8
9
查看babel.config.js文档 (opens new window),以查看更多配置选项。
# .babelrc.js
与.babelrc的配置结构相同,但你可以用JavaScript编写它。
const presets = [ ... ];
const plugins = [ ... ];
module.exports = { presets, plugins };
2
3
您可以访问任何Node.js API,例如基于流程环境的动态配置:
const presets = [ ... ];
const plugins = [ ... ];
if (process.env["ENV"] === "prod") {
plugins.push(...);
}
module.exports = { presets, plugins };
2
3
4
5
6
# 使用 CLI (@babel/cli)
用于命令行编译文件,可以进行配置以及输出等操作
babel --plugins @babel/plugin-transform-arrow-functions script.js
查看babel-cli文档 (opens new window),以查看更多配置选项。
# 用法
babel input.js -options #编译 input.js 文件
# 配置
-o : 输出路径
-w :监听编译文件,实时编译
-s :输出source-map ,可以内敛
-d :当编译一个目录时候,-d 输出目录
--ignore : 忽略的文件
--plugins : 使用插件,用 , 隔开
--presets : 使用预设,用 , 隔开
--config-file : 使用 .babelrc 文件路径
# 使用 API (@babel/core)
require("@babel/core").transform("code", {
plugins: ["@babel/plugin-transform-arrow-functions"]
});
2
3
查看babel-core文档 (opens new window),以查看更多配置选项。
# 配置说明
# 简单配置预览
{
"plugins": [], // 插件配置
"presets": [] // 预设配置
}// 这里 .babelrc 配置的
// 官网建议用 module 的形式来配置,bable.config.js
const plugins = []
const presets = []
module.exports = { presets, plugins }
2
3
4
5
6
7
8
9
# 编译的执行顺序
1、执行 plugins 中所有的插件
2、plugins 的插件,按照顺序依赖编译
3、所有 plugins 的插件执行完成,再执行 presets 预设。
4、presets 预设,按照倒序的顺序执行。(从最后一个执行)
5、完成编译。
# 配置介绍
plugin : 将某一种需要转化的代码,转为浏览器可以执行代码。
presets :是某一类 plugin 的集合,包含了某一类插件的所有功能。
# plugin
该属性是告诉babel要使用那些插件,这些插件可以控制如何转换代码。
# presets
presets属性告诉Babel要转换的源码使用了哪些新的语法特性,presets是一组Plugins的集合。
# 配置详解
# babel 三种不同作用
- 用于语法转义的配置。 preset-env / stage-x 主要用于 es 的语法转义,
- 用于补丁的配置,即 polyfill 的配置。主要是 transform-runtime 。
- 用于不同框架的配置,如:react的jsx /flow 等
{
"presets":[
"react",
["env",{...}],
"stage-0"
],
"plugins":[
"pluginName",
["transform-runtime",{...}]
],
"ignore":["path",'file'],
"miniied":boolean,
"comments":boolean,
"env":{
"test":{
"presets":[],
"plugins":[]
}
},
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 主要几个配置
- presets : 预设,插件的集合,倒序执行
- plugins : 插件,先执行插件,在执行预设,顺序执行
- ignore : 忽略的文件
- minify : 压缩代码
- comments: 是否需要注释
- env : 设置不同的环境,应用不同的配置,配置取值:BABEL_ENV,如若没有取 NODE_ENV 的值,默认为 development.
# 常用插件【plugin】
该属性是告诉babel要使用那些插件,这些插件可以控制如何转换代码。
# babel-polyfill
理解 babel-polyfill 和 babel-runtime 及 babel-plugin-transform-runtime
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-folyfill 是修改全局的对象的原型,添加不兼容的 api 方法,或者修改不兼容的 api 方法。
# 用法
1、在入口文件的顶部添加:
require('@babel-polyfill');
import '@babel-polyfill'
2
2、webpack 中添加
{
entry:['@babel-polyfill','./app.js']
}
2
3
在 webpack 的配置文件中添加。
3、直接在 html 中引用,在打包 js 的文件之前
<script src = './babel-polyfill.js' ></script>
# babel-runtime
# polyfill与runtime的区别
原理上
- babel-polyfill 的原理是当运行环境中并没有实现的一些方法,babel-polyfill会做兼容。
- babel-runtime 它是将es6编译成es5去执行。使用es6的语法来编写,最终会通过babel-runtime编译成es5.也就是说,不管浏览器是否支持ES6,只要是ES6的语法,它都会进行转码成ES5.所以就有很多冗余的代码。
使用上
- babel-polyfill 它是通过向全局对象和内置对象的prototype上添加方法来实现的。比如运行环境中不支持Array.prototype.find 方法,引入polyfill, 我们就可以使用es6方法来编写了,但缺点就是会造成全局空间污染。
- babel-runtime: 它不会污染全局对象和内置对象的原型,比如说我们需要Promise,我们只需要import Promise from
babel-runtime/core-js/promise
即可,这样不仅避免污染全局对象,而且可以减少不必要的代码。- 虽然 babel-runtime 可以解决 babel-polyfill中的避免污染全局对象,但是它自己也有缺点的,比如上,如果现在有100个文件甚至更多的话,难道我们需要一个个文件加import Promise from 'babel-runtime/core-js/promise' 吗?那这样肯定是不行的,
- 因此这个时候出来一个 叫 babel-plugin-transform-runtime,它就可以帮助我们去避免手动引入 import的痛苦,并且它还做了公用方法的抽离。比如说我们有100个模块都使用promise,但是promise的polyfill仅仅存在1份。这就是 babel-plugin-transform-runtime 插件的作用。
# babel-plugin-transform-runtime
对 babel 转化后的代码,进行再次转化,与 babel-polyfill 一样,解决不兼容的全局 api 。
**与 babel-polyfill 不同是它不是添加/修改全局对象。**它是对不兼容的方法进行特殊处理,也就是添加辅助方法来做兼容。并且 transform-runtime 是在需要进行兼容转化时候引用。transform-runtime 是依赖 babel-runtime ,且辅助方法都是引用的 babel-runtime
# 特点
不会污染全局变量,因为不会修改全局对象, 没有修改实例的方法,所以有些方法不能实现。
["transform-runtime", {
"corejs":false,
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}]
2
3
4
5
6
7
- coresjs : 是否用 @babel-runtime/corejs 中的辅助方法来替换 Map / Set 等方法
- **helpers: 默认值为true,**表示是否开启内联的babel helpers(即babel或者环境本来存在的某些对象方法函数)如:extends,etc这样的在调用模块名字时将被替换名字。
- polyfill:默认值为true,表示是否把内置的东西(Promise, Set, Map)等转换成非全局污染的。
- **regenerator:默认值为true,**是否开启generator函数转换成使用regenerator runtime来避免污染全局域。
- **moduleName:默认值为 babel-runtime,**当调用辅助 设置模块(module)名字/路径.
比如如下这样设置:
{
"moduleName": "flavortown/runtime"
}
2
3
import引入文件如下这个样子:
import extends from 'flavortown/runtime/helpers/extends';
# 其他插件
transform-async-to-generator : 异步函数 async/await 插件
transform-decorators-leagacy : 装饰器插件
syntax-dynamic-import :import() 插件
transform-runtime : 辅助器插件,用于ployfill
transform-object-rest-spread : 用于合并 var test = {a:1,b2};var t = {...test,n:1}
transform-funciton-bind : 用于编译 obj::fun => fun.bind(obj)
# 常见预设【presets】
presets属性告诉Babel要转换的源码使用了哪些新的语法特性,presets是一组Plugins的集合。
总括
- env : 用于替换 es2015 / es2016 / es2017 的预设。根据环境引入插件
- react :react的插件集合
- react-optimize : react 代码优化,如去除 propsType 减少生产上面代码
- stage-x :草案代码插件集合
- flow : flow 插件集合
- minify : 代码优化的集合
- typescript : typescript 插件集合
# babel-preset-env
# @babel/preset-env
是一个智能预设。根据浏览器和运行环境,自动的确定 babel 插件和 polyfills 。
没有任何配置的情况下,和 babel-preset-latest 一样 ( es2015/es2016/es2017一起使用 )。
比如:
babel-preset-es2015: 可以将es6的代码编译成es5. babel-preset-es2016: 可以将es7的代码编译为es6. babel-preset-es2017: 可以将es8的代码编译为es7. babel-preset-latest: 支持现有所有ECMAScript版本的新特性。
# 详细历史过程
比如我们需要转换es6语法,我们可以在 .babelrc的plugins中按需引入一下插件,比如:
check-es2015-constants、es2015-arrow-functions、es2015-block-scoped-functions等等几十个不同作用的plugin; 那么配置项可能是如下方式:.babelrc
{
"plugins": [
"check-es2015-constants",
"es2015-arrow-functions",
"es2015-block-scoped-functions",
// ...
]
}
2
3
4
5
6
7
8
但是Babel团队为了方便,将同属ES2015的几十个Transform Plugins集合到babel-preset-es2015一个Preset中,这样我们只需要在.babelrc的presets加入es2015一个配置就可以完成全部ES2015语法的支持;如下配置:.babelrc
{
"presets": [
"es2015"
]
}
2
3
4
5
但是我们随着时间的推移,将来可能会有跟多的版本插件,比如 bebel-preset-es2018,.... 等等。 因此 babel-preset-env 出现了,它的功能类似于 babel-preset-latest,它会根据目标环境选择不支持的新特性来转译。
首先需要在项目中安装,如下命令:
npm install babel-preset-env --save-dev
在.babelrc配置文件中 可以如下简单的配置:
{
"presets": ['env']
}
2
3
# 常用配置及其详解
{
"presets": [
[
//'@babel/preset-env',
"env",
{
'targets':{
//支持每个浏览器最后两个版本和safari大于等于7版本所需的polyfill代码转换
//'browsers': ['last 2 versions', 'safari >= 7']
//'browsers': '> 5%'//支持市场份额超过5%的浏览器
'chrome': 56 // 指定浏览器版本
"browser":["ie>=8","chrome>=62"],
// "node": "current"//如果通过Babel编译Node.js代码的话,可以设置 "target.node" 是 'current', 含义是 支持的是当前运行版本的nodejs。
"node":"8.9.0",
"safari":"tp"
},
"modules":false,
"debug":true,
"uglify":true
"useBuiltIns":true
}
]
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
target : 描述您为项目配置的 支持/定位 的环境; targets: {[string]: number | string }, 默认为{}; 含义是支持一个运行环境的对象,比如支持node版本;可以如下配置: node: '6.0';运行环境: chrome, opera, edge, firefox, safari, ie, ios, android, node, electron
- browsers :浏览器的支持情况,将返回支持浏览器列表。 会被 target.ie 覆盖; 支持浏览器的配置项,该配置项使用方式可以到 browserslist来查询(https://github.com/browserslist/browserslist) (opens new window)
- [ 'ie>=8' ] 支持 ie8 的环境
- "default" 默认
- node :指定是 node 环境,且可以指定版本
- safari : safari 版本
modules : 启用将 es6 转为其他模块;
- 启用将ES6模块语法转换为另一种模块类型。将该设置为false就不会转换模块。默认为 'commonjs'. 该值可以有如下:'amd' | 'umd' | 'systemjs' | 'commonjs' | false
- 以前需要使用babel来将ES6的模块语法转换为AMD, CommonJS,UMD之类的模块化标准语法,但是现在webpack都帮做了这件事,所以我们不需要babel来做,因此需要在babel配置项中设置modules为false,因为它默认值是commonjs, 否则的话,会产生冲突。
loose, 该参数值默认为false。含义是:允许它们为这个 preset 的任何插件启用”loose” 转换。
debug : 是否启用 console.log
include / exclude : 必须启用的 plugin 功能 / 不启用的 plugin 功能;
- **include: 包含一些插件,默认为 [];**比如包含箭头函数,可以如下配置:
{ "presets": [ ["env", { "targets": { "browsers": ["last 2 versions", "safari >= 7"] }, "include": ["transform-es2015-arrow-functions", "es6.map"] }] ] }
1
2
3
4
5
6
7
8
9
10**exclude; 排除哪些插件,默认为 [];**比如 排除生成器,可以如下配置:
{ "presets": [ ["env", { "targets": { "browsers": ["last 2 versions", "safari >= 7"] }, "exclude": ["transform-regenerator", "es6.set"] }] ] }
1
2
3
4
5
6
7
8
9
10
uglify : 压缩代码// 目前没有用了,用
["minify"]
替换使用;useBuiltIns :
- false : 引用所有的 babel-polyfill , 在 webpack 中添加 babel-polyfill 入口处:entry:[ 'babel-polyfill' , 'index.js' ] , 引用所有的 polyfill,体积变大
- true : 引用部分,根据配置的 preset-env 环境,引用 polyfill ,在入口文件中要引用 babel-polyfill
# babel-presets-stage-x
官方预设(preset), 有两种,一个是按年份(babel-preset-es2017),一个是按阶段(babel-preset-stage-0)。 这主要是根据TC39 委员会ECMASCRPIT 发布流程来制定的。因此到目前为止 有4个不同的阶段预设:
babel-preset-stage-0
babel-preset-stage-1
babel-preset-stage-2
babel-preset-stage-3
2
3
4
以上每种预设都依赖于紧随的后期阶段预设,数字越小,阶段越靠后,存在依赖关系。也就是说stage-0是包括stage-1的,以此类推。因此 stage-0包含stage-1/2/3的内容。所以如果我们不知道需要哪个stage-x的话,直接引入stage-0就好了。
stage0 (https://babeljs.io/docs/en/babel-preset-stage-0) (opens new window)只是一个美好激进的想法,一些 Babel 插件实现了对这些特性的支持 ,但是不确定是否会被定为标准.
stage1 (https://babeljs.io/docs/en/babel-preset-stage-1) (opens new window) 值得被纳入标准的特性.
stage2 (https://babeljs.io/docs/en/babel-preset-stage-2) (opens new window) 该特性规范己经被起草,将会被纳入标准里.
stage3 (https://babeljs.io/docs/en/babel-preset-stage-3) (opens new window)该特性规范已经定稿,大浏览器厂商和 Node.js 社区己开始着手实现.
但是在我们使用的时候只需要安装你想要的阶段就可以了:比如 babel-preset-stage-2, 安装命令如下:
npm install --save-dev babel-preset-stage-2
# @babel-register
通过 @babel-register 来注册当前所有的代码需要转码
var fs = require('fs');
var babelConfig = JSON.parse(fs.readFileSync('./.babelrc'));
require('babel-register')(babelConfig);
2
3
很显然,这里可以传入一个 .babelrc 配置文件来进行转码时候的配置。
只需要添加到入口文件中。
node 端就可以这这样实现 babel 的转码。
# tools
- babel-parser : babel 的解析器,用于解析 Javascript / jsx / Typescript / flow 等代码
- babel-core : babel 的核心文件,用于转码的
- babel-generator : 代码的转化,源码和转化代码
- babel-code-frame : 代码转化位置等转码映射
- babel-helpers : 辅助器方法
- babel-runtime : 提供了一些列的辅助方法,与 polyfill 类似
# 在webpack中配置babel
# 由于babel所做的事情是转换代码,所有需要使用loader去转换,因此我们需要配置babel-loader。
# 依赖安装
在安装babel-loader之前,我们需要安装babel-core, 因为babel-core是Babel编译器的核心,因此也就意味着如果我们需要使用babel-loader进行es6转码的话,我们首先需要安装 babel-core, 安装命令如下即可:
npm install --save-dev babel-core
npm install --save-dev babel-loader
npm install --save-dev babel-preset-env babel-plugin-transform-runtime babel-preset-stage-2
2
3
因此 .babelrc 配置如下即可:
{
"plugins": [
[
"transform-runtime",
{
"polyfill": false
}
]
],
"presets": [
[
"env",
{
"modules": false
}
],
"stage-2"
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 设置配置
webpack.config.js
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ClearWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: './js/samy.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),// 将输出的文件都放在dist目录下
publicPath: '/dist'
},
mode: 'development',
module: {
rules: [
{
test: /\.css$/,// 使用正则去匹配要用该loader转换的css文件
loaders: ExtractTextPlugin.extract({
use: ['css-loader']// 转换 .css文件需要使用的Loader
})
},
{
test: /\.(png|jpg)$/,
loader: 'url-loader',
options: {
limit: 10000,
name: '[name].[ext]'
}
},
{
test: /\.js$/,
exclude: /(node_modules)/, // 排除文件
loader: 'babel-loader'
}
]
},
resolve: {
// modules: ['plugin', 'js']
},
externals: {
jquery: 'jQuery'
},
devtool: 'source-map',
plugins: [
// new ClearWebpackPlugin(['dist']),
new ExtractTextPlugin({
filename: `main.css`// 从js文件中提取出来的 .css文件的名称
})
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package.json
{
"name": "demo1",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack-dev-server --progress --colors --devtool source-map --hot --inline",
"build": "webpack --progress --colors"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-2": "^6.24.1",
"clean-webpack-plugin": "^0.1.19",
"css-loader": "^1.0.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^1.1.11",
"path": "^0.12.7",
"style-loader": "^0.21.0",
"uglifyjs-webpack-plugin": "^1.2.7",
"url-loader": "^1.0.1",
"webpack": "^4.16.1",
"webpack-cli": "^3.0.8",
"webpack-dev-server": "^3.1.4"
},
"dependencies": {
"axios": "^0.18.0",
"jquery": "^3.3.1"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
samy.js
: 然后重新运行打包命令 npm run dev 后,打开浏览器运行 可以看到控制台输出 {value: "a", done: false},说明babel已经转译了。
function* g() {
yield 'a';
yield 'b';
yield 'c';
return 'ending';
}
var gen = g();
console.log(gen.next()); // 返回Object {value: "a", done: false}
for(let a of [1,2,3,4]) {
console.log(a); // 打印出 1, 2, 3, 4
}
2
3
4
5
6
7
8
9
10
11
12
13
# 简版设置
- 首先我们创建一个空白项目
- 进入项目,并安装以下各个依赖
npm install --save webpack
npm install --save babel-loader
npm install --save babel-core
npm install --save babel-preset-es2015
2
3
4
自从babel升级6.x版本后就分成了两个插件,一个是babel-core【终端运行】,(如果是node请安装babel-cli ),一个是babel-preset-es2015
安装完上述内容之后,需要设置一个.babelrc的文件放在根目录下,内容为
{
"presets": ["es2015"]
}
2
3
并且在webpack.config.js中配置babel-loader
module.exports = {
entry: "./js/main.js",
output:{
filename: 'bundle.js'
},
module: {
loaders: [{
test: /\.js$/,
loader: "babel-loader"
}]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
配置完成后,就可以直接在JS文件中使用es6的语法,然后通过webpack命令打包生成,即可
# 相关链接
https://babel.docschina.org/docs/en/7.0.0/configuration