這周接手了一個前后端未分離的老項目,需要用webpack重新打包,重新設置html入口,之前沒接觸過webpack,急着學了點皮毛,遇到了很多問題,踩了很多坑,我這邊簡單總結一下。
下載的webpack5,用webpack4語法打包,資源文件(如圖片)加載不出來
因為是拼布式的學習,急着趕項目進度,webpack中文文檔看了前幾章就着急上手項目想要解決問題了,直接install,所以下載的是最新版的5
具體的安裝版本:
webpack 5.52.1
webpack-cli 4.8.0
配置如下
webpack.config.js中代碼:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'production',
entry: {
index: './src/index.js',
init: './src/init.js'
},
plugins: [
new HtmlWebpackPlugin({
template: 'cloudTrial.html',
title: '測試',
}),
],
output: {
filename: './js/[name].js',
path: path.resolve(__dirname, './dist'),
clean: true,
},
module: {
rules: [
{
test: /\.css$/i,
exclude: path.resolve(__dirname, 'node_modules'),
use: ['style-loader','css-loader'],
},
],
}
};
到這里我都是一步一步跟着5的文檔走的,所以沒有出現什么問題,因為不希望每次都要更改入口文件,方便打包入口和開發的html分離開來,也引入了html-webpack-plugin(結果這成了我遇到終極大坑的由頭,后面細說),希望盡量減少請求的調用所以加了css-loader和style-loader將css代碼也並入了js里,但是打包完運行,發現有404,css里的url圖片並沒有跟着一起打包出來,所以找不到圖片。然后文檔繼續項下看了一些,講到webpack關於資源打包的方式,用文檔中的方式打包,但不知道為什么,報錯了,我這邊沒有記錄報錯的內容,估計是有什么字段寫錯了。
然后就又進入了一個深坑:我去瀏覽器上查找怎么打包css中的圖片。跟着這些博主的文章,我安裝了url-loader和file-loader(這邊一開始我只裝了url-loader,還報了缺失file-loader的問題)去重新配置了我的webpack,加入了以下內容:
{
test: /\.(jpg|png|gif)$/,
loader:"url-loader", //url-loader依賴於file-loader
options:{
limit: 8 * 1024 //圖片小於8kb就是用base64的方式
}
}
能正常打包沒有報錯了,也沒有404找不到圖片的問題了,但是圖片還是沒有加載出來,我直接在vscode里也無法打開打包的圖片,網上幾乎沒有一篇提到這個問題的博客,可能是因為5用的人少吧
后來就是不斷嘗試,我去重新翻5的文檔看,這次自己重新建了個demo,一步一步跟着文檔做,然后發現到了5,已經不需要什么url-loader和file-loader之類的加載器了,網上給的都是4的配置方式,我卻用5的版本運行,可能就導致了這個問題(關鍵是也沒有報錯,就特別難以排查,而且我第一次配置webpack,一開始錯以為打包后的圖片就是應該無法正常打開的)。
所以后面我用下面這樣的配置,就可以解決圖片打包的問題了(第二行是排除node_modules里的內容,我這邊也踩了坑,不好好配置這個也會報錯):
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
exclude: path.resolve(__dirname, 'node_modules'),
type: 'asset/resource',
},
但這時候不太美觀,圖片的輸出路徑直接暴露在dist目錄底下了,然后又翻了大半天博客,往下翻了翻文檔,用4的寫法是沒法法配置的,最后問了mentor,她幫我又往下翻了翻文檔,最后在很下面竟然找到了配置方法,按照說明配置如下:
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
exclude: path.resolve(__dirname, 'node_modules'),
type: 'asset/resource',
generator: {
filename: 'img/[hash][ext][query]',
},
},
這樣就好看多了
到此webpack最最最簡單的配置就差不多結束了,我以為到這差不多簡單打包應該就結束了,結果提測那邊反映了項目在實機運行中存在偶發性問題(ios,還是一個ipod),所以又把我的提交打回了,我只的繼續探索,並排查原因
配置babel又踩了一些坑
一開始是不想引入babel的,但是沒辦法,在測試機上運行經常出現問題,然后我在自己手機和同事的iphone上運行好像又沒有問題,所以認為是瀏覽器版本導致的兼容性問題,那就不得不用babel了,其實也用簡單方法測試了一下是不是瀏覽器版本問題,但是因為項目特殊性,無法直接通過代碼的方式測試,只能先配上再說(后來證明其實和瀏覽器兼容性沒關系)
這邊因為babel的文檔給我感覺稍微有點亂,然后引用的方式也太多,而我只想在webpack中配置使用,所以我直接找了個博主,跟着他的方法一步一步配置的:原文
整體沒毛病,但是要注意babel-core和babel-loader也有個版本適配性的問題,不過沒事這邊就算報錯了也會清晰提示你該換哪個的版本,所以還算順利,但是這邊配置好了還是有報錯:
BabelLoaderError: SyntaxError: Unexpected token
| ...someString,
就是認不得擴展運算符,這個給我感覺蠻匪夷所思的,其他語法都能轉換,竟然這個不能轉換,談到的博客也比較少,但總算也找到個靠譜的博客,安裝一個插件即可解決
所以我的最后babel系列版本如下(url-loader和file-loader沒用到,刪了也可以)
package.json中部分內容:
"dependencies": {
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-runtime": "^6.26.0",
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.7.0",
"css-loader": "^6.2.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.3.2",
"style-loader": "^3.2.1",
"url-loader": "^4.1.1"
},
webpack.config.js中部分內容:
{
test: /\.js$/,
exclude: path.resolve(__dirname, 'node_modules'),
use: {
loader: 'babel-loader',
},
},
.babelrc中內容:
{
"presets": [["babel-preset-env", {
"targets": {
"browser": ["> 1%", "last 2 versions"]
}
}]],
"plugins":[
"transform-runtime",
"transform-object-rest-spread" // 解決無法識別拓展運算符babel插件
]
}
到此babel也配置完畢了,但我嘗試測試項目,bug卻還是沒有解決。
終極bug排查,真相大白,問題出在html-webpack-plugin上
這個問題特別難以排查,而且因為項目整體建立在websocket通信上,我這邊的代碼也只是一些外部的操作,導致我一直認為是內部一個sdk的問題,我問了我們這最強的同事,他幫我調出了查看websocket通信的調試頁面(我以前沒接觸過,不知道這玩意兒),但是也沒發現到底是什么問題,但是他調出的websocket連接查看,幫了我大忙。我看了看連接情況,對比原版(前后端未分離時候版本的該項目的的運行狀態),發現它只建立了一次websocket連接,而我每次啟動項目都會建立兩條(而當我這邊建立的線路通往不同地區的主機時,bug就發生了)。然后我就一份js一份js的累加在項目中,發現不用webpack打包的話,也只建立一條,但是一嘗試使用webpack打包,運行時就會建立兩條通信連接,然后我在打包生成的index.html代碼,格式化了一下(因為打包會變成一行,我以前一直都不願意),發現我打包的js,它的引用自動注入到了html前面,而我在我自己寫的html里,本身又寫了調用js的方法,所以等於一條js,調用了兩次的情況。
還記得我提到的html-webpack-plugin嗎,其描述有一段這樣的話:
如果在代碼編輯器中打開 index.html,你會看到 HtmlWebpackPlugin 創建了一個全新的文件,所有的 bundle 會自動添加到 html 中。
也就是說,凡是在我的webpack.config.js配置文件中有的入口文件,都會自動放置到生成的html文件頭部,如下:
<script defer="defer" src="./js/index.js"></script>
<script defer="defer" src="./js/init.js"></script>
如果我再在我原始html文件里引用js的話,就會造成js執行兩遍的情況
果然,在我刪除原始html中關於js的引入之后,bug就不復存在了,真相終於大白了。
所以這也告訴我們一個深刻的道理:
學習知識要踏實,系統性的學習一些工具和知識點還是很重要的,不然像我這樣到處找補丁,急着完成任務就反而會被拖的更慢。