zoukankan      html  css  js  c++  java
  • 如何⾃⼰编写一个Loader

    如何⾃⼰编写一个Loader

    ⾃己编写一个Loader的过程是比较简单的,

    Loader就是⼀个函数,声明式函数,不能⽤用箭头函数

    拿到源代码,作进一步的修饰处理,再返回处理后的源码就可以了

    简单案例

    创建一个替换源码中字符串的loader

    //index.js

    console.log("hello world");

    //replaceLoader.js

    module.exports = function(source) {  

    console.log(source, this, this.query);  

    return source.replace(world,'大家好')

     };

    //需要⽤声明式函数,因为要上到上下文的this,用到this的数据,该函数接受一个参数

    在配置文件中使用loader

     //需要使用node核心模块path来处理路径

    const path = require('path');

    ......

     module: {    

    rules: [      

    {        

    test: /.js$/,        

    use: path.resolve(__dirname, "./loader/replaceLoader.js")      

    }    

    ]  

    },

    如何给loader配置参数,loader如何接受参数?

    ² this.query

    ² loader-utils

    ² 

    //需要使用node核⼼心模块path来处理路径

    const path = require('path'); 

    module: {    

    rules: [      

    {        

    test: /.js$/,        

    use: path.resolve(__dirname, "./loader/replaceLoader.js")      

    }   

     ]  

    },

    //webpack.config.js

    .........

    module: {    

    rules: [      

    {        

    test: /.js$/,        

    use: [          

    {            

    loader: path.resolve(__dirname, "./loader/replaceLoader.js"),            

    options: {              

    name: "你好吗"            

    }          

    }        

    ]      

    }    

    ]  

    },    

    //replaceLoader.js

    //方式一: this.query获取参数

    module.exports = function(source) {  

    //this.query 通过this.query来接受配置文件传递进来的参数

       //return source.replace("world", this.query.name);   

    return source.replace("world", options.name);

    }

    //方式二: loader-utils获取参数,官方推荐

    const loaderUtils = require("loader-utils");//官方推荐处理loader,query的⼯具

    module.exports = function(source) {  

    //this.query 通过this.query来接受配置文件传递进来的参数

    const options = loaderUtils.getOptions(this);  

    return source.replace("world", options.name);

    }

    this.callback :如何返回多个信息,不止是处理好的源码呢,可以使用this.callback来处理

     //replaceLoader.js

    const loaderUtils = require("loader-utils");//官⽅方推荐处理理loader,query的⼯工具

    module.exports = function(source) {  

    const options = loaderUtils.getOptions(this);  

    const result = source.replace("world", options.name);  

    this.callback(null, result);

    };

    //this.callback(  

    err: Error | null,  

    content: string | Buffer,  

    sourceMap?: SourceMap,  

    meta?: any );

    this.async:如果loader⾥⾯有异步的事情要怎么处理理呢

    我们使⽤this.async来处理,他会返回this.callback

                

    //replaceLoader.js

    const loaderUtils = require("loader-utils");//官⽅方推荐处理理loader,query的⼯工具

    module.exports = function(source) {  

    const options = loaderUtils.getOptions(this);  

    const result = source.replace("world", options.name);  

    this.callback(null, result);

    };

    //this.callback(  

    err: Error | null,  

    content: string | Buffer,  

    sourceMap?: SourceMap,  

    meta?: any

    );

    const loaderUtils = require("loader-utils");

    module.exports = function(source) {  

    const options = loaderUtils.getOptions(this);  

    setTimeout(() => {    

    const result = source.replace("world", options.name);

    return result;  

    }, 1000);

    };

    //先⽤setTimeout处理下试试,发现会报错

    我们使⽤用this.async来处理理,他会返回this.callback

    const loaderUtils = require("loader-utils");

    module.exports = function(source) {  

    const options = loaderUtils.getOptions(this);

    //定义一个异步处理,告诉webpack,这个loader⾥有异步事件,在里⾯调⽤下这个异步

    //callback 就是 this.callback 注意参数的使⽤  

    const callback = this.async();  

    setTimeout(() => {    

    const result = source.replace("world", options.name);    

    callback(null, result);  

    }, 3000);

    };

    多个loader的使⽤

    //replaceLoader.js

    module.exports = function(source) {  

    return source.replace("软谋课堂", "word");

    };

    //replaceLoaderAsync.js

    const loaderUtils = require("loader-utils");

    module.exports = function(source) {  

    const options = loaderUtils.getOptions(this);

      //定义⼀一个异步处理理,告诉webpack,这个loader⾥里里有异步事件,在⾥里里⾯面调⽤用下这个异步  

    const callback = this.async();  

    setTimeout(() => {    

    const result = source.replace("world", options.name);    

    callback(null, result);  

    }, 3000); };

    //webpack.config.js

    module: {    

    rules: [      

    {        

    test: /.js$/,        

    use: [          

    path.resolve(__dirname, "./loader/replaceLoader.js"),          

    {            

    loader: path.resolve(__dirname, "./loader/replaceLoaderAsync.js"),            

    options: {              

    name: "你好吗"            

    }          

    }        

    ]        

    // use: [path.resolve(__dirname, "./loader/replaceLoader.js")]      

    }   

     ]

    顺序,自下⽽上,自右到左

    处理loader的路径问题

     resolveLoader: {    

    modules: ["node_modules", "./loader"]  

    },  

    module: {    

    rules: [      

    {        

    test: /.js$/,        

    use: [          

    "replaceLoader",          

    {            

    loader: "replaceLoaderAsync",            

    options: {              

    name: "软谋课堂"            

    }          

    }        

    ]        

    }    

    ]  

    },

    ..............

    参考:loader API

    https://webpack.js.org/api/loaders

    如何⾃己编写一个Plugins

    Plugin: 开始打包,在某个时刻,帮助我们处理一些什么事情的机制 plugin要比loader稍微复杂一些,在webpack的源码中,用plugin的机制还是占有非常大的场景,可以说pluginwebpack的灵魂

    l 设计模式

    l 事件驱动

    l 发布订阅

     

    plugin是一个类,⾥面包含一个apply函数,接受一个参数,compiler

     

    案例:

    创建copyright-webpack-plugin.js

    1. plugin实际是一个类(构造函数),通过在plugins配置中实例化进行调用
    2. 它在原型对象上指定了一个apply方法,入参是compiler对象
    3. 指定一个事件钩子,并调用内部提供的API
    4. 完成操作后,调用webpack 提供的callback方法

    class CopyrightWebpackPlugin {  

    constructor() {  }

        // 将 `apply` 定义为其原型方法,此方法以 compiler 作为参数

    apply(compiler) {  }

     }

    module.exports = CopyrightWebpackPlugin;

    配置文件里使⽤

    const CopyrightWebpackPlugin = require("./plugin/copyright-webpack-plugin");

    plugins: [new CopyrightWebpackPlugin()]

    如何传递参数

    //webpack配置⽂文件

    plugins: [    

    new CopyrightWebpackPlugin({      

    name: "你好吗"    

    })  

    ]  

    //copyright-webpack-plugin.js

    class CopyrightWebpackPlugin {  

    constructor(options) {    

    //接受参数    

    console.log(options);  

    }

      apply(compiler) { }

    }

    module.exports = CopyrightWebpackPlugin;

    配置plugin在什么时刻进⾏

    class CopyrightWebpackPlugin {  

    constructor(options) {    

    // console.log(options);  

    }

      apply(compiler) {    

    //hooks.emit 定义在某个时刻

    // 指定要附加到的事件钩子函数

       

    compiler.hooks.emit.tapAsync(      

    "CopyrightWebpackPlugin",      

    (compilation, cb) => {     

    // 使用 webpack 提供的 plugin API 操作构建结果   

    compilation.assets["copyright.txt"] = {          

    source: function() {            

    return "hello copy";          

    },          

    size: function() {            

    return 20;          

    }        

    };        

    cb();      

    } );        

    //同步的写法    

    //compiler.hooks.compile.tap("CopyrightWebpackPlugin", compilation => {   

     //  console.log("开始了了");    

    //});

     }

    }

    module.exports = CopyrightWebpackPlugin;

    参考:compiler-hooks

    https://webpack.js.org/api/compiler-hooks

    实现插件的背景知识

    由上面的步骤可知,插件功能的实现主要依赖于compilercomplation对象,而两者都是继承自Tapable对象。它暴露三种注册监听的方法Tapable对象主要是9种钩子:

    const {

        SyncHook,      

        SyncBailHook,  

        SyncWaterfallHook,

        SyncLoopHook,

        AsyncParallelHook,

        AsyncParallelBailHook,

        AsyncSeriesHook,

        AsyncSeriesBailHook,

        AsyncSeriesWaterfallHook

     } = require("tapable");

    其中同步四种,异步并行两种,异步串行3种。

    同步钩子进行同步操作;异步钩子中进行异步操作。

    compiler和compilation中的钩子都是来自9种钩子。钩子的工作机制类似于浏览器的事件监听。

    1)生成的钩子可以注册监听事件,其中同步钩子通过tap方法监听,异步钩子通过tapAsync(+回调函数)和tapPromise(+返回promise)进行监听。

    2)还可以进行拦截,通过intercept方法。

    3)对于监听事件的触发,同步钩子通过call方法; 异步钩子通过callAsync方法和promise

      

  • 相关阅读:
    CF353D Queue(结论)
    CF1147C Thanos Nim(博弈论、结论推导)
    牛客:CBX and children(平衡树+二分+贪心)
    牛客:Gambling Monster(权值线段树+离散化+离线)
    剑指49.把字符串转换成整数
    剑指48.不用加减乘除做加法
    剑指47.求1+2+3+...+n
    剑指46.孩子们的游戏(圆圈中最后剩下的数字)
    剑指45.扑克牌顺子
    剑指44.翻转单词顺序
  • 原文地址:https://www.cnblogs.com/laneyfu/p/12269965.html
Copyright © 2011-2022 走看看