看完就会的webapck loader编写教程

806 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

虽然是五月的尾巴,但是竟然也算6月更文呢!就很棒哟~😄

前言

大家好,我是小阵 🔥,一路奔波不停的码字业务员
身为一个前端小菜鸟,总是有一个飞高飞远的梦想,因此,每点小成长,我都想要让它变得更有意义,为了自己,也为了更多值得的人
如果喜欢我的文章,可以关注 ➕ 点赞,与我一同成长吧~😋
加我微信:zzz886885,邀你进群,一起学习交流,摸鱼学习两不误🌟

开开心心学技术大法~~

开心

来了来了,他真的来了~

正文

开干之前照理说下whatwhy

  • 什么是loader?
    • 我们这里说的loaderwebpack中的module>rules>loader中引入的一个工具方法
  • 为什么需要loader
    • 因为webpack只认识基础的文件,比如jsjson,怎样让webpack认识更多的文件资源呢?那loader就是充当了webpack的翻译官,可以将webpack不认识的资源转变成他认识的

说了whatwhy,接下来就是关键的how

一、loader的本质

loader的本质就是一个module.exports暴露出去的一个函数方法

注意module.exports暴露出去的一定不要是箭头函数,因为异步loader的使用是需要用到this的,而箭头函数不存在this

二、实现简单的loader

综合上面了解的,我们只需要return一个结果就行

module.exports = function (content, map, meta) {
  // 其中content就是通过loader的test规则匹配到的文件资源内容
  // map就是指sourceMap,如果想要遵循webpack的sourceMap规则,用this.callback或者this.async结束loader的时候最好手动传入该map,当然如果是普通的return,那就直接return就好
  console.log('loader', content)
  return content
}

三、在webpack中如何引入你的loader

// webpack.config.js
const path = require('path');

module.exports = {
  module:{
    rules:[
      {
        test:/\.js$/,
        loader: path.resolve(__dirname,'loaders/index.js')
      }
    ]
  }
}

可以通过配置resolveLoader来让你的loader引入更加优雅

module.exports = {
  module:{
    rules:[
      {
        test:/\.js$/,
        loader: 'index'
      }
    ]
  },
  resolveLoader: {
    modules: [resolve(__dirname, 'loaders')]
  },
}

四、loader加载顺序

我们都知道,当想要多个loader作用时,需要将loader属性用use来代替,通过use:['loader1','loader2']的方式来实现。

基本的loader执行顺序也是从后向前执行的。

但是loader并不是整体都是从后向前的,loader中注册的pitch是从前向后执行的。我们来看一下

// loader中pitch的注册写法
module.exports.pitch = function () {
  console.log("index1-pitch")
}

我们可以通过注册多个loader的pitch来看下打印

loader打印顺序

五、同步loader和异步loader

我们js有同步和异步,那相应的,loader也有自己的同步和异步

同步loader

// 写法一
module.exports = function (content, map, meta) {
  console.log('loader-index3', content, map, meta)
  // console.log('options', getOptions(this))
  return content
}

// 写法二
module.exports = function (content, map, meta) {
  this.callback(null, content, map, meta)
}

异步loader

// 写法一
module.exports = function (content, map, meta) {
  setTimeout(() => {
    this.callback(null, content, map, meta)
  }, 2000);
}

// 写法二
module.exports = function (content, map, meta) {
  const callback = this.async();
  setTimeout(() => {
    callback(null, content, map, meta)
  }, 2000);
}

六、接收loader传入的options

我们的loader可能需要传入一些自定义的配置,那怎样在loader内部接收到这写参数呢?

可以通过loader-utils这个库来接收,注意库的版本和使用方法,我这里用的是v2.0.0的版本,所以我就以这个版本来做演示

//webpack.config.js
module.exports = {
  ///...
  module:{
    rules:[
      {
        test:/\.js$/,
        loader:'index',
        options:{
          name: 'zzz',
          age: 21
        }
      }
    ]
  }
}

// loader
const {getOptions} = require('loader-utils');

module.exports = function(content,map,meta){
  // 只需要调用暴露出来的getOptions,传入this即可
  const options = getOptions(this) || {};
  console.log('loader-options',options)
  this.callback(content,map,meta)
}

打印传入loader的options

可以看到用法很简单,只需要用暴露出的getOptions传入this就会返回我们在webpack.config.js中定义的options字段

七、对loader传入的options进行校验

上面我们看了怎样接收到传入的options,那怎样对这些options进行校验呢,如果用户输入的options不是我们符合预期的options的话,可以在进入到loader的时候将错误信息提示给用户

这里我们需要借助一个schema-utils的库,同样需要注意库的版本和用法,我这里是v2.7.1,所以就以这个版本举例

首先我们需要定义一个schema.json的文件来做一些字段的约束

{
  // 整体options传入的类型一般都是object
  "type": "object",
  // 对传入的属性进行约束
  "properties": {
    // key值为options传入的key,value是一个对象,对传入的value进行约束
    "name": {
      // 表述传入的options的类型
      "type": "string",
      // 如果未按照要求传的话,会抛出description中定义的信息
      "description": "输入的name必须是string类型"
    },
    // 同上
    "age": {
      "type": "number",
      "description": "输入的age必须是number类型"
    }
  },
  // 是否允许options中存在以上属性之外的属性,为true允许,false为不允许
  "additionalProperties": true
}

然后在loader中通过schema-utils暴露的方法来约束下用户输入的options

  // loader
  const loaderUtils = require('loader-utils');
  const validate = require('schema-utils');

  const schema = require('./schema.json')

  module.exports = function (content, map, meta) {
    console.log('loader-index3', content)
    console.log('laoder-options', loaderUtils.getOptions(this))
    const options = loaderUtils.getOptions(this) || {}
    // 校验传入参数是否符合你的规范
    // 入参第一个是我们指定的约束规则scema.json,第二个是传入的options
    validate(schema, options)

    this.callback(null, content, map, meta)
    // return content
  }

  module.exports.pitch = function (content, map, meta) {
    console.log("loader-index3-pitch")
  }

因为上面我们约束了optionsnameage的类型,那我们特意来写错一下来看下会是什么样的效果

假设我把name改成number类型的123

更改name类型

然后npm run build来看下效果

image.png

可以看到,webpack把loader中我们通过schema-utils约束过的options中有误的信息给抛了出来

八、没有什么八了

看到这里你基本已经会写自己的loader了,发动你的小脑瓜去实践吧!!!嘎嘎嘎~~

加油努力

结语

往期好文推荐「我不推荐下,大家可能就错过了史上最牛逼vscode插件集合啦!!!(嘎嘎~)😄」