突然想看CJS的导入导出源代码了

目前为止,前端的模块化方案基本趋于2类,一类是CommonJs,一类是ESModule。当然,我见过的厂,也有些使用AMD规范的。

CommonJs的导入导出语法如下:

node 复制代码
let a = require('./a.js');

module.exports = {
    add: function(){}
}

ESModule的导入导出语法如下:

javascript 复制代码
import a from './a.js';

export ...

export default ...;

但是今天突然心血来潮,我想看一下require是如何实现的,所以我就随便建了个工程,然后打了包。

工程目录如下:

javascript 复制代码
| - moduleProject
    | - index.js
    | - module1.js
    | - module2.js
    | - module11.js

这4个文件的关系如下:

1、index文件依赖 module1、module2文件。

2、module1、module2文件都依赖module11文件。

module11.js文件内容如下:

javascript 复制代码
// module11.js文件内容如下
module.exports = {
    add11: function(a, b){
        return a + b;
    }
}

module1.js文件内容如下:

javascript 复制代码
// module1.js文件内容如下:
let a1 = require('./module11.js');

module.exports = {
    add1: function(a, b){
        return a + b;
    }
}

module2.js文件内容如下:

javascript 复制代码
// module2.js文件内容如下:
let a2 = require('./module11.js');

module.exports = {
    add2: function(a, b){
        return a + b;
    }
}

index.js文件内容如下:

javascript 复制代码
let obj1 = require('./module1');
let obj2 = require('./module2');

console.log(obj1.add1(1, 2));

然后我们将整个工程打包一下,此时省略打包配置,打包后的文件如下:

从上图中我们可以看到啊,首先它会将入口文件的依赖项配置成一个大的对象,key就是你引入的文件名,value就是每个文件的内容。

然后入口index文件在执行过程中如果使用了某个依赖方法,那就会去全局对象里get对应的 moduleId,以此来完成相应操作,所以commonjs默认它做不了tree shaking。因为整个过程就好比你定义了一个对象,这个对象在打包后的文件里确确实实存在,对象里有个a属性,但是这个a属性你并没有用到,那你认为这个a属性会凭空消失吗?那肯定不会啊。

这里还有个说法叫做"编译阶段",这个编译阶段说白了就是"编译后文件",只是听起来高大尚一点。

这个require函数也是相当简单,源码如下:

javascript 复制代码
// 1、先声明一个全局对象
let _webpack_module_cache_ = {};

function require(moduleId){
    // 2、看看当前依赖项之前是否被导入过,如果被导入过,就直接返回之前导入的结果。
    let cacheModule = _webpack_module_cache_[moduleId];
    if (cacheModule !== undefined){
        return cacheModule;
    }
    // 3、对当前模块进行初始化,并且将它放入到缓存里
    let module = _webpack_module_cache_[module] = {
        exports: {}
    }
    // 4、执行当前引入的模块
    _webpack_module[moduleId](module, module.exports, require);
    // 5、返回当前模块导出的对象
    return module.exports;
}

好啦,本期文章到这里就结束啦,如果这篇文章能够帮助到你,欢迎给个红心一键三连,我们下期再见啦,拜拜~~

相关推荐
Summer不秃2 分钟前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰6 分钟前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
Viktor_Ye13 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm15 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
乐闻x41 分钟前
Vue.js 性能优化指南:掌握 keep-alive 的使用技巧
前端·vue.js·性能优化
一条晒干的咸魚43 分钟前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
花海少爷1 小时前
第十章 JavaScript的应用课后习题
开发语言·javascript·ecmascript
Amd7941 小时前
Nuxt.js 应用中的 webpack:compiled 事件钩子
前端·webpack·开发·编译·nuxt.js·事件·钩子
生椰拿铁You1 小时前
09 —— Webpack搭建开发环境
前端·webpack·node.js
狸克先生1 小时前
如何用AI写小说(二):Gradio 超简单的网页前端交互
前端·人工智能·chatgpt·交互