ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

实现一个 webpack

2022-01-26 15:32:01  阅读:140  来源: 互联网

标签:function exports const 实现 require add js 一个 webpack


在浏览器中是不能够直接使用模块化的,尽管现在已经支持了Es Module ,但是还需要进一步的转换。webpack 可以将我们的模块进行打包成 bundle 文件 ,为浏览器能够识别的语法。

add.js 文件

exports.default = (a, b) => a + b;

index.js 文件

const add = require('add.js').default;
console.log(add(2, 4));

上面的 exports 和 require 浏览器是不能够识别的。但是我们可以模拟一个 exports 和 require 来在浏览器上运行。

const exports = {}; // 在 CommonJS 中 exports 指向一个对象.
exports.default = function (a, b) {return a + b};
exports.default(2, 4); 

上面的代码可以正常的运行,并且得到 6,但是这个相当于 add.js 文件将会暴露在全局下面。如果 add.js 文件还有其他的变量,将会造成环境的污染。所以我们需要提取到一个函数作用域下。

const exports = {};
(function (exports, code) {
	eval(code);
})(exports, 'exports.default = function (a, b) {return a + b}');

使用 eval 是因为,Node 使用 fs读取文件读取出来的是字符串。所以,我们需要借助eval 来解析字符串。

接下来我们完成 require 来模拟导入。

function request() {
	const exports = {};
	(function (exports, code) {
		eval(code);
	})(exports, 'exports.default = function (a, b) {return a + b}');
}

这样子,我们就完成了仿写require,但是这种写死的,我们需要的是可以根据依赖来进行导入文件。

const list = {
	'add.js': `exports.default = function (a, b) {return a + b}`,
	'index.js': `
		var add = require('add.js').default;
	    console.log(add(2, 4));
	`
}
function request(file) {
	const exports = {};
	(function (exports, code) {
		eval(code);
	})(exports, list[file]);
}
require('index.js');

这样子,我们就可以先从 index.js 开始,加载index.js的文件,然后加载过程中,又遇到一个 require ,然后加载 add.js 文件,执行自执行 add.js 文件代码。

我们可以发现,上面的代码在全局下暴露了 listrequest 我们可以继续使用函数作用域。

(function (list) {
	function request(file) {
		const exports = {};
		(function (exports, code) {
			eval(code);
		})(exports, list[file]);
		return exports;
	}
	request('index.js');
})({
	'add.js': `exports.default = function (a, b) {return a + b}`,
	'index.js': `
		var add = require('add.js').default;
	    console.log(add(2, 4));
	`
})

现在就完成了一个类似的可以在浏览器运行的。但是我们的 list 是写死的,我们需要自动读取文件,自动收集依赖,打包 bundle.js 。我们可以使用 Node 来完成。

实现。

我们的实现可以分为三部分:收集依赖、ES6转ES5、替换 exports 和 require。

我们使用 Es Module 的 import 和 export 关键字来导入导出。
add.js 文件

export default (a, b) => a + b;

index.js 文件

import add from 'add.js';
console.log(add(2, 4));

我们要想收集依赖,首先得知道入口文件依赖了哪些文件。根据 import 关键字就可以知道应该要收集依赖项了。

const fs = require('fs');
const path = require('path');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const babel = require('@babel/core');

function getModuleInfo(file) {

	const body = fs.readFileSync(file, 'utf-8');
	
	const ast = parser.parse(body, {
		sourceType: 'module'
	});
	
	const deps = {};
	
	traverse(ast, {
		ImportDeclaration({node}) {
			const dirname = path.dirname(file);
			const abspath = './' + path.join(dirname, node.source.value);
			deps[node.source.value] = abspath;
		}
	})
	
	const {code} = babel.transformFromAst(ast, null, {
		presets: ['@babel/preset-env']
	});
	
	const moduleInfo = {file, deps, code};
	
	return moduleInfo;
	
}

function parseModules(file) {
	const entry = getModuleInfo(file);
	const temp = [entry];
	const depsGraph = {};
	getDeps(temp, entry);
	temp.forEach(info => {
		depsGraph[info.file] = {
			deps: info.deps,
			code: info.code
		}
	})
	return depsGraph;
}

function getDeps(temp, {deps}) {
    Object.keys(deps).forEach((key) => {
        const child = getModuleInfo(deps[key])
        temp.push(child);
        getDeps(temp, child);
    })
}

function bundle(file) {
    const depsGraph = JSON.stringify(parseModules(file));
    return `
    (function (graph) {
        function require(file) {
            function absRequire(relPath) {
                return require(graph[file].deps[relPath])
            }
            var exports = {};
            (function (require, exports, code) {
                eval(code);
            })(absRequire, exports, graph[file].code)
            return exports;
        }
    })(${depsGraph})
    `;
}
const content = bundle('./src/index.js');
!fs.existsSync('./dist') && fs.mkdirSync('./dist');
fs.writeFileSync('./dist/bundle.js', content);

标签:function,exports,const,实现,require,add,js,一个,webpack
来源: https://blog.csdn.net/m0_60703077/article/details/122701918

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有