Webpack-源码四,从源码分析如何写一个loader

从黑盒和白盒两个角度讲讲如何写一个loader以及loader这个东西是如何在webpack中运作起来的。loader相当于一个翻译器,具体的loader会有很大不同,比如less-loader和ejs-loader内部代码肯定差异极大,这里只以最简单的loader举例,入坑以后大家根据自己需求自行探索。

本系列所有博客的测试代码可以点击。

从黑盒角度学习写一个loader

所谓黑盒角度,就是我把webpack的运作过程当作一个黑盒,只从官网学习API。

官网API

官网告诉我们,loader就是一个export出来的function,这个函数接受的参数是源文件的字符串,返回经过“翻译”后的文件。这个过程可以是异步的。在函数内部,可以通过this获得loader API上的内容。

例如:有多个结果的同步loader,多个结果指该loader不仅返回翻译后的文件,还需要执行一系列操作返回其他结果。

module.exports = function(content) {
    this.callback(null, someSyncOperation(content), sourceMaps, ast);
    return; // always return undefined when calling callback()
};

例如:异步loader

module.exports = function(content) {
    var callback = this.async();
    someAsyncOperation(content, function(err, result) {
        if(err) return callback(err);
        callback(null, result);
    });
};

例如:有多个结果的异步loader

module.exports = function(content) {
    var callback = this.async();
    someAsyncOperation(content, function(err, result, sourceMaps, ast) {
        if(err) return callback(err);
        callback(null, result, sourceMaps, ast);
    });
};

不再举例,API很简单,大家可以自己看看。

一个最简单的loader

下面写一个最简单的loader,仅返回源文件原文,捋顺一下写loader的流程。

webpack.config.js中进行配置

 module: {
         //加载器配置
        loaders:[
         {
            test: /\.tpl\.html$/,
            loader: 'html-template-loader'
         }
         ]
     }

我要去找.tpl.html文件,打包前先通过我写的html-template-loader进行翻译。于是我现在文件中写一个test.tpl.html文件,并在index.jsrequire它。

// index.js
let tpl = require('./src/loaders/test.tpl.html');
// test.tpl.html
this is a template.
what's wrong?

node_modules文件夹中新建目录开始loader编写

Webpack-源码四,从源码分析如何写一个loader_第1张图片

新建了一个html-template-loader文件夹,我只在里面装了一个index.js。这个index.js就是需要利用上面提到的loader
API进行编写的地方了。

// node_modules/html-template-loader/index.js
var _ = require('lodash');

module.exports = function(source){
    // console.log(source);
    var template = _.template(source + 'loader has worked!');
    return 'module.exports = ' + template;
};

原谅我写的这么简单,毕竟我没有什么要写loader的需求,就不想陷进去。

只需这两步,你就可以进行打包了,打包成功证明我的loader工作了。
Webpack-源码四,从源码分析如何写一个loader_第2张图片

从白盒角度学习写一个loader

所谓白盒角度,就是我想弄明白webpack到底怎么把这个loader运行起来的,不再关心loader内部代码。

从打包文件看

以常用的less编译打包为例看。配置文件:

loaders:[
         {
            test: /\.less$/,loader: 'style-loader!css-loader!less-loader'
          }
]

这个配置文件翻译过来就是,遇到.less结尾的文件,首先用less-loader翻译成css,再把css文本传入css-loader,来处理@importurl()等情况,再把结果传给style-loader,在最终的html文件中加入style标签。

用我上篇博客中写的plugin看看打包出来的chunk和modules:
Webpack-源码四,从源码分析如何写一个loader_第3张图片

  • moudle 0: 入口文件
  • moudle 1: less文件

那么模块2,3,4呢,为什么会多出这些模块?

答案大家都想得到,应该是less文件对应的三个loader增加的内容,下面去打包后的bundle文件中看看这些模块的内部代码。

/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};

/******/    // The require function
/******/    function __webpack_require__(moduleId) {

/******/        // 省略内部代码,这部分代码在webpack系列第一篇博客中我已经分析过了
/******/        return module.exports;
/******/    }


/******/    // 省略部分代码
/******/    return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

    /*
    * @Author: Cynthia
    * @Date:   2017-03-06 14:51:16
    * @Last Modified by:   Cynthia
    * @Last Modified time: 2017-05-19 12:46:30
    */

    'use strict';

    __webpack_require__(1);


/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

    // style-loader: Adds some css to the DOM by adding a