认识模块化开发
1、模块化最终的目的是将程序划分成为一个个小的结构
2、这个结构中编写属于自己的逻辑代码,有自己的作用域,定义变量名词时不会用影响到其他结构
3、这个结构希望暴露自己的变量、函数、对象等导出给其他结构使用
4、也可以通过某种方式,导入另外结构中的变量、函数、对象
上面提到的结构,就是模块,按照这种结构划分开发程序的过程,就是模块化开发的过程
commonJS
一、commonJS规范和node关系
1、CommonJS是一个规范,最初提出来是在浏览器以外的地方使用,并且当时被命名为ServerJS,后来为了体现其广泛性,修改为CommonJS, 简称为CJS
(1) node是CommonJS在服务器端一个具有代表性的实现;
(2)Browserify是CommonJS在浏览器中的一种实现;
(3)webpack打包工具具备对CommonJS的支持和转换
2、所以,Node中对CommonJS进行了支持和实现,让我们在开发node的过程中可以方便的进行模块化开发:
(1) 在Node中每一个js文件都是一个单独的模块;
(2) 这个模块中包括CommonJS规范的核心变量:exports、module.exports、 require;
我们可以使用这些变量来方便的进行模块化开发;
3、前面我们提到过模块化的核心是导出和导入,Node中对其进行了实现:
(1) exports和module.exports可以负责对模块中的内容进行导出;
(2) require函数可以帮助我们导入其他模块(自定义模块、系统模块、第三方库模块)中的内容;
二、expoerts导出
exports是一个对象,可以在这个对象中添加多个属性,添加的属性会导出:bar.js文件
exports.name = name;
exports.age = age;
exports.num = num;
另一个文件可以导入;main.js文件(.js可以省略)
const bar = require('./bar');
main中的bar变量等于exports对象,也就是require通过各种查找方式,最终找到了exports对象;,并且将这个export对象赋值给了bar变量,bar变量就是exports对象了
三、module.exports导出
1、Node中我们经常导出东西的时候,又是通过module.exports导出的:module.exports和exports有什么关系或者区别呢?
2、我们追根湖源,通过维基百科中对CommonJS规范的解析:
(1)CommonJS中是没有module.exports的概念的;
(2)但是为了实现模块的导出,Node中使用的是Module的类,每一个模块都是Module的一个实例,也就是module;
(3)所以在Node中真正用于导出的其实根本不是exports,而是module.exports;
(4)因为module才是导出的真正实现者;
3、为什么exports也可以导出呢?
(1) 这是因为module对象的exports属性是exports对象的一个引用;
(2) 也就是说 module.exports = exports =main中的bar;
四、require细节
1、require是一个函数,可以帮助我们引入一个文件(模块)中导出的对象
2、require的查找规则:导入格式require(X)
(1)情况一:X是一个Node核心模块,比如path、 http
直接返回核心模块,并停止查找
(2)情况二:X是以./或../或根目录开头的
第一步:将X当作文件在对应的目录下查找
1、如果有后缀名,按照后缀名的格式1查找对应的文件
2、如果没有后缀名,会按照如下顺序:
1>直接查找X文件
2>查找X.js文件
3>查找X.json文件
4>查找X.node文件
第二步:没有找到对应的文件,将X作为一个目录
查找目录下面的index.js文件
1>查找X/index.js文件
2>查找X/index.json文件
3>查找X/index.node文件
如果没有找到,那么报错:not found
(3)情况三:直接是一个X(没有路径)并且不是一个核心模块
ES Module
一、认识ES Module
1、JavaScript没有模块化一直是它的缺陷,所以才会产生我们前面学习的社区规范:CommonJS、AMD、CMD等。
2、ES Module和CommonJS的模块化有一些不同之处:
(1)一方面它使用了import和export关键字;
(2)另一方面它采用编译期的静态分析,并且也加入了动态引用的方式;
3、 ES Module模块采用export和import关键字来实现模块化:
(1) export负责将模块内的内容导出;
(2) import负责从其他模块导入内容;
4、了解:采用ES Module将自动采用严格模式:use strict
二、export关键字
1、 export关键字将一个模块中的变量、函数、类等导出;
2、我们希望将其他中内容全部导出,它可以有如下的方式:
(1)方式一:在语句声明的前面直接加上export关键字
export const name = "fh"
export const age = 18
export function sayHello() {
console.log("sayhello")
}
(2)方式二:将所有需要导出的标识符,放到export后面的{ }中
export {
name,
age,
sayHello
}
注意:这里的(里面不是ES6的对象字面量的增强写法,{ } 也不是表示一个对象的;
所以:export (name: name),是错误的写法;
(3) 方式三:导出时给标识符起一个别名
通过as关键字起别名
export {
name as fname,
age as fage
sayHello as fsayhello
}
三、import关键字
1、import关键字负责从另外一个模块中导入内容(.js不可以省略)
2、导入内容的方式也有多种:
(1)方式一:import (标识符列表)from '模块';
import { name, age, sayHello } from "./foo.js"
注意:这里的{}也不是一个对象,里面只是存放导入的标识符列表内容;
(2)方式二:导入时给标识符起别名
import { name as fname, age, sayHello } from "./foo.js"
通过as关键字起别名
(3)方式三:通过*将模块功能放到一个模块功能对象 (a module object)上
import * as foo from "./foo.js"
四、export和import结合使用
补充:export和import可以结合使用
export {sum as bar} from './bar.js';
(1)在开发和封装一个功能库时,通常我们希望将暴露的所有接口放到一个文件中;
(2)这样方便指定统一的接口规范,也方便阅读;
(3)这个时候,我们就可以使用export和import结合使用;
五、default用法
1、前面我们学习的导出功能都是有名字的导出(named exports)
(1)在导出export时指定了名字;
(2)在导入import时需要知道具体的名字;
2、还有一种导出叫做默认导出 (default export)
(1)默认导出export时可以不需要指定名字;
(2)在导入时不需要使用{},并且可以自己来指定名字;
(3)它也方便我们和现有的CommonJS等规范相互操作;
3、注意:在一个模块中,只能有一个默认导出(default export);
六、import函数
1、通过import加载一个模块,是不可以在其放到逻辑代码中的,比如:
if(true) {
import sub from './bar.js'
}
为什么会出现这个情况呢?
(1)这是因为ES Module在被JS引擎解析时,就必须知道它的依赖关系;
(2)由于这个时候jS代码没有任何的运行,所以无法在进行类似于if判断中根据代码的执行情况;(3)甚至拼接路径的写法也是错误的:因为我们必须到运行时能确定path的值;
2、但是某些情况下,我们确确实实希望动态的来加载某一个模块:
(1)如果根据不同的条件,动态来选择加载模块的路径;
(2)这个时候我们需要使用 import() 函数来动态加载;
注意: import函数返回一个Promise,可以通过then获取结果;
let flag = true;
if(flag) {
import('./foo.js').then(foo => {
foo.foo();
})
} else {
import('./bar.js').then(bar => {
bar.bar();
})
}
七、import meta
1、 import.meta是一个给JavaScript模块暴露特定上下文的元数据属性的对象。
(1)它包含了这个模块的信息,比如说这个模块的URL;
(2)在ES11 (ES2020)中新增的特性;