手把手教你写一个 Webpack Loader

本文示例源代码请戳github博客,发起人人着手敲敲代码。

本文不会引见loader的一些运用要领,不熟悉的同砚请自行检察
Webpack loader

1、背景

起首我们来看一下为何须要loader,以及他醒目什么?
webpack 只能明白 JavaScriptJSON 文件。loaderwebpack 能够去处置惩罚其他范例的文件,并将它们转换为有用模块,以供应用程序运用,以及被添加到依靠图中。

本质上来讲,loader 就是一个 node 模块,这很相符 webpack 中「万物皆模块」的思绪。既然是 node 模块,那就一定会导出点什么。在 webpack 的定义中,loader 导出一个函数,loader 会在转换源模块resource的时刻挪用该函数。在这个函数内部,我们能够经由历程传入 this 上下文给 Loader API 来运用它们。终究装换成能够直接援用的模块。

2、xml-Loader 完成

前面我们已晓得,由于 Webpack 是运转在 Node.js 之上的,一个 Loader 实在就是一个 Node.js 模块,这个模块须要导出一个函数。 这个导出的函数的事情就是取得处置惩罚前的原内容,对原内容实行处置惩罚后,返回处置惩罚后的内容。
一个简朴的loader源码以下

module.exports = function(source) {
  // source 为 compiler 传递给 Loader 的一个文件的原内容
  // 该函数须要返回处置惩罚后的内容,这里简朴起见,直接把原内容返回了,相当于该 Loader 没有做任何转换
  return source;
};

由于 Loader 运转在 Node.js 中,你能够挪用任何 Node.js 自带的 API,或许装置第三方模块举行挪用:


const xml2js = require('xml2js');
const parser = new xml2js.Parser();

module.exports =  function(source) {
  this.cacheable && this.cacheable();
  const self = this;
  parser.parseString(source, function (err, result) {
    self.callback(err, !err && "module.exports = " + JSON.stringify(result));
  });
};

这里我们事简朴完成一个xml-loader;

注重:假如是处置惩罚递次排在末了一个的
loader,那末它的返回值将终究交给
webpack
require,换句话说,它一定是一段可实行的
JS 剧本 (用字符串来存储),更准确来讲,是一个
node 模块的
JS 剧本,所以我们须要用
module.exports =导出。

全部历程相当于这个 loader 把源文件

// 这里是 source 模块

转化为

// example.js
module.exports = '这里是 source 模块';

然后交给 require 挪用方:

// applySomeModule.js
var source = require('example.js'); 
console.log(source); // 这里是 source 模块

写完后我们要怎样在当地考证呢?下面我们来写个简朴的demo举行考证。

2.1、考证

起首我们建立一个根目次xml-loader,此目次下 npm init -y天生默许的package.json文件 ,在文件中设置打包敕令

"scripts": {
    "dev": "webpack-dev-server"
  },

以后npm i -D webpack webpack-cli,装置完webpack,在根目次 建立设置文件webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.xml$/,
        use: ['xml-loader'],
      }
    ]
  },
  resolveLoader: {
    modules: [path.join(__dirname, '/src/loader')]
  },
  devServer: {
    contentBase: './dist',
    overlay: {
      warnings: true,
      errors: true
    },
    open: true
  }
}

在根目次建立一个src目次,内里建立index.js,

import data from './foo.xml';

function component() {
  var element = document.createElement('div');
  element.innerHTML = data.note.body;
  element.classList.add('header');
  console.log(data);
  return element;
}

document.body.appendChild(component());

同时另有一个foo.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<note>
    <to>Mary</to>
    <from>John</from>
    <heading>Reminder  dd</heading>
    <body>Call Cindy on Tuesday dd</body>
</note>

末了把上面的xml-loader放到src/loader文件夹下。
完全的demo源码请看
终究我们的运转效果以下图
《手把手教你写一个 Webpack Loader》

至此一个简朴的webpack loader就完成完成了。固然终究运用你能够宣布到npm上。

3、一些谈论学问补充

3.1、取得 Loader 的 options

当我们设置loader时我们常常会看到有如许的设置

ules: [{
    test: /\.html$/,
    use: [ {
      loader: 'html-loader',
      options: {
        minimize: true
      }
    }],
  }]

那末我们在loader中怎样猎取这写设置信息呢?答案是loader-utils。这个由webpack供应的东西。下面我们来看下运用要领

const loaderUtils = require('loader-utils');
module.exports = function(source) {
  // 猎取到用户给当前 Loader 传入的 options
  const options = loaderUtils.getOptions(this);
  return source;
};

