webpack 打包前端静态项目
前言
最近新写了一个 H5 项目,不需要后端交互,使用了原生js,然后再次体会到webpack的强大。
最开始项目只有一个index.html和 几个js文件,js文件直接在index.html通过<script>标签进行依次引入,感觉没有太大问题。之后这个项目在服务器使用nginx做个简单托管也就部署好了,但是部署的时候我需要对这些js文件进行压缩来缩小资源体积,同时转换ES6、ES7语法到浏览器普遍支持的 ES5,我当时就想反正就几个js文件嘛,按照在index.html的引入顺序全部复制到一个js文件里,再把这个文件复制到一个js在线压缩工具进行压缩就OK啦。
<script src="preset.js"></script>
<script src="utils.js"></script>
<script src="features.js"></script>
<script src="main.js"></script>
之后为了进一步减小项目体积,我又把html、css也复制到对应的在线压缩工具进行压缩,最后又为了减少网络请求,又将css内容直接放到head的style下,而不使用link标签。
后来js逻辑变多了,得使用模块语法了,Oops!这可怎么办呢,然后我就想到了webpack~。我最后只需要yarn build一下,一切问题都搞定了。
配置 webpack
可参考webpack 中文文档。
使用yarn添加一下webpack,顺便生成package.json。
yarn add -D webpack webpack-cli
写一个简单的配置文件webpack.config.js设置一下入口文件和输出文件。
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
}
项目src/index.js这个入口文件就使用了模块语法,举个例:
import AudioController from './AudioController'
import { refreshAll } from './Timer'
const canvas = document.getElementById('content')
const ctx = canvas.getContext('2d')
canvas.onclick = event => {
const relativeY = event.pageY - canvas.offsetTop
if (relativeY > 200 && relativeY < 400) {
refreshAll()
ctx.fillText('Welcome to Anand\'s Blog', 120, 120)
}
}
为 webpack 打包写一个script,在package.json的scripts添加build操作,这样之后打包只需要运行yarn build就好啦。
{
"private": true,
"scripts": {
"build": "webpack"
},
"devDependencies": {
"webpack": "^5.13.0",
"webpack-cli": "^4.3.1"
}
}
使用yarn build命令进行打包,在dist目录会生成bundle.js文件,然后去除index.html里之前引入js文件的script标签,现在不需要再像之前一样还要考虑引入顺序,只需要引入这个bundle.js就好啦。
<script src="dist/bundle.js"></script>
Nice! 这样简单的配置,就解决了之前复制几个js文件到一起然后再压缩、转语法的操作了。
丰富 webpack
虽然现在已经能满足js文件打包、压缩等需求了,但是js文件里使用图片、音频等文件等路径怎么办呢?
图片、音频资源
如何项目只有图片资源,只需要简单添加module规则配置。
const path = require('path')
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource'
}
]
}
}
但是我的这个项目还会用到音频资源,所以就需要借助file-loader了,先安装一下。
yarn add -D file-loader
顺便配置一下资源解析,每次引入图片、音频这些资源的路径都需要./、../../慢慢去找,这也太麻烦了吧。我们配置一下resolve。
const path = require('path')
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|svg|jpg|jpeg|gif|mp3)$/i,
loader: 'file-loader',
options: {
name: '[path][hash:18].[ext]'
}
}
]
},
resolve: {
alias: {
Assets: path.resolve(__dirname, 'assets/')
}
}
}
但是项目中new Audio('Assets/audios/demo.mp3')会存在问题,在chrome的开发工具看请求是[object%20Module],挺奇怪。

