webpack 自定义 Loader

Loader 是用于对模块的源代码进行转换得,使其能在浏览器上运行。之前使用过很多 loader,比如 css-loader、less-loader、babel-loader

自定义 loader

  • loader 本质上是一个导出为函数的 js 模块,函数会接受三个参数
    • content:资源文件的内容
    • map:sourcemap 相关的数据
    • meta:一些元数据
  • loader runner 库会调用这个函数,然后将上一个 loader 产生的结果或资源文件传入进去
  • 自定义一个 loader js 模块
js 复制代码
module.exports = function(content, map, meta) {
  console.log("hy_loader01:", content)
  return content
}
  • 然后定义好打包入口文件,配置好 webpack.config.js 文件
  • 解释下 resolveLoader 属性运用

    • 上面的 loader 模块是在 hy-loaders 文件夹下的 hy_loader01.js 模块
    • 在 webpack 打包中,对 loader 进行解析时会默认去 node_modules 文件夹下去找对应的 loader,如果想使用上面模块,书写 use 参数时应该是 use: ['./hy-loaders/hy_loader01.js']
    • 如果不想使用文件路径的书写方式,就可以借助 resolveLoader 属性,它用于控制 Webpack 如何解析 loader 的路径,modules 是一个数组,指出搜索 loader 的目录
  • 进行打包,可以看到 hy_loader01.js 模块输出结果,content 就是 main.js 文件内容

loader 执行顺序

  • 前面有讲过,loader 执行顺序是从后向前、从右向左的
  • 实际上,在解析 loader 时,顺序仍然是从前向后,但是起作用时是从后向前
  • 在上面的 hy_loader01.js 文件中加个另一个loader,称之为 PitchLoader
  • 简单了解 PitchLoader
js 复制代码
module.exports = function(content, map, meta) {
  console.log("hy_loader01:", content)
  return content
}

module.exports.pitch = function() {
  console.log("loader pitch 01")
}
  • 然后整三个这样的文件,改变下数字,然后配置好 webpack
js 复制代码
use: [
    "hy_loader01",
    "hy_loader02",
    "hy_loader03",
]
  • 看下输出结果
  • 出现这个原因是,webpack 源码里有个 run-loader 的功能,优先执行 PitchLoader,执行时进行 loaderIndex++,之后执行 NormalLoader(类似css-loader、babel-loader),执行时进行 loaderIndex--

  • 在配置好 use 参数后,执行顺序默认是从后向前执行的,但是进行一些特殊配置,仍然是能改变执行顺序的,比如使用行内设置loader,又或者通过 enforce 设置 pre 和 post,这里不做展开

同步 loader

  • 默认创建的 loader 就是同步的,这个 loader 必须通过 returnthis.callback 来返回结果,交给下一个 loader 处理
  • 通常在有错误的情况下,会使用 this.callback,一般时候,同步 loader 仍是使用 return 返回结果
  • 下面两个代码段返回的东西是一样的
js 复制代码
module.exports = function(content, map, meta) {
  return content
}

module.exports = function(content, map, meta) {
    // 获取到同步的callback
    const callback = this.callback
    // callback进行调用:
    // 参数一: 错误信息,一般设置为 null
    // 参数二: 传递给下一个loader的内容
    callback(null, content)
}

异步 loader

  • 有些时候需要在 loader 里进行一些异步操作,希望在异步操作完成后,再返回这个 loader 处理的结果
  • 这个时候就需要异步 loader 了
js 复制代码
module.exports = function(content) {
    const callback = this.async()

    // 进行异步操作
    setTimeout(() => {
        // 传参与上面一样
        callback(null, content + "aaaa")
    }, 2000);
}

loader 传参处理

  • webpack 中 use 配置 loader,可以是字符串写法,也可以是对象写法,对象写法时可以往 loader 中传递参数
js 复制代码
use: [
    {
        loader: "hy_loader04",
        options: {
            name: "why",
            age: 18
        }
    }
]
  • 在 loader 定义中可以获取传递进来的参数
js 复制代码
module.exports = function(content) {

  // 方式一: 早期时, 需要单独使用loader-utils(webpack开发)的库来获取参数
  // 方式二: 目前, 已经可以直接通过this.getOptions()直接获取到参数
  const options = this.getOptions()
  console.log(options)

  return content
}
  • 除了获取参数外,也可以对参数进行校验。需要借助 webpack 官方提供的校验库 schema-utils
js 复制代码
npm install schema-utils -D
  • 校验是需要配置校验规则的,配置文件是一个 json 文件,有固定的格式
js 复制代码
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "请输入名称, 并且是string类型"
    },
    "age": {
      "type": "number",
      "description": "请输入年龄, 并且是number类型"
    }
  }
}
js 复制代码
const { validate } = require('schema-utils')
const loader04Schema = require('./schema/loader04_schema.json')

module.exports = function(content) {
  
  const options = this.getOptions()

  // 校验参数是否符合规则
  validate(loader04Schema, options)

  return content
}
  • 如果 age 格式设置为 string 类型,然后进行打包,会报错

自定义 babel-loader 案例

  • babel-loader 可以对 js 代码进行转换,比如ES6转换为ES5。如果是自己一点点写的话,需要写大量的词法分析等,但是一般这些东西都有第三方库支持,这里就借助 @babel/core
js 复制代码
npm install @babel/core -D
  • 直接看demo
js 复制代码
const babel = require('@babel/core')

module.exports = function(content) {
  // 1.使用异步loader
  const callback = this.async()

  // 2.获取options
  let options = this.getOptions()
  if (!Object.keys(options).length) {
    options = require('../babel.config')
  }

  // 使用Babel转换代码
  babel.transform(content, options, (err, result) => {
    if (err) {
      callback(err)
    } else {
      callback(null, result.code)
    }
  })
}
  • 使用的是异步 loader 的方式,前面篇章有讲,bable的配置可以在 webpack.config.js 中配置,也可以在根目录中配置 babel.config.js 文件,类似下面的
js 复制代码
module.exports = {
  presets: [
    "@babel/preset-env"
  ]
}
  • 上面获取 options 的操作兼顾了两种情况
  • babel.transform 就是借助第三方库对代码进行转换
相关推荐
垣宇11 小时前
Vite 和 Webpack 的区别和选择
前端·webpack·node.js
小纯洁w1 天前
Webpack 的 require.context 和 Vite 的 import.meta.glob 的详细介绍和使用
前端·webpack·node.js
海盗强1 天前
Webpack打包优化
前端·webpack·node.js
祈澈菇凉2 天前
如何优化 Webpack 的构建速度?
前端·webpack·node.js
懒羊羊我小弟2 天前
常用 Webpack Plugin 汇总
前端·webpack·npm·node.js·yarn
祈澈菇凉3 天前
Webpack的持久化缓存机制具体是如何实现的?
前端·webpack·gulp
懒羊羊我小弟4 天前
Webpack 基础入门
前端·webpack·rust·node.js·es6
刽子手发艺4 天前
Selenium+OpenCV处理滑块验证问题
opencv·selenium·webpack
懒羊羊我小弟4 天前
常用Webpack Loader汇总介绍
前端·webpack·node.js
真的很上进6 天前
【1.8w字深入解析】从依赖地狱到依赖天堂:pnpm 如何革新前端包管理?
java·前端·vue.js·python·webpack·node.js·reactjs