深刻淺出的webpack構建工具---babel之配置文件.babelrc(三)

閱讀目錄javascript

一:理解 babel之配置文件.babelrc 基本配置項css

二:在webpack中配置babelhtml

一:理解 babel之配置文件.babelrc 基本配置項java

1. 什麼是babel? 它是幹什麼用的?node

   ES6是2015年發佈的下一代javascript語言標準,它引入了新的語法和API,使咱們編寫js代碼更加駕輕就熟,好比class,let,for...of promise等等這樣的,可是惋惜的是這些js新特性只被最新版本的瀏覽器支持,可是低版本瀏覽器並不支持,那麼低版本瀏覽器下就須要一個轉換工具,把es6代碼轉換成瀏覽器能識別的代碼,babel就是這樣的一個工具。能夠理解爲 babel是javascript語法的編譯器。react

2. Babel編譯器
在Babel執行編譯的過程當中,會從項目的根目錄下的 .babelrc文件中讀取配置。.babelrc是一個json格式的文件。
在.babelrc配置文件中,主要是對預設(presets) 和 插件(plugins) 進行配置。.babelrc配置文件通常爲以下:jquery

{
  "plugins": [
     [
      "transform-runtime",
      {
        "polyfill": false
      }
     ]
   ],
   "presets": [
     [
       "env",
       {
         "modules": false
       }
     ],
     "stage-2",
     "react"
  ]
}

2.1 plugins
該屬性是告訴babel要使用那些插件,這些插件能夠控制如何轉換代碼。android

1. 理解 babel-polyfill 和 babel-runtime 及 babel-plugin-transform-runtimewebpack

   Babel默認只轉換新的javascript語法,而不轉換新的API,好比 Iterator, Generator, Set, Maps, Proxy, Reflect,Symbol,Promise 等全局對象。以及一些在全局對象上的方法(好比 Object.assign)都不會轉碼。
好比說,ES6在Array對象上新增了Array.form方法,Babel就不會轉碼這個方法,若是想讓這個方法運行,必須使用 babel-polyfill來轉換等。ios

所以:babel-polyfill和babel-runtime就是爲了解決新的API與這種全局對象或全局對象方法不足的問題,所以可使用這兩個插件能夠轉換的。

那麼他們二者的區別是什麼?
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 插件的做用。

2. 理解 babel-plugin-transform-runtime 的配置一些選項

所以經過上面的理解,咱們能夠對 transform-runtime 經過以下配置 plugins; 以下代碼:

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

配置項能夠看官網,查看官網

helpers: 默認值爲true,表示是否開啓內聯的babel helpers(即babel或者環境原本存在的某些對象方法函數)如:extends,etc這樣的
在調用模塊名字時將被替換名字。

polyfill:默認值爲true,表示是否把內置的東西(Promise, Set, Map)等轉換成非全局污染的。

regenerator:默認值爲true,是否開啓generator函數轉換成使用regenerator runtime來避免污染全局域。

moduleName:默認值爲 babel-runtime,當調用輔助 設置模塊(module)名字/路徑.
好比以下這樣設置:

{
  "moduleName": "flavortown/runtime"
}

import引入文件以下這個樣子:

import extends from 'flavortown/runtime/helpers/extends';

3 presets
presets屬性告訴Babel要轉換的源碼使用了哪些新的語法特性,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: 支持現有全部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",
    // ...
  ]
}

可是Babel團隊爲了方便,將同屬ES2015的幾十個Transform Plugins集合到babel-preset-es2015一個Preset中,這樣咱們只須要在.babelrc的presets加入es2015一個配置就能夠完成所有ES2015語法的支持了:
以下配置:

// .babelrc
{
  "presets": [
    "es2015"
  ]
}

可是咱們隨着時間的推移,未來可能會有跟多的版本插件,好比 bebel-preset-es2018,.... 等等。
所以 babel-preset-env 出現了,它的功能相似於 babel-preset-latest,它會根據目標環境選擇不支持的新特性來轉譯。

首先須要在項目中安裝,以下命令:

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

在.babelrc配置文件中 能夠以下簡單的配置:

{
  "presets": ['env']
}

咱們還能夠僅僅配置項目所支持的瀏覽器的配置

