webpack下编写loader和plugin

2020-04-28  本文已影响0人  李牧敲代码

【前言】

本文的目的是对loader和plugin的编写有一个宏观的概念,具体的东西之后会增加。
我们都知道loader(就是一个函数)是将不同的文件资源转换为js(将整个文件作为字符串传给loader,然后返回一个JS),插件是通过在监听webpack生命周期中的不同钩子去做一些事情,赋予webpack一些额外的能力。

【本文目录】

1. loader
2. plugin

loader

举个例子:

项目目录:

QQ截图20200428155925.png

我们的webpack配置文件可能是这样的:


//webpack.config.js
const path = require('path');

const HtmlWebpackPlugin = require('html-webpack-plugin');
const MyPlugin = require('my-plugin');

module.exports = {
    entry: ['./src/index.js'],
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    mode:"development",
    module: {
        rules: [
            {
                test: /\.txt$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'my-loader',
                        options: {
                            name: 'my-loader'
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin()
        new MyPlugin()
    ]
}

然后我们可以看到我对.txt文件使用了我自己的loader(my-loader)
然后在当前的node_modeules下新建一个my-loader文件夹(为了能是webpack直接能找到),并通过npm init --y 初始化并新建一个index.js


//index.js


import { getOptions } from 'loader-utils';
import validateOptions from 'schema-utils';

// import path from 'path';

// import memoryfs from 'memory-fs';

// import webpack from 'webpack'



const schema = {
    type: 'object',
    properties: {
        name: {
            type: 'string'
        }
    }
}


export default function loader(source) {
    // let callback = this.async();
    // const textpath = path.resolve(__dirname, 'test.txt');

    // this.addDependency(textpath);

    // fs
    // const options = getOptions(this);

    // fs.readFile(textpath, 'utf-8', function(err, data) {
    //     if(err) {
    //         return callback(err)
    //     }else {
    //         callback(null, `${data} \n ${source}`)
    //     }
    // })



    const options = loaderUtils.getOptions(this);
    validateOptions(schema, options, 'Example loader')
    source = {
        content: source.replace(/\[name\]/g, options.name)
    }

    return `export default ${JSON.stringify(source)}`
}

这里看到我参照官网用了babel,如果你没用babel,就用require好了。其中getOptions就是去拿我们平常传给loader的option的,schema-utils使用来校验option是否合法的。参数source就是我们要处理的文件整个的字符串形式。最后的return就是我们返回的JS(字符串).最后webpack会调用eval或者Function去执行这些字符串的.

再来看下项目入口文件index.js的内容。


//index.js

const b = require('./test.txt').default

console.log('b', b.content)

最后在package.json下面新建一个script:"build":"webpack"

{
  "name": "webpack-test",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.9.0",
    "@babel/plugin-transform-runtime": "^7.9.0",
    "@babel/polyfill": "^7.8.7",
    "@babel/preset-env": "^7.9.5",
    "@babel/runtime": "^7.9.2",
    "babel-loader": "^8.1.0",
    "css-loader": "^3.5.3",
    "html-webpack-plugin": "^4.2.0",
    "style-loader": "^1.2.0",
    "webpack": "^4.43.0"
  }
}


然后运行npm run build
最后用chrome打开dist目录下的index.html:


QQ截图20200428165115.png

看到了么?我将一个.txt文件转成了js,并最终使用!

plugin

plugin是一个构造函数,我们所有的工作就是在这个构造函数的原型上去定义一个一个apply方法,webpack调用插件的时候会去调用这个apply方法的。
同样的按照loader的形式我们在项目根目录下的node_modules下新建一个my-plugin的文件夹,并在里面初始化npm并新建一个index.js文件。


function MyPlugin () {

}

MyPlugin.prototype.apply = function (compiler) {

    compiler.plugin('done', function() {
        console.log('this is my plugin')
    })




    compiler.plugin('compilation', function(compilation) {
        compilation.plugin('optimize', function() {
            console.log("Assets are being optimized.");
        })       
    })



    compiler.plugin('emit', function(compilation, callbacks) {


        let fileList = 'in this build: \n'
        for (let fileName in compilation.assets) {
            fileList += '-' + fileName + '\n'
        }

        compilation.assets['filelist.md'] = {
            source: function() {
                return fileList
            },
            size: function () {
                return fileList.length
            }
        }
        callbacks();

    })

}

module.exports = MyPlugin;

看到上面我调用了一系列的钩子了么?在每个钩子里我们可以拿到一些资源并做一些事情,这就是plugin的大致的原理。
比如:
我在'emit'这个钩子中生成了一个filelist.md的文件,记录了最终在经过webpack打包后所有文件的文件名列表。运行完npm run build后看下dist目录下的filelist.md文件:

in this build: 
-bundle.js
-index.html

看到没,我生成的2个文件都被记录了下来。

【完】

上一篇下一篇

猜你喜欢

热点阅读