前言
2017/12/18/更新
已經有一段時間沒有更新webpack了,從現在的角度看,文章有一部分內容也是不夠成熟,後續還是會繼續梳理更新的。
網上關於webpack的教程已經數不勝數了,也無意再重新寫一篇複製文。但是實際操作過程中,發現各種教程版本都不一致,有的教程已經過時了,有的教程模糊不清,因此還是遇到了各種問題,因此特將自身實際操作過程中遇到的問題記錄下來,並附上相應的示例demo,也希望能給他人帶來幫助!
本文主要是記錄的一些遇到的問題以及提供了示例,如果想要看入門教程還是去官網上或者參考參考資源中的鏈接。
問題記錄較多,想要直接看示例Demo的請拉到最下方。
另外,此文會持續更新。
題綱
- 遇到的那些問題
- 示例,各階段的示例demo,配有
README
遇到的那些問題
問題一:package.json註釋帶來的問題
説明:項目的package.json中,只要帶有註釋,必然編譯不能通過
解決:禁止在package.json中註釋
問題二:全局安裝帶來的問題
説明:在最開始做項目時,由於本地已經全局安裝過了對應的依賴包,因此沒有再重新在本地安裝,導致運行時報錯,全局安裝版本與本地依賴的版本不一致導致。
解決:每一個項目中,所有的依賴包均在本地安裝,依賴情況通過npm install *** --save -dev注入package.json中,其中一些環境以外的依賴,比如第三方庫express等可以通過npm install *** --save安裝。
所有的依賴包都在本地安裝是一個好習慣,因為這樣可以隨時打包帶走與重新裝箱。
問題三:css-loader省略-loader帶來的問題
説明:在老版本的webpack中,經常會用省略的寫法來寫loader,比如
loaders: [{
test: /\.css$/,
loader: "style!css"
}],
但是,使用此種寫法後,編譯時會報錯。
解決:現在新版本中,已經不允許省略了,比如使用全稱,比如:
loaders: [{
test: /\.css$/,
loader: "style-loader!css-loader"
}],
問題四:默認情況,在js中require(css)後,樣式嵌入在js中,沒有獨立的css文件
説明:webpack的默認設置中,如果沒有引入特殊的第三方插件,在js中require的css文件是會自動寫入到相應打包出的js中的,這樣一來有一些缺點:
- js是阻塞加載的,樣式會出現很慢
- 沒有單獨的css文件,緩存也不便,而且不符合開發習慣
解決:需要引入一個第三方插件extract-text-webpack-plugin,具體如下:
//頭部引入css打包插件
const ExtractTextPlugin = require("extract-text-webpack-plugin");
//聲明對應的loaders
loaders: [{
test: /\.css$/,
//請注意loader裏的寫法,有一些低版本的例子中是過時的寫法
loader: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}],
plugins: [
...
//這樣會定義,所有js文件中通過require引入的css都會被打包成相應文件名字的css
new ExtractTextPlugin("[name].css"),
],
注意,以上loaders和plugins中都必須聲明,缺一不可
問題五:ExtractTextPlugin 插件loader寫法不對導致報錯
説明:最初在引用ExtractTextPlugin 插件時,使用瞭如下寫法,導致了報錯
loaders: [{
test: /\.css$/,
loader: ExtractTextPlugin.extract(["style-loader","css-loader"])
}],
解決:原因是這種寫法已經過時,根據命令台中的提示,修改為最新寫法即可:
loaders: [{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}],
問題六:在js中直接require(html)不會輸出成html
説明:在使用webpack時,習慣性的用了萬物皆模塊這個概念,於是在js中引入html,如下:
//***.js中
require('./index.html')
...
結果是並不會生成一個index.html的文件,而是會將html代碼嵌入到js中,作為模塊代碼而存在。
解決: 一般webpack項目中,會用htmlPagePluginConfig插件來引入html(這時候會給每一個html制定一個入口js文件),然後接下來所有操作都可以從這個入口js文件中進行(比如引入css,比如邏輯操作)
需要注意的是,這個插件聲明html時,對應引入的js路徑一定要對,如:
{
template: 'pages/index.html',
// 如果是隻聲明index,由於路徑不對,則不會自動插入
chunks: ['js/index'],
}
問題七:html中的img資源不會被自動替換
説明:開發過程中使用的htmlPagePluginConfig插件加載html,但是發現構建過程中,html內部引入的img等標籤不會被自動替換。
解決:需要引入html-loader插件,有了這個插件後,引入,默認會將html內部的img自動替換掉
問題八:在html中link(css)後,不會替換對應的css,或者是會報錯
説明:正常開發是所有css都是在js中引入的,但是在某一次嘗試中,準備嘗試在html裏用原始的link方式引入css,並引入了html-loader,如下:
<meta charset="utf-8" />
<link rel="stylesheet" href="test.css" />
loaders: [{
test: /\.html$/,
use: [{
loader: 'html-loader',
options: {
minimize: false,
}
}],
結果按上述的方法進行後發現對於的html裏的css路徑沒有替換,這時發現html-loader默認是不會替換css,於是又做如下改動
loaders: [{
test: /\.html$/,
use: [{
loader: 'html-loader',
options: {
minimize: false,
//開啓link的替換
attrs: ['img:src', 'link:href']
}
}],
但是採用上述配置後,提示:Error: "extract-text-webpack-plugin" loader is used without the corresponding plugin
解決:webpack中ExtractTextPlugin需要配合js模塊化引入css使用。
如果想要實現html內部linkcss,目前沒有找到完美的解決方法,只知道使用webpack構建項目時,css都通過js require引入就不會有錯了
問題九:css-loader中,.min.css重複壓縮會報錯
説明:在項目中引入第三方lib文件後,發現,如果引入的文件本身已經壓縮過了(如min.css文件),這時候再採用壓縮配置css-loader?minimize,則會報錯
解決:採用了正在表達式進行了過濾,含有.min.css的文件不會壓縮,其它css則會壓縮,如下:
loaders: [{
//20170314更新:以下是錯誤寫法,比如common.css也無法匹配的
//test: /[^((?!\.min\.css).)*$]\.css$/,
//以下是正確寫法
test: /^((?!\.min\.css).)*\.css/,
loader: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader?minimize&-autoprefixer"
})
}, {
test: /\.min\.css$/,
loader: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}],
20170320更新
最終發現重複壓縮錯誤的原因是因為.min.css文件中包含如下代碼:
background-image: url('data:image/svg+xml;charset=utf-8,<svg viewBox=\'0 0 120 120\' xmlns=\'http://www.w3.org/2000/svg\' xmlns:xlink=\'http://www.w3.org/1999/xlink\'><defs><line id=\'l\' x1=\'60\' x2=\'60\' y1=\'7\' y2=\'27\' stroke=\'%236c6c6c\' stroke-width=\'11\' stroke-linecap=\'round\'/></defs><g><use xlink:href=\'%23l\' opacity=\'.27\'/><use xlink:href=\'%23l\' opacity=\'.27\' transform=\'rotate(30 60,60)\'/><use xlink:href=\'%23l\' opacity=\'.27\' transform=\'rotate(60 60,60)\'/><use xlink:href=\'%23l\' opacity=\'.27\' transform=\'rotate(90 60,60)\'/><use xlink:href=\'%23l\' opacity=\'.27\' transform=\'rotate(120 60,60)\'/><use xlink:href=\'%23l\' opacity=\'.27\' transform=\'rotate(150 60,60)\'/><use xlink:href=\'%23l\' opacity=\'.37\' transform=\'rotate(180 60,60)\'/><use xlink:href=\'%23l\' opacity=\'.46\' transform=\'rotate(210 60,60)\'/><use xlink:href=\'%23l\' opacity=\'.56\' transform=\'rotate(240 60,60)\'/><use xlink:href=\'%23l\' opacity=\'.66\' transform=\'rotate(270 60,60)\'/><use xlink:href=\'%23l\' opacity=\'.75\' transform=\'rotate(300 60,60)\'/><use xlink:href=\'%23l\' opacity=\'.85\' transform=\'rotate(330 60,60)\'/></g></svg>');
所以包含這種代碼的css使用css-loader的minify時會報錯,解決方法是暫時將這類的css單獨放入文件中,並且不進行minify
問題十:css中和html中的img路徑引用出錯,不符合預期
説明:在將項目劃分目錄結構後,html內和css內都有引用img或font,採用html-loader替換html內部資源,之後發現構建出來後的文件中,img和font等資源的引用路徑不對。如:
本來應該是路徑 ../img.***.img
結果直接就變成了 img/***.img
導致路徑不一致
解決:默認打包路徑無法替換為相對路徑。這時候需要引入publicPath這個變量,將所有的資源用全局路徑替換,如:
output: {
// 用來解決css中的路徑引用問題
publicPath: config.publicPath,
},
以上publicPath由開發者自身根據實際情況進行配置,一般是指向項目的發佈路徑根目錄,比如http://localhost:8080/dist/,可以自由切換開發模式和發佈模式
問題十一:webpack-dev-serve配置錯誤帶來的問題
説明:在使用webpack-dev-serve時遇到了如下問題:
one:
- helloworld項目時,
webpack-dev-server --port 8082即可開啓服務,並且自動監聽進行刷新(並且支持iframe刷新與inline刷新) - 但是在config的output中加入了
publicPath後,默認的服務端配置,啓動後不會自動監聽進行刷新 - 問題最終分析出了,是因為
devServer和構建的路徑outputPath不一致導致的,如
Project is running at http://localhost:8082/
webpack output is served from http://localhost:8080/dist/
- 解決方法為:增加devServer的配置,自動讀取publicPath,如下:(默認用了iframe刷新)
//dev版才有serve
devServer: {
historyApiFallback: true,
hot: false,
//不使用inline模式,inline模式一般用於單頁面應用開發,會自動將socket注入到頁面代碼中
inline: false,
//content-base就是 codeResource -需要監聽源碼
contentBase: path.resolve(__dirname, config.codeResource),
watchContentBase: true,
// 默認的服務器訪問路徑,這裏和配置中一致,需要提取成純粹的host:ip
public: /https?:\/\/([^\/]+?)\//.exec(config.publicPath)[1]
},
two:
- 在運行devServer時,將contentBase指向了發佈目錄,如
contentBase:"dist",但是卻發現修改dist裏的任何文件,服務端都不會刷新 - 最終發現是功能理解錯誤,在啓動devServer的命令中,並不會構建出dist,也就是説,它並不是訪問的dist目錄下的文件,而是基於源碼src直接發佈到服務端的
- 解決方法,將contentBase指向src目錄,修改src目錄下的文件就可以看到自動刷新了(一定要注意,server用到的不是dist下的文件)
three:
- 使用devServe時,開啓了
inline和hot,但是熱更新無效 - 原因是,沒有引入這個插件
HotModuleReplacementPlugin,需要如下聲明
new webpack.HotModuleReplacementPlugin()
four:
- 熱更新模式下,提示
ERR_NAME_NOT_RESOLVED,不能實時刷新,一直都無法找到原因 - 説實話,正常情況下不會遇到這個問題,一般遇到這個問題請檢查是否版本號不對應,也可去github上找issue
- 但是,我這裏僅僅是因為自己設置devServe時的public路徑設置錯誤了
//正常設置
localhost:8080
//我的設置
http://localhost:8080
- 從而導致了提示
Project is running at http://http://localhost:8080/,所以只需要改成正常配置即可 - 另外需要注意的是,如果想要
iframe刷新,inline和hot都要是false
five:(20170322更新)
webpack-dev-server默認只能在localhost訪問,換為內網ip就不行了(比如192.×××,就算是本機也不行的)- 解決: 修改server的默認配置,如
//這個配置可以運行其它機器訪問
host: '0.0.0.0',
//或者運行時
-- host 0.0.0.0
問題十二:hash與chunkhash造成的問題
説明:項目發佈時,為了解決緩存,需要進行md5簽名,這時候就需要用到hash和chunkhash等了,但是卻遇到了如下問題:(hash系列問題的解決大多參考了參考來源中的博客)
one:(hash問題)
- 使用
hash對js和css進行簽名時,每一次hash值都不一樣,導致無法利用緩存 - 原因是因為,
hash字段是根據每次編譯compilation的內容計算所得,也可以理解為項目總體文件的hash值,而不是針對每個具體文件的。(所以每一次編譯都會有一個新的hash,並不適用) - 解決:不用hash,而用
chunkhash(js和css要使用chunkhash),chunkhash的話每一個js的模塊對應的值是不同的(根據js裏的不同內容進行生成)
two:(img的chunkhash問題)
- 前面有提到,hash在js和css中不實用,所以在項目中所有的文件都準備用
chunkhash,但是又有了新的問題-img和font等資源中,使用chunkhash會報錯 - 解決:因為
chunkhash只適用於js和css,img中是沒有這種東西的,仍然需要用到hash(這個hash有點區別,每一個資源本身有自己的hash)
three:(chunkhash重複問題)
- 打包時發現,js和js引入的css的
chunkhash是相同的,導致無法區分css和js的更新,如下
index2-ddcf83c3b574d7c94a42.css
index2-ddcf83c3b574d7c94a42.js
- 原因是因為webpack的編譯理念,webpack將css視為js的一部分,所以在計算chunkhash時,會把所有的js代碼和css代碼混合在一起計算
*解決:css是使用ExtractTextPlugin插件引入的,這時候可以使用到這個插件提供的contenthash,如下(使用後css就有獨立於js外的指紋了),
new ExtractTextPlugin("[name]-[contenthash].css")
- 需要注意的是,在新版本中,親自測試過,修改css的內容並不會引起js中的
chunkhash變動(原因估計是webpack內置的算法變為了只計算js chunk),所以css請務必使用contenthash,否則修改後無法生成新的簽名,而是會覆蓋以前的資源
問題十三:提示UNMET PEER DEPENDENCY node-sass,依賴包安裝失敗
説明:剛開始試了下,sass的編譯,裏面有引入node-sass這個依賴包,之後基於這個出現了一些問題
one:(安裝node-sass失敗)
- 在用
sass時,依賴了這個包,但是用npm安裝時,安裝失敗 - 原因是這個包放在github上的,導致裝不下了
- 解決:使用淘寶鏡像安裝或者使用淘寶的
cnpm安裝,安裝cnpm如下
npm install -g cnpm --registry=https://registry.npm.taobao.org
two:(node-sass引起的其它安裝包安裝失敗)
- 使用node.js安裝某個依賴包時,提示UNMET PEER DEPENDENCY node-sass,而且關鍵是這個包本身不會依賴於它,提示如下:
+-- UNMET PEER DEPENDENCY node-sass@^4.0.0
-- webpack-dev-server@2.4.1 extraneous
- 原因是,以前這個項目中曾經有安裝過node-sass,但是安裝失敗了,導致node_modules裏一直記錄了這個任務,後續安裝時都會先嚐試去安裝它。
- 解決:去node_modules目錄下,刪除與node-sass這個依賴包的相關內容(可以全局搜索),重新安裝即可
問題十四:sourceMap配置帶來的問題
説明:在使用sourceMap時,遇到了以下問題
one:(uglify壓縮去掉了sourceMap)
- 在使用sourceMap時,由於用到了uglify壓縮插件,所以默認去除了js尾部的註釋,導致無法找到map文件
- 解決: uglify插件加上如下配置
sourceMap:true,
- 另外config裏的output可以配置
sourceMapFilename:'maps/[name].map',將map文件放入maps文件夾中
問題十五:htmlPagePluginConfig配置帶來的問題
20170318更新
説明: 同時引入了html-loader和html-webpack-plugin後,兩個插件都設置了minify屬性,則會編譯生成時報錯,錯誤配置如下:
loader: 'html-loader',
options: {
minimize: config.isRelease ? true : false,
}
new HtmlWebpackPlugin({
***
minify: config.isRelease ? {
collapseWhitespace: true,
collapseBooleanAttributes: true,
removeComments: true,
removeEmptyAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
minifyJS: true,
minifyCSS: true
}: null,
})
解決:只需要將HtmlWebpackPlugin中對應的minify屬性去掉即可。
問題十六:webpack中JS手動引入的圖片問題
20170318更新
説明: webpack是萬物皆模塊,但也就是説,不通過require引入的就不會算成模塊了(插件中的另算,那是處理過的)。所以,在JS中手動引入圖片時會遇到問題就是對應的圖片並不會被打包,導致之後找不到路徑。如下:
var GalleryData = [{
id: "testgallery1",
title: "",
//為空
//保持目錄結構
url: "../../static/img/gallery/img_testgallery1.jpg"
},
{
id: "testgallery2",
title: "",
//為空
url: "../../static/img/gallery/img_testgallery2.jpg"
}];
以上的url就是引入的源碼本地圖片,但是卻發現並不會被打包出來。
解決:
- 將以上的
static文件夾作為靜態資源,用copy-webpack-plugin插件提取出來(這時候需要遵守的一個約定就是static文件夾下的是專門給js引入或者外部資源訪問的,平時正常的css,html中的引入請放入其它文件夾中,比如img,避免相互影響,這就是約定大於配置) - 或者,通過require引入圖片後再設置,如下:(但是這樣會破壞代碼結構,個人並不建議)
var imgUrl = require('./images/bg.jpg'),
imgTempl = '';
示例Demo
本次進行webpack學習時。依次安裝功能遞增,循序漸進的寫了多個demo(每一個均可正常運行),每一個demo都有自身的READEME.MD説明,目錄結構如下;
├── 01helloWorld # 入門hellworld,一個html,一個js,一個css,css默認嵌入在js中,html採用`HtmlWebpackPlugin`加載
├
├── 02helloWorld2 # 基於第1個進行拓展,css使用`ExtractTextPlugin`單獨打包成一個文件
├
├── 03pageWithSingleJsAndCss # 基於第2個進行拓展,示例頁面由一個變為多個,並且抽取了通用配置文件`common.config`
├
├── 04pageWithStaticResource # 基於第3個進行拓展,增加了`html-loader`替換靜態資源,解決了css重複壓縮報錯問題,使用`publicPath`,解決資源文件引用路徑問題,增加了`webpack-dev-server`配置
├
├── 05withCommonChunk # 基於第4個進行拓展,增加了`CommonsChunkPlugin`提取公告js和css,增加了`UglifyJsPlugin`,修改了一些配置,更好應用於項目
├
├── 06withHashStaticAndRelease # 基於第5個進行拓展,增加了`CopyWebpackPlugin`複製靜態資源,增加了`chunkhash`,`contenthash`等指紋簽名功能,增加了`alias`別名設置,增加了release版本和dev版本的開關
├
├── 07withLocalServer # 基於第6個進行拓展,增加了一個`api-server`,來寫本地測試接口(已經進行了跨域配置)
├
└── 08withFamilyBucket # 基於第7個進行拓展,webpack全家桶項目,增加了`source-map`,增加了`assets-webpack-plugin`等等
源碼地址:
系列demo的源碼地址是: https://github.com/dailc/webpack-freshmanual
附錄
參考資料
- http://www.jianshu.com/p/42e11515c10f
- Webpack中hash與chunkhash的區別
博客
初次發佈2017.03.13於個人博客
- http://www.dailichun.com/2017/03/13/webpackfreshmanualAndBug.html