【webpack系列】从基础配置到掌握进阶用法
前言
本篇文章将介绍一些webpack
的进阶用法,演示内容继承自上一篇文章的内容,所以没看过上一篇文章的建议先学习上一篇内容再阅读此篇内容,会更有利于此篇的学习~
文件指纹
文件指纹指的是打包输出的文件名后缀,一般用来做版本管理、缓存等
webpack的指纹策略有三种:hash
、chunkhash
、contenthash
,它们之间最主要的区别就是每种hash影响的范围不同。
占位符
webpack提供占位符用于将特定信息附加在打包输出的文件上
名称 | 含义 |
---|---|
[ext] | 资源后缀名 |
[id] | 文件标识符 |
[name] | 文件名称 |
[path] | 文件的相对路径 |
[folder] | 文件所在的文件夹 |
[hash] | 模块标识符的 hash,默认是 md5 生成 |
[chunkhash] | chunk 内容的 hash,默认是 md5 生成 |
[contenthash] | 文件内容的 hash,默认是 md5 生成 |
[query] | 文件的 query,例如,文件名 ? 后面的字符串 |
[emoji] | 一个随机的指代文件内容的 emoji |
我们可以使用特定的语法,对 hash
、 chunkhash
、contenthash
进行切片:[chunkhash:4]
,像 8c4cbfdb91ff93f3f3c5
这样的哈希会最后会变为 8c4c
。
hash
与整个项目的构建有关,只要项目内文件有修改,整个项目构建的hash值就会改变
我们使用多入口打包来体验一下:
// webpack.config.js
module.exports = {
entry: {
main: './src/main.js',
index: './src/index.js'
},
output: {
filename: '[name].[hash:6].js',
path: __dirname + '/dist',
clean: true
},
// ...
}
此时我们使用了占位符来设置文件指纹[name].[hash:6].js
代表的是文件名+6位hash
此时我们执行npm run build
,看打包出来的内容如下:
此时两个js文件的hash
都是207495
我们修改一下index.js
的内容,再打包一次
我们会发现此时两个js文件的hash
都变成了9f0e2d
chunkhash
chunkhash 是和 webpack 打包的模块相关,每一个 entry 作为一个模块,会产生不同的 Chunkhash 值,文件改变时只会影响当前chunk组的hash值
我们再来看看chunkhash
// webpack.config.js
module.exports = {
entry: {
main: './src/main.js',
index: './src/index.js'
},
output: {
filename: '[name].[chunkhash:6].js',
path: __dirname + '/dist',
clean: true
},
// ...
}
还是延用上面的例子,这次我们只修改main.js
文件内容
修改前两个文件的hash值如下:
修改后:
此时只有main.js
的打包产物的hash发生了变化
contenthash
contenthash 是和根据文件内容相关,单个文件发生变化,只会引起此文件的hash值
这里我们使用miniCssExtractPlugin
将CSS内容提取成文件,并为它设置contenthash
// webpack.config.js
const miniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
entry: {
main: './src/main.js',
index: './src/index.js'
},
output: {
filename: '[name].[contenthash:6].js',
path: __dirname + '/dist',
clean: true
},
mudole: {
rules: [
{
test: /\.css$/,
use: [miniCssExtractPlugin.loader, 'css-loader']
},
// ...
]
},
plugins: [
// ...
new miniCssExtractPlugin({
filename: 'css/[name].[contenthash:6].css'
}),
]
// ...
}
然后打包看一下此时的hash:
我们修改index.css
内容再打包一次
此时只有index.css
的打包产物hash值发生了变化。
根据不同的文件类型一般选择不同的文件指纹策略,通常情况下:
- JS文件采用[chunkhash]文件指纹策略
- CSS文件采用[contenthash]文件指纹策略
- 图片资源采用[hash]文件指纹策略
代码压缩
压缩JS
目前最成熟的JavaScript代码压缩工具是UglifyJS
,它能够分析JavaScript语法树,理解代码含义,从而能做到诸如去掉无效代码、去掉日志输出代码、缩短变量名等优化。但很遗憾的是UglifyJS
不再维护,并且它不支持 ES6 + 。
现在推荐使用的是Terser
,它在 UglifyJS 基础上增加了 ES6 语法支持,并重构代码解析、压缩算法,使得执行效率与压缩率都有较大提升,并且Webpack5.0 后默认使用 Terser 作为 JavaScript 代码压缩器
简单实用:
// webpack.config.js
module.exports = {
//...
optimization: {
minimize: true
}
}
需要注意的是在生产模式中构建时,Terser压缩是默认开启的
当然它也允许你通过提供一个或多个定制过的TerserPlugin实例,覆盖默认的压缩工具,实现更精细的压缩功能
// webpack.config.js
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
//...
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
terserOptions: {
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
},
})
]
}
}
在Webpack4中 默认使用 uglifyjs-webpack-plugin
压缩代码,也可以通过 minimizer
数组替换为 Terser 插件
压缩CSS
CSS代码同样也可以使用webpack来进行压缩,比较常见的CSS压缩工具有:cssnano
、css-minimizer-webpack-plugin
对于 webpack5 或更高版本,官方推荐使用 CssMinimizerWebpackPlugin
,该插件是使用 cssnano
优化和压缩 CSS,支持缓存和并发模式下运行。
安装:
npm i css-minimizer-webpack-plugin
配置:
// webpack.config.js
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // 用压缩css
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 用来提取css成单独的文件
module.exports = {
//...
module: {
rules: [
{
test: /.css$/,
// 注意,MiniCssExtractPlugin.loader 与 style-loader不能同时使用
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
optimization: {
minimize: true,
minimizer: [
// Webpack5 之后,约定使用 '...' 字面量保留默认 minimizer 配置
"...",
new CssMinimizerPlugin(),
],
},
plugins: [new MiniCssExtractPlugin()],
};
️这里需要注意的是需要使用 mini-css-extract-plugin
将 CSS 代码抽取为单独的 CSS 产物文件,这样才能命中 css-minimizer-webpack-plugin
默认的 test
逻辑。
压缩HTML
我们之前使用的html-webpack-plugin
,它除了可以生成html模版,也可以用来对html进行压缩。
htmlWebpackPlugin常见参数
template
:模板的路径,默认会去寻找src/index.ejs
是否存在。filename
:输出文件的名称,默认为index.html
。inject
:是否将资源注入到模版中,默认为true
。minify
:压缩参数。在生产模式下(production
),默认为true
;否则,默认为false
。
// webpack.config.js
module.exports = {
// ...
plugins: [
// ...
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html',
minify: true
}),
]
}
生成的 HTML 将使用 html-minifier-terser
和以下选项进行压缩,所以它实际上的压缩功能其实是html-minifier-terser
来实现的,更多配置可以查看这个工具文档
{
collapseWhitespace: true,
keepClosingSlash: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
}
禁止生成LICENSE文件
经过上面这些配置后,我发现了一个奇怪的问题,那就是每个bundle
产物都多了一个同名的LICENSE.txt
文件,打开一看里面都是一些注释内容。
为什么会生成这些文件,带着疑惑我去翻了下官方文档,Webpack5 默认压缩代码工具为terser-webpack-plugin
,那就先从它入手吧。
在它的配置中找到了extractComments
参数,默认值为true
,表示将注释剥离到单独的文件中。
如果我们不想要,直接关掉该配置就行了
module.exports = {
// ...
optimization: {
minimize: true,
minimizer: [
new cssMinimizerPlugin(),
new terserPlugin({
extractComments: false, // 关闭注释剥离功能
}),
'...'
]
},
}
CSS增强(autoprefixer)
前端最头疼的问题莫过于处理兼容性,因为前端的运行环境并不固定,可以在各种浏览器以及各种webview中运行,并且每个浏览器厂商对CSS的写法也各不相同,这就势必会导致出现一些问题。
比如为了兼容各种浏览器内核,圆角属性应该这样写:
.container {
-moz-border-radius: 16px;
-webkit-border-radius: 16px;
-o-border-radius: 16px;
border-radius: 16px;
}
试想一下如果在开发中需要你这样写,那是不是太不合理了?
我们一般都会通过webpack配置插件来帮我们解决这个问题,处理CSS我们首先会想到postcss
,没错webpack也有使用postcss处理CSS的loader --- postcss-loader
,然后我们还需要使用postcss
的插件autoprefixer
来帮我们自动添加浏览器前缀。
安装:
npm i postcss postcss-loader autoprefixer
修改配置:
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
//...
{
test: /\.css$/,
use: [miniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['autoprefixer']
}
}
}]
},
]
}
//...
}
️这里需要注意的是,如果你想自定义转换的规则,最好是将 autoprefixer 的 browsers
选项替换为 browserslist
配置。在 package.json
或。Browserslistrc
文件。使用 browsers
选项可能导致错误,并且browserslist
配置可以用于 babel、 autoprefixer、 postcss-norize 等工具。
比如package.json中配置browserslist
:
// package.json
{
//...
"browserslist": [
"last 10 Chrome versions",
"last 5 Firefox versions",
"Safari >= 6",
"ie> 8"
]
}
此时我们打包的CSS的产物就会自动添加浏览器前缀
静态资源拷贝
假如我们需要在html中引用一些不需要打包处理的资源,比如下面这种情况
在index.html
中引入了一些日志的工具函数,这时候我们直接跑起来会发现这个文件直接404了,这是怎么回事?
首先我们写的路径肯定是没问题的,问题在于我们打包后这个utils
文件肯定是不在这个位置了,所以会报404
所以这里我们需要使用copy-webpack-plugin
将文件拷贝至dist
目录下
// webpack.config.js
const copyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
// ...
plugins: [
new copyWebpackPlugin({
patterns: [
{from: 'module', to: __dirname + '/dist/module/'}
]
}),
]
}
此时再打包,我们会发现dist
目录下已经有了module/utils.js
,并且页面也不会再报404了
sourcemap
SourceMap 就是一个信息文件,里面储存着代码的位置信息。这种文件主要用于开发调试,现在代码都会经过压缩混淆,这样报错提示会很难定位代码。通过 SourceMap 能快速定位到源代码,并进行调试。
比如我们没有开启sourcemap
,然后开发过程中报错了,它的报错信息是这样的:
定位过去是打包后的内容,这样的话对我们排查报错非常不方便。
当我们开启sourcemap
,再来看看这个同样的报错是怎样的:
// webpack.config.js
module.exports = {
// ...
devtool: 'eval-cheap-module-source-map',
}
此时的报错指向就非常清晰了~
关键字
devtool的值有20多种,并且都是由以下七种关键字的一个或多个组成
eval
关键字
当 devtool
值包含 eval
时,生成的模块代码会被包裹进一段 eval
函数中,且模块的 Sourcemap 信息通过 //# sourceURL
直接挂载在模块代码内
source-map
关键字
当 devtool
包含 source-map
时,Webpack 才会生成 Sourcemap 内容
cheap
关键字
当 devtool
包含 cheap
时,生成的 Sourcemap 内容会抛弃列维度的信息,这就意味着浏览器只能映射到代码行维度
module
关键字
module
关键字只在 cheap
场景下生效,例如 cheap-module-source-map
、eval-cheap-module-source-map
。当 devtool
包含 cheap
时,Webpack 根据 module
关键字判断按 loader 联调处理结果作为 source,还是按处理之前的代码作为 source
nosources
关键字
当 devtool
包含 nosources
时,生成的 Sourcemap 内容中不包含源码内容 —— 即 sourcesContent
字段
inline
关键字
当 devtool
包含 inline
时,Webpack 会将 Sourcemap 内容编码为 Base64 DataURL,直接追加到产物文件中
hidden
关键字
通常,产物中必须携带 //# sourceMappingURL=
指令,浏览器才能正确找到 Sourcemap 文件,当 devtool
包含 hidden
时,编译产物中不包含 //# sourceMappingURL=
指令
devtool的值以及各自的功能可以在webpack文档上查看
如何选择
- 对于开发环境,适合使用:
eval
:速度极快,但只能看到原始文件结构,看不到打包前的代码内容;cheap-eval-source-map
:速度比较快,可以看到打包前的代码内容,但看不到 loader 处理之前的源码;cheap-module-eval-source-map
:速度比较快,可以看到 loader 处理之前的源码,不过定位不到列级别;eval-source-map
:初次编译较慢,但定位精度最高;
- 对于生产环境,则适合使用:
source-map
:信息最完整,但安全性最低,外部用户可轻易获取到压缩、混淆之前的源码,慎重使用;hidden-source-map
:信息较完整,安全性较低,外部用户获取到.map
文件地址时依然可以拿到源码,慎重使用;nosources-source-map
:源码信息缺失,但安全性较高,需要配合 Sentry 等工具实现完整的 Sourcemap 映射。
解决跨域
在开发过程中,我们势必会遇到跨域问题,对于本地开发我们一般可以通过配置代理来解决
我们先来简单写一个接口:
const express = require('express')
const app = express()
app.get('/api/getInfo', (req, res) => {
res.json({
code: 200,
data: {
name: 'nanjiu',
age: 18
}
})
})
app.listen(3000, () => {
console.log('服务已启动~')
})
然后把服务跑起来,再到vue项目中去调用
const getInfo = async () => {
try {
const res = await axios.get('http://localhost:3000/api/getInfo')
console.log(res)
} catch(err) {
console.log(err)
}
}
这时候你会发现接口调用跨域了
配置代理
接着我们再来通过webpack配置代理解决跨域问题,由于我们本地使用了webpack-dev-server
,所以我们可以直接通过它来配置
// webpack.config.js
module.exports = {
// ...
devServer: {
hot: true,
open: true,
proxy: {
'/api': 'http://localhost:3000'
}
}
}
这个时候我们的接口请求就正常了
由于篇幅问题,这篇文章就介绍到这里了,后面会接着更新webpack
更多高级用法。
如果这篇文章有帮助到你,️关注+点赞️鼓励一下作者,文章公众号首发,关注 前端南玖
第一时间获取最新文章~
【webpack系列】从基础配置到掌握进阶用法的更多相关文章
- 【webpack 系列】基础篇
Webpack 基础篇 基本概念 Webpack 是一个现代 JavaScript 应用程序的静态模块打包器.当 webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每 ...
- 【webpack 系列】进阶篇
本文将继续引入更多的 webpack 配置,建议先阅读[webpack 系列]基础篇的内容.如果发现文中有任何错误,请在评论区指正.本文所有代码都可在 github 找到. 打包多页应用 之前我们配置 ...
- 【webpack系列】webpack4.x入门配置基础(一)
一.前言 webpack在不断的迭代优化,目前已经到了4.29.6.在webpack4这个版本中,做了很多优化,引入了很多特性,将获得更多模块类型,.mjs支持,更好的默认值,更为简洁的模式设置,更加 ...
- 【MM系列】SAP MM模块-基础配置第一篇
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP MM模块-基础配置第一篇 ...
- webpack+babel+react+antd技术栈的基础配置
webpack+babel+react+antd技术栈的基础配置 前段时间使用webpack+babel+react+antd做了一套后台管理系统,刚开始被一大堆的新知识压的喘不过气来,压力挺大的.还 ...
- 深入浅出 webpack 之基础配置篇
前言 前端工程化经历过很多优秀的工具,例如 Grunt.Gulp.webpack.rollup 等等,每种工具都有自己适用的场景,而现今应用最为广泛的当属 webpack 打包了,因此学习好 webp ...
- SpringBoot基础系列之自定义配置源使用姿势实例演示
[SpringBoot基础系列]自定义配置源的使用姿势介绍 前面一篇博文介绍了一个@Value的一些知识点,其中提了一个点,@Value对应的配置,除了是配置文件中之外,可以从其他的数据源中获取么,如 ...
- webpack(11)配置文件分离为开发配置、生成配置和基础配置
前言 上篇我们已经配置好了本地开发服务器,但是配置的相对比较凌乱,一个文件中有些是开发时用到的配置,有些是生成时用到的配置,有些是开发和生成都要用到的配置,所以我们这里把环境分为3个环境 webpac ...
- SpringCloud系列九:SpringCloudConfig 基础配置(SpringCloudConfig 的基本概念、配置 SpringCloudConfig 服务端、抓取配置文件信息、客户端使用 SpringCloudConfig 进行配置、单仓库目录匹配、应用仓库自动选择、仓库匹配模式)
1.概念:SpringCloudConfig 基础配置 2.具体内容 通过名词就可以发现,SpringCloudConfig 核心作用一定就在于进行配置文件的管理上.也就是说为了更好的进行所有微服务的 ...
- 2、webpack基础配置
我们需要安装webpack 还需要安装webpack cli 这两个都是我们的开发依赖 这里我们一般会加一个-D表示上线的时候不需要他们两个包 安装我们的webpack 先初始化一下,记住我们的安装依 ...
随机推荐
- webrtc QOS笔记三 Nack机制浅析
nack源码浅析 nack源码浅析 Video Nack nack模块 nack list keyFrame list & recovered list nack 发送的策略 nack 模块的 ...
- solidity中的mapping
mapping可以理解为python中对字典的键值遍历,键是唯一的而值是可以重复的 mapping函数的构造: mapping(_KeyType => _ValueType) mapping ...
- 如何将 CentOS 8 转换为 CentOS Stream
CentOS 未来是不会更新数字版本了.CentOS 项目组,未来会变更为Stream版本,也就是俗称的滚动版本,那么如何将数字版本升级为滚动版本呢? 若需要将其转换为滚动版本,那么即可参考本文进行升 ...
- vmware中安装windows11系统
1.官网下载windwos11镜像(点击跳转下载) 2.打开vmware,创建新的虚拟机 3.选择典型方便快捷 4.选择安装程序光盘文件,点击浏览选择刚刚下载好的iso镜像 5.选择windows版本 ...
- 解密prompt系列5. APE+SELF=自动化指令集构建代码实现
上一章我们介绍了不同的指令微调方案, 这一章我们介绍如何降低指令数据集的人工标注成本!这样每个人都可以构建自己的专属指令集, 哈哈当然我也在造数据集进行时~ 介绍两种方案SELF Instruct和A ...
- Vue3中无法为el-tree-select设置反选问题分析
好久没有写博客了,刚好上周遇到一个难缠问题,这里记录一下. 环境:Vue3.2.Element Plus 问题:子组件 setting.vue => 弹窗组件 Dialog => 树选择组 ...
- Exception-List
一.500错误:找不到 jar包 应用根目录/WEB-INF/lib目录中没有对应的jar包. ctrl+shift+alt+s,打开artifacts,发现outputRoot里缺少lib目录.添加 ...
- Linux(四)软件包管理
软件包管理 1 RPM 简介 RPM(RedHat Package Manager),是红帽系linux操作系统的软件包管理工具,类似于windows中的setup.exe能够进行软件包的更新.卸载. ...
- 【数据结构与算法】无向图的结构与遍历 BFS&DFS
1 表示无向图的数据类型 1.1 邻接矩阵 可以使用一个V*V的二维布尔矩阵,当定点v和定点w相连的时候,定义第v行第w列的值为true,否则为false.邻接矩阵不适合定点较多的情况,含有百万的顶点 ...
- 3520. 【NOIP2013模拟11.7B组】原根(math)
题目: 考试想法: 考试的时候觉得这些数学公式太恶心了,所以就直接跳过了. 正解: 直接暴力模拟就可以了. 代码: #include<bits/stdc++.h> using namesp ...