没错就是这么简朴。

3.2、加载当地 Loader

1、path.resolve
能够简朴经由历程在 rule 对象设置 path.resolve 指向这个当地文件

{
  test: /\.js$/
  use: [
    {
      loader: path.resolve('path/to/loader.js'),
      options: {/* ... */}
    }
  ]
}

2、ResolveLoader
这个就是上面我用到的要领。ResolveLoader 用于设置 Webpack 怎样寻觅 Loader。 默许情况下只会去 node_modules 目次下寻觅,为了让 Webpack 加载放在当地项目中的 Loader 须要修正 resolveLoader.modules
假如当地的 Loader 在项目目次中的 ./loaders/loader-name 中,则须要以下设置:

module.exports = {
  resolveLoader:{
    // 去哪些目次下寻觅 Loader,有先后递次之分
    modules: ['node_modules','./loaders/'],
  }
}

加上以上设置后, Webpack 会先去 node_modules 项面前目今寻觅 Loader,假如找不到,会再去 ./loaders/ 目次下寻觅。
3、npm link
npm link 特地用于开辟和调试当地 npm 模块,能做到在不宣布模块的情况下,把当地的一个正在开辟的模块的源码链接到项目的 node_modules 目次下,让项目能够直接运用当地的 npm 模块。 由于是经由历程软链接的体式格局完成的,编辑了当地的 Npm 模块代码,在项目中也能运用到编辑后的代码。

完成 npm link 的步骤以下:

  • 确保正在开辟的当地 npm 模块(也就是正在开辟的 Loader)的 package.json 已准确设置好;
  • 在当地 npm 模块根目次下实行 npm link,把当地模块注册到全局;
  • 在项目根目次下实行 npm link loader-name,把第2步注册到全局的当地 Npm 模块链接到项目的 node_moduels 下,个中的 loader-name 是指在第1步中的package.json 文件中设置的模块称号。

链接好 Loader 到项目后你就能够像运用一个真正的 Npm 模块一样运用当地的 Loader 了。(npm link不是很熟,复制被人的)

3.3、缓存加快

在有些情况下,有些转换操纵须要大批盘算异常耗时,假如每次构定都从新实行反复的转换操纵,构建将会变得异常迟缓。 为此,Webpack 会默许缓存一切 Loader 的处置惩罚效果,也就是说在须要被处置惩罚的文件或许其依靠的文件没有发生变化时, 是不会从新挪用对应的 Loader 去实行转换操纵的。

假如你想让 Webpack 不缓存该 Loader 的处置惩罚效果,能够如许:

module.exports = function(source) {
  // 封闭该 Loader 的缓存功用
  this.cacheable(false);
  return source;
};

3.4、处置惩罚二进制数据

在默许的情况下,Webpack 传给 Loader 的原内容都是 UTF-8 花样编码的字符串。 但有些场景下 Loader 不是处置惩罚文本文件,而是处置惩罚二进制文件,比方 file-loader,就须要 Webpack 给 Loader 传入二进制花样的数据。 为此,你须要如许编写 Loader:

module.exports = function(source) {
    // 在 exports.raw === true 时,Webpack 传给 Loader 的 source 是 Buffer 范例的
    source instanceof Buffer === true;
    // Loader 返回的范例也能够是 Buffer 范例的
    // 在 exports.raw !== true 时,Loader 也能够返回 Buffer 范例的效果
    return source;
};
// 经由历程 exports.raw 属性通知 Webpack 该 Loader 是不是须要二进制数据 
module.exports.raw = true;

以上代码中最症结的代码是末了一行 module.exports.raw = true;,没有该行 Loader 只能拿到字符串。

3.5、同步与异步

Loader 有同步和异步之分,上面引见的 Loader 都是同步的 Loader,由于它们的转换流程都是同步的,转换完成后再返回效果。 但在有些场景下转换的步骤只能是异步完成的,比方你须要经由历程收集要求才得出效果,假如采纳同步的体式格局收集要求就会壅塞全部构建,致使构建异常迟缓。

在转换步骤是异步时,你能够如许:

module.exports = function(source) {
    // 通知 Webpack 本次转换是异步的,Loader 会在 callback 中回调效果
    var callback = this.async();
    someAsyncOperation(source, function(err, result, sourceMaps, ast) {
        // 经由历程 callback 返回异步实行后的效果
        callback(err, result, sourceMaps, ast);
    });
};

参考

编写一个webpack loader

    原文作者:Alan
    原文地址: https://segmentfault.com/a/1190000018980814
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