webpack5 多页脚手架搭建
webpack 作为目前最流行的构建工具,几乎成为了前端开发中必备的工具之一,如果想前端进阶的话,懂得 webpack 配置也是必不可少的一部分。然而,也是由于 webpack 的配置多样化,导致劝退了很多想学习入门 webpack 的童鞋。写这个文章,主要是把平时使用 webpack 的一些经验和心得分享给大家,同时也是为了巩固自己的知识。
观看该文章时,默认已经对 node.js 和 webpack 有一定了解。
基础依赖
想要搭建 webpack 脚手架,我们需要先安装 webpack
、webpack-serve
、webpack-cli
,这三个是必不可少的。
以下我们都默认使用 pnpm
进行依赖安装, node
版本为 16.0.0
pnpm i webpack webpack-cli webpack-serve -D
安装完成即可在控制台查看对应版本号。安装命令如果没有添加对应版本号,则默认取的是最新的稳定版本。
devDependencies:
+ webpack 5.70.0
+ webpack-cli 4.9.2
+ webpack-serve 4.7.4
依赖 | 版本 | 作用 |
---|---|---|
webpack | 5.70.0 | 核心库,提供了很多 API |
webpack-cli | 4.9.2 | webpack 命令行工具,通过命令行输入命令来使用 webpack |
webpack-serve | 4.7.4 | webpack 提供用来开发调试的服务器,可以通过 url 打开页面来调试 |
公用配置
一般来说,一个完整的项目构建,是需要区分 开发环境
和 生产环境
的,那这两个环境必然是存在一些公用部分,如公用的 loader
和 plugin
,所以这里我们按照实际情况来进行配置抽离。
首先在根目录下创建 build
目录,并在其中创建 webpack.base.config.js
文件,用于存在一些公用配置项。
安装以下依赖
pnpm i copy-webpack-plugin -D
依赖 | 版本 | 作用 |
---|---|---|
copy-webpack-plugin | 10.2.4 | 用于复制一些静态资源文件,如 public ,不然在 html 中引入 favicon.ico 会 404 |
创建完成之后,在 webpack.base.config.js
中新增以下配置。
// 引入 path 模块
const path = require('path')
// 引入静态资源复制插件
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
// 打包入口地址
entry: {
// 由于可能是多页,所以采用对象的形式
index: ['./src/views/index/index.js']
},
// 模块resolve的规则
resolve: {
//自动的扩展后缀,比如一个js文件,则引用时书写可不要写.js
extensions: ['.js', '.json', '.css', '.less'],
// 路径别名
alias: {
'@': path.join(__dirname, '../src')
}
},
// 构建目标
target: ['web', 'es5'],
// context 是 webpack entry 的上下文,是入口文件所处的目录的绝对路径,默认情况下是当前根目录。
// 由于我们 webpack 配置文件放于 build 目录下,所以需要重新设置下 context ,使其指向根目录。
context: path.resolve(__dirname, '../'),
plugins: [
// 把public的一些静态文件复制到指定位置,排除 html 文件
new CopyWebpackPlugin({
patterns: [
{
from: path.resolve(__dirname, '../public'),
globOptions: {
dot: true,
gitignore: true,
ignore: ['**/*.html']
}
}
]
})
]
}
path
模块为 node.js
自带,主要用于帮助我们方便的解决了路径设置问题
开发环境配置
公用配置先告一段落,接下来,我们要进行 开发环境
的配置,先把项目给跑起来。
首先,在 build
目录下创建 webpack.dev.config.js
文件。
接着安装以下依赖
pnpm i webpack-merge html-webpack-plugin -D
依赖 | 版本 | 作用 |
---|---|---|
webpack-merge | 5.8.0 | 用于合并webpack配置项 |
html-webpack-plugin | 5.5.0 | 用来打包入口 html 文件,entry 配置的入口是 js 文件,webpack 以 js 文件为入口,遇到 import, 用配置的 loader 加载引入文件,但作为浏览器打开的入口 html, 是引用入口 js 的文件,它在整个编译过程的外面, 所以,我们需要 html-webpack-plugin 来打包作为入口的 html 文件 |
安装完成之后,在 webpack.dev.config.js
中新增以下配置。
// 引入合并对象插件
const { merge } = require('webpack-merge')
// 引入 path 模块
const path = require('path')
// 引入打包html插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 引入基础配置文件
const baseWebpackConfig = require('./webpack.base.config')
const devWebpackConfig = merge(baseWebpackConfig, {
// 模式,必填项
mode: 'development',
// 开启持久化缓存
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
},
output: {
// 输出文件目录
path: path.resolve(__dirname, '../dist'),
// 输出文件名
filename: 'js/[name].js',
},
// 源码映射
devtool: 'eval-cheap-module-source-map',
// 开发服务配置
devServer: {
// 服务器 host,默认为 localhost
host: '0.0.0.0',
// 服务器端口号,默认 8080
port: 7001,
// 静态资源属性
static: {
// 挂载到服务器中间件的可访问虚拟地址
// 例如设置为 /static,在访问服务器静态文件时,就需要使用 /static 前缀
// 相当于webpack-dev-server@3.X的 contentBasePublicPath 属性
publicPath: './',
// 告诉服务器从哪里提供内容
directory: path.join(__dirname, '../public')
},
// 需要监听的文件,由于是多页应用,无法实现热更新,所以都只能刷新页面
watchFiles: ['src/**/*']
},
// 插件
plugins: [
new HtmlWebpackPlugin({
template: './src/views/index/index.html',
favicon: path.resolve(__dirname, '../public/favicon.ico')
})
]
})
module.exports = devWebpackConfig
接着在根目录下创建 public
目录,用于存放一些静态资源,如 favicon.ico
然后在根目录下创建 src/views/index
目录,用于存放相关业务代码 ,并在 index
目录内新建 index.html
和 index.js
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></title>
</head>
<body>
<div>index test</div>
</body>
</html>
index.js
console.log('index.js')
开发环境到这边,就算简单的配置完成了,然后我们在 package.json
中的 scripts
添加运行命令
"serve": "webpack serve --progress --no-stats --config build/webpack.dev.config.js",
控制台运行命令
pnpm run serve
当控制台出现以下信息则代表启动成功,打开浏览器输入 http://localhost:7001/index.html
,发现页面和控制台多出现相应的信息,这样一个最简单的开发环境就配置好了。
配置loader
启动运行只有,我们会发现,不管是引入样式文件,还是使用图片,控制台都会报错,这是因为,webpack
只能识别 .js
类型文件,所以,我们需要配置一些 loader
来是脚手架的功能更加完善。
因为 loader
配置,不管是在 开发环境
还是 生产环境
,都是需要配置的,所以我们把这些配置项放到 webpack.base.config.js
中。
样式资源配置
首先安装以下插件
pnpm i style-loader css-loader less-loader postcss-loader postcss autoprefixer mini-css-extract-plugin cross-env -D
依赖 | 版本 | 作用 |
---|---|---|
css-loader | 6.7.1 | 解析 css 文件 |
style-loader | 3.3.1 | 将 css-loader 解析好的通过 style 标签的形式添加到页面上,建议开发环境使用 |
less | 4.1.2 | less 核心库 |
less-loader | 10.2.0 | 解析 less 文件 |
postcss | 8.4.12 | postcss 核心库 |
postcss-loader | 6.2.1 | 要使用 autoprefixer ,就需要使用到 postcss-loader |
postcss-preset-env | 7.4.3 | 帮 postcss 找到 browserslist 里面的配置,通过配置加载指定的 css 兼容性样式 |
mini-css-extract-plugin | 2.6.0 | 分离样式文件,与 style-loader 不共存,建议生产环境使用 |
cross-env | 2.6.0 | 运行跨平台设置和使用环境变量的脚本,主要为了区分当前环境,用于判断是使用 style-loader 还是 mini-css-extract-plugin |
安装完成之后,在 webpack.base.config.js
中添加以下代码
// ...
+ // 判断当前环境是否是生产环境
+ const isProduction = process.env.NODE_ENV === 'production'
+ // 样式单独分离到一个文件中
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// ...
context: path.resolve(__dirname, '../'),
// 不同类型模块的处理规则
+ module: {
+ rules: [
+ // 处理 css、less 文件
+ {
+ test: /\.(css|less)$/,
+ use: [
+ isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
+ {
+ loader: 'css-loader',
+ options: {
+ // 是否使用source-map
+ sourceMap: !isProduction,
+ // 兼容IE11
+ esModule: false
+ }
+ },
+ {
+ loader: 'postcss-loader',
+ options: {
+ // 是否使用source-map
+ sourceMap: !isProduction
+ }
+ },
+ 'less-loader'
+ ]
+ }
+ ]
+ }
// ...
}
然后在根目录下创建 .browserslistrc
和 postcss.config.js
文件
.browserslistrc 用于告诉 postcss
的浏览器作用范围
defaults
not ie < 11
last 2 versions
> 1%
iOS 7
last 3 iOS versions
postcss.config.js 配置 postcss
配置项
module.exports = {
plugins: [
'postcss-preset-env'
]
}
然后重新修改 package.json
中的 serve
命令,然后重新运行,就会发现不管是在 js
中引入 .css|.less
文件,都可以作用到 html
上了,并且也给样式加上了对应的浏览器兼容前缀。
- "serve": "webpack serve --progress --no-stats --config ./build/webpack.dev.config.js",
+ "serve": "cross-env NODE_ENV=development webpack serve --progress --no-stats --config ./build/webpack.dev.config.js",
图片资源配置
安装 html-loader
,用于解析 html
中的图片路径
pnpm i html-loader -D
然后在 webpack.base.config.js
中新增以下配置
// ...
module.exports = {
// ...
// 不同类型模块的处理规则
module: {
rules: [
// ...
+ // 解析 html 中的 src 路径
+ {
+ test: /\.html$/,
+ use: 'html-loader'
+ },
+ // 对图片资源文件进行处理,webpack5已经废弃了url-loader,改为type
+ {
+ test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+ type: 'asset',
+ exclude: [path.resolve(__dirname, 'src/assets/imgs')],
+ generator: {
+ filename: 'imgs/[name].[contenthash][ext]'
+ }
+ },
// ...
]
}
// ...
}
配置完之后,重新运行项目,我们会发现,不管是何种方式的图片使用,都可以准确的显示在页面中了。
配置字体图标
为了让目录结构更加清晰美观,我们单独把字体图标提取到一个目录中,还是在 webpack.base.config.js
中添加以下代码
// ...
module.exports = {
// ...
// 不同类型模块的处理规则
module: {
rules: [
// ...
+ // 对字体资源文件进行处理,webpack5已经废弃了url-loader,改为type
+ {
+ test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+ type: 'asset',
+ generator: {
+ filename: 'fonts/[name].[contenthash][ext]'
+ }
+ },
// ...
]
}
// ...
}
这样我们字体图标在打包的时候,就可以输出到 fonts
这个目录下了。
配置音频资源
同理,对于音频的处理,我们也是采用上述的方式,在 webpack.base.config.js
中添加以下代码
// ...
module.exports = {
// ...
// 不同类型模块的处理规则
module: {
rules: [
// ...
+ // 对音频资源文件进行处理,webpack5已经废弃了url-loader,改为type
+ {
+ test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
+ type: 'asset',
+ exclude: [path.resolve(__dirname, 'src/assets/medias')],
+ generator: {
+ filename: 'medias/[name].[contenthash][ext]'
+ }
+ },
// ...
]
}
// ...
}
音频资源在 html
中的引入方式也是跟图片的引入方式一样。
配置javaScript语法转换
当我们会用 ES6
语法的时候,在 IE11
浏览器上,我们会发现控制台报错了,这是因为语法的不兼容,所以我们进行一层语法的转换。
由于市场上的浏览器众多,每个浏览器的语法支持程度都不一样,所以为了能够让浏览器兼容我们写的 javaScript
代码,我们需要对 ES6
以上语法进行兼容处理,也就是降级为 ES5
,保证每个浏览器的语法兼容问题,这里我们以 IE11
浏览器为例。
安装以下插件
pnpm i babel-loader @babel/core @babel/preset-env core-js -D
依赖 | 版本 | 作用 |
---|---|---|
babel-loader | 8.2.4 | 将 ES2015+ 代码转换为 ES5 |
@babel/core | 7.17.8 | babel 核心包 |
@babel/preset-env | 7.16.11 | Babel 编译的预设 |
core-js | 3.21.1 | 新功能的 es 'api' 转换为大部分现代浏览器都可以支持 |
运行的一个 'api' 补丁包集合 |
在 webpack.base.config.js
中添加以下代码
// ...
module.exports = {
// ...
// 不同类型模块的处理规则
module: {
rules: [
// ...
+ {
+ test: /\.(m|j)s$/,
+ exclude: /node_modules/,
+ use: [
+ {
+ loader: 'babel-loader',
+ options: {
+ cacheDirectory: true
+ }
+ }
+ ]
+ }
// ...
]
}
// ...
}
在根目录下创建 babel.config.json
文件,并且添加以下内容
{
"presets": [
[
"@babel/preset-env",
{
// useBuiltIns: false 默认值,无视浏览器兼容配置,引入所有 polyfill
// useBuiltIns: entry 根据配置的浏览器兼容,引入浏览器不兼容的 polyfill
// useBuiltIns: usage 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加
"useBuiltIns": "usage",
"corejs": {
"version": 3
},
"modules": false
}
]
]
}
配置好之后,重新项目,就可以愉快的使用 ES5
以上的语法了,并且在 IE11
上完美展示。
webpack.base.config.js
和 webpack.dev.config.js
到这里就算配置完成了,最后我们还需要把项目打包成一个静态项目,方便部署到生产服务器上。
生产环境配置
在生产环境中,我们会针对性的进行一些项目优化,方便后续如果项目庞大起来,在构建速度上也不会有太大的时间差。
在根目录下创建 webpack.pro.config.js
,并添加以下代码
// 引入合并对象插件
const { merge } = require('webpack-merge')
// 引入 path 模块
const path = require('path')
// 引入打包html插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 引入基础配置文件
const baseWebpackConfig = require('./webpack.base.config')
const proWebpackConfig = merge(baseWebpackConfig, {
// 模式,必填项
mode: 'production',
output: {
// 输出文件目录
path: path.resolve(__dirname, '../dist'),
// 输出文件名
filename: 'js/[name].[contenthash].js',
// 清空目录
clean: true
},
// 源码映射
devtool: false,
// 插件
plugins: [
new HtmlWebpackPlugin({
template: './src/views/index/index.html',
filename: 'html/index.html',
favicon: path.resolve(__dirname, '../public/favicon.ico')
}),
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash].css'
})
]
})
module.exports = proWebpackConfig
在 package.json
中添加打包命令
"build": "cross-env NODE_ENV=production webpack --config ./build/webpack.pro.config.js",
这样一个最基本的打包配置就完成了,可以运行 pnpm run build
查看效果,可以发现在 主流浏览器
上和 IE11
与开发环境展示效果保持一致。
接着,我们来对打包进行一些优化
安装 css-minimizer-webpack-plugin
对 css
进行优化
pnpm i css-minimizer-webpack-plugin -D
然后在 webpack.pro.config.js
中添加新代码
// ...
+ // 压缩css
+ const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
+ const TerserPlugin = require('terser-webpack-plugin')
const proWebpackConfig = merge(baseWebpackConfig, {
// ...
+ optimization: {
+ minimize: true,
+ minimizer: [
+ new CssMinimizerPlugin(),
+ new TerserPlugin({
+ terserOptions: {
+ // 生产环境自动删除console
+ compress: {
+ drop_debugger: true,
+ drop_console: true,
+ pure_funcs: ['console.log']
+ }
+ },
+ parallel: true,
+ exclude: /[\\/]node_modules[\\/]/
+ })
+ ],
+ splitChunks: {
+ cacheGroups: {
+ // 配置提取模块的方案
+ default: false,
+ styles: {
+ name: 'styles',
+ test: /\.(css|less)$/,
+ chunks: 'all',
+ enforce: true,
+ priority: 10
+ },
+ vendors: {
+ name: 'chunk-vendors',
+ test: /[\\/]node_modules[\\/]/,
+ chunks: 'all',
+ priority: 2,
+ enforce: true,
+ reuseExistingChunk: true
+ }
+ }
+ }
}
})
module.exports = proWebpackConfig
到这里,我们 生产环境
的优化就基本完成了。
总结
由于项目比较小,所以做过多的优化反而会加重构建负担,所以本文章就大概了做一些基础优化,也是为了巩固下 webpack
的配置,毕竟 webpack
配置多而杂,难免会有忘记的时候。
感兴趣的可以拉取代码运行看看~
转载自:https://juejin.cn/post/7083413557278146591