手把手教你写一个 Webpack Loader小白教程_webpack零基础入门

本文示例源代码请戳github博客,建议大家动手敲敲代码。本文不会介绍loader的一些使用方法,不熟悉的同学请自行查看Webpack loader1、背景首先我们来看一下为什么需要loader,以及他能干什么?webpack 只能理解 JavaScript 和 JSON 文件。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模

手把手教你写一个 Webpack Loader小白教程

本文示例源代码请戳github博客,建议大家动手敲敲代码。

手把手教你写一个 Webpack Loader小白教程_webpack零基础入门

1、背景

首先我们来看一下为什么需要loader,以及他能干什么?
webpack 只能理解 JavaScript 和 JSON 文件。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。

本质上来说,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 把源文件

// 这里是 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零基础入门

至此一个简单的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

海计划公众号
(0)
上一篇 2020/03/30 16:07
下一篇 2020/03/30 16:07

您可能感兴趣的内容

  • 控制预期,走向成功使用说明_活动新手入门

    长辈们是否常对您说:不要抱有太大期望,否则很容易失望。事实上,老人家的话很有道理。当您对自己的事业有很高的预期时,失败往往近在咫尺。一些成功导师希望您志存高远,但脚踏实地总是没错。管理客户预期是商务领域中的重要一环。我们许下小诺言,并争取带来大回报,这样才有利于建立坚不可摧的客户关系。在商务中我们对这一方面过于重视,常常导致我们忘记自己的期望,以及这些期望对

    2020/03/30
  • Flutter官方WebView使用零基础入门_webview小白知识

    使用过人气很高的flutter_webview_plugin,但是缺少2个重要的功能。也在打开多个WebView时会出错。不能在JS中调用Flutter方法不能在H5进入某个URL之前拦截虽然该插件不够完整,但是使用起来很方便,封装了很多功能。如果交互不多可以用该插件。官方的 webview_flutter之后使用官方的webview_flutter插件。附

    2020/03/29
  • Doctype的作用?区分严格模式与混合模式意义?入门攻略_严格模式指南教程

    Doctype作用声明叫做文件类型定义(DTD),声明的作用为了告诉浏览器该文件的类型。让浏览器解析器知道应该用哪个规范来解析文档。声明必须在 HTML 文档的第一行,这并不是一个 HTML 标签。严格模式又称标准模式,是指浏览器按照 W3C 标准解析代码。混合模式又称怪异模式或兼容模式,是指浏览器用自己的方式解析代码

    2020/03/31
  • 程序员自我欺骗的 9 个谎言使用指南_谎言小白知识

    “我们对计算机的自信可能使我们犯错误,因为我们希望将现实世界都转化为代码。”程序员有充分的理由感到自豪,因为其他人是无权进入 数据库 并更改的。世界越是依赖计算机定义,程序员的能力就越强。实际上,没有什么代码是完美的代码,计算机也会经常犯错误。当然,许多问题源于我们的程序员所做的假设,这些假设虽然在某些时候是正确的,但在根本上是不正确的。正如马克·吐温(Ma

    2020/03/22
  • JS、Jquery中判断checkbox是否选中使用教程_属性零基础入门

    HTMLJSvar check = document.getElementsByTagName(‘input’)[0];
    console.log(check.checked);Jquery$(“input[type=’checkbox’]”).is(‘:checked’);

    2020/03/24
  • 常见的Hadoop十大应用误解使用攻略_数据入门基础

    Hadoop是一个由Apache基金会所开发的分布式系统基础架构。 用户可以在不了解分布式底层细节的情况下,开发分布式程序。 充分利用集群的威力进行高速运算和存储。 以下是常见的Hadoop十大应用误解和正解。1. (误解) Hadoop什么都可以做(正解) 当一个新技术出来时,我们都会去思考它在各个不同产业的应用,而对于平台的新技术来说,我们思考之后常会出

    2020/03/29
  • 把微信小程序异步API为Promise,简化异步编程基础入门_小程序入门知识

    把微信小程序异步API转化为Promise。用Promise处理异步操作有多方便,谁用谁知道。微信官方没有给出Promise API来处理异步操作,而官方API异步的又非常多,这使得多异步编程会层层回调,代码一复杂,回调起来就想砸电脑。于是写了一个通用工具,把微信官方的异步API转化为Promise,方便处理(多)异步操作。你可以这样用:准备转化后的方法并暴

    2020/04/03
  • ES6 系列之 defineProperty 与 proxy小白知识_proxy基础教程

    前言我们或多或少都听过“数据绑定”这个词,“数据绑定”的关键在于监听数据的变化,可是对于这样一个对象:var obj = {value: 1},我们该怎么知道 obj 发生了改变呢?definePropetyES5 提供了 Object.defineProperty 方法,该方法可以在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。语法

    2020/04/03
  • css简单实现带箭头的边框新手入门_边框小白教程

    实现一个普通边框 .border {width: 100px;height: 50px;border: 1px solid red;}实现由四个三角形组成的正方形 .triangle {width: 0;height: 0;border: 100px

    2020/03/23
  • Reducer的深入理解小白帮助_Reducer基础教程

    有一些小伙伴,对JavaScript的 reduce 方法还不够理解,我们来看下面两段代码:const nums = [1, 2, 3];
    let value = 0;for (let i = 0; i < nums.length; i++) {value += nums[i];
    }const nums = [1, 2, 3];
    const value =

    2020/03/26
  • 通过focusout事件解决IOS键盘收起时界面不归位的问题小白教程_键盘基础知识教程

    问题症状今天在开发一个移动端的 H5 页面时,遇到了 IOS 上键盘收起时界面无法归位的问题。下面详细描述下问题和症状:页面结构出问题的页面是一个表单结构。即类似于一个 div 下有4个 input 表单的结构,用于用户填写邮寄信息。类似:<input type="

    2020/03/29
  • mpvue – 美团点评开源的基于 Vue 的微信小程序前端框架菜鸟攻略_mpvue使用教程

    mpvue 是一个使用 Vue.js 开发小程序的前端框架。框架基于 Vue.js 核心,mpvue 修改了 Vue.js 的 runtime 和 compiler 实现,使其可以运行在小程序环境中,从而为小程序开发引入了整套 Vue.js 开发体验。实践案例美团旗下小程序:美团汽车票 和 美团充电,此外,正有一大批小程序正在接入中。快速开始我们精心准备了一

    2020/04/05
  • EXIF.Js读取图片的EXIF信息小白攻略_插件基础入门

    EXIF是什么?EXIF(Exchangeable Image File)是“可交换图像文件”的缩写,当中包含了专门为数码相机的照片而定制的元数据,可以记录数码照片的拍摄参数、缩略图及其他属性信息,简单来说,Exif信息是镶嵌在 JPEG/TIFF 图像文件格式内的一组拍摄参数,需要注意的是EXIF信息是不支持png,webp等图片格式的。(建议自己试的时候

    2020/03/26
  • WebWorker进阶小白帮助_线程菜鸟知识

    WebWorker与主线程之间的通信这篇文章主要分享介绍了WebWorker特殊应用场景, 扩展了WebWorker的能力, 为跨页面通信提供了另外一种思路。在上一篇文章里面也有了解到webworker与主线程之间的通信,使用的是一个PostMessage。在上一篇文章中,有个例子是,从主线程到子线程传了一个json,从子线程到主线程传了一个number,由

    2020/03/29
  • 你真的理解了比较运算符吗?菜鸟知识_运算小白指南

    平常我们都是不建议在代码上编写一些比较难理解的代码,例如 x == y 和 ‘A’ > ‘B’ 。这篇文章或许不能给你带来什么大的帮助,但是却可以让你了解一些你可能没接触到的知识点。由于有些参考资料来源于 ECMA 规范,所以感兴趣的可能需要先看《读懂 ECMAScript 规格》这篇文章,当然也可以忽略。类型之间的转换表首先我们需要先了解基本的类型转换规则

    2020/03/23
  • CSS Color Adjust 速览菜鸟知识_color入门基础知识

    近来,各大平台的操作系统纷纷引入 Dark Mode 深色模式,包括 Windows、MacOS 以及 Android 甚至 iOS 13 也将引入该模式。深色模式引入的同时,同时允许我们基于用户的偏好设计特定的样式。近期,W3C CSS 工作组发布了 CSS Color Adjust Level 1 编辑草案,也是第一版公开的工作组草案。该标准引入三个新的

    2020/03/29