webpack插件@babel/polyfill mini-css-extract-plugin babel-loader
拆分webpack配置文件后,和正常項目的結(jié)構(gòu)更接近了,但是還有不是缺少幾個關(guān)鍵的插件。
1. css樣式分離成獨立的文件,并不會壓縮css代碼
1.extract-text-webpack-plugin
2.mini-css-extract-plugin
extract-text-webpack-plugin配置好后,npm run dev執(zhí)行后,發(fā)現(xiàn)報錯:
$ webpack
(node:18800) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
C:\Users\EEE\AppData\Roaming\npm\node_modules\webpack\lib\Chunk.js:752
throw new Error(
^
Error: Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead
......
百度一查,據(jù)說是版本問題,咱也不懂,又去git倉庫查看了一下這個插件,發(fā)現(xiàn)好長一段時間沒更新過了,既然這樣直接放棄,換一個。
換成mini-css-extract-plugin這個插件,功能是一樣的。
#webpack.common.js
把module.rules里面css打包的那一段代碼注釋。
#webpack.dev.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = true;
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development')
}),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: devMode ? '[name].css' : '[name].[hash].css',
chunkFilename: devMode ? '[id].css' : '[id].[hash].css',
})
],
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: process.env.NODE_ENV === 'development',
},
},
'css-loader'
/*,
'postcss-loader',
'sass-loader',*/
]
}
]
}
#webpack.prod.js
#mini-css-extract-plugin 本身不具有壓縮css代碼的功能,所以需要添加下面兩個插件配合
#npm install --save-dev terser-webpack-plugin optimize-css-assets-webpack-plugin
const TerserJSPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
}),
......
],
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
}
2.babel-loader
npm install babel-loader @babel/core @babel/preset-env --save-dev
babel-loader7/8 要和 @babel/core ,@babel/preset-env 搭配使用,babel-loader6則使用babel-core/babel-preset-env。
@babel/preset-env 或 babel-preset-env // 有了它,你不再需要添加2015、2016、2017,全都支持。
# .babelrc (文件名不要搞錯了)
{
"presets": [
["@babel/preset-env",{
"targets": {
"browsers": ["> 1%", "last 2 versions"]
}
}]
]
}
.babelrc文件,babel-loader8必須使用對應(yīng)的"@babel/preset-env",低于babel-loader8可以用"env"。
# webpack.common.js
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader'
},
// 不希望babel處理的 node_modules 目錄下的文件
// UglifyJsPlugin不能壓縮es6,如果過濾這個目錄,
// 可能會導(dǎo)致node_modules里面的es6代碼不能轉(zhuǎn)換導(dǎo)致壓縮報錯
// exclude: '/node_modules/'
},
......
]
}
到了這一步好像已經(jīng)OK了,已經(jīng)可以把es6轉(zhuǎn)換成es5了,但如果你在頁面里面用了promise,你就會發(fā)現(xiàn),好像轉(zhuǎn)換有不好使了。
這時有兩個選擇:
1. @babel/polyfill
2. @babel/plugin-transform-runtime搭配@babel/runtime
npm install --save @babel/polyfill
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
polyfill
中文翻譯是墊片。
es2015里不僅只有新的語法,還有實例的擴展,比如String.prototype.includes,其實這里只是調(diào)用了String實例的一個方法,我們無論怎么語法轉(zhuǎn)換也沒有什么用吧,如果我們在不支持String.prototype.includes的編譯器里跑這些代碼,會得到 'foo'.includes is not a function. 這樣的一個報錯,而不是語法報錯。
Polyfill提供的就是一個這樣功能的補充,實現(xiàn)了Array、Object等上的新方法,實現(xiàn)了Promise、Symbol這樣的新Class等。到這里應(yīng)該能明白了,為什么安裝@babel/polyfill沒有-dev,因為就算代碼發(fā)布后,編譯后的代碼依然會依賴這些新特性來實現(xiàn)功能。
雖然@babel/polyfill提供了我們想要的所有新方法新類,但是這里依然存在一些問題:
1.體積太大:比如我只用了String的新特性,但是我把整個包都引進來了,這不是徒增了很多無用的代碼,解決這個問題可以采用按需加載,我們有env這個preset,它又一個useBuiltIns選項,如果設(shè)置成"usage",那么將會自動檢測語法幫你require你代碼中使用到的功能。
[
"@babel/env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1",
},
"useBuiltIns": "usage"
},
]
2.污染全局環(huán)境:如果你引用了 @babel/polyfill,那么像Promise這樣的新類就是掛載在全局上的,這樣就會污染了全局命名空間??赡茉谝粋€團建建立的項目問題不太大,但是如果你是一個工具的開發(fā)者,你把全局環(huán)境污染了,別人用你的工具,就有可能把別人給坑了。解決這個問題就只能引入@babel/plugin-transform-runtime 搭配@babel/runtime來替代 @babel/polyfill。
transform-runtime
主要功能:
- 避免多次編譯出helper函數(shù):
Babel轉(zhuǎn)移后的代碼想要實現(xiàn)和原來代碼一樣的功能需要借助一些幫助函數(shù),比如:
class Person {}
會被轉(zhuǎn)換為:
"use strict";
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Person = function Person() {
_classCallCheck(this, Person);
};
這里_classCallCheck就是一個helper函數(shù),試想下如果我們很多文件里都聲明了類,那么就會產(chǎn)生很多個這樣的helper函數(shù),積少成多增大了代碼體積。
這里的@babel/runtime包就聲明了所有需要用到的幫助函數(shù),而@babel/plugin-transform-runtime的作用就是將所有需要helper函數(shù)的文件,依賴@babel/runtime包:
"use strict";
var _classCallCheck2 = require("@babel/runtime/helpers/classCallCheck");
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
var Person = function Person() {
(0, _classCallCheck3.default)(this, Person);
};
這里就沒有再編譯出helper函數(shù)classCallCheck了,而是直接引用了@babel/runtime中的helpers/classCallCheck。
- 解決@babel/polyfill提供的類或者實例方法污染全局作用域的情況。
@babel/plugin-transform-runtime會為代碼創(chuàng)建一個沙盒環(huán)境,為core-js這里內(nèi)建的實例提供假名,你可以無縫的使用這些新特性,而不需要使用require polyfill。
但是,記住,babel-runtime有個缺點,它不模擬實例方法,即babel-runtime沒有模擬內(nèi)置對象原型上的方法,所以類似Array.prototype.find這樣的方法,你通過babel-runtime是無法使用的。
用法.babelrc:
{
"presets": [
["@babel/preset-env",{
"targets": {
"browsers": ["> 1%", "last 2 versions"]
}
}]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": false,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
貌似測試代碼只用了@babel/plugin-transform-runtime搭配@babel/runtime做測試時,竟然頁面可以用”foolish".includes("foo")這樣的方法,根本沒有引入polyfill,再次記錄一下。這似乎和上面的說法有點相悖。
參考2:https://github.com/creeperyang/blog/issues/25
參考3:https://github.com/kaivin/webpack4.x
2019-5-9找到的新資料,網(wǎng)絡(luò)上的資料比較亂,每個人的環(huán)境也不相同,一定要多做幾次測試代碼
useBuiltIns 和 transform-runtime 不能同時使用。

cnpm install core-js@3 --save
.babelrc
{
// targets, useBuiltIns 等選項用于編譯出兼容目標環(huán)境的代碼
// 其中 useBuiltIns 如果設(shè)為 "usage"
// Babel 會根據(jù)實際代碼中使用的 ES6/ES7 代碼,以及與你指定的 targets,按需引入對應(yīng)的 polyfill
// 而無需在代碼中直接引入 import '@babel/polyfill',避免輸出的包過大,同時又可以放心使用各種新語法特性。
"presets": [
["@babel/preset-env",{
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions"]
},
"useBuiltIns": "usage",
"corejs": 3
}],
"@babel/preset-react"
],
"plugins": [
]
}
3.uglifyjs-webpack-plugin
webpack4已經(jīng)不支持使用移除 webpack.optimize.UglifyJsPlugin 壓縮配置了, 推薦使用 optimization.minimize 屬性替代。
不需要install這個插件,相關(guān)代碼注釋,只用一行代碼minimize: true即可:
module.exports = merge(common, {
mode: 'production',
//devtool: 'source-map',
optimization: {
minimize: true, //取代 new UglifyJsPlugin(/* ... */)
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
},
...
4. postcss-loader 和 autoprefixer
npm install --save-dev postcss-loader autoprefixer
#webpack.common.js
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: process.env.NODE_ENV === 'development',
},
},
'css-loader',
{
loader:'postcss-loader',
options: {
plugins: [
require("autoprefixer")({
//必須設(shè)置支持的瀏覽器才會自動添加添加瀏覽器兼容
browsers : ['last 10 versions']
})
]
}
}
/*,
'postcss-loader',
'sass-loader',*/
]
}
]
}