写一个webpack-loader-插件

DEMO Code 地址

Webpack Loader

loader 用于对模块的源代码进行转换。loader 可以使你在 import 或”加载”模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。

loader 是导出为一个函数的 node 模块。该函数在 loader 转换资源的时候调用。给定的函数将调用 loader API,并通过 this 上下文访问。

loader是一个node module,那么它的基本形式如下

1
2
3
module.exports = function(source) {
return source;
};

loader只能传入一个包含包含资源文件内容的字符串(source)
如果是同步loader,可以用 return 或者 this.callback(err, value…) 将代码返回
异步loader:在一个异步的模块中,回传时需要调用 Loader API 提供的回调方法 this.async 来获取 callback 函数:

1
2
3
4
5
6
7
module.exports = function(content, map, meta) {
var callback = this.async();
someAsyncOperation(content, function(err, result) {
if (err) return callback(err);
callback(null, result, map, meta);
});
};

txt-loader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const { getOptions } = require('loader-utils');
const validateOptions = require('schema-utils');

const schema = require('./options');

module.exports = function rawLoader(source) {
const options = getOptions(this) || {};

validateOptions(schema, options, 'Raw Loader');

const json = JSON.stringify(source)
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
return `module.exports = ${json}`;
};

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
let path=require("path");
let HtmlWebpackPlugin=require("html-webpack-plugin");
let ExtractTextWebpackPlugin=require("extract-text-webpack-plugin");
let CleanWebpackPlugin=require("clean-webpack-plugin");
let webpack=require("webpack");
let VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports={
entry:"./src/index.js",
output:{
filename:"bundle.[hash:4].js",
path:path.resolve("dist")
},
resolveLoader: {
modules: [
path.resolve(__dirname, 'loaders'),
'node_modules'
]
},
module:{
rules:[
{
test:/\.css$/,
use:ExtractTextWebpackPlugin.extract({
use:"css-loader",
publicPath:"../"
})
},
{
test:/\.less$/,
use:[
{loader:"style-loader"},
{loader:"css-loader"},
{loader:"less-loader"}
]
},
{
test:/\.(jpe?g|png|gif)$/,
use:[
{
loader:"url-loader",
options:{
limit:8192,
outputPath:"images/"
}
}
]
},
{
test:/\.(htm|html)$/,
use:"html-withimg-loader"
},
{
test:/\.(eot|ttf|woff|svg)$/,
use:"file-loader"
},
{
test:/\.js$/,
include:/src/,
exclude:/node_modules/,
use:{
loader:"babel-loader",
options:{
presets:["@babel/preset-env"]
}
}
},
{
test:/\.vue$/,
use:"vue-loader"
},
{
test: /\.txt$/,
use: {
loader:'txt-loader',
options:{

}
}
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:"./src/index.html",
hash:true
}),
new ExtractTextWebpackPlugin("css/style.css"),
new CleanWebpackPlugin("dist"),
new webpack.HotModuleReplacementPlugin(),
new VueLoaderPlugin()
],
devServer:{
contentBase:"./dist",
host:"localhost",
port:8888,
open:true,
hot:true
}
};

file.txt

1
2
3
//file.txt

123456
1
2
import txt from './file.txt';
console.log(txt) //123456

写一个webpack插件:

主要的步骤如下:

  • 编写一个JavaScript命名函数。
  • 在它的原型上定义一个apply方法。
  • 指定挂载的webpack事件钩子。
  • 处理webpack内部实例的特定数据。
    功能完成后调用webpack提供的回调。
    编写插件之前要理解compiler和compilation两个对象,以及webpack生命周期的各个阶段和钩子,plugin比loader强大,通过plugin你可以访问compliler和compilation过程,通过钩子拦截webpack的执行。

比如我们可以在构建读取txt文件内容生成到文件名到out.txt的文件中

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
var fs = require('fs');
var RawSource = require('webpack-core/lib/RawSource');

module.exports = TestPlugin;

function TestPlugin() {
this.imports = [];
}

TestPlugin.prototype.apply = function(compiler) {
var imports = this.imports;

compiler.plugin('this-compilation', function(compilation) {
compilation.plugin('normal-module-loader', function(loaderContext, module) {
loaderContext[__dirname] = function(file) {
if(imports.indexOf(file) === -1) {
imports.push(file);
}
};
});
});

compiler.plugin('after-compile', function(compilation, callback) {
var filename = 'out.txt';
var content = imports.map(function(file) {
return fs.readFileSync(file);
}).join('\n');
console.log(content);
compilation.assets[filename] = new RawSource(content);
callback();
});
};

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var MyPlugin = require('./plugin');

module.exports = {
entry: './index.js',
output: {
filename: 'out.js'
},
module: {
loaders: [
{
test: /\.txt$/,
loader: './loader',
}
]
},
plugins: [
new MyPlugin()
]
};

index.js

1
2
3
require('./sample.txt');
require('./sample2.txt');
require('./sample3.txt');

webpack loader—自己写一个按需加载插件

webpack 之 loader 和 plugin 简介

webpack doc