webpack5 | loader 原理

102 阅读1分钟

本文是Webpack系列第五篇,主要学习Webpack5 loader原理

**介绍
**

帮助webpack将不同类型的文件转换为webpack可识别的模块

执行顺序

分类

  • pre:前置 loader

  • normal:普通 loader

  • inline:内联 loader

  • post:后置loader

执行顺序

  • 执行优先级为:pre > normal > inline > post
  • 相同优先级:从右到左,从上到下

使用方式

  • 配置方式:在 webpack.config.js 文件中指定,可配置 pre、normal、post ⭐️推荐 
{   
    enforce: 'pre'test: /.js$/,  
    loader: 'loader',
}
  • 内联方式:在import语句中指定
import styles from 'style-loader!css-loader?modules!./styles.css''

注:

  • css-loader,style-loader处理 style.css文件
  • 通过!将资源中的loader分开;

inline-loader可以通过添加不同前缀,跳过其他类型loader

  • !跳过normal loader
import styles from '!style-loader!css-loader?module!./styles.css';
  • ~!跳过 pre 和normal loader
import styles from '~!style-loader!css-loader?module!./styles.css';
  • !!跳过pre,normal和postloader
import styles from '!!style-loader!css-loader?module!./styles.css';

开发第一个loader

例子🌰

module.exports = function (content, map, meta) {
    console.log(content);  
    return content;
};

loader本质是一个函数,当解析资源时,会调用相应的loader去处理,接收到文件内容作为参数,返回内容出去

  • content:文件内容
  • map:SourceMap
  • meta:别的loader传递的数据

同步loader

// 写法一
module.exports = function (content) { 
    return content;
};

// 写法二
module.exports = function (content, map, meta) {
    // err: 代表是否有错误,将错误传出去 
    // content:处理后的内容 
    // map:继续传递source-map 
    // meta:给下一个loader传递参数  
    const err = null; 
    this.callback(err, content, map, meta);
};

同步loader中不能出现异步操作 ⭐️⭐️ 推荐写法二:出错可以传出,也可以给下一个loader传参
异步loader

例子🌰

module.exports = function (content, map, meta) {
    const callback = this.async();  
    setTimeout(() => {   
        console.log("test asyc");
        callback(null, content, map, meta); 
    }, 1000);};

raw loader

例子🌰

// 写法一
module.exports = function (content) {  
    return content;
};

module.exports.raw = true;


// 写法二
function testRawLoader(content) { 
    return content;
}

testRawLoader.raw = true;

module.exports = testRawLoader;

接收到的content是buffer数据流,处理图片时用这种形式
pitchloader
例子🌰

module.exports = function (content) { 
    return content;
};

module.exports.picth = function () { 
    console.log("pith");
};

pitch方法执行在loader执行之前loader API

方法名含义用法
this.async异步回调loader,返回this.callbackconst callback = this.async()
this.callback可以同步或异步调用的并返回多个结果的函数this.callback(err, content, sourceMap?, meta?)
this.getOptions(schema)获取loader的optionsthis.getOptions(schema)
this.emitFile产生一个文件this.emitFile(name, content,sourceMap)
this.utils.contextify返回一个相对路径this.utils.contextify(context, request)
this.utils.absolutify返回一个绝对路径this.utils.absolutify(context, request)

自定义loader

clean-log-loader

module.exports = function (content) {
    return content.replace(/console.log(.*);?/g"");
};

******banner-loader
**

// loaderconst schema = require("./schema.json");

module.exports = function (content) {
const options = this.getOptions(schema);  
const prefix = ` 
    /*
    * Authour: ${options.author}    
    */  `return prefix + content;
};
    
    // schema
    {  
        "type""object",
        "properties": {  
            "author": {  
                "type""string" 
            }  
        },  
        "additionalProperties"false
    }
        
// webpack.config.js 使用
{  
    test/.js$/,  
    loader'./loaders/banner-loader',  
    options: {  
        author'A',
    }
},

******babel-loader
**

// loader
const babel = require("@babel/core");
const schema = require("./schema.json");

module.exports = function (content) { 
const callback = this.async(); 
const options = this.getOptions(schema);

  babel.transform(content, options, function (err, result) {   
  if (err) {     
      callback(err);  
  } else {    
      callback(null, result.code);  
  } 
 }); 
  return content;
};
  
  
// schema"type""object",  
    "properties": { 
        "presets": {   
            "type""array"  
        }  
    },
"additionalProperties"true
}

// webpack.config.jstest/.js$/,
    loader"./loaders/file-loader"options: { 
        presets: ["@babel/preset-env"], 
    },
},

/ End感谢阅读,如果觉得有用,点个赞、关注、在看***


涉及代码repo:github.com/qiqzhao/loa…