《写出自己的loader》

    写出一个loader,要求每个模块文件依赖于各不相同的敏感词汇json文件。

    1、node module:一个loader就是一个npm包,输出一个function;
    2、npm publish:发布npm包;
    3、loader API:通过在loader函数中使用可以调用loader的API;
    4、loader-utils:推荐使用 loader-utils 来处理loader被调用时传递进来的参数;
    5、从webpack2.0开始,webpack默认支持对json文件的解析,而不需要引入json-loader了;
    6、异步解析:根据项目实际需要,解析文件时可能需要依赖于其他文件,直到该文件异步读取完毕之后再对原文件做解析。

    自定义一个webpack loader是比较简单的事情,要使用到的API也很少,非常容易上手。
    我们知道,前端开发时数据和代码是要分开管理的,比如说我司前端用的是vm模版引擎,流程是后台java渲染数据之后再返回到前端,为了在本地开发时方便,我们的做法是在本地生成模拟的json数据,通过webpack loader引入渲染之后直接在本地展现出来。
    一般每个模块的数据类型和数据量都是各不相同的,所以把每个模块的数据分别保存到独立的json文件中进行管理是比较合理的做法。在这里我抛砖引玉,本次课程会讲单个数据文件的引入,在课程挑战中由读者独立思考并实现,如果存有疑问的话可以参照。

    在loader中,咱们可以通过关键词this访问当前执行环境的所有变量
    1、同步回调时,可以执行this.callback(),默认第一个参数是err错误信息(不报错时返回null),第二个参数是解析完模块后的返回结果,第三个参数是sourceMap(可选),在链式调用时可将sourceMap传给下一个loader;
    2、异步回调时,可以执行this.async(),参数同上;
    3、this.addDependency(filePath)可以把对应filePath的文件添加到webpack的依赖树,webpack可以监测它的文件变动并刷新(filePath要是绝对路径);
    4、this.resolve()可以解析处理文件路径;
    5、this.query:获取loader的配置选项。

    1、getOptions:检索被调用loader的配置选项;
    2、parseQuery:解析被调用loader的配置选项;
    3、stringifyRequest:将一个请求转化为非绝对路径的可被require或import的字符串;
    4、urlToRequest:将url转换成适合webpack环境的模块请求;
    5、interpolateName:自定义资源名称、hash等;
    6、getHashDigest:通过限制字符长度获取文件部分哈希值。

    先在github上创建一个test-loader仓库,

    在跳转的页面复制仓库链接,clone到本地(可能会clone不成功,需要事先配置好ssh key,也可以直接下载压缩包到本地)
    《写出自己的loader》 - 图1

    1. 'use strict';
    2. // loader-utils可以解析webpack配置文件中loader传入的参数
    3. const loaderUtils = require('loader-utils');
    4. module.exports = function(source) { // source是字符串,包含静态资源的文件内容
    5. // webpack2 默认使用缓存,启动webpack-dev-server时,只热更新被修改的模块
    6. // 如果你想要禁止缓存功能,只要传入fasle参数即可
    7. // this.cacheable(false);
    8. const params = loaderUtils.parseQuery(this.query);
    9. if (typeof params === "object" && params.signStr && typeof params.signStr === "string") {
    10. source = '<!-- ' + params.signStr + ' -->\n' + source;
    11. }
    12. return source;
    13. };

    做完这一步之后,咱们已经能够实现给html模块添加签名了,接着添加敏感词汇替换的功能。
    修改index.js如下:

    1. 'use strict';
    2. // loader-utils可以解析webpack配置文件中loader传入的参数
    3. const loaderUtils = require('loader-utils'),
    4. path = require('path'),
    5. module.exports = function(source) { // source是字符串,包含静态资源的文件内容
    6. // webpack2 默认使用缓存,启动webpack-dev-server时,只热更新被修改的模块
    7. // 如果你想要禁止缓存功能,只要传入fasle参数即可
    8. // this.cacheable(false);
    9. const params = loaderUtils.parseQuery(this.query),
    10. callback = this.async(); // 异步解析模块
    11. if (typeof params === "object") {
    12. // 添加个人签名
    13. if (params.signStr && typeof params.signStr === "string") {
    14. }
    15. // 自动替换掉敏感词汇
    16. if (params.dataPath && typeof params.dataPath === "string") {
    17. let dataPath = path.resolve(params.dataPath); // 转换为绝对路径
    18. this.addDependency(dataPath); // 添加依赖关系,当文件修改时会被webpack检测到
    19. // 异步读取敏感词汇的json文件
    20. fs.readFile(dataPath, 'utf-8', function(err, text) {
    21. if (err) {
    22. console.error('数据文件路径出错', params.dataPath, '找不到该文件');
    23. return callback(err, source);
    24. }
    25. let data = JSON.parse(text),
    26. regexRule = '(';
    27. for (let value in data) {
    28. regexRule += data[value] + '|';
    29. }
    30. regexRule = regexRule.slice(0, -1) + ')';
    31. let regex = new RegExp(regexRule, 'g'); // 正则替换
    32. source = source.replace(regex, '');
    33. callback(null, source); // 异步回调处理结果
    34. });
    35. } else {
    36. callback({
    37. error: 'dataPath is not legal'
    38. }, source);
    39. }
    40. // console.log(source);
    41. }
    42. };

    至此,自动替换敏感词汇的功能也已经实现了,咱们试着将它运行到项目中去,在运行到具体项目之前,咱们需要先把它发布到npm的包管理服务器上,可供所有人在线下载使用。

    写入以下代码:

    发布之前需要先登陆你的npm账号,运行npm adduser,输入你在npm官网上的账户—username、password、email,如果没有的话就新注册一个,完成之后运行npm whoami就可以看到你的名字了。点击发布

    1. npm publish

    提示发布失败,失败原因是没有权限发布名为test-loader的包,也就是这个包名字已经被别人占用了,上npm官网搜索显示确实已经存在这个包,咱们换个名字,把package.json的name属性修改一下:

    1. "name": "test-webpack-loader"

    再尝试发布一下

    1. npm publish

    咱们依然在lesson6的基础上做修改,把lesson6的src目录、app.jswebpack.config.jswebpack.entry.jswebpackDevServer.jscopy进来,初始化再安装依赖包

    再安装下咱们刚刚发布到npm的test-webpack-loader

    1. npm install test-webpack-loader --save-dev

    webpack.config.js需要修改下html文件的loader配置

    1. {
    2. test: /\.html$/,
    3. use: ['html-loader?interpolate=require', 'test-webpack-loader?signStr=CreatedByKingvid&dataPath=./src/words.json']
    4. }

    另外在body.html中加入一些敏感词汇

    1. <h1 class="body-title">this is body</h1>
    2. <i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
    3. <ul class="body-list">
    4. <li class="body-list-item" id="body-input">你可以使用BannerPlugin给你的每个打包文件加上你的签名<br>webpack教程<br>by kingvid</li>
    5. </ul>
    6. <button id="body-btn" class="btn">点我</button>
    7. <button id="pack-btn" class="btn">打包</button>
    8. <button id="getData-btn" class="btn">获取本地测试数据</button>
    9. <div id="mockData-con"></div>
    10. <h3>这里包含敏感词汇:比如说最便宜、销量第一等等等等</h3>

    运行node app.js,可以看到敏感词汇已经自动过滤了

    点击打包按钮,完成后查看index.html代码,签名以及敏感词汇过滤的功能都已经实现。
    《写出自己的loader》 - 图2

    注意不要给被处理模块中添加绝对路径,当项目被移动到别的位置时,会破坏webpack的hash解析,可以使用loaderUtilsstringifyRequest函数,它能将绝对路径转化为相对路径;

    总结