常言道授人以鱼不如授人以渔,当开发中某些loader满足不了我们特有的需求时,与其去网上搜罗,不如自己动手写个loader。
假如我们需实现这一个需求,对于html中的每个img标签,假设img标签中都没有alt属性(为了简单),统一给他们加上alt属性,并赋予相同的值,值由option配置。
为了实现该需求,首先创建index.js
, index.html
,webpack.config.js
。
1.index.js
比较简单,只需import
引入index.html
即可:
2.index.html中,只需添加多个img 标签即可:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>This is a test page</title> </head> <body> <div class="wrapper"> <img src="./123.png" /> </div> <img src="./123.png" /> </body> </html>
|
3.webpack配置文件如下,webpack.config.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| const path = require('path'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = { entry: './src/index.js', devtool: 'false', plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'Myself loader' }) ], output: { chunkFilename: '[name].js', filename: '[name].js', path: path.resolve(__dirname, 'dist'), }, module: { rules: [ { test: /\.html$/, use: [ { loader: 'html-loader', options: { minimize: false } }, { loader: path.resolve(__dirname, './img-alt-loader/index.js'), options: { title: 'this is img tag' } } ] }, { test: /\.(png|jpg|gif|jpeg|svg)$/, use: { loader: 'url-loader', options: { limit: 1 } } }, ] }, }
|
下面开始创建loader,从上面webpack配置文件可以看出,我们在当前根目录下新建一个img-alt-loader
文件夹,并在该文件夹下创建index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
const loaderUtils = require('loader-utils'); const validateOptions = require('schema-utils');
const attrParse = require('./attributesParser'); const schema = require('./option.json');
module.exports = function loader(content) { this.cacheable && this.cacheable();
const options = loaderUtils.getOptions(this) || {}; validateOptions(schema, options, 'img alt loader');
const altTitle = options.title || 'default img'; const attributes = ['img:src'];
const links = attrParse(content, function(tag, attr) { return attributes.find(function(ar) { return (tag + ":" + attr) === ar; }); });
links.reverse(); content = [content];
links.forEach(function(link) { var c = content.pop(); content.push(c.substr(link.start + link.length + 1)); content.push(` alt="${altTitle}"`); content.push(c.substr(0, link.start + link.length + 1)); });
content.reverse(); content = content.join(''); return content; }
|
options.json 中只需check title
类型是string
1 2 3 4 5 6 7 8 9
| { "type": "object", "properties": { "title": { "type": "string" } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| var Parser = require("fastparse"); var processMatch = function(match, strUntilValue, name, value, index) { if(!this.isRelevantTagAttr(this.currentTag, name)) return; this.results.push({ start: index + strUntilValue.length, length: value.length, value: value }); };
var parser = new Parser({ outside: { "<!--.*?-->": true, "<![CDATA[.*?]]>": true, "<[!\\?].*?>": true, "<\/[^>]+>": true, "<([a-zA-Z\\-:]+)\\s*": function(match, tagName) { this.currentTag = tagName; return "inside"; } }, inside: { "\\s+": true, ">": "outside", "(([0-9a-zA-Z\\-:]+)\\s*=\\s*\")([^\"]*)\"": processMatch, "(([0-9a-zA-Z\\-:]+)\\s*=\\s*\')([^\']*)\'": processMatch, "(([0-9a-zA-Z\\-:]+)\\s*=\\s*)([^\\s>]+)": processMatch } });
module.exports = function parse(html, isRelevantTagAttr) { return parser.parse("outside", html, { currentTag: null, results: [], isRelevantTagAttr: isRelevantTagAttr }).results; };
|
目录结构如下:
1 2 3 4 5 6 7 8 9
| |-- project |-- src |-- index.js |-- index.html |-- img-alt-loader |-- index.js |-- option.json |-- attributesParser.js |-- webpack.config.js
|
使用webpack 开发模式打包--mode development
,可以看出如下结果,img标签上都添加了alt="this is img tag"
属性。
由于nodejs是单线程操作的,所以对于某些异步操作,尽量把loader设为async loader,这样就不影响后续的操作并加快了打包速度。
img-alt-loader可能没有什么实际意义,但对于如何自己写loader提供了入门。O(∩_∩)O