vue.config.js简单实现loader和plugin

394 阅读3分钟

Webpack手写loader和plugin

上述代码示例

loader: 对css js img 特殊文件vue 各种模块的处理

统一原则: 遵循Webpack制定的设计规则和结构,输入与输出均为字符串,各个Loader完全独立,即插即用

loader默认导出一个函数,接受匹配到的文件资源字符串和SourceMap,我们可以修改文件内容字符串后再返回给下一个loader进行处理,因此最简单的一个loader如下:

module.exports = function(source, map){
    return source
}

异步loader: 有时候会进行异步操作,一种方法是通过async/await,阻塞操作执行;另一种方法可以通过loader本身提供的回调函数callback

处理参数: 配置loader时,一般是通过options属性来传递的.借助一个官方的包loader-utils帮助处理

获取options参数

const options = getOptions(this);

示例: 生产环境清除console.log

  1. 根目录创建loader文件夹,创建cleanlog-loader.js
const { getOptions } = require('loader-utils')

module.exports = function(source, map){
	const option = getOptions(this)
	console.log('清除')
	if (process.env.NODE_ENV === 'production' && option.clear) {
		source = source.replace(/console\.log\(.*\);?/g, "")
	}
    return source
}
  1. 使用cleanlog-loader, 放在configureWebpack函数形式 + exclude排除项。

/console.log(.*);?/g 正则不适合 node_modules 里面的文件

configureWebpack: (config) => {
    config.module.rules.push({
        test: /\.(js|vue)$/,
        exclude: /node_modules/,
        use: [{
            loader: './loader/cleanlog-loader',
            options: {
                clear: true
            }
        }]
    })		
}

plugin 对webpack 编译或构建功能的扩展

plugin的本质是类. 工作流程如下:

  1. webpack启动,执行new myPlugin(options),初始化插件并获取实例
  2. 初始化complier对象,调用myPlugin.apply(complier)给插件传入complier对象
  3. 插件实例获取complier,通过complier监听webpack广播的事件,通过complier对象操作webpack

可以通过apply函数中注入的compiler对象进行注册事件

compiler不仅有同步的钩子,通过tap函数来注册,还有异步的钩子,通过tapAsynctapPromise来注册

这里又有一个compilation对象,它和上面提到的compiler对象都是Plugin和webpack之间的桥梁:

  • compiler对象包含了 Webpack 环境所有的的配置信息。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。
  • compilation对象包含了当前的模块资源、编译生成资源、变化的文件等。当运行webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。

  compiler和compilation的区别在于:

  • compiler代表了整个webpack从启动到关闭的生命周期,而compilation只是代表了一次新的编译过程
  • compiler和compilation暴露出许多钩子,我们可以根据实际需求的场景进行自定义处理

示例: 在打包目录生成一个filelist.md文件,文件的内容是将所有构建生成文件展示在一个列表中

  1. 根目录创建plugins/FileListPlugin.js
class FileListPlugin {
	constructor (options = {}) {
		this.options = options
	}
	
    apply(compiler){
        compiler.hooks.emit.tapAsync('FileListPlugin', (compilation, callback)=>{
    
            var filelist = 'In this build:\n\n';
            // 遍历所有编译过的资源文件,
            // 对于每个文件名称,都添加一行内容。
            for (var filename in compilation.assets) {
                filelist += '- ' + filename + '\n';
            }
            // 将这个列表作为一个新的文件资源,插入到 webpack 构建中:
            compilation.assets['filelist.md'] = {
                source: function() {
                    return filelist;
                },
                size: function() {
                    return filelist.length;
                }
            };
            callback();
        })
    }
}
module.exports = FileListPlugin
  1. FileListPlugin 使用

vue.config.js 添加config.plugins.push(new FileListPlugin())

const FileListPlugin = require('./plugins/FileListPlugin')
configureWebpack: (config) => {
    config.plugins.push(new FileListPlugin())
}