1. 支持每一個瀏覽器最後兩個版本和safari大於等於7版本所需的polyfill代碼轉換,咱們能夠以下配置:

{
  '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代碼的話,能夠設置 "target.node" 是 'current', 含義是 支持的是當前運行版本的nodejs。
以下配置代碼:

{
  "presets": [
    ["env", {
      "targets": {
        "node": "current"
      }
    }]
  ]
}

理解 babel-preset-env 中的選項配置:
1. targets: {[string]: number | string }, 默認爲{};
含義是支持一個運行環境的對象,好比支持node版本;能夠以下配置: node: '6.0';
運行環境: 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
該參數的含義是:啓用將ES6模塊語法轉換爲另外一種模塊類型。將該設置爲false就不會轉換模塊。默認爲 'commonjs'.
該值能夠有以下:
'amd' | 'umd' | 'systemjs' | 'commonjs' | false

咱們在項目中通常會看到以下配置,設置modules: false, 以下代碼配置:

"presets": [
   'env',
   {
     'modules': false
   }
]

這樣作的目的是:之前咱們須要使用babel來將ES6的模塊語法轉換爲AMD, CommonJS,UMD之類的模塊化標準語法,可是如今webpack都幫我作了這件事了,因此咱們不須要babel來作,所以須要在babel配置項中設置modules爲false,由於它默認值是commonjs, 不然的話,會產生衝突。

4. loose, 該參數值默認爲false
含義是:容許它們爲這個 preset 的任何插件啓用」loose」 轉換。

5. include: 包含一些插件,默認爲 [];
好比包含箭頭函數,能夠以下配置:

{
  "presets": [
    ["env", {
      "targets": {
        "browsers": ["last 2 versions", "safari >= 7"]
      },
      "include": ["transform-es2015-arrow-functions", "es6.map"]
    }]
  ]
}

6. exclude; 排除哪些插件,默認爲 [];
好比 排除生成器,能夠以下配置:

{
  "presets": [
    ["env", {
      "targets": {
        "browsers": ["last 2 versions", "safari >= 7"]
      },
      "exclude": ["transform-regenerator", "es6.set"]
    }]
  ]
}

3.2 理解 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

以上每種預設都依賴於緊隨的後期階段預設,數字越小,階段越靠後,存在依賴關係。也就是說stage-0是包括stage-1的,以此類推。所以 stage-0包含stage-1/2/3的內容。因此若是咱們不知道須要哪一個stage-x的話,直接引入stage-0就行了。

stage0 (https://babeljs.io/docs/en/babel-preset-stage-0) 只是一個美好激進的想法,一些 Babel 插件實現了對這些特性的支持 ,可是不肯定是否會被定爲標準.

stage1 (https://babeljs.io/docs/en/babel-preset-stage-1) 值得被歸入標準的特性.

stage2 (https://babeljs.io/docs/en/babel-preset-stage-2) 該特性規範己經被起草,將會被歸入標準裏.

stage3 (https://babeljs.io/docs/en/babel-preset-stage-3) 該特性規範已經定稿,大瀏覽器廠商和 Node.js 社區己開始着手實現.

可是在咱們使用的時候只須要安裝你想要的階段就能夠了:好比 babel-preset-stage-2, 安裝命令以下:

npm install --save-dev babel-preset-stage-2

二:在webpack中配置babel

在上面瞭解了babel後,如今咱們須要知道如何在webpack中使用它了。因爲babel所作的事情是轉換代碼,全部須要使用loader去轉換,所以咱們須要配置babel-loader。

在安裝babel-loader以前,咱們須要安裝babel-core, 由於babel-core是Babel編譯器的核心,所以也就意味着若是咱們須要使用babel-loader進行es6轉碼的話,咱們首先須要安裝 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以前,咱們仍是先看下目錄結構變成以下:

### 目錄結構以下:
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轉碼文件

所以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轉換的css文件
        test: /\.css$/,
        loaders: ExtractTextPlugin.extract({
          // 轉換 .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"
  }
}

如今咱們繼續在 main.js 代碼內 編寫 Generator 函數,代碼以下:

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
}

而後從新運行打包命令 npm run dev 後,打開瀏覽器運行 能夠看到控制檯輸出 {value: "a", done: false},說明babel已經轉譯了。

相關文章
相關標籤/搜索