webpack开发环境基本配置
2021年6月2号更新
官方提供webpack配置描述信息:https://www.webpackjs.com/configuration/
我的项目地址:https://github.com/cirry/webpack-template
项目根据最新webpack版本,不断优化代码,复制即用,github上有没有注释的webpack配置文件,webpack.config.no-comment.js。
目录结构如下图
webpack.config.js
下图是我的简单配置,供日常开发使用:
const {resolve} = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
// optimize-css-assets-webpack-plugin
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin')
// 设置nodejs的环境变量
// process.env.NODE_ENV = "development"
// process.env.NODE_ENV = "production"
const isProduction = process.env.NODE_ENV === 'production';
// css兼容性配置
const CommonCSSLoader = [
{
loader: "postcss-loader",
options: {
postcssOptions: {
ident: 'postcss',
//打包后有兼容性样式代码,只处理css文件,不处理less文件
plugins: [
require('postcss-preset-env')
],
}
}
},
]
module.exports = {
mode: "development",
// 下面这个是开启全部的js兼容性方式,开启的话需删除js中的按需加载
// entry: ["@babel/polyfill", "./src/index.js"],
// 第一个入口是js,第二个是html,这样再修改js和html都可以做到热更新
entry: ["./src/index.js", "./src/index.html"],
output: {
filename: "js/[name].[contenthash:10].js",
// __dirname, nodejs的变量,代表当前文件的目录绝对路径
// path: resolve(__dirname, 'build'),
publicPath: "/",
chunkFilename: "js/[name].[contenthash:10]_chunk.js"
},
module: {
rules: [
{
test: /\.css$/, use: [
isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
...CommonCSSLoader,
]
},
{
test: /\.less$/, use: [
isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
...CommonCSSLoader,
'less-loader',
]
},
// 仅能处理css中的引入的图片
{
test: /\.(jpg|png|gif|jpe?g)$/, loader: "url-loader", options: {
// 小于8kb的图片会被处理为base64
limit: 8 * 1024,
name: '[name].[hash:10].[ext]',
outputPath: 'image'
}
},
// 处理html文件中的img图片(负责引入img,从而能被url-loader进行处理)
{
test: /\.html$/, loader: "html-loader", options: {
// 问题: 因为url-loader默认使用es6模块化解析,而html-loader引入图片使用的是common.js[解析时会出问题]
esModule: false
}
},
//npm install -D babel-loader @babel/core @babel/preset-env webpack
// 只处理基本的js语法,全部js兼容性处理:@babel/polyfill
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env',
{
// 指定兼容性做到哪个版本的浏览器,使用默认,配置方式跟browserList差不多
"targets": {
"ie": 7,
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
},
// 按需加载
useBuiltIns: "usage",
// 指定corejs版本
corejs: {
version: 3
}
}
]
],
cacheDirectory: true
}
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 1024 * 8,
name: "[name].[hash:10].[ext]",
outputPath: "fonts"
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 1024 * 8 * 5,
name: "[name].[hash:10].[ext]",
outputPath: "media"
}
},
],
},
plugins: [
// 在内存中创建index.html文件,并自动引入js和css文件,html模板为template路径的页面
new HtmlWebpackPlugin({
template: "./src/index.html",
// 压缩html代码
minify: {
collapseWhitespace: true,
removeComments: true,
}
}),
// 提取css代码为单独的文件,默认会被压缩在js文件中
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:10].css',
}),
//压缩css代码
new OptimizeCssAssetsWebpackPlugin()
],
optimization: {
// 配置生产环境的压缩方案,压缩js和css
minimizer: [
// For webpack@5 you can use the `...` syntax to extend existing minimizers (i.e. `terser-webpack-plugin`), uncomment the next line
// `...`,
new CssMinimizerPlugin(),
new TerserPlugin()
],
// 可以将node_modules中的代码单独打包成一个chunk输出
splitChunks: {
chunks: "all",
//下面所有的属性都是默认值,不用写
// minSize: 30 * 1024, // 小于30kb不分割
// maxSize: 0, // 最大没有限制
// minChunks: 1, // 要提取的chunks最少被引用1次
// maxAsyncRequests: 5, // 按需加载时,并行加载文件的最大数量
// maxInitialRequests: 3, //入口js文件,最大并行请求数量
// automaticNameDelimiter: "~", // 名称连接符
// name: true, // 可以使用命名规则
// cacheGroups: { // 分割chunk的组
// // node_modules文件中的文件会被打包到vendors组的chunk中 --> vendors~xxx.ks,连接符号是波浪线是因为前面的automaticNameDelimiter属性
// // 满足上面的公共规则,如大小超过30kb,至少被引用一次
// vendors: {
// test: /[\\/]node_modeuls[\\/]/,
// // 优先级
// priority: -10
// },
// default:{
// // 要提取的chunk最少被引用两次
// minChunks: 2,
// priority: -20,
// // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会被复用,而不是重新打包模块
// reuseExistingChunk: true
// }
// }
},
// 将当前模块的记录其他模块的hash单独打包为一个文件,叫runtime文件
// 解决:修改a文件导致b文件的contenthash变化
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}`,
},
},
// 打包在内存中, 自动编译,自动打开浏览器,自动刷新
devServer: {
contentBase: resolve(__dirname, 'dist'), // 运行的目录,不是源代码,而是构建后的目录
compress: true,
host: 'localhost',
port: 3000,
open: true,
hot: true,
// 没有跨域问题,忽略proxy配置
proxy: {
// 一旦devServer(3000)服务器接收到/api/xxx的请求,就会把请求转发到另外一个服务器(5000)上
'/api': {
target: 'http://localhost:5000',
// 发送请求时,请求路径重写: 将/api/xxx -->/xxx
pathRewrite: {
'^/api': ''
}
}
}
},
// 热更新需要属性
target: 'web',
// 开发生产坏境报错需要
devtool: 'source-map',
// 排除jquery不打包
externals: {
// 拒绝jquery被打包
jquery: 'jQuery',
}
}
package.json 需要装的包:
{
"name": "webpack-template",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "cross-env NODE_ENV=development webpack serve",
"build": "cross-env NODE_ENV=production webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.14.3",
"@babel/preset-env": "^7.14.4",
"babel-loader": "^8.2.2",
"core-js": "^3.13.1",
"cross-env": "^7.0.3",
"css-loader": "^5.2.6",
"css-minimizer-webpack-plugin": "^3.0.0",
"file-loader": "^6.2.0",
"html-loader": "^2.1.2",
"html-webpack-plugin": "^5.3.1",
"less": "^4.1.1",
"less-loader": "^9.0.0",
"mini-css-extract-plugin": "^1.6.0",
"optimize-css-assets-webpack-plugin": "^6.0.0",
"postcss-loader": "^5.3.0",
"postcss-preset-env": "^6.7.0",
"style-loader": "^2.0.0",
"terser-webpack-plugin": "^5.1.3",
"url-loader": "^4.1.1",
"webpack": "^5.38.1",
"webpack-cli": "^4.7.0",
"webpack-dev-server": "^3.11.2"
},
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
},
"dependencies": {
"@babel/polyfill": "^7.12.1",
"jquery": "^3.6.0"
}
}
详细说明配置含义
基础属性
- 在webpack5中,入口entry 默认值为:'./src',出口output默认值为:'./dist',可以不用配置。
- mode属性:可以设置为“development”或“production”,我已通过
cross-env
设置node环境。 - devtool属性:开发生产的时候代码报错,用来定位错误代码位置。
- externals属性:排除外来第三方包,不参与打包,减少打包代码量和加载速度,用第三方引入。
hash含义
为什么要引入hash?
首先webpack中有三种hash模式,分别为:hash/fullhash,chunkhash,contenthash,参考output中filename: "js/[name].[contenthash:10].js"
的使用方式。
- 从不同包中引入的相同名称的图片,可以通过打包hash还来区分他们。
- 缓存问题。
- 比如我们加载一个页面需要有a.js和a.css文件,当文件被缓存的时候,我们不加hash值,修改a.css的文件内容,重新打包部署,浏览器不会去请求服务器拿新页面而是使用缓存中的页面。因为浏览器会认为文件名称没有变化,内容没有变更,不需要去获取新的文件。
- 这里我们给文件名称加上第一种hash值,
js/[name].[hash:10].js, css/[name].[hash:10].css
,这一种hash值是每次webpack打包都会生成的整体的hash值,这会让每个文件的hash值都是相同的。当我们修改css的文件内容然后重新打包的时候,会重新生成hash值,刷新浏览器,会去重新请求js和css文件,而我们只改了一个css文件,却也请求了两个文件,因为两个文件的名称都变了。 - 第二种hash值,
js/[name].[chunkhash:10].js, css/[name].[chunkhash:10].css
,这里因为我们就一个chunk, 目录结构比较简单,所以会跟第一种情况。当项目复杂,有不同的chunk的时候,就会有差别。 - 第三种hash值,
js/[name].[contenthash:10].js, css/[name].[contenthash:10].css
,这里使用的是根据文件内容生成hash值,当我们只修改css文件后重新打包,会发现,js文件还是之前的名称,而css文件名称已经发生了变化。刷新浏览器会去重新请求css文件,而js文件是走的缓存。
module用法
module中主要是对文件的处理操作,根据rules的匹配规则对不同的文件加载不同的loader处理方式。
例如对less文件,我们依次使用:['style-loader','css-loader','less-loader'],数组中的三个文件,在文件为.less文件的时候,会从右向左依次被处理。
- 先被less-loader处理,将less转化为css
- 再被css-loader处理,将css转化为commonjs
- 再被style-loader处理,将样式通过
<style>
加载到html页面中
其中,css文件和js文件都有兼容性问题,js有例如promise,匿名函数,解构,es6新特性。css有例如3d样式,flex等等。
处理js文件的兼容性问题是使用了babel-loader,处理css文件兼容性问题是使用了postcss-loader。
插件作用
webpack.config.js中共用到了三个插件。
- HtmlWebpackPlugin 插件
此插件,在开发过程中,将打包号的代码编译在内存中,并选择./src/index.html作为模版内容,并挂在上需要引入的css文件和js文件。 - MiniCssExtractPlugin 插件
webpack默认会将css代码压缩在js文件中,通过此插件可以将css文件提取成单独的文件。 - OptimizeCssAssetsWebpackPlugin 插件
第二个插件会将css文件提取出来,但并没有压缩css代码,通过此插件可压缩css代码。
optimization作用
- minimizer
配置生产环境的代码压缩方法,CssMinimizerPlugin 压缩css代码,TerserPlugin 压缩js代码。 - splitChunks
可以将node_modules中的代码单独打包成一个chunk输出,上图中被注释掉的代码都是webpack默认的参数配置。 - runtimeChunk
将当前模块的记录其他模块的hash单独打包为一个文件,叫runtime文件。
比如,我们在a.js中引入了b.js,当b.js的文件内容修改了之后,重新打包,会重新打包a.js文件和b.js文件。我们用runtimeChunk把a.js和b.js的hash值抽离出来单独管理,当b.js的hash改变了之后,a.js的内容并没有改变就不需要重新打包a.js。
externals 使用方式
externals: {
jquery: 'jQuery',
Konva: 'Konva'
}
其中小写的"jquery"是你在项目中引用的包名,import $ from 'jquery'
中的 'jquery'。
大写的"jQuery"是包暴露出来的对象名,比如jquery包中,最后暴露出来的名称是 export default jQuery
。