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 就是借助第三方库对代码进行转换
相关推荐
熊的猫10 小时前
ES6 中 Map 和 Set
前端·javascript·vue.js·chrome·webpack·node.js·es6
前端青山13 小时前
webpack指南
开发语言·前端·javascript·webpack·前端框架
理想不理想v16 小时前
执行npm run build -- --report后,生产report.html文件是什么?
java·前端·javascript·vue.js·webpack·node.js
王解2 天前
【Webpack配置全解析】打造你的专属构建流程️(4)
前端·webpack·node.js
几何心凉4 天前
Webpack 中无法解析别名路径的原因及解决方案
运维·前端·webpack
friend_ship5 天前
Vue Cli的配置中configureWebpack和chainWebpack的主要作用及区别是什么?
vue.js·webpack·chainwebpack
friend_ship5 天前
Vite与Vue Cli的区别与详解
vue.js·webpack·rollup·vite·vue脚手架·vue cli
Man6 天前
webpack分包的几种方式和优缺点
前端·webpack
熊的猫6 天前
webpack 核心模块 — loader & plugins
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
理想不理想v6 天前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript