关于webpack:webpack实战手写loader和plugin

7次阅读

共计 5279 个字符,预计需要花费 14 分钟才能阅读完成。

序言

对于 webpack 来说,loaderplugin 能够算是需要水平最为宽泛的配置项了。然而呢,单单止步于配置可能还不够。如果咱们本人有时候想要 diy 一个需要,然而 webpack 又没有相干的 loaderplugin。那这个时候咱们可能就得开始造点轮子来供应本人应用了。

因而,在明天的文章当中,将率领大家手写一个繁难的 loaderplugin,并学会如何在我的项目中使用本人所编写的 loaderplugin

一起来学习吧~📢

一、如何编写一个 Loader

1. 碎碎念

之前的文章中咱们讲到了对于 loader 的一些配置。那如果把那些援用的 loader 改为咱们写的 loader,该怎么解决呢?

当初,咱们来理解一下,如何手写一个繁难的 loader,并使用到咱们的我的项目当中。

2. 我的项目构造

首先用一张图,来看咱们的 我的项目构造 如下图所示:

其中 loaders 文件夹下搁置咱们想要写的 loader,同时外面的 replaceLoader.js 文件搁置咱们行将要写的 loader 的代码逻辑。之后,index.js 文件是咱们的 入口文件,搁置咱们的业务逻辑。webpack.config.js 文件搁置对于 webpack 的相干配置,而 dist 文件夹内的内容,搁置的是咱们通过 webpack 打包后,生成的打包文件。

相干 webpack 视频解说:进入学习

3. 业务代码编写

(1)入口文件代码

当初,咱们先来编写入口文件 index.js 的代码。具体代码如下:

console.log('hello monday');

(2)编写 loader

入口文件的内容很简略,咱们想要达到的目标就是输入 hello monday 这个语句。当初,咱们来编写 loader 的内容,已达到对入口文件 index.js 的内容进行批改。replaceLoader.js 文件的 代码具体如下:

module.exports = function(source) {const result = source.replace('monday', 'mondaylab');
    this.callback(null, result);
}

以上的代码意思为,将入口文件 index.js 文件中的 monday 替换为 mondaylab。这样写仿佛没啥问题,然而大家有没有想过,咱们有时候传的属性可能会很诡异,不肯定每次都能像这样以字符串的模式来替换。

所以,咱们援用 webpack 官网举荐的 loadertils 这个工具,来解决这个问题。

第一步: 装置 loader-utils 插件。具体命令如下:

npm install loader-utils --save-dev

第二步: 革新 loader 文件。接下来,咱们对 replaceLoader.js 文件进行革新降级,具体代码如下:

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

// 用 function 的起因在于为了业务层能够调用 this
//source 为引入文件的源代码
module.exports = function(source) {
    //getOptions 会主动地帮咱们剖析 this.query, 而后把参数的所有内容放在 options 外面去
    const options = loaderUtils.getOptions(this);

    const result = source.replace('monday', options.name);

    this.callback(null, result);
}

大家能够看到,通过应用 loaderUtils 插件,间接地,调用 getOptions 办法,来主动的帮咱们剖析 this.query,从而取到咱们想要的内容。

值得注意的是,咱们还须要再理解一下 this.callback 的内容。

个别状况下,如果咱们接管到了源代码 source,那么当初咱们只能对源代码做解决。然而呢,有的时候,咱们想要应用一些 sourceMap,或者对源代码剖析好了之后,咱们不仅想要返回源代码,还要把 sourceMap 也带回去。

因为咱们 return 的时候只能 return 一个参数,其余的一些额定的内容就带不进来了。这个时候呢,咱们就须要 this.callback 来帮咱们把 sourceMap 给带进来。因而,个别用 this.callback 来返回内容。

(3)援用 loader

当初,咱们在 webpack.config.js 中,来引入咱们下面的 loader具体配置如下:

const path = require('path');

module.exports = {
    mode: 'development',
    entry: {main: './src/index.js'},
    module: {
        rules: [{
            test: /\.js/,
            use: [
                {loader: path.resolve(__dirname, './loaders/replaceLoader.js'),
                    // 下面的 options.name 中的 name
                    options: {name: 'mondaylab'}
                }   
            ]
        }]
    },
    output: {path: path.resolve(__dirname, 'dist'),
        filename: '[name].js' 
    }
}

通过以上形式,咱们写了一个繁难的 loader,这个 loader 实现了将 monday 替换为 mondaylab 的性能。并且供咱们在 webpack 中应用本人书写的 loader

(4)在 loader 外面做一些异步的操作

好了当初,如果咱们想要给 loader 做一些异步操作,该怎么实现呢?

在咱们所写的 loader 当中,退出异步操作,那么咱们须要调用官网提供给咱们的 this.async() 这个 API 来实现。当初,咱们来革新一下 replaceLoader.js 文件的代码。具体代码如下:

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

module.exports = function(source) {const options = loaderUtils.getOptions(this);
    // 调用 this.async()这个 API,来给异步代码应用
    const callback = this.async();

    setTimeout(() => {const result = source.replace('monday', options.name);
        callback(null, result);
    }, 1000);
}

通过这种形式,咱们就能够在 loader 中编写异步代码,来达到咱们想要的成果。

(5)loader 门路自定义

