如何编写一个Loader

直接上例子,简单易懂!

创建一个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配置文件

目录结构如下:

|--make_loader
	|--node_modules
	|--src
		|--index.js
	|--loaders
		|--replaceLoader.js
	|--package-lock.json
	|--package.json
	|--webpack.config.js

src/index.js中的业务代码:

 console.log('hello xiaochengzi');

在loaders/replaceLoader.js文件里书写我们的loader代码:

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的使用配置:

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

打包后的文件为:hellow world

由此可见,我们在业务代码中打印的 ‘hello xiaochengzi’ 通过我们编写的loader替换成了 ‘hello world’。
到此,一个最最简单的loader就编写好了~
————————— ?———————————

loader中常用的API

使用loader-utils分析参数:

// 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的使用:

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里要写一些异步的代码的时候,需要先声明:

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配置:

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)

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)

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,如:

其实,我们可以通过其他配置,去除这部分冗余代码:(如)

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在我们项目开发中的用处还是比较大的,比如:代码的异常捕获、中英文网站的切换等。

发布了54 篇原创文章 · 获赞 22 · 访问量 7236

猜你喜欢

转载自blog.csdn.net/Riona_cheng/article/details/100929439