webpack插件@babel/polyfill mini-css-extract-plugin babel-loader
拆分webpack配置文件后,和正常项目的结构更接近了,但是还有不是缺少几个关键的插件。
1. css样式分离成独立的文件,并不会压缩css代码
1.extract-text-webpack-plugin
2.mini-css-extract-plugin
extract-text-webpack-plugin配置好后,npm run dev执行后,发现报错:
$ 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
......
百度一查,据说是版本问题,咱也不懂,又去git仓库查看了一下这个插件,发现好长一段时间没更新过了,既然这样直接放弃,换一个。
换成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必须使用对应的"@babel/preset-env",低于babel-loader8可以用"env"。
# webpack.common.js
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader'
},
// 不希望babel处理的 node_modules 目录下的文件
// UglifyJsPlugin不能压缩es6,如果过滤这个目录,
// 可能会导致node_modules里面的es6代码不能转换导致压缩报错
// exclude: '/node_modules/'
},
......
]
}
到了这一步好像已经OK了,已经可以把es6转换成es5了,但如果你在页面里面用了promise,你就会发现,好像转换有不好使了。
这时有两个选择:
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,其实这里只是调用了String实例的一个方法,我们无论怎么语法转换也没有什么用吧,如果我们在不支持String.prototype.includes的编译器里跑这些代码,会得到 'foo'.includes is not a function. 这样的一个报错,而不是语法报错。
Polyfill提供的就是一个这样功能的补充,实现了Array、Object等上的新方法,实现了Promise、Symbol这样的新Class等。到这里应该能明白了,为什么安装@babel/polyfill没有-dev,因为就算代码发布后,编译后的代码依然会依赖这些新特性来实现功能。
虽然@babel/polyfill提供了我们想要的所有新方法新类,但是这里依然存在一些问题:
1.体积太大:比如我只用了String的新特性,但是我把整个包都引进来了,这不是徒增了很多无用的代码,解决这个问题可以采用按需加载,我们有env这个preset,它又一个useBuiltIns选项,如果设置成"usage",那么将会自动检测语法帮你require你代码中使用到的功能。
[
"@babel/env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1",
},
"useBuiltIns": "usage"
},
]
2.污染全局环境:如果你引用了 @babel/polyfill,那么像Promise这样的新类就是挂载在全局上的,这样就会污染了全局命名空间。可能在一个团建建立的项目问题不太大,但是如果你是一个工具的开发者,你把全局环境污染了,别人用你的工具,就有可能把别人给坑了。解决这个问题就只能引入@babel/plugin-transform-runtime 搭配@babel/runtime来替代 @babel/polyfill
。
transform-runtime
主要功能:
- 避免多次编译出helper函数:
Babel转移后的代码想要实现和原来代码一样的功能需要借助一些帮助函数,比如:
class Person {}
会被转换为:
"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函数,试想下如果我们很多文件里都声明了类,那么就会产生很多个这样的helper函数,积少成多增大了代码体积。
这里的@babel/runtime包就声明了所有需要用到的帮助函数,而@babel/plugin-transform-runtime的作用就是将所有需要helper函数的文件,依赖@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函数classCallCheck了,而是直接引用了@babel/runtime中的helpers/classCallCheck。
- 解决@babel/polyfill提供的类或者实例方法污染全局作用域的情况。
@babel/plugin-transform-runtime会为代码创建一个沙盒环境,为core-js这里内建的实例提供假名,你可以无缝的使用这些新特性,而不需要使用require polyfill。
但是,记住,babel-runtime有个缺点,它不模拟实例方法,即babel-runtime没有模拟内置对象原型上的方法,所以类似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找到的新资料,网络上的资料比较乱,每个人的环境也不相同,一定要多做几次测试代码
useBuiltIns 和 transform-runtime 不能同时使用。
cnpm install core-js@3 --save
.babelrc
{
// targets, useBuiltIns 等选项用于编译出兼容目标环境的代码
// 其中 useBuiltIns 如果设为 "usage"
// Babel 会根据实际代码中使用的 ES6/ES7 代码,以及与你指定的 targets,按需引入对应的 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已经不支持使用移除 webpack.optimize.UglifyJsPlugin 压缩配置了, 推荐使用 optimization.minimize 属性替代。
不需要install这个插件,相关代码注释,只用一行代码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")({
//必须设置支持的浏览器才会自动添加添加浏览器兼容
browsers : ['last 10 versions']
})
]
}
}
/*,
'postcss-loader',
'sass-loader',*/
]
}
]
}