webpack-底层原理(Loader编写Plugin编写 Bundler编写)

Posted 火腿肠烧烤大赛冠军

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了webpack-底层原理(Loader编写Plugin编写 Bundler编写)相关的知识,希望对你有一定的参考价值。

Loader编写

  • 编写replaceLoader.js
//不可以写箭头函数
module.exports = function(source) {
    //this.query种存放参数(可以用loader-util帮助分析)
	return source.replace('lee', 'world');
	//异步方法
    const callback = this.async();
	setTimeout(() => {
		const result = source.replace('dell', options.name);
		callback(null, result);
	}, 1000);
}

在引入loader时做一些事情

	resolveLoader: {
		modules: ['node_modules', './loaders']
	},

这样就可以直接这样写路径了

		rules: [{
			test: /\\.js/,
			use: [
				{
					loader: 'replaceLoader',
				},
				{
					loader: 'replaceLoaderAsync',
					options: {
						name: 'lee'
					}
				},
			]
		}]

Plugin编写

class CopyrightWebpackPlugin {
	constructor(a){
		// a为接受的参数
	}
	//自动执行compiler可以理解为webpack实例compilation为当次打包实例
	apply(compiler) {
		//同步时刻
		compiler.hooks.compile.tap('CopyrightWebpackPlugin', (compilation) => {
			console.log('compiler');
		})
		//emit为钩子异步时刻
		compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin', (compilation, cb) => {
			//打断点 然后查看compilation内的参数
			debugger;
			//新增文件
			compilation.assets['copyright.txt']= {
				//内容
				source: function() {
					return 'copyright by dell lee'
				},
				//大小
				size: function() {
					return 21;
				}
			};
			cb();
		})
	}

}

module.exports = CopyrightWebpackPlugin;

文档:compiler 钩子举例文档
创建plugin对象:copyright-webpack-plugin
之后使用即可
debugger

Bundler编写(模块分析部分)

//获取文件信息
const fs = require('fs');
const path = require('path');
//分析语法(抽象语法树)
const parser = require('@babel/parser');
//解析抽象语法树
const traverse = require('@babel/traverse').default;
//代码转换
const babel = require('@babel/core');
//接收入口文件内容
const moduleAnalyser = (filename) => {
	//读文件
	const content = fs.readFileSync(filename, 'utf-8');
	const ast = parser.parse(content, {
		//ES6解析方式
		sourceType: 'module'
	});
	const dependencies = {};
	traverse(ast, {
		ImportDeclaration({ node }) {
			//获取绝对路径
			const dirname = path.dirname(filename);
			//相对路径转化为相对于根目录绝对路径
			const newFile = './' + path.join(dirname, node.source.value);
			// 同时存入相对路径和绝对路径
			dependencies[node.source.value] = newFile;
		}
	});
	//翻译成浏览器可执行文件
	const { code } = babel.transformFromAst(ast, null, {
		presets: ["@babel/preset-env"]
	});
	return {
		filename,
		dependencies,
		code
	}
}

const moduleInfo = moduleAnalyser('./src/index.js');
console.log(moduleInfo);

高亮显示代码

Bundler编写(Dependencies Graph)

//依赖图谱
const makeDependenciesGraph = (entry) => {
	const entryModule = moduleAnalyser(entry);
	const graphArray = [ entryModule ];
	//数组递归
	for(let i = 0; i < graphArray.length; i++) {
		const item = graphArray[i];
		const { dependencies } = item;
		if(dependencies) {
			for(let j in dependencies) {
				graphArray.push(
					moduleAnalyser(dependencies[j])
				);
			}
		}
	}
	const graph = {};
	graphArray.forEach(item => {
		graph[item.filename] = {
			dependencies: item.dependencies,
			code: item.code
		}
	});
	return graph;
}

Bundler编写(生成可运行代码)

const generateCode = (entry) => {
	//将字符串转化为对象
	const graph = JSON.stringify(makeDependenciesGraph(entry));
	return `
		(function(graph){
			function require(module) { 
				//相对路径转化函数
				function localRequire(relativePath) {
					return require(graph[module].dependencies[relativePath]);
				}
				var exports = {};
				//引入当前模块文件
				(function(require, exports, code){
					//执行代码
					eval(code)
				})(localRequire, exports, graph[module].code);
				return exports;
			};
			require('${entry}')
		})(${graph});
	`;
}

okk~

以上是关于webpack-底层原理(Loader编写Plugin编写 Bundler编写)的主要内容,如果未能解决你的问题,请参考以下文章

Webpack

webpack配置之自定义loader

webpack 总结

webpack原理篇(六十):使用 loader-runner 高效进行 loader 的调试

webpack-loader原理

webpack5原理loader概述