CommonJS
-
Node 应用由模块组成,采用 CommonJS 模块规范。
-
在CommonJs规范中,如果想在多个文件分享变量,必须定义为
global
对象的属性(不推荐)
js
global.warning = true;
-
CommonJS规范规定,每个模块内部,
module
变量代表当前模块。这个变量是一个对象,它的exports
属性(即module.exports
)是对外的接口。加载某个模块,其实是加载该模块的module.exports
属性 -
Node内部提供一个
Module
构建函数。所有模块都是Module
的实例。 -
每个模块内部,都有一个
module
对象,代表当前模块。它有以下属性。- module.id 模块的识别符,通常是带有绝对路径的模块文件名。
- module.filename 模块的文件名,带有绝对路径。
- module.loaded 返回一个布尔值,表示模块是否已经完成加载。
- module.parent 返回一个对象,表示调用该模块的模块。
- module.children 返回一个数组,表示该模块要用到的其他模块。
- module.exports 表示模块对外输出的值。
-
require
命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错。 -
如果想得到
require
命令加载的确切文件名,使用require.resolve()
方法。 -
第一次加载某个模块时,Node会缓存该模块。以后再加载该模块,就直接从缓存取出该模块的
module.exports
属性。 -
所有缓存的模块保存在
require.cache
之中,如果想删除模块的缓存,可以像下面这样写。
js
// 删除指定模块的缓存
delete require.cache[moduleName];
// 删除所有模块的缓存
Object.keys(require.cache).forEach(function(key) {
delete require.cache[key];
})
// 缓存是根据绝对路径识别模块的,如果同样的模块名,但是保存在不同的路径,require命令还是会重新加载该模块。
-
如果发生模块的循环加载,即A加载B,B又加载A,则B将加载A的不完整版本。
-
require
方法有一个main
属性,可以用来判断模块是直接执行,还是被调用执行。 -
CommonJS模块的加载机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。请看下面这个例子。
-
require
命令是CommonJS规范之中,用来加载其他模块的命令。它其实不是一个全局命令,而是指向当前模块的module.require
命令,而后者又调用Node的内部命令Module._load
。 -
目前
commonjs
广泛应用于以下几个场景:Node
是 CommonJS 在服务器端一个具有代表性的实现;Browserify
是 CommonJS 在浏览器中的一种实现;webpack
打包工具对 CommonJS 的支持和转换;也就是前端应用也可以在编译之前,尽情使用 CommonJS 进行开发。
ESModule
-
Nodejs
借鉴了Commonjs
实现了模块化 ,从ES6
开始,JavaScript
才真正意义上有自己的模块化规范 -
Es Module 的产生有很多优势,比如:
- 借助
Es Module
的静态导入导出的优势,实现了tree shaking
。 Es Module
还可以import()
懒加载方式实现代码分割。
- 借助
-
ES6 module支持混合导出, 可以使用 export default 和 export 导入多个属性。
重属名导入
js
import { name as bookName , say, author as bookAuthor } from 'module'
重定向导出
可以把当前模块作为一个中转站,一方面引入 module 内的属性,然后把属性再给导出去。
js
export * from 'module' // 第一种方式
export { name, author, ..., say } from 'module' // 第二种方式
export { name as bookName , author as bookAuthor , ..., say } from 'module' //第三种方式
无需导入模块,只运行模块
js
import 'module'
动态导入 - 为了支持这种方式,需要在 webpack 中做相应的配置处理。
js
const promise = import('module')
promise.then((res) => {
...
})
静态语法
-
ES6 module 的引入和导出是静态的,
import
会自动提升到代码的顶层 ,import
,export
不能放在块级作用域或条件语句中。 -
下面是对 import 属性作出总结:
- 使用 import 被导入的模块运行在严格模式下。
- 使用 import 被导入的变量是只读的,可以理解默认为 const 装饰,无法被赋值
- 使用 import 被导入的变量是与原变量绑定/引用的,可以理解为 import 导入的变量无论是否为基本类型都是引用传递。
import() 可以做什么
动态加载
- 首先
import()
动态加载一些内容,可以放在条件语句或者函数执行上下文中。
js
if(isRequire){
const result = import('./b')
}
懒加载
import()
可以实现懒加载,举个例子 vue 中的路由懒加载;
js
[
{
path: 'home',
name: '首页',
component: ()=> import('./home') ,
},
]
React中动态加载
jsx
const LazyComponent = React.lazy(()=>import('./text'))
class index extends React.Component{
render(){
return <React.Suspense fallback={ <div className="icon"><SyncOutlinespin/></div> } >
<LazyComponent />
</React.Suspense>
}
-
React.lazy
和Suspense
配合一起用,能够有动态加载组件的效果。React.lazy
接受一个函数,这个函数需要动态调用import()
。 -
import()
这种加载效果,可以很轻松的实现代码分割。避免一次性加载大量 js 文件,造成首次加载白屏时间过长的情况。
tree shaking 实现
- Tree Shaking 在 Webpack 中的实现,是用来尽可能的删除没有被使用过的代码,一些被 import 了但其实没有被使用的代码。比如以下场景:
Commonjs 和 Es Module 总结
Commonjs 总结
- CommonJS 模块由 JS 运行时实现。
- CommonJs 是单个值导出,本质上导出的就是 exports 属性。
- CommonJS 是可以动态加载的,对每一个加载都存在缓存,可以有效的解决循环引用问题。
- CommonJS 模块同步加载并执行模块文件。
es module 总结
- ES6 Module 静态的,不能放在块级作用域内,代码发生在编译时。
- ES6 Module 的值是动态绑定的,可以通过导出方法修改,可以直接访问修改结果。
- ES6 Module 可以导出多个属性和方法,可以单个导入导出,混合导入导出。
- ES6 模块提前加载并执行模块文件。
- ES6 Module 导入模块在严格模式下。
- ES6 Module 的特性可以很容易实现 Tree Shaking 和 Code Splitting。
PS:如果有需要补充的内容,请在评论区留言
转载时请注明"来自掘金 - EvenZhu"