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

相关推荐
八月ouc1 天前
每日小知识点:10.14 webpack 有几种文件指纹
前端·webpack
街尾杂货店&1 天前
webpack - 单独打包指定JS文件(因为不确定打出的前端包所访问的后端IP,需要对项目中IP配置文件单独拿出来,方便运维部署的时候对IP做修改)
前端·javascript·webpack
jiangzhihao05153 天前
前端自动翻译插件webpack-auto-i18n-plugin的使用
前端·webpack·node.js
_孤傲_3 天前
webpack实现常用plugin
前端·webpack·node.js
Jonathan Star4 天前
Webpack 打包优化与骨架屏结合:双管齐下提升前端性能与用户体验
前端·webpack·ux
细节控菜鸡4 天前
Webpack 核心知识点详解:proxy、热更新、Loader与Plugin全解析
前端·webpack·node.js
dcloud_jibinbin5 天前
【uniapp】体验优化:开源工具集 uni-toolkit 发布
前端·webpack·性能优化·小程序·uni-app·vue
颜酱6 天前
理解 Webpack 的构建过程(实现原理),并实现一个 mini 版
前端·javascript·webpack
teeeeeeemo7 天前
Webpack 模块联邦(Module Federation)
开发语言·前端·javascript·笔记·webpack·node.js
小小前端_我自坚强8 天前
2025Webpack、Vite、Rollup详解
webpack·vite·rollup.js