模块化
什么是commonJS
commonjs是一个用于解决js模块化问题的方案
commonJS
- 模块导出
exports
此为一个空对象,可以为该对象添加任何需要导出的内容 - 模块导入
require
require是一个函数,传入模块的路径即可返回该模块导出的整个内容 - nodejs对CommonJS的实现
commonjs导出模块的具体流程和原理
为了实现CommonJS规范,nodejs对模块做出了以下处理
-
为了保证高效的执行,仅加载必要的模块。nodejs只有执行到
require函数时才会加载并执行模块 -
为了隐藏模块中的代码,nodejs执行模块时,会将模块中的所有代码放置到一个函数中执行,以保证不污染全局变量。
(function(){ //模块中的代码 })() -
为了保证顺利的导出模块内容,nodejs做了以下处理
- 在模块开始执行前,初始化一个值
module.exports = {} module.exports即模块的导出值- 为了方便开发者便捷的导出,nodejs在初始化完
module.exports后,又声明了一个变量exports = module.exports
(function(module){ module.exports = {}; var exports = module.exports; //模块中的代码 return module.exports; })() - 在模块开始执行前,初始化一个值
-
为了避免反复加载同一个模块,nodejs默认开启了模块缓存,如果加载的模块已经被加载过了,则会自动使用之前的导出结果
-
注意模块导出最终导出的是module.exports,如果给module.exports重新赋值一个新对象那么修改exports将不会影响导出,因为exports存储的依然是初始的module.exports对象
commonjs模块化流程概述总说
首先nodejs环境下只能执行一个js文件,这个文件就是整个程序的入口文件,其他的逻辑需要导入其他js文件进行处理
- 当运行到require方法的时候就会根据传入的相对路径去读取相应的文件,为了不污染全局变量(index文件即为全局)全局会隐式的
生成一个module对象 - 在执行导入的时候会将读取的js文件封装在一个立即执行函数中并将module对象作为参数传入这个立即执行函数
- 在执行这个立即执行函数的时候会在执行最开始声明一个 module.exports = {} 对象,为了用户方便操作接下来会 let exports = module.exports
此时exports中就存的是module.exports对象的引用,可以通过 exports.some 的方式各module.exports对象添加属性,此处需要注意的是 由于exports
中存的是隐式声明的 module.exports 对象的引用,如果直接给 module.exports 重新赋值为一个对象然后通过 exports 给module.exports对象添加属性
是无效的,因为exports指向系统声明的module.exports引用,而module.exports已经被赋值为新的对象了,而最后导出的是 module.exports 对象**(m.export变量存储了一个新的对象,export存储的是老的m.export对象,通过export进行赋值,其实是在个老的赋值,他已经不属于m.export变量,导出的是m.export存储的对象,所以导出的是新的对象)** - 执行完毕之后会导出 module.exports对象
- 我猜测导出的这个对象首先会在全局隐式的混合到module对象,但是使用模块化是不考虑模块间的命名冲突的,所以模块间导出的对象可以存在命名冲突的
为了不出现模块间命名冲突发生覆盖,module对象内部在返回结果混合的时候一定使用的是 symbol - 执行完毕的立即执行函数将需要返回的 module.exports 对象返回给全局的module,require方法根据路径拿到返回的结果
- 如果一个模块被多个模块引用可能出现性能问题,所以commonjs做出了模块缓存机制,一个模块被引用过后就会被缓存下来,其他模块引用相同模块的时候就会直接
将缓存的结果返回,这个功能可能用到了闭包 - 模块的加载是同步的,会阻塞后续代码执行
commonjs原理
1. 整体原理
在执行入口文件时,执行到require方法时,通过路径找到js文件,把这些代码放在一个立即执行函数里执行一次然后将需要返回的东西放在一个对象里赋值给require的变量即可。导出的对象即为在模块执行前初始化的module.export对象,模块导出需要用到。
2. 原理
在主模块执行到require的时候会根据路径找到文件,将js文件放在一个立即执行函数里,注入一个module.export对象并将export = module.export,执行这个函数最后导出的是module.export,导出的东西在一个文件里就能有效的避免命名冲突。(注意,commonjs默认缓存模块)
浏览器端模块化(AMD/CMD)
ES6模块化简介
- es6模块化的特点
- 使用依赖预声明的方式导入模块
- 依赖延迟声明(commonjs)
- 优点:某些时候可以提高效率
- 缺点:无法在一开始确定模块依赖关系(比较模糊)
- 依赖预声明
- 优点:在一开始可以确定模块依赖关系
- 缺点:某些时候效率较低
- 依赖延迟声明(commonjs)
- 灵活的多种导入导出方式
- 规范的路径表示法:所有路径必须以./或../开头
- 使用依赖预声明的方式导入模块
es6模块化
- 基本导入导出
-
导出
- export 声明语句
export let a = 1; 类似于 exports.a = 1;- export { age } 将age变量的名称作为导出的名称,age变量的值作为导出的值,注意这个大括号不是对象,可以将之理解为一个template,不具有意义,只是用来放置导出的元素name
-
导入
由于使用的是依赖预加载,因此,导入任何其他模块,导入代码必须放置到所有代码之前对于基本导出,如果要进行导入,使用下面的代码
import {导入的符号列表} from "模块路径"注意以下细节:
- 导入时,可以通过关键字
as对导入的符号进行重命名 - 导入时使用的符号是常量,不可修改
- 可以使用*号导入所有的基本导出,形成一个对象
- 导入时,可以通过关键字
-
import "road" 执行模块代码不使用导出的内容,用于一些只执行一次的文件
-
- 默认导入导出
- 导出
1.
~~~ js
export default obj
~~~
2.
~~~ js
export { a as default } // 重命名
~~~
默认导出只能有一个,而基本导出可以和默认导出共存并存在多个 - 导入
1.
js import 接收变量名 from "模块路径"
2.
~~~ js
import a, { b, c } from "./das" 将基本导出和默认导出分开导入
~~~
3.
~~~ js
import * as a from "./das" // 将所有的导出封装到一个对象中,如果有默认导出则在 a.default 中
~~~
- 导出
模块化
http://localhost:8090/archives/mo-kuai-hua