1. 程式人生 > 其它 >03.webpack中sourceMap等配置

03.webpack中sourceMap等配置

一、認識PostCSS工具

PostCSS是什麼?

PostCSS是一個通過將JavaScript來轉化css樣式的工具,它可以幫助我們實現css樣式的轉化和適配,比如自動新增瀏覽器字首來讓一些css樣式在不同版本的瀏覽器中都可以生效,比如統一原生html標籤如button在不同瀏覽器中的樣式差異等。

PostCSS的使用方法

PostCSS這個工具本身是沒有什麼用的,必須藉助於對應的外掛來實現css樣式的轉化和適配的,它的使用主要分為兩個步驟:

  1. 首先查詢PostCSS在當前構建工具中的拓展
    比如當前專案使用的構建工具是webpack,那麼就會查詢webpack中對於的postcss-loader這個拓展,通過這個loader為專案注入PostCSS這個工具

  2. 選擇添加當前專案中需要使用的PostCSS相關外掛
    PostCSS工具是獨立的,必須藉助於其對應的外掛,然後將外掛整合到PostCSS中,才可以實現最終的效果

PostCSS在命令列中使用

在使用webpack構建工具中使用postcss之前,我們先單獨的不依賴於webpack等構建工具來使用下postcss為一些css樣式自動新增瀏覽器字首:

  1. 首先安裝postcss
npm i postcss -D
  1. 由於要單獨在命令列中使用postcss工具,所以還要安裝postcss的命令列工具postcss-cli
npm i postcss-cli -D
  1. 新建一個test.css檔案
.example{
	/* 此css屬性用於規定使用者是否可以選中頁面上的文字,多個版本瀏覽器對其實現不一樣,需要在不同瀏覽器中新增對應字首 */
	user-select:none; 
	display: grid;
	transition: all .5s;
	background: linear-gradient(to bottom, white, black);
}
  1. 在命令列中執行命令如下,該命令的意思是使用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
  1. 在安裝好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的。

  1. 要讓webpack在打包的過程中使用postcss,首先必須安裝postcss和postcss-loader
npm i postcss postcss-loader -D
  1. 在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外掛來說有著更加強大的功能:

  1. 已經集成了autoprefixer外掛自動新增瀏覽器字首的能力;
  2. 可以幫助我們將一些現代的css特性轉化成為儘可能多的瀏覽器可以識別的css,並且會根據目標瀏覽器或者執行時環境新增所需polyfill墊片。

下面是postcss-preset-env外掛的使用過程,我們基於這一外掛將一個現代的css特性轉化為大多數瀏覽器可以識別的css屬性:

  1. 安裝postcss-preset-env外掛
npm i postcss-preset-env -D
  1. 配置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")
								]
							}
						}
					}
				]
			},
		]
	}
}
  1. 將一個文字的顏色值設定為十六進位制格式,並設定為8位數,一般來說顏色值都是由6位的十六進位制數字表示,而設定為8位的十六進位制數字其最後的兩位表示顏色的透明度,但這是一個css新特性,大多數瀏覽器並不會支援它。
.container{
	color:#12345678;
	font-weight: bold;
	font-size: 36px;
}
  1. 執行打包,觀察打包之後瀏覽器中的樣式,發現已經轉化為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就可以了。

另外一個在配置外掛的時候,有兩種寫法:

  1. 使用require引入的方式,這種寫法適合在呼叫外掛的時候還需要傳入引數的時候
  2. 直接使用包名字串,這種寫法比較簡潔,但不是所有外掛都支援這種寫法
{
	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,用來解決配置重複的問題。

  1. 根目錄新建postcss.config.js,然後匯出一個物件:
module.exports = {
	plugins:[
		'postcss-preset-env'
	]
}
  1. 有了配置檔案之後只需要在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進位制顏色值寫法*/
}
  1. 問題現象
    上面程式碼表示在index.css中基於@import語法引入了另外一個test.css檔案,此時webpack按照原來的配置執行打包的時候,只會對index.css中的樣式執行postcss-loader處理,並不會對引入的test.css檔案中的樣式執行postcss-loader的處理。

  2. 原因
    這是因為當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處理。

  3. 解決方法
    我們當然是希望所有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需要處理之外,圖片也是使用比較多的,目前在專案中使用圖片的方式基本有兩種:

  1. 新建一個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();
  1. 給一個元素如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資料夾中,這樣頁面就建立起了和資源的引用關係。

  1. 安裝
npm i file-loader -D
  1. 配置file-loader
    配置file-loader的時候有幾個注意點:
  2. 匹配資源的正則表示式中可以寫成/.(png|jpg|jpeg|gif|svg)$/這種形式的,也可以寫成 /.(png|jpe?g|gif|svg)$/,jpe?g中e後面的問好代表e可能會出現0次或者一次
  3. 匹配資源的字尾中不可以包含webp,這種格式的圖片經過webpack處理重新命名之後就會失效而無法載入
{
	test: /\.(png|jpe?g|gif|svg)$/,
	use: [
		{
			loader:"file-loader"
		}
	]
}

file-loader輸出資源和資料夾的重新命名

  1. 輸出資源名稱的重新命名
    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位
			}
		}
	]
}
  1. 指定輸出檔案的存放路徑
    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進行編碼,這樣做的好處在於:

  1. 大檔案如果經過base64編碼,會增大js檔案的體積並最終影響頁面的載入速度,所以大圖片檔案最好直接複製;
  2. 小檔案如果經過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進行替換:

  1. asset/resource 代替之前的file-loader
  2. asset/inline 代替之前的url-loader
  3. asset/source 代替之前的raw-loader
  4. asset 可以基於配置打包資源體積的最大值來決定最終打包的資源是一個獨立檔案還是一個base64 Data直接嵌入到js中

asset/resource的配置

相比於原來複雜的file-loader配置,這裡只需要在type屬性中聲明當前處理資源模組的型別為asset/resource即可:

{
	test: /\.(png|jpe?g|gif|svg)$/,
	type:"asset/resource",
},

預設情況下webpack會將資源打包到build資料夾下的根目錄中,如果要指定資源打包後的存放路徑,有兩種方法:

  1. 在output屬性中配置assetModuleFilename,這裡是全域性配置代表不僅僅是asset/resource這一種型別,其他所有資源模組型別打包後的檔案都會放在這個目錄下。並且這裡在配置最終生成的檔名的時候也可以和配置file-loader的時候一樣,通過各種placeholder來實現資源的重新命名,如下:
module.exports = {
	output: {
		filename: "bundle.js",
		path: path.resolve(__dirname, '../build'),
		assetModuleFilename:"img/[name]-[hash:6][ext]" 
	},
}
  1. 針對於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]"
	}
},