Loader进修,简析babel-loader

什么是Loader?

继上两篇文章webpack事变道理引见(上篇下篇),我们相识到Loader:模块转换器,也就是将模块的内容依据需求装换成新内容,而且每一个Loader的职责都是单一,只会完成一种转换,所以我们平常对源文件的处置惩罚,也是由多个Loader以链式递次实行的体式格局来举行屡次装换,然后取得我们要的效果。

那末如许Loader只须要体贴输入和输出,Loader实际上是一个Node.js模块,该模块导出的是一个函数(意味着,一切node.js的api我们都能够运用),以下:

    module.exports = function (source) {
        // 对source做一系列的转换
        return source;
    }

下面我们引见一下webpack供应了哪些供Loader挪用的api,对Loader有个比较深入的明白,然厥后剖析babel-loader的源码,看看我们经常使用的loader是怎样编写出来的。

取得Loader的options

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

返回其他效果

如上,我们返回的是转换后的内容,然则有些情况下,我们不单单议须要返回转换后的内容,还须要返回一些其他的内容,如sourceMap或是AST语法树,那末这时候我们能够运用webpack供应的APIthis.callback,当运用this.callback了,那末我们就必须须要在Loader函数返回undefined,以此来让webpack晓得返回的效果在this.callback中,API细致参数以下:

    this.callback(
        // 没法装换原内容时的Error
        err: Error || null,
        // 装换后的的内容,如上述的source
        content: string | Buffer,
        // 用于经由过程装换后的内容得出原内容的Source Map,轻易调试
        // 我们相识到,SourceMap我们只是会在开辟环境去运用,因而就会变成可掌握的,
        // webpack也供应了this.sourceMap去通知是不是须要运用sourceMap,
        // 固然也能够运用loader的option来做推断,如css-loader
        sourceMap?: SourceMap,
        // 假如本次转换同时天生ast语法树,也能够将这个ast返回,轻易后续loader须要复用该ast,如许能够进步机能
        abstractSyntaxTree? AST
    );

同步与异步

看看异步Loader在this.asyncAPI下怎样完成,

    module.exports = async function (source) {
        const callback = this.async();
        const { err, content, sourceMap, AST } = await Func();
        callback(err, content, sourceMap, AST); // 如上诉`this.callback`参数一样
    }

处置惩罚二进制数据

file-loader如许的Loader,处置惩罚的是二进制数据,那末就须要通知webpack给loader传入二进制花样的数据,代码能够以下:

    module.exports = function(source) {
        if (source instanceof Buffer) {
            // 一系列操纵
            return source; //固然我自身也能够返回二进制数据供应给下一个loader
        }
    }
    moudle.exports.raw = true; //不设置,就会拿到字符串

经由过程moudle.exports.raw = true;示知webpack,本身自身须要二进制数据。

缓存加快

优化的最好点,能够运用this.cacheable(Boolen),缓存loader转换后的内容,当处置惩罚文件或依靠文件没有发生变化时,运用缓存的转换内容,以此提速!

其他API

说到进修,固然越体系越好了,api多引见 ,除了上面经常使用的api以外,还存在以下经常使用的api。

  • this.context: 当前处置惩罚转换的文件地点的目次
  • this.resource: 当前处置惩罚转换的文件完全要求途径,包含querystring
  • this.resourcePath: 当前处置惩罚转换的文件的途径
  • this.resourceQuery: 当前处置惩罚文件的querystring
  • this.target: webpack设置的target
  • this.loadMoudle: 处置惩罚文件时,须要依靠其他文件的处置惩罚效果时,能够运用this.loadMoudle(request: string, callback: function(err, source, sourceMap, module))去猎取到依靠文件的处置惩罚效果。
  • this.resolve: 猎取指定文件的完全途径,this.resolve(context: string, request: string, callback: function(err, result: string))
  • this.addDependency: 为当前处置惩罚文件增加依靠文件,以便依靠文件发生变化时从新挪用Loader转换该文件,this.addDependency(file: string)
  • this.addContextDependency: 为当前处置惩罚文件增加依靠文件目次,以便依靠文件目次里文件发生变化时从新挪用Loader转换该文件,this.addContextDependency(dir: string)
  • this.clearDependencies: 消灭当前正在处置惩罚的文件的一切依靠
  • this.emitFile: 输出一个文件,运用的要领为this.emitFile(name: string, content: Buffer | string, sourceMap: {…})

babel-loader源码简析

