03.webpack中sourceMap等配置
一、认识PostCSS工具
PostCSS是什么?
PostCSS是一个通过将JavaScript来转化css样式的工具,它可以帮助我们实现css样式的转化和适配,比如自动添加浏览器前缀来让一些css样式在不同版本的浏览器中都可以生效,比如统一原生html标签如button在不同浏览器中的样式差异等。
PostCSS的使用方法
PostCSS这个工具本身是没有什么用的,必须借助于对应的插件来实现css样式的转化和适配的,它的使用主要分为两个步骤:
-
首先查找PostCSS在当前构建工具中的拓展
比如当前项目使用的构建工具是webpack,那么就会查找webpack中对于的postcss-loader这个拓展,通过这个loader为项目注入PostCSS这个工具 -
选择添加当前项目中需要使用的PostCSS相关插件
PostCSS工具是独立的,必须借助于其对应的插件,然后将插件集成到PostCSS中,才可以实现最终的效果
PostCSS在命令行中使用
在使用webpack构建工具中使用postcss之前,我们先单独的不依赖于webpack等构建工具来使用下postcss为一些css样式自动添加浏览器前缀:
- 首先安装postcss
npm i postcss -D
- 由于要单独在命令行中使用postcss工具,所以还要安装postcss的命令行工具postcss-cli
npm i postcss-cli -D
- 新建一个test.css文件
.example{
/* 此css属性用于规定用户是否可以选中页面上的文本,多个版本浏览器对其实现不一样,需要在不同浏览器中添加对应前缀 */
user-select:none;
display: grid;
transition: all .5s;
background: linear-gradient(to bottom, white, black);
}
- 在命令行中执行命令如下,该命令的意思是使用postcss工具将./src/css/test.css目录下的test.css文件中的有关css样式自动添加浏览器前缀之后输出到./src/css/result.css中。
npx postcss -o ./src/css/result.css ./src/css/test.css
执行命令之后发现确实新增了一个./src/css/result.css文件,但是该文件中的样式并没有添加前缀,这是因为postcss要实现自动添加浏览器前缀,必须要借助于其对应的插件autoprefixer来实现,所以下一步我们再安装插件autoprefixer:
npm i autoprefixer -D
- 在安装好Autoprefixer插件之后,执行命令如下:意思是使用postcss工具并使用插件autoprefixer将./src/css/test.css目录下的test.css文件中的有关css样式自动添加浏览器前缀之后输出到./src/css/result.css中。
npx postcss --use autoprefixer -o ./src/css/result.css ./src/css/test.css
在执行上面命令之后,在result.css中转化后的css文本如下:可见已经为需要添加的css样式自动添加了浏览器前缀。
.example{
/* 此css属性用于规定用户是否可以选中页面上的文本,多个版本浏览器对其实现不一样,需要在不同浏览器中添加对应前缀 */
-webkit-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
display: grid;
transition: all .5s;
background: linear-gradient(to bottom, white, black);
}
Autoprefixer CSS online:Autoprefixer是一个为css样式在不同条件的浏览器版本中对应添加浏览器前缀以增强css样式兼容性的网站
PostCSS在webpack中的使用
前面我们是单独的基于postcss和postcss-cli命令行工具实现了以下给css样式自动添加浏览器前缀的过程,但是在正式的项目中我们不可能为每一个css文件都执行一次上面的命令,然后将输出的结果再拷贝过去,所以我们就要基于webpack这个构建工具来简化这一过程,最终的目的是告诉webpack:当webpack在执行打包的过程中遇到css文件,首先借助于postcss-loader找到postcss这个工具,再基于postcss工具对应的插件autoprefixer来自动为css文件中的样式添加浏览器前缀,而具体要为哪些浏览器添加前缀则是由brswerslist这个工具提供的caniuse-lite这个小工具查询到然后共享给autoprefixer的。
- 要让webpack在打包的过程中使用postcss,首先必须安装postcss和postcss-loader
npm i postcss postcss-loader -D
- 在webpack.config.js中进行配置
该配置的意思是在webpack打包的过程中遇到css文件时,先借助于postcss-loader找到postcss工具,然后基于postcss插件autoprefixer来将css样式自动添加浏览器前缀,将添加前缀之后的css文件再依次交给css-loader和style-loader处理。
module.exports = {
module:{
rules:[
{
test: /\.css$/, // 匹配规则
use: [
"style-loader",
"css-loader",
{
loader:"postcss-loader",
options:{
postcssOptions:{
plugins:[
require("autoprefixer")
]
}
}
}
]
},
]
}
}
postcss-preset-env插件
其实在webpack中配置postcss-loader的时候,还有一个特殊的插件:postcss-preset-env,这个插件相比于autoprefixer插件来说有着更加强大的功能:
- 已经集成了autoprefixer插件自动添加浏览器前缀的能力;
- 可以帮助我们将一些现代的css特性转化成为尽可能多的浏览器可以识别的css,并且会根据目标浏览器或者运行时环境添加所需polyfill垫片。
下面是postcss-preset-env插件的使用过程,我们基于这一插件将一个现代的css特性转化为大多数浏览器可以识别的css属性:
- 安装postcss-preset-env插件
npm i postcss-preset-env -D
- 配置postcss-preset-env插件
module.exports = {
module:{
rules:[
{
test: /\.css$/, // 匹配规则
use: [
"style-loader",
"css-loader",
{
loader:"postcss-loader",
options:{
postcssOptions:{
plugins:[
require("autoprefixer"),
require("postcss-preset-env")
]
}
}
}
]
},
]
}
}
- 将一个文本的颜色值设置为十六进制格式,并设置为8位数,一般来说颜色值都是由6位的十六进制数字表示,而设置为8位的十六进制数字其最后的两位表示颜色的透明度,但这是一个css新特性,大多数浏览器并不会支持它。
.container{
color:#12345678;
font-weight: bold;
font-size: 36px;
}
- 执行打包,观察打包之后浏览器中的样式,发现已经转化为rgba的格式,这说明postcss-preset-env这个插件生效了。
.container {
color: rgba(18,52,86,0.47059);
font-weight: bold;
font-size: 36px;
}
postcss-preset-env和autoprefixer的关系
Vue官方的脚手架搭建的项目中,关于postcss-loader的配置只需要配置一个插件:postcss-preset-env即可,这说明postcss-preset-env插件集已经内置了或者实现了autoprefixer的功能,这两个插件不需要一起配置,只需要配置一个postcss-preset-env就可以了。
另外一个在配置插件的时候,有两种写法:
- 使用require引入的方式,这种写法适合在调用插件的时候还需要传入参数的时候
- 直接使用包名字符串,这种写法比较简洁,但不是所有插件都支持这种写法
{
loader:"postcss-loader",
options:{
postcssOptions:{
plugins:[
require("autoprefixer"),
require("postcss-preset-env"),
require("my-plugin")(arg1,arg2) //插件传参
]
/* 或者 */
plugins:[
"autoprefixer",
"postcss-preset-env"
]
}
}
}
postcss.config.js配置文件抽取
在项目中如果使用了less、sass这些css拓展语言的时候,按照加载loader的规则,我们在加载less-loader之后还需要先加载postcss-loader之后,才可以接着加载css-loader,但是这种配置方式会导致.css文件和.less文件有重复的配置,所以webpack为我们提供了专门的用于配置postcss的配置文件postcss.config.js,用来解决配置重复的问题。
- 根目录新建postcss.config.js,然后导出一个对象:
module.exports = {
plugins:[
'postcss-preset-env'
]
}
- 有了配置文件之后只需要在webpack.config.js按照如下配置postcss-loader即可:
module: {
rules: [
{
test: /\.css$/, // 匹配规则
use: [
"style-loader",
"css-loader",
"postcss-loader"
]
},
{
test: /\.less$/, // 匹配规则
use: [
"style-loader",
"css-loader",
"postcss-loader",
"less-loader"
]
}
]
}
css-loader中importLoaders属性的用法
在webpack中处理css文件或者less文件的时候,如果按照以下配置来处理:
rules:[
{
test:/\.css$/,
use:[
"style-loader",
"css-loader",
"postcss-loader"
]
}
]
但是上述这种写法会导致处理一个css文件中又通过@import语法引入另外一个css文件的时候出现问题:
/* index.css文件 */
@import "./test.css"
.content{
color:#12345678; /* 8位16进制颜色值写法*/
}
/* test.css文件 */
.test-demo{
color:#12345678; /* 8位16进制颜色值写法*/
}
-
问题现象
上面代码表示在index.css中基于@import语法引入了另外一个test.css文件,此时webpack按照原来的配置执行打包的时候,只会对index.css中的样式执行postcss-loader处理,并不会对引入的test.css文件中的样式执行postcss-loader的处理。 -
原因
这是因为当webpack在打包的过程中遇到index.css文件的时候,会按照配置依次执行postcss-loader和css-loader,在使用css-loader处理index.css文件的时候遇到@import这种css语法的时候,就会将test.css文件也使用css-loader进行处理,但是这里需要注意的在处理test.css文件的时候,并不会再回去使用postcss-loader处理一遍的,所以就会导致index.css中的css样式会经过postcss-loader处理,但是test.css文件中的样式并不会经过postcss-loader处理。 -
解决方法
我们当然是希望所有css文件无论是直接引入的还是通过@import语法引入的文件都可以正确的被postcss-loader进行处理,所以我们需要再原来配置的基础上加一个importLoaders配置,importLoaders的值为1那么代表在处理@import语法导入的css文件的时候,需要先经过当前loader配置的上1个loader处理下;如果是2就代表需要先经过当前loader配置的上2个loader处理之后再由当前loader处理。
rules:[
{
test:/\.css$/,
use:[
"style-loader",
{
loader:"css-loader",
options:{
importLoaders:1
}
}
"postcss-loader"
]
}
]
二、加载和处理其他资源
一般在项目中除了js和css需要处理之外,图片也是使用比较多的,目前在项目中使用图片的方式基本有两种:
- 新建一个img标签,然后给标签的src属性进行赋值
注意:通过require()方法引入的资源在经过webpack处理的时候其返回值和file-loader的版本有关联
- 如果file-loader的版本是4.x,那么直接返回资源本身;
- 如果file-loader的版本是5.x+,那么返回的是一个module对象,资源路径是存放在该对象的default属性上的;
如果采用import语法来加载,就不会有这种版本考虑的问题,直接import xxx from '文件路径'即可。
function createImage(){
let img = new Image();
img.src= require('../img/test1.png').default;
document.body.appendChild(img);
}
createImage();
- 给一个元素如div设置背景图片,也就是css的background-image属性用url引入
function createImage(){
const ele = document.createElement('div');
ele.style.width = 200 + 'px';
ele.style.height = 200 + 'px';
ele.className = 'bg-image';
return ele;
}
createImage();
/* 在css文件中 */
.bg-image{
background-image:url('../img/woman.webp');
background-size;cover;
}
file-loader的用法
webpack自身是不识别这些图片资源的,要处理这些资源必须要借助于其对应的file-loader来进行处理,file-loader的作用就是帮助我们处理import和require()方式引入文件资源,然后将处理之后的资源输出到最终的打包之后的dist文件夹中,这样页面就建立起了和资源的引用关系。
- 安装
npm i file-loader -D
- 配置file-loader
配置file-loader的时候有几个注意点: - 匹配资源的正则表达式中可以写成/.(png|jpg|jpeg|gif|svg)$/这种形式的,也可以写成 /.(png|jpe?g|gif|svg)$/,jpe?g中e后面的问好代表e可能会出现0次或者一次
- 匹配资源的后缀中不可以包含webp,这种格式的图片经过webpack处理重命名之后就会失效而无法加载
{
test: /\.(png|jpe?g|gif|svg)$/,
use: [
{
loader:"file-loader"
}
]
}
file-loader输出资源和文件夹的重命名
- 输出资源名称的重命名
webpack默认基于file-loader处理文件资源之后会对文件进行重命名,一般来说会基于MD4摘要算法生成一个128位的hash值,然后每4位用一个16进制数字表示,最后输出到dist文件夹中的文件名称都会是一个32位的16进制数字组成的名称。很显然这种默认的配置不利于我们查询资源的对应关系,所以我们可以通过配置来自定义输出资源的名称规则。
webpack中关于file-loader最终输出的文件名称是基于[Placeholder]来进行处理的,下面是常见的Placeholder:
- [name]:处理文件的名
- [hash]:文件的内容,采用MD4散列函数处理生成的一个128位的hash值,用32个16进制数字表示
- [contentHash]:在file-loader中和[hash]保持一致,但是在其他地方会不一样
- [path]:处理文件相对于webpack配置文件的路径
在了解了以上占位符之后,就可以在webpack中进行配置了(下面这个配置也是Vue官方脚手架中对于file-laoder的配置),此配置输出后的文件名为:test1.6f97a9.png
{
test: /\.(png|jpe?g|gif|svg)$/,
use: [
{
loader:"file-loader",
options:{
name:[name].[hash:6].[ext] // 代表输出的文件保留文件名和拓展,并且取hash值的前6位
}
}
]
}
- 指定输出文件的存放路径
webpack在打包的时候会见文件资源都默认全部打包到build文件夹下,这样不利于资源的分类和统一管理,如果要指定最终输出资源到一个文件夹有两个办法:
- 通过outputPath属性声明
{
test: /\.(png|jpe?g|gif|svg)$/,
use: [
{
loader:"file-loader",
options:{
name:"[name].[hash:6].[ext]", // 代表输出的文件保留文件名和拓展,并且取hash值的前6位
outputPath:'image' // 代表输出的文件夹名称为build下的image文件夹
}
}
]
}
- 直接在name属性中和文件名称一起声明
{
test: /\.(png|jpe?g|gif|svg)$/,
use: [
{
loader:"file-loader",
options:{
name:"./image/[name].[hash:6].[ext]", // 代表输出的文件保留文件名和拓展,并且取hash值的前6位,然后统一输出到image文件夹下
}
}
]
}
url-loader的用法
在webpack中除了file-loader之外,还有一个专门处理图片文件资源的url-loader,url-loader的用法基本和file-loader一样,主要是原理不同:
- file-loader的原理是对所有需要打包的图片资源做一个拷贝,拷贝到最终的dist文件夹中,然后对文件名做一个重命名
- url-loader的原理是对所有需要打包的图片经过base64算法转化为base64data,直接嵌入到打包出来的bundle.js当中,然后在加载页面的时候会随着js一起被请求下来
但是在开发中我们希望文件大小比较大的图片直接使用图片,而对文件比较小的图片使用base64进行编码,这样做的好处在于:
- 大文件如果经过base64编码,会增大js文件的体积并最终影响页面的加载速度,所以大图片文件最好直接复制;
- 小文件如果经过base64编码,可以随着页面的js一起被请求下来,减少不必要的http请求;
上述的需求可以提供配置url-loader的limit字段来实现,limit字段的值的单位是byte。
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
use: [
{
loader:"url-loader",
options:{
name:"img/[name].[hash:6].[ext]" ,// 代表输出的文件保留文件名和拓展,并且取hash值的前6位,然后打包后存在build目录下的img文件夹中
limit:10 * 1024,
esModule:false // 配置
}
}
],
type: 'javascript/auto' // V5官网文档说的防止重复打包图片
}
三、asset module type的介绍
在webpack5之前打包的时候处理图片资源要借助于url-loader、file-loader等专门的loader去实现图片资源的打包,但是在webpack5中已经不推荐这种做法了,而是加载这些资源都由一个内置的Asset module type(资源模块类型)来代替上面的那些loader完成图片等资源的打包。
Asset module type(资源模块类型)有下面四种类型,来对不同的loader进行替换:
- asset/resource 代替之前的file-loader
- asset/inline 代替之前的url-loader
- asset/source 代替之前的raw-loader
- asset 可以基于配置打包资源体积的最大值来决定最终打包的资源是一个独立文件还是一个base64 Data直接嵌入到js中
asset/resource的配置
相比于原来复杂的file-loader配置,这里只需要在type属性中声明当前处理资源模块的类型为asset/resource即可:
{
test: /\.(png|jpe?g|gif|svg)$/,
type:"asset/resource",
},
默认情况下webpack会将资源打包到build文件夹下的根目录中,如果要指定资源打包后的存放路径,有两种方法:
- 在output属性中配置assetModuleFilename,这里是全局配置代表不仅仅是asset/resource这一种类型,其他所有资源模块类型打包后的文件都会放在这个目录下。并且这里在配置最终生成的文件名的时候也可以和配置file-loader的时候一样,通过各种placeholder来实现资源的重命名,如下:
module.exports = {
output: {
filename: "bundle.js",
path: path.resolve(__dirname, '../build'),
assetModuleFilename:"img/[name]-[hash:6][ext]"
},
}
- 针对于asset/resource这一种资源模块类型单独配置,需要指定一个generator对象,generator是生成的意思代表生成资源的路径,推荐这种写法:
{
test: /\.(png|jpe?g|gif|svg)$/,
type:"asset/resource",
generator:{
filename:"img/[name]-[hash:6][ext]" // [ext]自身代表"."+后缀名 前面不再需要补一个点
}
},
注意点:在配置资源文件的打包之后的存放路径的时候,可以基于各种placeholder来实现资源的重命名,但是这里有一个和配置loader时不同的地方在于:ext在这里是代表了资源的文件后缀名加一个点的;而之前配置loader的时候ext只代表文件后缀名。比如:
webpack5中的资源模块:filename:"img/[name]-hash:6",[hash:6]和ext中间不加点,最终打包出来的也是xxx.png这种资源名
webpack5之前的loader配置:filename:"img/[name]-[hash:6].ext",[hash:6]和ext中间有一个点
asset/inline的配置
asset/inline是用来代替url-loader的,它的配置很简单,只需要注意在配置的时候不要加generator对象就好了,因为asset/inline模块类型会将资源转化为base64编码的dataUrl,不存在最终输出的资源路径:
{
test: /\.(png|jpe?g|gif|svg)$/,
type:"asset/inline",
}
一个完整的dataUrl组成:data: + MIME type; base64,base64编码值
data:image/png;base64,YABgAAD/2wBDAAkGBwgHBgkICAgKCgkLDhcPDg0ND...
data:image/jpeg;base64,6V4mvDoaiTgEnpROIyHog6nrXegxUV5Jc966TRFP...
asset的配置
asset主要是用来解决资源体积较大的文件打包成为独立的文件,而资源提交较小的文件被打包成为base64编码的dataUrl,具体是基于parser对象中dataUrl配置的maxSize属性来配置,具体配置如下:
{
test: /\.(png|jpe?g|gif|svg)$/,
type:"asset",
generator:{
filename:"img/[name][hash:6][ext]",
},
parser:{
dataUrlCondition:{
maxSize:200 *1024
}
}
}
四、处理字体文件或者字体图标、音频、视频等资源
在项目中有可能会用到特殊的字体文件,或者字体图标、音视频等资源,在webpack5之前这些资源都可以基于file-loader来进行加载,但是在webpack5的asset module type推出之后,就不用专门去安装loader,而是可以配置资源类型就可以处理这些资源了,比如:
{
test:/\.ttf|woff|woff2$/i,
type:"asset/resource", // 等于file-loader 因为字体文件这些资源一般不会转化为base64编码的
generator:{
filename:"font/[name][ext]"
}
},
{
test:/\.mp3|mp4|flv$/i,
type:"asset/resource",
generator:{
filename:"audio/[name][ext]"
}
},