同时,我们在 webpack.config.js 中使用这个 loader ,我们这里使用 resolveLoader 配置项,指定 loader 查找文件路径,这样我们使用 loader 时候可以直接指定 loader 的名字
const path = require('path') module.exports = { mode: 'development', entry: { main: 'https://www.jb51.net/article/src/index.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, resolveLoader: { // loader路径查找顺序从左往右 modules: ['node_modules', './'] }, module: { rules: [ { test: /\.js$/, use: 'syncLoader' } ] } }
接下来我们运行打包命令,可以看到命令行输出了source内容,也就是loader作用文件的内容。
接着我们改造我们的loader:
module.exports = function (source) { source += '升值加薪' return source }
我们再次运行打包命令,去观察打包后的代码:
这样,我们就实现了一个简单的loader,为我们的文件增加一条信息。 我们可以尝试在 loader 的函数里打印 this ,发现输出结果是非常长的一串内容, this 上有很多我们可以在 loader 中使用的有用信息,所以,对于 loader 的编写,一定不要使用箭头函数,那样会改变 this
的指向。
一般来说,我们会去使用官方推荐的 loader-utils 包去完成更加复杂的 loader 的编写
我们继续安装 loader-utils ,版本是 ^2.0.0
我们首先改造 webpack.config.js :
const path = require('path') module.exports = { mode: 'development', entry: { main: 'https://www.jb51.net/article/src/index.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, resolveLoader: { // loader路径查找顺序从左往右 modules: ['node_modules', './'] }, module: { rules: [ { test: /\.js$/, use: { loader: 'syncLoader', options: { message: '升值加薪' } } } ] } }
注意到,我们为我们的 loader 增加了 options 配置项,接下来在loader函数里使用loader-utils获取配置项内容,拼接内容,我们依然可以得到与之前一样的打包结果
// syncLoader.js const loaderUtils = require('loader-utils') module.exports = function (source) { const options = loaderUtils.getOptions(this) console.log(options) source += options.message // 可以传递更详细的信息 this.callback(null, source) }
这样,我们就完成了一个简单的同步 loader 的编写
2.1.2 如何实现一个异步 loader
和同步loader的编写方式非常相似,我们在根目录下建立一个asyncLoader.js的文件,内容如下:
const loaderUtils = require('loader-utils') module.exports = function (source) { const options = loaderUtils.getOptions(this) const asyncfunc = this.async() setTimeout(() => { source += '走上人生颠覆' asyncfunc(null, res) }, 200) }
注意这里的 this.async() ,用官方的话来说就是 Tells the loader-runner that the loader intends to call back asynchronously. Returns this.callback. 也就是让webpack知道这个loader是异步运行,返回的是和同步使用时一致的 this.callback
接下来我们修改webpack.config.js
const path = require('path') module.exports = { mode: 'development', entry: { main: 'https://www.jb51.net/article/src/index.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, resolveLoader: { // loader路径查找顺序从左往右 modules: ['node_modules', './'] }, module: { rules: [ { test: /\.js$/, use: [ { loader: 'syncLoader', options: { message: '走上人生巅峰' } }, { loader: 'asyncLoader' } ] } ] } }
注意loader执行顺序是从下网上的,所以首先为文本写入‘升值加薪',然后写入‘走上人生巅峰'
到此,我们简单介绍了如何手写一个 loader ,在实际项目中,可以考虑一部分公共的简单逻辑,可以通过编写一个 loader 来完成(比如国际化文本替换)
2.2 如何自己实现一个 plugin
plugin 通常是在 webpack 在打包的某个时间节点做一些操作,我们使用 plugin 的时候,一般都是 new Plugin() 这种形式使用,所以,首先应该明确的是, plugin 应该是一个类。
我们初始化一个与上一接实现loader时候一样的项目,根目录下创建一个 demo-webpack-plugin.js 的文件,我们首先在 webpack.config.js 中使用它