有一个很小的留神点就是,当咱们在配置 webpack.config.js 文件中,loader 的门路时,每回都要 path.resolve 去寻找门路文件。文件少的时候还好,但如果遇到多文件的时候呢?岂不是会很麻烦。

所以,咱们援用 resolveLoader 来简化它。当初咱们在 webpack.config.js 文件中进行革新。具体配置如下:

const path = require('path');

module.exports = {
    // 先到 node_modules 中去找,找不到则去./loaders 目录上来找
    resolveLoader: {modules: ['node_modules', './loaders']
    },
    module: {
        rules: [{
            test: /\.js/,
            use: [
                {loader: 'replaceLoader'}
            ]
        }]
    }
}

通过配置 resolveLoader,来对文件文件目录进行查找,从而简化了门路内容。

二、如何编写一个 Plugin

1. 碎碎念

在解说 plugin 之前,咱们先来理解 loaderplugin 的区别。

当咱们在源代码外面,去引入一个新的 js 文件,或者是一个其余格局的文件时,这个时候咱们能够借用 loader,来帮咱们解决咱们援用的 loader 文件。loader 的作用就在于,帮忙咱们 解决援用的模块

plugin 呢,是当咱们在做打包的时候,在 某些具体时刻上,比如说,当咱们打包完结之后,咱们要生成一个 html 文件,这个时候,咱们就能够应用一个 htmlWebpackPlugin 的插件。应用它之后,他就会在打包完结之后,帮咱们生成对应的 html 文件。

再比方,咱们要在打包之前,把 dist 目录进行清空,这个时候咱们就能够应用 cleanWebpackPlugin 来帮忙咱们做这件事件。

所以,plugin 插件,在什么时候失效呢?

它在咱们 打包过程中的某些时刻里 ,就是 插件失效的场景

plugin 的编写绝对于 loader 来说,会难一点点。然而呢,如果有看过 webpack 源码的小伙伴们可能会晓得,webpack 的一些底层原理都是根据 plugin 来进行编写的。所以,咱们还是有必要来学习一下 plugin 的编写。

上面就率领大家来编写一个繁难的 plugin ~

2. 我的项目构造

对于 webpackplugin 来说,它是是基于 发布者订阅 的设计模式,也能够说是基于 事件驱动 来实现的。在这个事件驱动里,代码之间的执行,是通过事件来进行驱动的。

接下来,咱们就来写一个繁难的 plugin

首先用一张图,来看咱们的 我的项目构造 如下图所示:

其中 plugins 文件夹下搁置咱们想要写的 plugin,同时外面的 copyright-webpack-plugin.js 文件搁置咱们行将要写的 plugin 的代码逻辑。之后,index.js 文件是咱们的 入口文件,搁置咱们的业务逻辑。webpack.config.js 文件搁置对于 webpack 的相干配置,而 dist 文件夹内的内容,搁置的是咱们通过 webpack 打包后,生成的打包文件。

3. 业务代码编写

(1)入口文件代码

当初,咱们先来编写入口文件 index.js 的代码。具体代码如下:

console.log('hello monday');

(2)编写 plugin

当初,咱们来编写 plugin 的内容,copyright-webpack-plugin.js 文件的 代码具体如下:

class CopyrightWebpackPlugin {
    // 编写一个结构器
    constructor(options) {console.log(options)
     }

    apply(compiler) {
        // 遇到同步时刻
        compiler.hooks.compile.tap('CopyrightWebpackPlugin',() => {console.log('compiler');
        });

        // 遇到异步时刻
        // 当要把代码放到 dist 目录之前,要走上面这个函数
        //Compilation 寄存打包的所有内容,Compilation.assets 搁置生成的内容
        compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin', (Compilation, cb) => {
            debugger;
            // 往代码中减少一个文件,copyright.txt
            Compilation.assets['copyright.txt'] = {source: function() {return 'copyright by monday';},
                size: function() {return 19;}
            };
            cb();})
    }
}

module.exports = CopyrightWebpackPlugin;

下面的这个插件中想要实现的性能就是,获取版权信息

(3)援用 plugin

当初,咱们在 webpack.config.js 中,来引入咱们下面的 plugin具体配置如下:

const path = require('path');
const CopyrightWebpackPlugin = require('./plugins/copyright-webpack-plugin');

module.exports = {
    mode: 'development',
    entry: {main: './src/index.js'},
    plugins: [
        new CopyrightWebpackPlugin({name: 'monday'})
    ],
    output: {path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    }
}

通过上述代码,咱们能够理解到,在(2)中,咱们首先须要 定义一个类,之后呢,在类中写一个结构器和一个 apply() 办法来调用。而后呢,大家看到(3),通过 require 的形式,来进行 new 实例,实例化一个插件,从而在我的项目中应用这个插件。

最终,咱们我的项目进行打包时,就会生成一个 dist 目录,并且在目录下减少一个 copyright.txt 文件,并且文件中的内容就是 copyright by monday

三、结束语

在下面的文章中,解说了对于 loader 和 plugin 的根本编写思路,以及如何在我的项目中对他们进行使用,置信大家对这一块内容有了根底的意识。

到这里,loader 和 plugin 的编写解说就完结啦!心愿对大家有帮忙~

正文完
 0