node采用commonJS实现模块管理
module.exports
- node同一进程内只有一个上下文,不同文件内部只是node对js文件进行的封装,源代码被包裹在一个自执行函数中,文件内部的 require 和 module 等由node注入自执行函数中。
- 标准的导出命令只有 module.exports, exports 其实是个语法糖,本质上 exports = module.exports,所以才能通过exports导出内容
1 | // a.js |
require
require命令第一次加载脚本文件时,就自动执行整个脚本,然后在内存中生成一个对象,形如:1
2
3
4
5
6
7
8
9
10
11
12
13
14{
// 模块名
id: 'a',
// 模块输出的接口
exports: {
log: function(){},
...
},
// 脚本是否执行完
loaded: true,
...
}
这个对象会被缓存起来。
- 如果需要用到这个模块,则到exports上取值使用
- 如果重复require同一个模块,则node从系统缓存中取值,返回第一次脚本执行的结果,除非手动清除系统缓存。
commonJS vs ESModule
commonJS 输出的是一个值的拷贝,一旦输出,模块内部变化不会影响这个值; ESModule输出的是值的引用,随时反应模块内部值的变化
- ESModule export 输出的是接口, 和接口对应的值是动态绑定关系; commonJS module.exports输出的是值的缓存,不能动态更新
commonJS 在代码运行时才会加载; ESModule只是一个静态定义,在代码静态分析时就会生成。
- import 具有最高优先级,会被js引擎做静态分析优化,在模块中最先执行,必须置于模块顶层,因此无法做条件判断(有个提案叫import()可完成异步动态加载)
- require 运行时加载, 同步
commonJS中顶层this指向当前模块;ESModule顶层this指向undefined
1 |
|
ESModule 加载 commonJS
1 | // a.js |
tips: commonJS的缓存机制依然有效
commonJS 加载 ESModule
- import()提案
- babel-register转码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39// react ssr
// 引入babel-register来转码react组件
require('babel-register')({
// 忽略哪些文件
ignore(filename) {},
presets: [ 'env', 'react', 'stage-0' ],
plugins: [
'transform-decorators-legacy',
[
'module-alias',
[
// alias别名配置
{ src: './src/xx', expose: '$XX' },
]
],
[
'babel-plugin-transform-require-ignore',
{
extensions: [ '.scss' ]
}
],
[
'transform-runtime',
{
helpers: false,
polyfill: false,
regenerator: true,
moduleName: 'babel-runtime'
}
]
]
});
// react本身有适配
const React = require('react');
// 业务组件ESModule
const Card = require('./src/card').default;