Node.js

二、JavaScript模块化开发

2020-10-26  本文已影响0人  AShuiCoder

什么是模块化呢?

没有模块化带来很多的问题

CommonJS和Node

CommonJS是一个规范,最初提出来是在浏览器以外的地方使用,并且当时被命名为ServerJS,后来为了
体现它的广泛性,修改为CommonJS,平时我们也会简称为CJS

所以,Node中对CommonJS进行了支持和实现,让我们在开发node的过程中可以方便的进行模块化开发:

模块化的核心是导出和导入,Node中对其进行了实现:

理解对象的引用赋值

let obj = { name: 'kobe', age: 18 }
let info = obj
微信截图_20201024153845.png

module.exports和exports

导入和导出的原理就是对象的引用。
在node中,每个js文件都是一个new Module的实例,该实例名字赋值为module,所以在模块内能访问它,模块内的导出操作都是由module.exports来负责的,为什么exports也能实现导出操作呢?node默认做了一个操作module.exports = exports,一旦模块内将module.exports重新赋值,exports就失效了。

require细节

require是一个函数,可以帮助我们引入一个文件(模块)中导入的对象。和es6的import是不一样的。

查找规则

导入格式如下:require(X)

情况一:X是一个核心模块,比如path、http

直接返回核心模块,并且停止查找

情况二:X是以 ./ 或 ../ 或 /(根目录)开头的

情况三:直接是一个X(没有路径),并且X不是一个核心模块

先在该文件所在目录的平级目录查找node_modules里有没有该包,有返回,无往上一级找node_modules,以此类推,直到找到硬盘的根路径为止,找不到直接报错 not found

模块的加载过程

node的require函数加载是同步的。

这个其实是一种数据结构:图结构;图结构在遍历的过程中,有深度优先搜索(DFS, depth first search)和广度优先搜索(BFS, breadth first
search);
Node采用的是深度优先算法:main -> aaa -> ccc -> ddd -> eee ->bbb

ES Module

不能以file协议打开es module的代码,会报错,用http协议访问,如vscode可以用live-server插件运行。
在浏览器环境下运行es module代码:

<script src="main.js" type="module"></script>

加入一个属性type="module",就表示该文件以module方式运行,会自动加入一个async属性,即es module是异步的


关键字export表示导出

const name = "why";
const age = 18;
const sayHello = function(name) {
  console.log("你好" + name);
}
// 1.方式一:
export const name = "why";
export const age = 18;
export const sayHello = function(name) {
  console.log("你好" + name);
}

// 2.方式二:
export {
  name,
  age,
  sayHello
}

// 3.方式三: {} 导出时, 可以给变量起名别
export {
  name as fName,
  age as fAge,
  sayHello as fSayHello
}

注意:export后面跟着的花括号并不是对象,这是一个新语法,虽然看上去有点像es6对象的简写。即import引入的也不是对象。


关键字import表示导入

// 方式一: import {} from '路径';
import { name, age, sayHello } from './modules/bar.js';

// 方式二: 导出变量之后可以起别名
import { name as wName, age as wAge, sayHello as wSayHello } from './modules/foo.js';

// 方式三: * as foo
import * as foo from './modules/foo.js';

Export和import结合使用

export和import可以结合使用:

export {name, age, sayHello} from './foo.js';

为什么要这样做呢?

default用法

还有一种导出叫做默认导出(default export)

import函数

通过import加载一个模块,是不可以在其放到逻辑代码中的,比如:

if (flag) {
  import xxx from xxx
}

这样写会直接报错,原因请看后面的es module加载过程。
但是某些情况下,我们确确实实希望动态的来加载某一个模块,es module提供了一个import函数,返回一个promise

let flag = true;
if (flag) {
  // require的本质是一个函数
  // require('')

  // 执行函数 
  // 如果是webpack的环境下: 模块化打包工具: es CommonJS require()
  // 纯ES Module环境下面: import()
  // 脚手架 -> webpack: import()
  import('./modules/foo.js').then(res => {
    console.log("在then中的打印");
    console.log(res.name);
    console.log(res.age);
  }).catch(err => {

  })
}

CommonJS的加载过程

CommonJS模块加载js文件的过程是运行时加载的,并且是同步的。
CommonJS通过module.exports导出的是一个对象。

ES Module加载过程

ES Module加载js文件的过程是编译(解析)时加载的,并且是异步的。

异步的意味着:JS引擎在遇到import时会去获取这个js文件,但是这个获取的过程是异步的,并不会阻塞主线程继
续执行;

ES Module通过export导出的是变量本身的引用:

所以,如果在导出的模块中修改了变化,那么导入的地方可以实时获取最新的变量。
注意:在导入的地方不可以修改变量,因为它只是被绑定到了这个变量上(其实是一个常量)。

思考:如果bar.js中导出的是一个对象,那么main.js中是否可以修改对象中的属性呢?
答案是可以的,因为他们指向同一块内存空间。


微信截图_20201024175938.png

Node对ES Module的支持

在最新的Current版本(v14.13.1)中,支持es module我们需要进行如下操作:

在最新的LST版本(v12.19.0)中,我们也是可以正常运行的,但是会报一个警告。

CommonJS和ES Module交互

通常情况下,CommonJS不能加载ES Module

多数情况下,ES Module可以加载CommonJS

上一篇 下一篇

猜你喜欢

热点阅读