使用webpack对原有web工程进行改造,遇到各种问题,适当地使用各种loader加以解决。感觉有些问题具备普遍性,还是有必要记录一下的。故有此文!
在webpack中对javascript的管理也是模块化管理,所以需要使用require和exports或者define来对js模块进行引用或者导出。
当你的前端web工程没有采用js模块化编码,那么就会遇到一些问题。问题的根源分为两种:js的调用者,没有require;js的提供者,没有exports。
此时,你一个个文件去修改,按规范添加require和exports,当然可以解决这些问题。你也可以使用webpack提供的各种loader去解决这些问题。
下面,我们分情况讨论解决。
一、和jQuery相关的那些事儿
jQuery本身并没有问题,自身代码里兼容了exports和define方法。
只是,很多依赖jQuery模块的js文件,都没有require('jquery'),导致文件内的jQuery对象不可识别。
比如Bootstrap v3.3.2,走上来,就是如下代码:
if (typeof jQuery === 'undefined') {
throw new Error('Bootstrap\'s JavaScript requires jQuery')
}
作为模块化调用,当然无法识别jQuery了。
解决方法有三个:
1. 方法一:采用import-loader
1.1 解决方法
在加载bootstrap.js时,使用imports-loader,
代码如下:
require('imports?jQuery=jquery!bootstrap/dist/js/bootstrap');
好了,可以正常使用bootstrap.js了!
1.2 原理
imports-loader会在bootstrap的源码前面,注入如下代码:
/*** IMPORTS FROM imports-loader ***/
var jQuery = require("jquery");
1.3 使用说明:
| 查询参数 | 等效代码 |
|---|---|
angular |
var angular = require('angular'); |
$=jquery |
var $ = require('jquery'); |
config=>{size:50} |
var config = {size:50}; |
this=>window |
(function(){...}).call(window); |
支持一次多个查询参数,之间通过逗号分隔,比如:
require("imports?$=jquery,jQuery=jquery,angular,config=>{size:50}!./file.js");
2. 方法二:采用expose-loader
expose-loader的思路是将某个对象暴露成一个全局变量。
具体到这个问题中,就是把jQuery对象暴露成全局变量。这样,那些bootstrap.js之类的文件就都能访问这个变量了。
具体做法,修改webpack.config.js文件:
module: {
loaders: [
{ test: require.resolve("jquery"), loader: "expose?$!expose?jQuery" },
]
}
3. 方法三:使用webpack.ProvidePlugin
具体做法,修改webpack.config.js文件:
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
]
4. 小节
从上面的介绍可以看出,三种解决方法的思路分两类:方法一,是修改调用者;方法二、三是修改提供者。
二、自定义的js文件
这里问题,属于模块提供方没有调用export导出模块,或者没有define定义模块。
好办,使用exports-loader!这个作用和前面提到的imports-loader类似,他是在文件的最后添加一行,类似如下的代码:
/*** EXPORTS FROM exports-loader ***/
module.exports = YouModule
同样,如果需要一次导出多个对象,如同imports-loader调用,多个对象间也是逗号分隔。比如:
require("exports?file,parse=helpers.parse!./file.js");
那么,./file.js文件的最后会添加如下代码:
/*** EXPORTS FROM exports-loader ***/
exports["file"] = (file);
exports["parse"] = (helpers.parse);
三、稍微复杂一点的情况
自定义的多个js模块直接存在相互调用的情况。
比如,b.js里面调用了a.js提供的对象,但是都没有exports或者require。见示例:
文件: a.js
var A = { ... };
文件: b.js
A.exec(....)
解决办法:
require([
'expose?A!exports?A!./a.js',
'./b.js'
], ...);
如上,把a.js中的A定义成全景对象公布出去,这样b.js里面就可以正常使用A对象了。
除此之外,还有一个解决方案(不推荐):使用script-loader,它可以在全局环境下执行一次指定的脚本,比如:
require([
'script!./a.js',
'./b.js'
], ...);
这里,会把./a.js直接作为脚本执行一次。在node.js环境下,script-loader什么都不做。
四、其他
-
file-loader: 修改文件名,放在输出目录下,并返其对应的 url .默认修改后的文件名,是文件内容的MD5哈希串。你也可以自定义文件名。比如:
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 -
url-loader: 这个加载器的工作方式很像file-loader。只是当文件大小小于限制值时,它可以返回一个Data Url。限制值可以作为查询参数传入。默认不限制。比如:require("url?limit=10000!./file.png"); // => 如果"file.png"小于10kb,则转成一个DataUrl require("url?mimetype=image/png!./file.png"); // => 指定文件的mimetype (不指定,则根据文件后缀推测.) -
style-loader、css-loader: 这个很常用了,不解释。推荐用法是:require("style!css!./file.css");,类似的还有less-loader,推荐使用:require("style!css!less!./file.less");; -
raw-loader: 把文件内容作为字符串返回。var fileContent = require('raw!./file.txt');这里,把
./file.txt的内容作为字符串返回。 -
html-loader: 把Html文件输出成字符串。与raw-loader不同的是,它默认处理html中的<img src="image.png">为require("./image.png"),你同时需要在你的配置中指定image文件的加载器,比如:url-loader或者file-loader。-
你可以通过加载器的查询参数
attrs来指定哪些html标签可以被处理。比如:attrs=img:src。html-loader默认只处理图片,你也可以通过attrs,告诉他也处理javascript文件,比如:require("html?attrs=attrs=script:src img:src!./file.html");或者
require("html?attrs[]=script:src&attrs[]=img:src!./file.html");多个标签用空格分隔或者使用数组设定。
-
你也可以告诉加载器,什么都不转换,包括默认处理的
image。如下设置:require("html?attrs=false!./file.html");或者
require("html?-attrs!./file.html"); -
当使用
webpack --optimize-minimize时,html-loader会对加载的html内容最小化。
-
-
source-map-loader: 方便调试,不解释。module.exports = { module: { preLoaders: [ { test: /[\.-]min\.js$/, loader: "source-map-loader" } ] } };上例,是对后缀为
.min.js或者-min.js结尾的文件,做source-map操作。不过,你要确保min.js文件中按规范指定了//# sourceMappingURL=xxxx.map才行!
五、总结
我们讨论了常用的loader,包括:imports-loader、exports-loader、expose-loader、script-loader、file-loader、url-loader、css-loader、style-loader、raw-loader、html-loader、source-map-loader。
官网还提供了一些其他的loader,你也可以根据个性化需要,自定义一个loader。这里就不再继续讨论了。
更多webpack的知识可以从官网获取。网上也有一些不错的第三方入门的博文,比如下面两个: