file-loader引起的html-webpack-plugin坑


引言

最近,我們的一個后台系統要改版為基於react + redux + react-router + ant-design 技術棧,切換到當下比較新的技術來實現后台系統;在項目實施過程中,選擇了基於react的ant-design組件庫,為螞蟻金服出品。使用該UI組件庫,該團隊推薦使用其為其配套的內部定制化的構建工具atool-build

推薦使用atool-build工具,因為該工具內部對webpack的一些基本配置內容都做了底層配置,其實atool-build內部定制化了一些基本的配置;

這樣,開發者就不在需要一個一個的來配置這些基本的、一般都會用上的配置項,提升了項目構建效率(當然,也可以不使用其推薦的atool-build工具,組件來配置webpack基本配置項);話說回來,雖然atool-build內部定制化了一些基本配置,但是可能不滿足開發者的需求,那么我可以在項目根目錄中添加webpack.config.js來覆蓋該工具內部的一些不滿足要求的定制化配置。

本文所說的就是是atool-build定制化的一個配置引起的,下面就描述一下這個問題。

這個坑是由於atool-build內部為后綴為.html文件配置了file-loader導致,具體配置如下:

{ test: /\.html?$/, loader: 'file?name=[name].[ext]' }

這個module配置項的意思大家都清楚,即js或者jsx文件引入的html文件,會被抽取為單獨的html文件,相對於webpack中output配置項的path路徑,file-loader具體的用法可以參考這里

於是否,在webpack中配置一下html-webpack-plugin插件信息,代碼如下:

new HtmlWebpackPlugin({
          title:'rd平台',
          template: 'entries/index.html', // 源模板文件
          filename: './index.html', // 輸出文件【注意:這里的根路徑是module.exports.output.path】
          showErrors: true,
          inject: 'body',
          chunks: ["common",'index']
      })

其中,entries/index.html文件的內容如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>RD工具平台</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

然后在應用的入口的js文件中import ./index.html,最后使用npm run build調用atool-build來構建前端代碼,生成的index.html出現問題,里面的內容變成:

<head>
    <link href="index-9b9df6507771a310f270.css" rel="stylesheet">
</head>index.html
<script type="text/javascript" src="common-1442be5d0c23365a109d.js"></script>
<script type="text/javascript" src="index-9b9df6507771a310f270.js"></script>

根據生成的index.html內容可以看出html-webpack-plugin沒有找到指定的模板文件entries/index.html,但是目錄結構中確實是有這個文件的。

因此在這種情況下,該插件生成的html內容就是在head中插入抽取的css文件,以及在body中插入入口js文件,其html文檔內容為指定模板的文件名index.html;

為啥html-webpack-plugin會出現找不到指定模板文件的情況,而該文件確實是存在的???

百思不得其解,折騰大半天后發現,原來是內部配置的file-loader導致的問題;具體原因,筆者猜測:

可能是因為file-loader將導入的index.html文件**移動到**指定輸出位置后導致`html-webpack-plugin`找不到原有位置的模板文件,從而生成上述情況的index.html文件內容。

於是否,就想到覆蓋atool-build內置的file-loader配置,對后綴為.html的文件修改成使用html-loader作為loader,在webpack.config.js中覆蓋原有指定配置項具體的代碼如下:

if(loader.test.toString() === '/\\.html?$/'){
        loader.loader = 'html';
    }

然后,在應用的入口js文件中remove掉import './index.html',最后使用atool-build編譯構建代碼后發現,生成的index.html是期望中的效果。

由此可以說明,file-loader的使用導致了html-webpack-plugin出現了問題。

file-loader與html-loader區別

二者作為loader,其區別還是挺明顯的。下面就簡述一下二者的區別。

file-loader

file-laoder是對require或者import的指定文件(比如A.html)進行抽取(上面例子中從js中抽取index.html)。

在這一個過程中,抽取文件可以指定具體的目錄信息文件名稱hash信息后綴信息等等,導入(A.html)的文件在構建編譯后不會有該文件A.html的任何痕跡,因為文件被抽取了。

比如,可以將上面index.html文件指定到某個目錄下如html目錄下,那么file-loader配置中可以這樣寫:

