文章目录
commonJS和ES6模块化
面试题
- commonJS的特性
- commonjs和es6的模块化区别
- commonjs 和 esm 是怎么解决循环引用问题的
- 模块化了解过吗?延伸出说一下CommonJS和Es Modulde的底层原理?
commonJS
common规范的应用
- node 是CommonJS在服务器端一个具有代表性的实现
- Browserify 是CommonJS在浏览器中的一种实现
- webpack打包工具对CommonJS的支持和转换,前端应用在编译之前使用CommonJS进行开发
commonjs的特点
- 在
commonjs中每个js文件都是一个单独的模块 - 使用
require()方法加载其他模块时,会执行被加载模块中的代码 module.exports指向暴露的对象,exports是简写情况,默认情况下,exports和module.exports指向同一个对象exports==module.exports,如果不同module.exports为准- CommonJS 同步加载并执行模块文件,CommonJS 模块在执行阶段分析模块依赖。采用深度优先算法。
require()可以在任意的位置,动态加载(运行时加载)模块,不会提升到最开头
commonJs实现原理
需要关注的问题
- 如何解决变量污染的问题
script标签直接引入的方式,没有模块化,script内部的变量是可以相互渲染的
- module.exports,exports,require 三者是如何工作的?又有什么关系?
模块执行的原理
- 在编译过程中,commonJs对js的代码块进行了收尾包装
每个模块文件上存在require、module、exports方法,但是这三个变量在文件中是没有定义的。 Commonjs 会将我们写代码包装起来,形成包装函数,require、module、exports本质是通过形参的方式传递到包装函数中的。
require: 引入模块的方法
module: 记录当前模块信息
exports:当前模块导出的属性 - 在模块加载的时候,会传入
require、module、exports等参数
const sayName = require('./hello.js')
module.exports = function say(){
return {
name:sayName(),
author:'ranan'
}
}
//包装后
(function(exports,require,module,__filename,__dirname){
const sayName = require('./hello.js')
module.exports = function say(){
return {
name:sayName(),
author:'ranan'
}
}
})
require模块加载原理
- CommonJS 模块同步加载并执行模块文件,CommonJS 模块在执行阶段分析模块依赖。采用深度优先算法。
require()可以在任意的位置,动态加载(运行时加载)模块,不会提升到最开头。
module :在 Node 中每一个 js 文件都是一个 module ,module 上保存了 exports 等信息之外,还有一个 loaded 表示该模块是否被加载,没有加载过就先缓存后执行,已经加载过,就直接去缓存不用执行来避免循环引用问题。
- 为 false 表示还没有加载;
- 为 true 表示已经加载
/*
根据文件标识符,先查找有没有缓存,有缓存直接返回缓存的内容
没有缓存,创建module对象,将其缓存到module上,然后加载文件,将 loaded 属性设置为 true ,然后返回 module.exports 对象。
*/
// id 为路径标识符
function require(id) {
/* 查找 Module 上有没有已经加载的 js 对象*/
const cachedModule = Module._cache[id]
/* 如果已经加载了那么直接取走缓存的 exports 对象 */
if(cachedModule){
return cachedModule.exports
}
/* 创建当前模块的 module */
const module = { exports: {} ,loaded: false , ...}
/* 将 module 缓存到 Module 的缓存属性中,路径标识符作为 id */
//只会在第一次加载时运行一次,后面都会从缓存中读取,
Module._cache[id] = module
/* 加载文件 */
runInThisContext(wrapper('module.exports = "123"'))(module.exports, require, module, __filename, __dirname)
/* 加载完成 *//
module.loaded = true
/* 返回值 */
return module.exports
}
Es Module
Es Module
- Es Module 的静态导入导出的优势,实现了 tree shaking
- Es Module 还可以 import() 懒加载方式实现代码分割。
ES6 module的特性
ES6 module的引入和导出是静态的,import会自动提升到代码的顶层import,export不能放在块级作用域或条件语句中。
这种静态语法,在编译过程中确定了导入和导出的关系,所以更方便去查找依赖,更方便去 tree shaking
- ES6 模块提前加载并执行模块文件,ES6 模块在预处理阶段分析模块依赖,在执行阶段执行模块
- 使用
import导入的变量是只读的,可以理解默认为 const 装饰,无法被赋值 - 使用
import导入的变量是与原变量绑定/引用的,可以理解为 import 导入的变量无论是否为基本类型都是引用传递。
import() 函数 动态引入
import() 返回一个 Promise 对象, 返回的 Promise的 then 成功回调中,可以获取模块的加载成功信息。
特点
import()动态加载一些内容,可以放在条件语句或者函数执行上下文中
if(isRequire){
const result = import('./b')
}
import()可以实现懒加载
[
{
path: 'home',
name: '首页',
component: ()=> import('./home') ,
},
]
import() 可以很轻松的实现代码分割。避免一次性加载大量 js 文件,造成首次加载白屏时间过长的情况。
commonJs和es module的区别
| - | commonJs | es module |
|---|---|---|
| 导入方式 | require()动态加载模块,可以在任意的位置,不会被提升到最前面 | 导入方式分为静态导入和动态导入 静态导入:不能放在块级作用域和条件中,会提升到最前面 动态导入 import()类似require(),但他是异步加载的 |
| 构建模块依赖的时期 | require同步加载并执行模块文件,CommonJS 模块在执行阶段分析模块依赖。 | 在编译阶段就建立起了模块之间的依赖关系 ES6 模块在预处理阶段分析模块依赖,在执行阶段执行模块 |
| 输出的值 | 输出值的拷贝值,一旦输出了某个值,如果模块内部发生变化,不会影响外部的值 | 输出的是值的引用,JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行的时候,再根据这个只读引用,到被加载的那个模块里去取值。所以内部发生变化会影响外部的值。 |
版权声明:本文为qq_41370833原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。