webpack自定义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配置文件

目录结构如下:

**

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

相关推荐
学前端的小朱14 小时前
处理字体图标、js、html及其他资源
开发语言·javascript·webpack·html·打包工具
Domain-zhuo1 天前
如何提高webpack的构建速度?
前端·webpack·前端框架·node.js·ecmascript
m0_748234522 天前
前端Vue3字体优化三部曲(webFont、font-spider、spa-font-spider-webpack-plugin)
前端·webpack·node.js
Web阿成2 天前
3.学习webpack配置 尝试打包ts文件
前端·学习·webpack·typescript
小木_.2 天前
【python 逆向分析某有道翻译】分析有道翻译公开的密文内容,webpack类型,全程扣代码,最后实现接口调用翻译,仅供学习参考
javascript·python·学习·webpack·分享·逆向分析
Web阿成2 天前
5.学习webpack配置 babel基本配置
前端·学习·webpack
理想不理想v4 天前
webpack最基础的配置
前端·webpack·node.js
臣妾没空4 天前
全栈里程碑二:前端基础建设
webpack
Domain-zhuo4 天前
如何利用webpack来优化前端性能?
前端·webpack·前端框架·node.js·ecmascript
初学者7.4 天前
Webpack学习笔记(2)
笔记·学习·webpack