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

相关推荐
阿珊和她的猫11 小时前
`require` 与 `import` 的区别剖析
前端·webpack
jayaccc18 小时前
Webpack配置详解与实战指南
前端·webpack·node.js
阿珊和她的猫20 小时前
Webpack中import的原理剖析
前端·webpack·node.js
AI前端老薛20 小时前
webpack中loader和plugin的区别
前端·webpack
遗憾随她而去.1 天前
Webpack5 基础篇(二)
前端·webpack·node.js
Misnice1 天前
Webpack、Vite 、Rsbuild 区别
前端·webpack·node.js
阿珊和她的猫1 天前
Webpack 动态引入的实现原理与应用
前端·webpack·node.js
GhostPaints2 天前
Vue 2 项目中 template 使用可选链 ?. 导致的诡异编译报错及 webpack loader 配置坑
前端·vue.js·webpack
黎明初时2 天前
React基础框架搭建6-添加业务模块示例(用于做一些示例/demo):react+router+redux+axios+Tailwind+webpack
javascript·react.js·webpack
Aliex_git2 天前
性能优化 - 构建效率优化
前端·笔记·webpack·性能优化·vue·团队开发