{ test: /\.html?$/, loader: 'file?name=html/[name].[ext]' }

具體的其他配置參考一下其官網給出的例子:

require("file?name=js/[hash].script.[ext]!./javascript.js");
// => js/0dcbbaa701328a3c262cfd45869e351f.script.js

require("file?name=html-[hash:6].html!./page.html");
// => html-109fa8.html

require("file?name=[hash]!./flash.txt");
// => c31e9820c001c9c4a86bce33ce43b679

require("file?name=[sha512:hash:base64:7].[ext]!./image.png");
// => gdyb21L.png
// use sha512 hash instead of md5 and with only 7 chars of base64

require("file?name=img-[sha512:hash:base64:7].[ext]!./image.jpg");
// => img-VqzT5ZC.jpg
// use custom name, sha512 hash instead of md5 and with only 7 chars of base64

require("file?name=picture.png!./myself.png");
// => picture.png

require("file?name=[path][name].[ext]?[hash]!./dir/file.png")
// => dir/file.png?e43b20c069c4a01867c31e98cbce33c9

html-loader

html-loader是將require或者import的html文件轉換為html字符串並導出。

在這一過程中,在導出HTML字符串之前,會將html內容中指定元素的一些屬性按照attrs=<tag>:<attribute>形式進行更改,一般是一些外部資源的鏈接替換,默認attrs=img:src

例如,對上面介紹的index.html增加一個img和一個a元素,其內容入下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>RD工具平台</title>
</head>
<body>
<img src="./blue.png"/>
<a href="./blue.png">blue.png</a>
<div id="app"></div>
</body>
</html>

然后對html-loader的配置項配置html文件中的imgsrc屬性和a元素的href屬性進行變更,其具體的配置如下

 //對圖片應用url-loader,其中圖片小於100byte會使用base64形式,否則抽取一個圖片文件
{ test: /\.(png|jpg|jpeg|gif)(\?v=\d+\.\d+\.\d+)?$/i, loader: 'url?limit=100' }

if(loader.test.toString() === '/\\.html?$/'){
        loader.loader = 'html?attrs[]=img:src&attrs[]=a:href'; //配置img的src屬性和a的href屬性
    }

這樣配置編譯后生成的index.html內容如下:

<!DOCTYPE html>
<html lang=en>

<head>
    <meta charset=UTF-8>
    <title>RD工具平台</title>
    <link href="index-245b9c326ea3ec1b2089.css" rel="stylesheet">
</head>

<body> <img src=fd0ae22733627f001267a408ec1581ea.png /> <a href=fd0ae22733627f001267a408ec1581ea.png>blue.png</a>
    <div id=app></div>
    <script type="text/javascript" src="common-56bf69cceda96b513b37.js"></script>
    <script type="text/javascript" src="index-245b9c326ea3ec1b2089.js"></script>
</body>

</html>

另外,注意一點:

與file-loader不同的是,html-loader會將導入的html內容字符串作為js中一個模塊而存在,模塊內容掛在module.exports下面。

例如在index.js文件中import './index.html'后,經編譯后index.js中該部分的代碼如下:

/* 445 */
/***/ function(module, exports, __webpack_require__) {

	module.exports = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>RD工具平台</title>\n</head>\n<body>\n<img src=\"" + __webpack_require__(249) + "\"/>\n<a href=\"" + __webpack_require__(249) + "\">blue.png</a>\n<div id=\"app\"></div>\n</body>\n</html>\n";

/***/ },
/* 446 */

由此可以看出:

  • html-loader將html文件作為js的一個模塊,模塊向外提供編譯后的html文件內容字符串,其實可以將其看成一個輸出字符串的模塊而已。

  • html中的外部鏈接,如上面的img的src和a的href在html內容字符串所在的模塊內部,其實是通過webpack的require形式來加載的,盡管圖片是在html文件中通過img形式引入。如上面代碼中的__webpack_require__(249),這些require是需要對應的loader來處理的,上面的.png是用url-loader來處理的

由於本人知識有限,文章有什么不正確的地方,請各位批評指正,謝謝!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM