Webpack入門之遇到的那些坑,系列示例Demo

前言

2017/12/18/更新
已經有一段時間沒有更新webpack了,從如今的角度看,文章有一部份內容也是不夠成熟,後續仍是會繼續梳理更新的。css

網上關於webpack的教程已經數不勝數了,也無心再從新寫一篇複製文。可是實際操做過程當中,發現各類教程版本都不一致,有的教程已通過時了,有的教程模糊不清,所以仍是遇到了各類問題,所以特將自身實際操做過程當中遇到的問題記錄下來,並附上相應的示例demo,也但願能給他人帶來幫助!html

本文主要是記錄的一些遇到的問題以及提供了示例,若是想要看入門教程仍是去官網上或者參考參考資源中的連接。node

問題記錄較多,想要直接看示例Demo的請拉到最下方。webpack

另外,此文會持續更新。git

題綱

  • 遇到的那些問題
  • 示例,各階段的示例demo,配有README

遇到的那些問題

問題一:package.json註釋帶來的問題

說明:項目的package.json中,只要帶有註釋,必然編譯不能經過github

解決:禁止在package.json中註釋web

問題二:全局安裝帶來的問題

說明:在最開始作項目時,因爲本地已經全局安裝過了對應的依賴包,所以沒有再從新在本地安裝,致使運行時報錯,全局安裝版本與本地依賴的版本不一致致使。算法

解決:每個項目中,全部的依賴包均在本地安裝,依賴狀況經過npm install *** --save -dev注入package.json中,其中一些環境之外的依賴,好比第三方庫express等能夠經過npm install *** --save安裝。express

全部的依賴包都在本地安裝是一個好習慣,由於這樣能夠隨時打包帶走與從新裝箱。npm

問題三: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時,開啓了inlinehot,可是熱更新無效
  • 緣由是,沒有引入這個插件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

問題十二:hashchunkhash形成的問題

說明:項目發佈時,爲了解決緩存,須要進行md5簽名,這時候就須要用到hashchunkhash等了,可是卻遇到了以下問題:(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-loaderhtml-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 = '![]('+imgUrl+')';

示例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

附錄

參考資料

博客

初次發佈2017.03.13於我的博客

相關文章
相關標籤/搜索