现在做前端开发用 webpack 很多,之前有深入研究过,并且做了很多相关笔记。一直想着整理出来,刚好同事这两天有问到我 webpack 相关的问题,趁着这个机会我自己也好好梳理一下。
准备写 webpack 的系列文章,从基础、优化到原理。
最基本使用
安装 webpack 依赖:
npm install webpack@4 webpack-cli@3 -D
注意: 由于我们公司当前所有项目都未升级到 webpack5.x,所以这系列文章基于 webpack4.x 来写,后续单独写 webpack5.x 相关的。
创建 webpack.config.js
如果不创建 webpack.config.js,webpack 会使用默认的配置的文件,但是默认的配置文件功能很弱,这里就不再讲了。项目开发过程中都会自己写配置文件。
配置文件的名字也可以不叫 webpack.config.js,只需要在执行 webpack 命令的时候声明即可:
npx webpack --config 你自己的定义的文件名
注意:正常情况下,我们都不会去改配置文件的名称。
entry、output
在项目目录下建一个 src 目录,在 src 目录下创建 index.js、test1.js
test1.js
exports.name = 'iceman';
index.js
const test1 = require('./test1');
console.log(test1.name);
写 webpack.config.js 中的配置的时候需要知道 webpack 是 node 写出来的,所以要遵循 node 的语法。
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
}
}
entry 为入口文件。
output 为输出的配置:
- filename:打包后的文件名,可以给 filename 加上 hash ,每次打包后的文件名就不一样了,可以防止缓存。filename: 'main.[hash].js',如果觉得 hash 太长,可以设置长度 filename: 'main.[hash:8].js'
- path:打包后的文件的输出路径,这里的路径必须是一个绝对路径,可以使用 node 的 path 模块中的 resolve 生成绝对路径。
mode
做完以上配置之后,输入打包命令:
npx webpack
能打包,但是输出警告信息:
这段警告告诉我们没有设置 mode,webpack 会默认使用 production。mode 的设置可以在控制台输入打包命令的时候添加:
npx webpack --mode=production
也可以在配置文件中指定:
注意:这里只是演示,一般不会直接写死 “production” 、“development”,都是通过控制台输入打包命令的时候设置变量,然后动态判断。
#plugins
我们现在将一个 HTML 文件也打包进 dist 文件中,作为我们项目的入口 HTML。
在这个 HTML 中还要将 dist 中的 JavaScript 入口文件引进去,这个操作肯定不可能每次都手动的操作。
这时候就要用到 plugins 了,plugins 是个数组,里面放所有的 webpack 插件。拷贝 HTML 可以用 html-webpack-plugin 插件。
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),
]
这个插件中的 template、filename 这两个参数很好理解,就是需要拷贝的模板和输出的文件名。输出到 dist 中的 HTML 如下:
输出的 HTML 文件中,已经自动引入了入口 JavaScript 文件。
该插件还有其他几个参数:
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "index.html",
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true,
},
hash: true
})
- minify#removeAttributeQuotes:去除 HTML 中的引号
- minify#collapseWhitespace:将空行压缩
- hash:引入 JavaScript 文件加上随机的 url 参数
module
到现在为止,已经将 HTML、JavaScript 都打包了。如果这时候要引入样式文件呢?
如果要打包样式,需要用到样式相关的 loader。loader 写在 module 的 rules 中。
安装 css-loader 和 style-loader:
npm install css-loader style-loader -D
module: {
rules: [
{test: /\.css$/, use: ['style-loader', 'css-loader']}
]
}
loader 的执行顺序是 从右向左
css-loader 的作用是解析 @import 这样的语法,style-loader 的作用是把 css 插入到 head 标签中。
从这里也可以看出,loader 的设计理念是功能单一,一个 loader 只做一件事,然后组合做个 loader。
loader 的写法除了上面的字符串的形式,还可以写成对象的形式,用对象的形式有一个好处就是可以给 loader 加参数:
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
options: {
insertAt: 'top',
}
},
'css-loader'
]
}
]
}
在 src 下写一个样式文件 test02.css:
body {
background-color: #ddd;
}
index.js 中引入:
require('./test02.css');
以上的操作之后,就可以打包 css 文件了。如果要打包 Less,也是一样的思路,加上相应的 loader 即可:
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
webpack-dev-server
平时开发过程中,修改了某个 JavaScript 文件,或者某个 css 文件,如果每次都要手动打包一次,然后再手动打开 index.html,那么效率相当低。这一点 webpack 也考虑到了,提供了 webpack-dev-server 给我们使用。
npm install webpack-dev-server
在配置文件中加入相关配置:
devServer: {
port: 8080,
// 显示进度条
progress: true,
// 根目录
contentBase: './dist',
// 是否开启 gzip 压缩
compress: true
}
样式处理
走完一遍 最基本的使用 之后,对 webpack 已经不那么陌生了。接下来我们继续学习使用 webpack。
在上面讲解 module 的时候,有介绍到样式的处理,介绍了 style-loader、css-loader、less-loader,有了这三个插件,已经能正常的处理样式了。不过还不够,对于样式的处理,还有一些细节。
样式抽取为单独的文件
现在所有的样式都是直接插入到 <head></head> 中。
可以使用 mini-css-extract-plugin 来抽取样式。
webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: 'main.css',
}),
],
}
除了在 plugins 中添加相应的插件之外。还需要将 module 的 rules 中和样式相关的 style-loader 换成 MiniCssExtractPlugin.loader。
自动补齐 CSS3 属性前缀
安装依赖:
npm i postcss-loader autoprefixer@8 -D
注意:在使用中控制台报错,Error: PostCSS plugin autoprefixer requires PostCSS 8. Update PostCSS or downgrade this plugin,提示我们要使用8.x的版本。
postcss-loader 必须放在 css-loader 之前。
根目录下新建 postcss.config.js
module.exports = {
plugins: [require('autoprefixer')],
}
压缩 CSS 文件
安装依赖:
npm i optimize-css-assets-webpack-plugin -D
在配置文件中新加 optimization ,就是优化项的意思。
optimization: {
minimizer: [
new OptimizeCssAssetsPlugin(),
]
}
做完以上配置之后,css 文件就会压缩。
既然 CSS 文件可以压缩了,那么 JS 文件是不是也可以压缩呢?
安装依赖:
npm i uglifyjs-webpack-plugin -D
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true,
}),
]
}
参数的具体含义以及其他参数可查看文档:https://www.npmjs.com/package/uglifyjs-webpack-plugin。
注意:以上压缩的配置一定是在 mode: 'production' 的情况生效的,在 development 的情况下不会生效。
ES6+
编译 ES6
const fn = () => {
console.log('aa');
}
fn();
这段箭头函数的代码,默认情况下是不会被编译的。需要用 babel。
安装依赖
npm i babel-loader @babel/core @babel/preset-env -D
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
],
},
},
}
]
}
编译 decorator
以上的配置无法编译 decorator,需要加额外配置。
function Controller(target) {
console.log(target);
}
@Controller
class A {
}
npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
},
},
}
]
}
打包图片
图片一般会以是那种情况出现:
- JS 代码中直接使用
- CSS 中引入
- img 标签中使用
JS、CSS 中使用
安装依赖
npm install file-loader@3 -D
配置文件:
module: {
rules: [
{
test: /\.(png|jpg|gif)&/,
use: {
loader: 'file-loader',
}
}
]
}
JS 中使用:
import st from './st.jpg';
const img = new Image();
img.src = st;
document.body.appendChild(img);
CSS中使用:
body {
background-image: url("./st.jpg");
}
注意这里的如果 file-loader 用的不是 3.x 版本的话,编译出来的 css 文件会有问题:
以上两种方式都是使用的 file-loader,file-loader 的机制是在内部生成一张图片,到build目录下,再把生成的图片的名字返回回来。
还有一个 url-loader,它会将图片 BASE64 编码:
{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 10 * 1024,
outputPath: '/img/',
}
}
}
outputPath 为图片的输出路径。
当图片小于 limit 的时候会把图片 BASE64 编码,大于 limit 参数的时候还是使用 file-loader 进行拷贝。
注意:BASE64 编码之后,图片的体积会变大三分之一左右,所以对于较大得图片不要用 url-loader,在 options 中设置 limit。
img 标签中使用
在 img 标签中使用图片,需要 html-withimg-loader
安装依赖
npm i html-withimg-loader -D
配置文件
{
test: /\.html$/,
use: 'html-withimg-loader'
}