源码第一行以下:

    let babel;
    try {
        babel = require("@babel/core");
    } catch (err) {
        if (err.code === "MODULE_NOT_FOUND") {
            err.message +=
            "\n babel-loader@8 requires Babel 7.x (the package '@babel/core'). " +
            "If you'd like to use Babel 6.x ('babel-core'), you should install 'babel-loader@7'.";
        }
        throw err;
    }

babel-loader依靠了@babel/core,这就是装置babel-loader须要同时装置@babel/core(一般会再装置babel-preset-envbabel-plugin-transform-runtimebabel-runtime)的缘由。我们接下去看,src/index.js全部文件是不是是依据我们前面所讲编写Loader的要领来构造代码的。

//引入package.json
const pkg = require("../package.json");
/* 
依据babel-loader是不是设置cacheDirectory属性来通知
babel-loader是不是缓存loader的实行效果,假如true,
便会运用cache要领去完成,`cache.js`文件有着read、write、filename(文件定名要领)
以及怎样处置惩罚缓存的handleCache要领(有则读,无则写再读),有兴致能够去看看。
*/
const cache = require("./cache");
/*
    transfrom.js用来转换内容,内部挪用了babel.transform要领举行转换,这里简朴引见一下babel的道理:
    babylon将es6/es7代码剖析成ast,babel-traverse对ast举行转译,取得新的ast,新的ast经由过程
    babel-generator转换成es5,中心要领在@babel/core/lib/transformation/index.js中的`runSync`
    要领,有兴致能够去相识一下。
*/
const transform = require("./transform");
const injectCaller = require("./injectCaller");
const path = require("path");
// 猎取Loader参数options
const loaderUtils = require("loader-utils");

module.exports = makeLoader();
module.exports.custom = makeLoader;

function makeLoader(callback) {
  const overrides = callback ? callback(babel) : undefined;

  return function(source, inputSourceMap) {
    // 上面引见过的api能够得知,这是个异步Loader,做的是异步装换的事变
    const callback = this.async();

    loader
      .call(this, source, inputSourceMap, overrides)
      .then(args => callback(null, ...args), err => callback(err));
  };
}

async function loader(source, inputSourceMap, overrides) {
    ....
}

能够看到确切和我们Loader编写体式格局是一样的,经由过程module.exports = makeLoader();导出一个函数,makeLoader()是一个高阶函数,又返回了一个函数,经由过程const callback = this.async();能够晓得,这是一个异步的loader,不难看出最重要的完成都在这一步函数loader内里了,那末到底在loader函数内里终究做了些什么呢?我们来看看,在浏览源码前,最好先看看babel-loaderREADME,先做个基础相识.

上面代码能够看出loader(source, inputSourceMap, overrides)函数入参有三个,分别是source=>待转换的codeinputSourceMap=>上一个loader处置惩罚后的sourceMap,有的话overrides=>自定义加载器,整块源码能够分红几部份,

  • let loaderOptions = loaderUtils.getOptions(this) || {};,猎取options,而且猎取当前处置惩罚转换的文件的途径this.resourcePath
  • 推断是不是自定义加载器转换,这里会举行一系列对options.customize举行推断,options.customize一个相对途径,loader函数参数overrides为空时起效,实行let override = require(loaderOptions.customize);,有了override今后,后续逻辑(如转换、猎取option)override都邑举行参与处置惩罚。
  • 将函数传入参数和LoaderOptions合并,取得programmaticOptions。
  • 挪用babel.loadPartialConfig能够拿到babel设置并赋值给config变量,实在就是为了许可体系轻松操纵和考证用户的设置,此功用处理了插件和预设
  • 天生cacheIdentifier
  • 推断options.cacheDirectory是不是须要缓存Loader转换内容,如为true,挪用cache.js的module.export Cache要领(上面已做引见)
  • config.babelrc不为空,则有.babelrc文件,依靠.babelrc文件变化,运用this.addDependency(config.babelrc);
  • metadataSubscribers 定阅元数据,重要作用是定阅一些编译过程当中的一些元数据,定阅今后这些元数据将会被增加 到webpack的高低文中。一般我们是用不上的,预计在某些babel-plugin中能够会运用到。
  • 末了将处置惩罚后的效果返回

小结

每一个Loader实在返回值就是一个Function,而且就是把带转换内容传入,取得转换后的内容,做的事变就是如许,这篇文章先对Loader的基础概念举行引见,而且相识webpack为Loader的编写供应一些经常使用的API,末了经由过程简析babel-loader的源码,我以为应当差不多晓得怎样去写一个简朴的Loader了,原文地点-个人博客

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