您的位置:首页 > Web前端 > HTML

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

2016-11-03 23:49 821 查看

引言

最近,我们的一个后台系统要改版为基于
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文件中的img
src
属性和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
来处理的


由于本人知识有限,文章有什么不正确的地方,请各位批评指正,谢谢!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: