这篇文件主要介绍以下基本配置:
- 拆分不同环境的配置文件:
webpack-merge
- 自动生成模板文件:
html-webpack-plugin
- 自动删除以前的打包文件:
clean-webpack-plugin
- 定义全局变量:
- 使用
cross-env
设置命令行参数,并在配置文件中获取process.env.xxx
,这个只能在脚本配置文件中获取 - 使用
webpack.DefinePlugin
设置模块中可以使用的全局变量,注意要使用JSON.stringify
封装变量
- 使用
- 开发环境跨域设置:
devServer.proxy
中设置代理 - 样式资源导入处理
- css:
css-loader
,style-loader
- less:
less-loader
- 增加浏览器兼容前缀:
postcss-loader
和autoprefixer
插件结合使用 - 抽离css文件:
mini-css-extract-plugin
的loader
和插件
- css:
- 图片资源导入处理
- file-loader:将资源文件抽离出来,打包到指定位置
- url-loader:同
file-loader
,但url-loader
增加文件大小阈值,小于阈值使用base64
导入
- 将es6转换成es5:
babel-loader
创建demo
- 创建项目
mkdir webpack-demo
cd webpack-demo
npm init -y
npm i webpack webpack-cli -D
可以看到package.json中webpack 和 webpack-cli
的版本,现在最新的已经是webpack5
了
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.40.0",
"webpack-cli": "^4.7.2"
}
}
- 创建基本目录
- src:资源目录,随便创建一个文件
index.js
,作为文件入口
// index.js
function hello() {
console.log('hello')
}
hello() // 注意要调用这个函数,不然会被默认tree-shaking掉
- dist:创建打包输出目录
- webpack.config.js:webpack默认的基本配置文件
const path = require('path')
module.exports = {
entry: path.join(__dirname, 'src', 'index.js'), // 单文件入口
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist') // 输出目录
}
}
- 改下
package.json
脚本命令
"scripts": {
"build": "webpack"
},
- 启动打包:
npm run build
会在dist/bundle.js
打包出以下结果,可以看出来webpack5的tree-shaking
已经非常强大,无用的代码不再被保留
console.log("hello");
区分 开发环境dev 和 生产环境prod
在大项目中,我们往往需要根据不同的环境来进行不同的配置,所以需要拆分webpack
配置文件
- 创建一个
config
目录,用来区分不同环境的配置文件:-
webpack.common.js
:用来配置公共的webpack配置 -
webpack.dev.js
:用来配置开发环境专有的webpack配置 -
webpack.prod.js
:用来配置生产环境专有的webpack配置
-
- 在不同环境的配置文件中,使用
webpack-merge
中的merge
来合并公共的webpack
配置文件(webpack4
中是通过smart
来合并的)
npm i webpack-merge -D
// webpack.common.js 公共的配置
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')
module.exports = {
entry: path.join(srcPath, 'index')
}
// webpack.dev.js
const path = require('path')
const commonConfig = require('./webpack.common')
const {merge} = require('webpack-merge')
const devConfig = {
mode: 'development', // 开发环境
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, '..', 'dist') // 输出目录
}
}
module.exports = merge(commonConfig, devConfig)
// webpack.prod.js
const path = require('path')
const commonConfig = require('./webpack.common.js')
const {merge} = require('webpack-merge')
const prodConfig = {
mode: 'production', // 生产环境
output: {
filename: 'bundle.[chunkhash].js', // 输出文件名,一般要加上hash
path: path.join(__dirname, '..', 'dist') // 输出目录
}
}
module.exports = merge(commonConfig, prodConfig)
- 在
package.json
脚本中区分不同环境的启动命令
先要安装:npm install webpack-dev-server -D
PS:在webpack4
中开发环境使用的命令是webpack-dev-server --config config/webpack.dev.js
,在webpack5
后,改成了webpack server
"scripts": {
"build": "webpack --config config/webpack.prod.js",
"dev": "webpack server --config config/webpack.dev.js"
},
- 运行
npm run build
,打包
// dist/bundle.004799ddc36231aeaad8.js
console.log("hello");
html-webpack-plugin 自动创建index.html
文件
打包的js
文件,需要我们有一个载体来查看结果,也就是我们一般说的模板文件index.html
- 手动创建一个
index.html
(根目录)文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./dist/bundle.004799ddc36231aeaad8.js"></script>
</body>
</html>
使用浏览器打开,可以看到正常输出的结果
这个载体文件,不单单是开发环境需要,生产环境也是需要的,但是生产环境我们往往会生成不同的hash
值来防止资源缓存,所以每次生成的文件名都是不同的,手动引入入口文件往往不太现实,所以需要借助html-webpack-plugin
插件来自动生成项目中的html页面,并实现自动导入对应脚本
- html-webpack-plugin 自动创建
index.html
文件
- 安装:
npm i html-webpack-plugin -D
- 在
plugins
中使用,因为是公用的,所以写在webpack.common.js
配置文件中:
// webpack.common.js 公共的配置
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: path.join(srcPath, 'index'),
plugins: [
new htmlWebpackPlugin({
// 以根目录中的index.html文件作为模板来自动生成dist/index.html文件
// 注意,这里是相对根目录而言的,因为脚本的上下文是在根目录下
template: 'index.html'
// inject: 'head', // 脚本注入的位置,可以是head, body,或者为 false默认
// 在这里还可以自定义参数,在模板中,使用ejs方式 <%= htmlWebpackPlugin.options %>获取自定义属性
title: 'webpack demo',
// webpack5默认开启压缩属性,webpack4要手动设置
// minify: {
// removeComments: true, // 删除注释
// collapseWhitespace: true // 删除空格
// }
})
]
}
- 删除根目录下
index.html
模板中手动导入的语句,并接收自在htmlWebpackPlugin
设置的自定义参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!-- 这是注释 -->
</body>
</html>
- 执行打包命令
npm run build
,可以看到dist
目录下生成了index.html
a. 自动导入了打包后的文件
b. 显示了自定义参数的title
标题
c. 默认开启了压缩,删除了注释和空格
<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>webpack demo</title><script defer="defer" src="bundle.e97c675afbdcfa8f531d.js"></script></head><body></body></html>
clean-html-plugin 删除之前的打包文件
随便修改下入口文件,发现打包新的文件时,之前生成的打包文件每次都会存在,所以我们往往配合clean-html-plugin
,在每次生成新的打包文件之前,会先删除之前的打包文件
- 安装
npm i clean-webpack-plugin -D
- 在
webpack.common.js
中配置
// webpack.common.js 公共的配置
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')
const htmlWebpackPlugin = require('html-webpack-plugin')
// webpack5写法
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
entry: path.join(srcPath, 'index'),
plugins: [
new CleanWebpackPlugin(), // 清除之前的打包文件
new htmlWebpackPlugin({
// 以根目录中的index.html文件作为模板来自动生成dist/index.html文件
// 注意,这里是相对根目录而言的,因为脚本的上下文是在根目录下
template: 'index.html'
})
]
}
PS:webpack4
中导入插件的写法为const CleanWebpackPlugin = require('clean-webpack-plugin')
- 再次打包时,就发现之前的包被清除了
定义全局变量
我们通常会有个配置文件,需要根据环境的不同,配置不同的接口地址,这时一般就会用到全局变量,webpack
可以使用DefinePlugin
插件来设置全局变量
我们一般在package.json
文件运行脚本中,会自定义一个环境变量NODE_ENV
(,这个变量其实是自定义的,但前端约定俗成用这个变量名):
// mac电脑
"scripts": {
"build": "NODE_ENV=prod webpack --config config/webpack.prod.js",
"dev": "NODE_ENV=dev webpack server --config config/webpack.dev.js NODE_ENV=development"
},
// windows电脑
"scripts": {
"build": "set NODE_ENV=prod && webpack --config config/webpack.prod.js",
"dev": "set NODE_ENV=dev && webpack server --config config/webpack.dev.js NODE_ENV=development"
},
然后在其它脚本中,我们就可以通过process.env.NODE_ENV
来获取
为了写一个脚本配置,适配所有电脑,所以要安装cross-env
解决跨平台问题:
npm i cross-env -D
,这样,就可以只使用一个脚本:
"scripts": {
"build": "cross-env NODE_ENV=prod webpack --config config/webpack.prod.js",
"dev": "cross-env NODE_ENV=dev webpack server --config config/webpack.dev.js"
},
然后在webpack.common.js
中,打印process.env.NODE_ENV
可以获取到脚本中的设置值,注意,这个process.env
只有在配置文件中才能获取到,在其它index.js
中是获取不到的,除非设置了全局变量
// webpack.common.js
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
]
在webpack4
之后,上面这些操作,webpack
都帮我们内置了,所以可以在任何js
文件中都能获取到process.env.NODE_ENV
的值,这个值对应的是webpack
配置文件中的mode
值,即development
或production
这里主要是说明怎么接收命令行的参数,并在DefinePlugin
设置全局变量的方法,方便我们在模块中使用,但注意的是,设置的对象需要使用JSON.stringify
转换,包括字符串(否则字符串会被识别成变量而报错)
开发环境启动和配置
之前配置了开发环境脚本,但一直没用,现在有了index.html
模板文件和打包后的js
文件,就可以使用npm run dev
启动命令,根据提示打开浏览器,访问http://localhost:8080/
就可以查看结果了
跨域配置
在开发环境最常用的配置是处理跨域问题,在webpack.dev.js
中设置devServer
,这点和webpack4
是一样的
// webpack.dev.js
const path = require('path')
const commonConfig = require('./webpack.common')
const {merge} = require('webpack-merge')
const devConfig = {
mode: 'development', // 开发环境
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, '..', 'dist') // 输出目录
},
devServer: {
// 设置代理
proxy: {
// 将本地 /api/xxx 代理到 localhost:6666/api/xxx
// '/api': 'http://localhost:3000',
// 将本地 /api2/xxx 代理到 localhost:6666/xxx,通常用这个,/api2仅作为代理转发的标识
'/api2': {
target: 'http://localhost:3000',
changeOrigin: true,
pathRewrite: {
'/api2': ''
}
}
}
}
}
module.exports = merge(commonConfig, devConfig)
修改index.js
文件,请求跨域的接口
function hello() {
console.log('hello11')
}
fetch('/api2/data').then(res => {
console.log(1111)
})
// 显示跨域
fetch('http://localhost:3000/data').then(res => {
console.log(2222)
})
hello()
写个简单的express
服务器来测试:
const express = require('express')
const app = express()
app.listen(3000, () => {
console.log('server is running in port: 3000');
})
app.get('/data', (req, res) => {
console.log('11111')
res.send('hello')
})
然后启动开发环境:npm run dev
文件资源处理
在.js
文件中导入import './style/index.css'
样式文件,运行,发现会报错,这是因为webpack默认只认识js
文件,其它类型的文件都不识别。
要使webpack
识别它们,必须引入对应的loader
去处理,将其转换成js
认识的文件
1. 处理样式文件
识别.css文件
- 安装
npm i css-loader style-loader -D
- 在
webpack.common.js
中配置module.rules
规则:- css-loade:为了在js中使用import方式导入
css
文件 - style-loader:将样式写在
html
文件中head
标签内的<style>
标签中
- css-loade:为了在js中使用import方式导入
module.exports = {
entry: path.join(srcPath, 'index'),
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
]
},
},
识别.less文件(sass、stylus同理)
要在.js
中使用import './src/style/xxx.less'
文件:
- 安装
npm i css-loader style-loader less-loader less -D
- 配置
- less-loader:将
.less
文件转译成.css
文件
- less-loader:将
module: {
rules: [
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
]
},
自动添加hack前缀
- 安装
npm i postcss-loader autoprefixer -D
- 在
webpack.common.js
中配置
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader','postcss-loader', 'less-loader']
}
]
},
plugins: [
require('autoprefixer')
]
CSS文件抽离
在生产环境中,我们往往不会将样式文件给打包到html
文件中,而是单独抽离css文件
- 安装:
npm i mini-css-extract-plugin -D
- 在生产环境配置文件中
webpack.prod.js
配置,将前面的style-loader
换成mini-css-extract-plugin
的loader
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const prodConfig = {
mode: 'production', // 生产环境
output: {
filename: 'bundle.[chunkhash].js', // 输出文件名,一般要加上hash
path: path.join(__dirname, '..', 'dist') // 输出目录
},
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader']
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader']
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name]-[contenthash].css'
})
]
}
- 打包
npm run build
2. 图片资源文件
想在.css
中使用url导入图片
body {
background-color: yellow;
opacity: 0.7;
transform: rotate(45deg);
background: url('../imgs/img002.jpg');
}
或者在.js
中使用import
导入图片
import img from './imgs/img002.jpg'
需要借助file-loader
或者url-loader
,url-loader
和file-loader
差不多,只不过增加了一个设置文件大小阈值,当小于这个阈值时,使用base64
导入文件
- 开发环境
- 安装:
npm i file-loader -D
- 在开发环境,因为不用考虑压缩问题,所以
webpack.dev.js
可以配置file-loader
// 处理css样式文件中的url图片
{
test: /\.(jpg|png|jpeg|gif)$/,
use: ['file-loader']
}
- 生产环境
- 安装:
npm i url-loader -D
- 在生产环境,可以使用
url-loader
设置小图片使用base64
方式写入
// 处理css样式文件中的url图片
{
test: /\.(jpg|png|jpeg|gif)$/,
use: {
loader:'url-loader',
options: {
limit: 8192, //限制 8kb 以下使用base64
esMoudle: false,
name: '[name]-[hash:8].[ext]',
// 打包到/images目录下
outputPath: 'images'
}
}
}
解析ES6+代码
- 安装:
npm i babel-loader @babel/core @babel/preset-env -D
- 配置:
// webpack.common.js
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader'], // 1. 开启缓存
include: srcPath, // 2. 缩小适合范围(写include或exclude)
exclude: /node_modules/
}
]
}
// 根目录创建,.babelrc
{
"presets": ["@babel/preset-env"], // 一般使用@babel/preset-env就够了
"plugins": []
}
// 如果是高版本的babel,比如v7.8.0 或更高版本的babel等
// .babelrc 换成根目录创建 babel.config.json
{
"presets": [
[
"@babel/env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
]
]
}
总的来说,webpack5
对比webpack4
来说,改动不算太多,还算是比较平稳过渡,最后,借用网上一张图,说明webpack4
升级webpack5
的改动:
参考:
https://blog.csdn.net/xiaolinlife/article/details/107032533
https://www.imooc.com/article/287156#comment