一:理解 babel之配置文件.babelrc 基本配置項
1. 什么是babel? 它是干什么用的助赞?
ES6是2015年發(fā)布的下一代javascript語言標(biāo)準(zhǔn)康谆,它引入了新的語法和API,使我們編寫js代碼更加得心應(yīng)手映九,比如class梦湘,let,for...of promise等等這樣的,但是可惜的是這些js新特性只被最新版本的瀏覽器支持件甥,但是低版本瀏覽器并不支持捌议,那么低版本瀏覽器下就需要一個轉(zhuǎn)換工具,把es6代碼轉(zhuǎn)換成瀏覽器能識別的代碼引有,babel就是這樣的一個工具瓣颅。可以理解為 babel是javascript語法的編譯器譬正。
2. Babel編譯器
在Babel執(zhí)行編譯的過程中宫补,會從項目的根目錄下的 .babelrc文件中讀取配置。.babelrc是一個json格式的文件曾我。
在.babelrc配置文件中粉怕,主要是對預(yù)設(shè)(presets) 和 插件(plugins) 進(jìn)行配置。.babelrc配置文件一般為如下:
{
"plugins": [
[
"transform-runtime",
{
"polyfill": false
}
]
],
"presets": [
[
"env",
{
"modules": false
}
],
"stage-2",
"react"
]
}
2.1 plugins
該屬性是告訴babel要使用那些插件抒巢,這些插件可以控制如何轉(zhuǎn)換代碼贫贝。
1. 理解 babel-polyfill 和 babel-runtime 及 babel-plugin-transform-runtime
Babel默認(rèn)只轉(zhuǎn)換新的javascript語法,而不轉(zhuǎn)換新的API蛉谜,比如 Iterator, Generator, Set, Maps, Proxy, Reflect,Symbol,Promise 等全局對象稚晚。以及一些在全局對象上的方法(比如 Object.assign)都不會轉(zhuǎn)碼。
比如說型诚,ES6在Array對象上新增了Array.form方法客燕,Babel就不會轉(zhuǎn)碼這個方法,如果想讓這個方法運(yùn)行狰贯,必須使用 babel-polyfill來轉(zhuǎn)換等也搓。
因此:babel-polyfill和babel-runtime就是為了解決新的API與這種全局對象或全局對象方法不足的問題,因此可以使用這兩個插件可以轉(zhuǎn)換的暮现。
那么他們兩者的區(qū)別是什么还绘?
babel-polyfill 的原理是當(dāng)運(yùn)行環(huán)境中并沒有實(shí)現(xiàn)的一些方法,babel-polyfill會做兼容栖袋。
babel-runtime 它是將es6編譯成es5去執(zhí)行拍顷。我們使用es6的語法來編寫,最終會通過babel-runtime編譯成es5.也就是說塘幅,不管瀏覽器是否支持ES6昔案,只要是ES6的語法尿贫,它都會進(jìn)行轉(zhuǎn)碼成ES5.所以就有很多冗余的代碼。
babel-polyfill 它是通過向全局對象和內(nèi)置對象的prototype上添加方法來實(shí)現(xiàn)的踏揣。比如運(yùn)行環(huán)境中不支持Array.prototype.find 方法庆亡,引入polyfill, 我們就可以使用es6方法來編寫了,但是缺點(diǎn)就是會造成全局空間污染捞稿。
babel-runtime: 它不會污染全局對象和內(nèi)置對象的原型又谋,比如說我們需要Promise,我們只需要import Promise from 'babel-runtime/core-js/promise'即可娱局,這樣不僅避免污染全局對象彰亥,而且可以減少不必要的代碼。
雖然 babel-runtime 可以解決 babel-polyfill中的避免污染全局對象衰齐,但是它自己也有缺點(diǎn)的任斋,比如上,如果我現(xiàn)在有100個文件甚至更多的話耻涛,難道我們需要一個個文件加import Promise from 'babel-runtime/core-js/promise' 嗎废酷?那這樣肯定是不行的,因此這個時候出來一個 叫 babel-plugin-transform-runtime抹缕,
它就可以幫助我們?nèi)ケ苊馐謩右?import的痛苦澈蟆,并且它還做了公用方法的抽離。比如說我們有100個模塊都使用promise卓研,但是promise的polyfill僅僅存在1份丰介。
這就是 babel-plugin-transform-runtime 插件的作用。
2. 理解 babel-plugin-transform-runtime 的配置一些選項
因此通過上面的理解鉴分,我們可以對 transform-runtime 通過如下配置 plugins; 如下代碼:
{
'plugins': [
[
'transform-runtime',
{
'helpers': false,
'polyfill': false,
'regenerator': true,
'moduleName': 'babel-runtime'
}
]
]
}
helpers: 默認(rèn)值為true,表示是否開啟內(nèi)聯(lián)的babel helpers(即babel或者環(huán)境本來存在的某些對象方法函數(shù))如:extends带膀,etc這樣的
在調(diào)用模塊名字時將被替換名字志珍。
polyfill:默認(rèn)值為true,表示是否把內(nèi)置的東西(Promise, Set, Map)等轉(zhuǎn)換成非全局污染的垛叨。
regenerator:默認(rèn)值為true伦糯,是否開啟generator函數(shù)轉(zhuǎn)換成使用regenerator runtime來避免污染全局域。
moduleName:默認(rèn)值為 babel-runtime嗽元,當(dāng)調(diào)用輔助 設(shè)置模塊(module)名字/路徑.
比如如下這樣設(shè)置:
{
"moduleName": "flavortown/runtime"
}
import引入文件如下這個樣子:
import extends from 'flavortown/runtime/helpers/extends';
3 presets
presets屬性告訴Babel要轉(zhuǎn)換的源碼使用了哪些新的語法特性敛纲,presets是一組Plugins的集合。
3.1 理解 babel-preset-env
比如:
babel-preset-es2015: 可以將es6的代碼編譯成es5.
babel-preset-es2016: 可以將es7的代碼編譯為es6.
babel-preset-es2017: 可以將es8的代碼編譯為es7.
babel-preset-latest: 支持現(xiàn)有所有ECMAScript版本的新特性剂癌。
舉個列子淤翔,比如我們需要轉(zhuǎn)換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",
// ...
]
}
但是Babel團(tuán)隊為了方便,將同屬ES2015的幾十個Transform Plugins集合到babel-preset-es2015一個Preset中抡谐,這樣我們只需要在.babelrc的presets加入es2015一個配置就可以完成全部ES2015語法的支持了:
如下配置:
// .babelrc
{
"presets": [
"es2015"
]
}
但是我們隨著時間的推移裁奇,將來可能會有跟多的版本插件,比如 bebel-preset-es2018,.... 等等。
因此 babel-preset-env 出現(xiàn)了麦撵,它的功能類似于 babel-preset-latest刽肠,它會根據(jù)目標(biāo)環(huán)境選擇不支持的新特性來轉(zhuǎn)譯。
首先需要在項目中安裝免胃,如下命令:
npm install babel-preset-env --save-dev
在.babelrc配置文件中 可以如下簡單的配置:
{
"presets": ['env']
}
我們還可以僅僅配置項目所支持的瀏覽器的配置
1. 支持每個瀏覽器最后兩個版本和safari大于等于7版本所需的polyfill代碼轉(zhuǎn)換音五,我們可以如下配置:
{
'presets': [
['env', {
'target': {
'browsers': ['last 2 versions', 'safari >= 7']
}
}]
]
}
2. 支持市場份額超過5%的瀏覽器,可以如下配置:
{
'presets': [
['env', {
'target': {
'browsers': '> 5%'
}
}]
]
}
3. 指定瀏覽器版本杜秸,可以如下配置:
{
'presets': [
['env', {
'target': {
'chrome': 56
}
}]
]
}
Node.js
如果通過Babel編譯Node.js代碼的話放仗,可以設(shè)置 "target.node" 是 'current', 含義是 支持的是當(dāng)前運(yùn)行版本的nodejs。
如下配置代碼:
{
"presets": [
["env", {
"targets": {
"node": "current"
}
}]
]
}
理解 babel-preset-env 中的選項配置:
1. targets: {[string]: number | string }, 默認(rèn)為{};
含義是支持一個運(yùn)行環(huán)境的對象撬碟,比如支持node版本诞挨;可以如下配置: node: '6.0';
運(yùn)行環(huán)境: chrome, opera, edge, firefox, safari, ie, ios, android, node, electron
2. targets.browsers <Array | string>
支持瀏覽器的配置項,該配置項使用方式可以到 browserslist來查詢(https://github.com/browserslist/browserslist)
比如上面的 支持每個瀏覽器最后兩個版本和safari大于等于7版本呢蛤。如上配置惶傻。
3. modules
該參數(shù)的含義是:啟用將ES6模塊語法轉(zhuǎn)換為另一種模塊類型。將該設(shè)置為false就不會轉(zhuǎn)換模塊其障。默認(rèn)為 'commonjs'.
該值可以有如下:
'amd' | 'umd' | 'systemjs' | 'commonjs' | false
我們在項目中一般會看到如下配置银室,設(shè)置modules: false, 如下代碼配置:
"presets": [
'env',
{
'modules': false
}
]
這樣做的目的是:以前我們需要使用babel來將ES6的模塊語法轉(zhuǎn)換為AMD, CommonJS,UMD之類的模塊化標(biāo)準(zhǔn)語法励翼,但是現(xiàn)在webpack都幫我做了這件事了蜈敢,所以我們不需要babel來做,因此需要在babel配置項中設(shè)置modules為false汽抚,因為它默認(rèn)值是commonjs, 否則的話抓狭,會產(chǎn)生沖突。
4. loose, 該參數(shù)值默認(rèn)為false造烁。
含義是:允許它們?yōu)檫@個 preset 的任何插件啟用”loose” 轉(zhuǎn)換否过。
5. include: 包含一些插件,默認(rèn)為 [];
比如包含箭頭函數(shù)惭蟋,可以如下配置:
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
},
"include": ["transform-es2015-arrow-functions", "es6.map"]
}]
]
}
6. exclude苗桂; 排除哪些插件,默認(rèn)為 [];
比如 排除生成器告组,可以如下配置:
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
},
"exclude": ["transform-regenerator", "es6.set"]
}]
]
}
3.2 理解 babel-presets-stage-x
官方預(yù)設(shè)(preset), 有兩種煤伟,一個是按年份(babel-preset-es2017),一個是按階段(babel-preset-stage-0)。 這主要是根據(jù)TC39 委員會ECMASCRPIT 發(fā)布流程來制定的持偏。因此到目前為止 有4個不同的階段預(yù)設(shè):
babel-preset-stage-0
babel-preset-stage-1
babel-preset-stage-2
babel-preset-stage-3
以上每種預(yù)設(shè)都依賴于緊隨的后期階段預(yù)設(shè)驼卖,數(shù)字越小,階段越靠后鸿秆,存在依賴關(guān)系酌畜。也就是說stage-0是包括stage-1的,以此類推卿叽。因此 stage-0包含stage-1/2/3的內(nèi)容桥胞。所以如果我們不知道需要哪個stage-x的話,直接引入stage-0就好了考婴。
stage0 (https://babeljs.io/docs/en/babel-preset-stage-0) 只是一個美好激進(jìn)的想法贩虾,一些 Babel 插件實(shí)現(xiàn)了對這些特性的支持 ,但是不確定是否會被定為標(biāo)準(zhǔn).
stage1 (https://babeljs.io/docs/en/babel-preset-stage-1) 值得被納入標(biāo)準(zhǔn)的特性.
stage2 (https://babeljs.io/docs/en/babel-preset-stage-2) 該特性規(guī)范己經(jīng)被起草沥阱,將會被納入標(biāo)準(zhǔn)里.
stage3 (https://babeljs.io/docs/en/babel-preset-stage-3) 該特性規(guī)范已經(jīng)定稿缎罢,大瀏覽器廠商和 Node.js 社區(qū)己開始著手實(shí)現(xiàn).
但是在我們使用的時候只需要安裝你想要的階段就可以了:比如 babel-preset-stage-2, 安裝命令如下:
npm install --save-dev babel-preset-stage-2
二:在webpack中配置babel
在上面了解了babel后考杉,現(xiàn)在我們需要知道如何在webpack中使用它了策精。由于babel所做的事情是轉(zhuǎn)換代碼,所有需要使用loader去轉(zhuǎn)換崇棠,因此我們需要配置babel-loader咽袜。
在安裝babel-loader之前,我們需要安裝babel-core枕稀, 因為babel-core是Babel編譯器的核心询刹,因此也就意味著如果我們需要使用babel-loader進(jìn)行es6轉(zhuǎn)碼的話,我們首先需要安裝 babel-core, 安裝命令如下即可:
npm install --save-dev babel-core
然后我們再安裝 babel-loader, 命令如下:
npm install --save-dev babel-loader
接著我們需要安裝 babel-preset-env, babel-plugin-transform-runtime, babel-preset-stage-2, 如下命令安裝
npm install --save-dev babel-preset-env babel-plugin-transform-runtime babel-preset-stage-2
因此 .babelrc 配置如下即可:
{
"plugins": [
[
"transform-runtime",
{
"polyfill": false
}
]
],
"presets": [
[
"env",
{
"modules": false
}
],
"stage-2"
]
}
在做demo之前萎坷,我們還是先看下目錄結(jié)構(gòu)變成如下:
### 目錄結(jié)構(gòu)如下:
demo1 # 工程名
| |--- dist # 打包后生成的目錄文件
| |--- node_modules # 所有的依賴包
| |--- js # 存放所有js文件
| | |-- demo1.js
| | |-- main.js # js入口文件
| |
| |--- webpack.config.js # webpack配置文件
| |--- index.html # html文件
| |--- styles # 存放所有的css樣式文件
| |--- .gitignore
| |--- README.md
| |--- package.json
| |--- .babelrc # babel轉(zhuǎn)碼文件
因此webpack配置中需要添加 babel-loader 配置凹联,如下配置:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/, // 排除文件
loader: 'babel-loader'
}
]
}
}
webpack 所有配置如下代碼
const path = require('path');
// 提取css的插件
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ClearWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: './js/main.js',
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist'
},
mode: 'development',
module: {
rules: [
{
// 使用正則去匹配要用該loader轉(zhuǎn)換的css文件
test: /\.css$/,
loaders: ExtractTextPlugin.extract({
// 轉(zhuǎn)換 .css文件需要使用的Loader
use: ['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({
// 從js文件中提取出來的 .css文件的名稱
filename: `main.css`
})
]
};
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"
}
}
現(xiàn)在我們繼續(xù)在 main.js 代碼內(nèi) 編寫 Generator 函數(shù),代碼如下:
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
}
然后重新運(yùn)行打包命令 npm run dev 后哆档,打開瀏覽器運(yùn)行 可以看到控制臺輸出 {value: "a", done: false}匕垫,說明babel已經(jīng)轉(zhuǎn)譯了。