事实上模块化最终的目的就是将程序划分为一个个小小的结构,这个接口可以将自己希望暴露的变量、函数、对象等,这里提到的结构,就是模块,按照这种结构划分开发程序的过程,就是模块化开发的过程。
CommonJs是一个规范,Node是CommonJS在服务端的一个具有代表性的实现。在Node中每一个js文件都是一个单独的模块。这个模块包括CommonJS规范的变量:exports、module.exports、require
exports是一个对象,是对象,就会在内存中开辟一个存储空间,将这个对象存到对应的空间里,我们也就可以在对象中添加很多个属性。另一个文件中可以用require导入,require的本质是一个函数,可以帮助我们引入一个文件(模块)中导入的对象(下面会说)
CommonJS中是没有module.exports的概念的,但是为了实现模块的导出,Node中使用的是Module的类,每一个模块都是Module的一个实例,也就是 module。所以在Node中真正用于导出的其实根本不是exports,而是module.exports,因为module才是导出的真正实现者。
我们从上面知道,require本质是一个函数,可以帮助我们引入一个文件(模块)中导入的对象。
导入格式:require(*)
其中 * 可以是一个核心模块,比如path、http等
*也可以是意 ./或者…/或者(跟目录)开头的
**注意:**如果是一个没有路径的 * 引入,并且也不是一个核心模块,那就会报 not found 的错误。
模块在被第一次引入时,模块中的js代码会被运行一次,模块被多次引入时,会缓存,最终只运行加载一次,如果是循环引入,那么加载的顺序就是一个图结构中的深度优先搜索遍历(如图)
最终顺序是:mian–>a–>c–>d–>e–>b
ES Module 使用了import和export关键字,采用编译器的静态分析,也加入了动态引用的方式。其中,exports负责将模块内的内容导出,import负责从其他模块导入内容。
注意: 采用ES Module将自动采用严格模式:**use strict **
export关键字将一个模块中的变量、函数、类等导出。它有如下三种导出方式
方式一: 在语句声明的前面直接加上export关键字
方式二:将所有需要导出的标识符,放到export后面的 {}
注意:这里{}不是一个对象!!!是一个语法
方式三: 导出时给标识符起一个别名
import关键字负责从另外一个模块中导入内容。它有如下三种导入方式
方式一:import {标识符列表} from ‘模块’;
注意:这里的{}也不是一个对象,里面只是存放导入的标识符列表内容。
方式二:导入时给标识符起别名。
方式三:通过 * 将模块功能放到一个模块功能对象(a module object)上
在ES Module 中还有一种导出叫默认导出,默认导出是不需要指定名字的,在导入时不需要使用{},并且可以自己来指定名字
注意:在一个模块中,只能有一个默认导出(default export)
我们通过import 来加载模块时,是不可以将其放到逻辑代码中的,如果我们确确实实希望动态的来加载一个模块,这个时候就需要用到 import() 函数来动态加载。
注意:加了()的import就是一个函数了
CommonJS | ES Module | |
---|---|---|
加载过程 | 运行时加载,是同步的 | 解析时加载,是异步的 |
**CommonJS **运行时加载意味着是js引擎在执行js代码的过程中加载模块,同步的就意味着一个文件没有加载结束之前,后面的代码都不会执行。
ES Module加载js文件的过程是编译(解析)时加载的,并且是异步的,编译时(解析)时加载,意味着import不能和运行时相关的内容放在一起使用。①比如from后面的路径需要动态获取,②比如不能将import放到if等语句的代码块中。所以我们有时候也称ES Module是静态解析的,而不是动态或者运行时解析的。(异步意味着 JS引擎在遇到import时会去获取这个js文件,但是这个获取的过程是异步的,并不会阻塞主线程继 续执行 )
**ES Module **加载加载过程中,通过export导出的是变量本身的引用。export在导出一个变量时,js引擎会解析这个语法,并且创建模块环境记录(module environment record),模块环境记录会和变量进行 绑定(binding),并且这个绑定是实时的,而在导入的地方,我们是可以实时的获取到绑定的最新值的。
path模块用于对路径和文件进行处理,提供了很多好用的方法。可用于处理不同系统上路径不同的问题。
注意:尽量不要使用手动拼接路径,要让 node 帮我们执行
① 从路径中获取信息:
② 路径拼接:
③ 将文件和某个文件拼接
注意: resolve函数会判断我们拼接的路径前面是否有 /或…/或./ 开头的路径
有:表示一个绝对路径,会返回对应的拼接路径
没有:会和当前执行文件所在的文件夹路径进行拼接
借助于Node帮我们封装的文件系统,我们可以在任何的操作系统(window、Mac OS、Linux)上面直接去 操作文件。
① 文件系统的API大多数都是提供三种操作方式:
② 文件的读写(对文件内容进行操作)
其中,option中可填入flag(写入的方式)选项和 encoding(字符编码)
③ 文件夹操作
注意: Node中的核心API都是基于异步事件驱动,在这个体系中,某些对象( 发射器(Emitters))发射出某一个事件,我么可以监听(listenners)到这个时间,并且传入回调函数,这个回调函数会在监听到事件时来调用。
**① EventEmitter类 **
** ② EventEmitter的实例的一些属性 **
Node Package Manager,也就是Node包管理器。
官网:npm包的官网
包的存放:我们发布自己的包其实是发布到registry上面的,当我们安装一个包时也是从registry上面下载的包
在node环境下面(无论前端还是后端)配置文件都是 package.json
① 常见属性:
注意: 那么在生成环境如何保证不安装这些包呢?
答:生成环境不需要安装时,我们需要通过 npm install --production 来安装文件的依赖
npm的包通常需要遵循semver版本规范
① semver版本规范是X.Y.Z:
② 常见属性:
项目安装会在当前目录下生产一个 node_modules 文件夹
① 安装开发和生产依赖
② 开发依赖
③ 根据package.json中的依赖包
① 卸载某个依赖包
② 强制重新build
**③ 清除缓存 **
yarn 是为了弥补 npm 的一些缺陷而出现的