探索webpack-loader

一、什么是loader

1、loader的本质

       loader本质上是导出为函数的JavaScript模块。loader runner会调用此函数,然后将上一个loader产生的结果或者资源文件传入进去。
        函数中的this作为上下文会被webpack填充,并且提供一些实用的方法。如loader-runner包含的方法工具可以使loader调用方式变为异步,或者获取query参数。
        loader通过compiler处理预期会得到一个String或者Buffer(能够转换为string)类型的结果,代表模块的JavaScript源码的结果。

2、loader的基本代码结构

/**
     *
     * @param {string|Buffer} content 源文件的内容
     * @param {object} [map] 可以被 https://github.com/mozilla/source-map 使用的 SourceMap 数据
     * @param {any} [meta] meta 数据,可以是任何内容
*/
function webpackLoader(content, map, meta) {
     // 你的 webpack loader 代码
}

二、loader的分类

loader可以分为4类:同步loader、异步loader、"Raw" Loader、Pitching Loader

1、同步loader

    无论是return还是this.callback 都可以同步地返回转换后的content值。this.callback方法允许多个传参,调用时函数总是返回undefined。示例如下:

webpack简单配置

2、异步loader

    使用this.async来获取callback函数,this.async告诉loader-runner这个loader将会异步的回调,返回this.callback。示例如下:

官方建议:loader 最初被设计为可以在同步 loader pipelines(如 Node.js ,使用 enhanced-require),以及 在异步 pipelines(如 webpack)中运行。然而,由于同步计算过于耗时,在 Node.js 这样的单线程环境下进行此操作并不是好的方案,我们建议尽可能地使你的 loader 异步化。但如果计算量很小,同步 loader 也是可以的

这个有个问题,如果我们在同步loader中异步返回结果,会怎样呢?结果是传到异步loader中的值是undefined,示例如下:

从结果可以看出,同步loader中执行完同步代码后直接返回,不会返回异步代码执行的结果

3、"Raw" Loader

    默认情况下,资源文件会被转化为UTF-8字符串,然后传给loader。通过设置raw为true,loader可以接受原始的Buffer。每个loader都可以用String后者Buffer的形式传递它的处理结果。
compiler将会把他们在loader之间相互转换。示例如下:

4、Pitching Loader

    loader总是从右到左被调用。有些情况下,loader只关心request后面的元数据,并忽略前一个loader的结果。在实际(从右到左)执行loader之前,会先从左到右调用loader上的pitch方法。
    pitching阶段的作用
    1)传递给pitching方法的data,在执行阶段也会暴露在this.data之下,并且可以利用于在循环时,捕获并共享前面的信息。示例如下:

    2)如果某个loader在pitching方法中给出一个结果,那么这个过程会回过身来,并跳过剩下的loader。过程如下图(大佬的图片地址

示例如下:

三、手动实现loader示例

1、实现babel-loader
    babel的转换过程主要分为三步:解析(parse)->转换(transform)->生成(generate)
需要下载@babel/core包,使用包暴露的transfrom方法解析和生成ast抽象语法树,示例如下:

const babel = require('@babel/core')
const path = require('path')
module.exports = function (content, map, mete) {   
    const babelTransResule = babel.transform(content, {       
        filename: path.basename(this.resourcePath),       
        presets: ['@babel/preset-env']   
    })   
    const { code,  ast } = babelTransResule   
    return this.callback(null, code, map, ast)
}

2、实现file-loader

    直进直出,挪个位置

const loaderUtils = require("loader-utils"); //记得先导入loader-utils包
module.exports = function (content, map, mete) {   
    const filename = loaderUtils.interpolateName(this, '[hash].[ext]', {       
        content   
    })   
    this.emitFile(filename, content)   
    return `export default '${filename}'`  // 导出字符串供后续loader调用
}
module.exports.raw = true // 因为是图片等资源,所以设置为true,由UTF-8变成buffer才行

 

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容