创建一个demo(make_loader)
- 新建一个文件,名字为make_loader
- 在make_loader目录下初始化项目:npm init -y
- 安装webpack、webpack-li:npm i webpack webpack-cli -D
- 在make_loader目录下新建src目录,写业务代码
- 在make_loader目录下新建一个loaders文件夹,用来书写loader代码
- 在make_loader目录下新建webpack.config.js配置文件
目录结构如下:
**
css
|--make_loader
|--node_modules
|--src
|--index.js
|--loaders
|--replaceLoader.js
|--package-lock.json
|--package.json
|--webpack.config.js
src/index.js中的业务代码:
js
console.log('hello xiaochengzi');
在loaders/replaceLoader.js文件里书写我们的loader代码:
js
module.exports = function(source) { // 这里不能用箭头函数
return source.replace('xiaochengzi', this.query.name);
}
- loader对外暴露的函数,切记不能使用箭头函数。因为在这个函数里,我们会用到this指向,webpack在调用loader的时候,会把this做一些变更。变更之后才能用this里的一些方法,如果写成箭头函数,这里的this指向就会有问题,所以这里的function一定是声明式的function
- 参数source为我们引入文件的源代码
- 在这里拿到代码之后,就可以把代码做一个变更,然后再返回出去
- 可以通过this.query取到传递的options内的一些参数
在webpack.config.js中做loader的使用配置:
js
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
module: {
rules: [{
test: /.js/,
use: [
{
loader: path.resolve(__dirname, './loaders/replaceLoader.js'),
options: {
name: 'world' // 传递给loader的参数
}
}
] // 使用自己写的loader模块
}]
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
- 在module中配置loader使用规则
- 在options中传递loader参数
执行打包:npm run build
打包后的文件为:hello world
由此可见,我们在业务代码中打印的 'hello xiaochengzi' 通过我们编写的loader替换成了 'hello world'。
到此,一个最最简单的loader就编写好了~
loader中常用的API
使用loader-utils分析参数:
js
// loader-utils模块需要单独使用npm下载安装
const loaderUtils = require('loader-utils');
module.exports = function(source) {
// 使用loader-utils中的getOptions接收loader参数
const options = loaderUtils.getOptions(this);
return source.replace('xiaochengzi', options.name);
}
callback的使用:
**
js
const loaderUtils = require('loader-utils');
module.exports = function(source) {
const options = loaderUtils.getOptions(this);
const result = source.replace('xiaochengzi', options.name);
this.callback(null, result);
}
对于callback回调函数,官方解释是这样的:
如果loader里要写一些异步的代码的时候,需要先声明:
js
const loaderUtils = require('loader-utils');
module.exports = function(source) {
const options = loaderUtils.getOptions(this);
const callback = this.async(); // 声明一下异步操作
setTimeout(() => {
const result = source.replace('xiaochengzi', options.name);
callback(null, result); // 在回调里返回结果
}, 1000)
}
- 使用 this.async() 进行异步声明操作。
更多loader-API请参考官方文档:loader-API
编写打包多个loader时webpack.config.js配置:
js
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
module: {
rules: [{
test: /.js/,
use: [
{
loader: path.resolve(__dirname, './loaders/replaceLoader.js')
},
{
loader: path.resolve(__dirname, './loaders/replaceLoaderAsync.js'),
options: {
name: 'world'
}
}
] // 使用自己写的loader模块
}]
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
- 由于loader从下向上执行,所以先执行replaceLoaderAsync.js,再执行replaceLoader.js。
在loaders文件夹下新增replaceLoaderAsync.js文件:(第一个loader)
js
const loaderUtils = require('loader-utils');
module.exports = function(source) {
const options = loaderUtils.getOptions(this);
const callback = this.async(); // 声明一下异步操作
setTimeout(() => {
const result = source.replace('xiaochengzi', options.name);
callback(null, result);
}, 1000)
}
- 使用异步代码把我们打印的信息从 'hello xiaochengzi' 替换为 'world'.
./loaders/replaceLoader.js: (第二个loader)
js
module.exports = function(source) {
return source.replace('world', 'ranran')
}
再次执行打包:npm run build
- 根据配置好的loader,我们打印的信息将通过replaceLoaderAsync.js从 'hello xiaochengzi' 替换为 'hello world' ,最终再通过replaceLoader.js替换为 'hello ranran'。
知识补充
当配置多个自己编写的loader时,每次都需要使用path.resolve来指定路径读取loader,如:
其实,我们可以通过其他配置,去除这部分冗余代码:(如)
js
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
resolveLoader: {
modules: ['node_modules', './loaders']
}, // 当你去使用loader的时候,它会帮你去做一些事情
module: {
rules: [{
test: /.js/,
use: [
{
loader: 'replaceLoader'
},
{
loader: 'replaceLoaderAsync',
options: {
name: 'world'
}
}
] // 使用自己写的loader模块
}]
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
- webpack中的 resolveLoader 当你去使用loader的时候,它会帮你去做一些事情
- modules: ['node_modules', './loaders'] 指当你使用loader的时候,webpack首先会去node_modules文件夹下找对应的loader模块,如果找不到就会去loaders文件夹下去找。
执行打包:npm run build
最终打印为 'hello ranran' ,成功执行。
总结:loader在我们项目开发中的用处还是比较大的,比如:代码的异常捕获、中英文网站的切换等。