我们知道在nodejs中使用common.js进行模块化开发,但是在浏览器中并不支持,那么webpack在打包代码时,遇见common.js代码时,会模拟exports、require,比如:
// index.jsvar add = require('add.js').defaultconsole.log(add(1 , 2))// add.jsexports.default = function(a,b) {return a + b}
直接在浏览器中运行以上代码肯定会出问题,因为浏览器中并没有exports和require对象,那么webpack是怎么模拟他们的呢?
exports
既然我们想将一段代码共享给另一个模块使用,那么肯定得把他们打包起来,这里我们的common.js代码可以通过fs.readFileSync来读取,这会将代码转换为字符串,我们直接把这个字符串传给另一个模块不就行了?然后再通过eval来运行这段字符串代码!
转换后的代码:
exports = {}eval('exports.default = function(a,b) {return a + b}') // node文件读取后的代码字符串exports.default(1,3)// 为了保证变量不会污染,使用IIFE来包装var exports = {}(function (exports, code) {eval(code)})(exports, 'exports.default = function(a,b){return a + b}')
require
require函数的功能时将模块导入,就是根据file名称加载对应模块,下面以导入add模块为例
function require(file) {var exports = {};(function (exports, code) {eval(code)})(exports, 'exports.default = function(a,b){return a + b}')return exports}var add = require('add.js').defaultconsole.log(add(1 , 2))
如果我们有N个模块需要导入呢,这个时候就需要生成一个map,根据key-value来存储模块了,key为路径,value为对应的模块。
(function (list) {function require(file) {var exports = {};(function (exports, code) {eval(code);})(exports, list[file]);return exports;}require("index.js");})({"index.js": `var add = require('add.js').defaultconsole.log(add(1 , 2))`,"add.js": `exports.default = function(a,b){return a + b}`,});
webpack在打包的时候还会生成依赖图集,这里是利用babel将字符串转换为AST,然后从入口递归解析出代码里的import、export语句,进行依赖收集生成依赖map,然后利用babel将es6转换为es5。
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");// 转化AST语法树const ast = parser.parse(body, {sourceType: "module", //表示我们要解析的是ES模块});// 依赖收集const deps = [];traverse(ast, {ImportDeclaration({ node }) {deps.push(node.source.value)},});// ES6转成ES5const { code } = babel.transformFromAst(ast, null, {presets: ["@babel/preset-env"],});const moduleInfo = { file, deps, code };return moduleInfo;}const info = getModuleInfo("./src/index.js");console.log("info:", info);