之后发现这些资源的解析不需要使用到ES modules语法,我们把它关掉:
const path = require('path')
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|svg|jpg|jpeg|gif|mp3)$/i,
loader: 'file-loader',
options: {
name: '[path][hash:18].[ext]',
esModule: false
}
}
]
},
resolve: {
alias: {
Assets: path.resolve(__dirname, 'assets/')
}
}
}
配置需要的插件
现在还存在其他需求:html文件的压缩,js文件自动引入到html、css文件的压缩等。
html-webpack-plugin
如果修改了打包输出文件名,或者文件名使用了hash,这样打包都都需要手动去修改index.html里js文件的引入,太麻烦了,我们使用html-webpack-plugin插件来解决,同时解决了压缩需求。
安装一下
yarn add -D html-webpack-plugin clean-webpack-plugin
clean-webpack-plugin插件可以每次打包前自动删除打包生成的dist文件夹,可以不用。
配置一下
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: 'index.html'
})
]
}
mini-css-extract-plugin
使用mini-css-extract-plugin插件整合项目使用的多个css文件到一个文件。简单使用可以看它的文档,这里我的需求是项目不存在import 'Assets/styles/main.css'这样的资源使用,只在index.html中通过link加载。所以我们需要在 webpack 的entry入口配置中添加css文件,然后再利用这个插件,并且html-webpack-plugin插件也自动把输出文件引入到了index.html,完美。
安装一下,同时需要用到css-loader。
yarn add -D mini-css-extract-plugin css-loader
webpack 配置中添加这个插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
entry: [
'./src/index.js',
'./assets/styles/main.css'
],
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin(),
new HtmlWebpackPlugin({
template: 'index.html'
})
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
}
}
css-minimizer-webpack-plugin
你或许发现了mini-css-extract-plugin插件并没有压缩输出的css文件。之前这个需求我们一般用的optimize-css-assets-webpack-plugin,但是 webpack 5 及更高版本建议使用了css-minimizer-webpack-plugin。
安装一下
yarn add -D css-minimizer-webpack-plugin
配置一下
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin(),
]
},
}
但是使用它之后出现了一下新问题,那就是之前打包的生成的bundle.js文件不是之前压缩的样子了,这里设置了minimizer后需要加上 webpack 5 本身带有的terser-webpack-plugin优化。
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserWebpackPlugin = require("terser-webpack-plugin")
module.exports = {
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin(),
new TerserWebpackPlugin(),
]
},
}
另外,bundle.js文件中其实会存在一些项目依赖打包后的license许可注释,但是我们并不需要它存在,可以这样简单的配置一下terser-webpack-plugin插件。
new TerserWebpackPlugin({
terserOptions: {
format: {
comments: false
}
},
extractComments: false
})
html-inline-css-webpack-plugin
因为我们需要打包压缩生成好的css文件直接插入到index.html文件的head的style,而不是使用link方式,所以我们还可以再使用一个插件html-inline-css-webpack-plugin。
安装一下
yarn add -D html-inline-css-webpack-plugin
配置一下
const HTMLInlineCSSWebpackPlugin = require('html-inline-css-webpack-plugin').default
module.exports = {
plugins: [
new HTMLInlineCSSWebpackPlugin()
],
}
完成需求的 webpack 配置
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const HTMLInlineCSSWebpackPlugin = require('html-inline-css-webpack-plugin').default
const TerserWebpackPlugin = require("terser-webpack-plugin")
module.exports = {
mode: 'production',
entry: [
'./src/index.js',
'./assets/styles/main.css'
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: ''
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin(),
new HtmlWebpackPlugin({
template: 'index.html'
}),
new HTMLInlineCSSWebpackPlugin()
],
module: {
rules: [
{
test: /\.(png|svg|jpg|jpeg|gif|mp3)$/i,
loader: 'file-loader',
options: {
name: '[path][hash:18].[ext]',
esModule: false
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin(),
new TerserWebpackPlugin()
]
},
resolve: {
alias: {
Assets: path.resolve(__dirname, 'assets/')
}
}
}
最后
🦢 Oops!太舒服了,现在只需要yarn build一下,部署需要的资源就在dist文件里满意的放着了。
版权声明:
Anand's Blog文章皆为站长Anand Zhang原创内容,转载请注明出处。
包括商业转载在内,注明下方要求的文章出处信息即可,无需联系站长授权。
请尊重他人劳动成果,用爱发电十分不易,谢谢!
请注明出处:
本文出自:Anand's Blog
本文永久链接:https://anandzhang.com/posts/frontend/19