ES6模块化与工程化、export导出、import导入、CJS模块化、模块对象exports导出require导入

28 篇文章 2 订阅

目录

模块化与工程化

ES6模块化

export导出

         import导入

CJS模块化

模块对象

exports导出

require导入

差异

拓展:(授课不做要求)

path模块

querystring模块

url模块


模块化与工程化

Javascript一直没有模块体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。这对开发大型的、复杂的项目形成了巨大障碍。

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

注意:从 v13.2 版本开始,Node.js 已经默认打开了 ES6 模块支持。在v12的版本里面,需要在package.json中设置"type": "module",

Tip:模块化是项目工程化的前提。

ES6模块化

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。

ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";

Tip:export用来从模块中导出,import用来从模块中导入。

// ES6模块使用示例
import { stat, exists, readFile } from 'fs'; 

export导出

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系,也就是说外部接口需要用这个接口名来引用。

在编写文档的时候编者的node版本是v14.16.0,查看node版本使用node -v命令。

 在src下创建testModule.js文件,内部内容如下:

let firstName = 'Michael'; 
let lastName = 'vicky'; 
// 列表导出
export { firstName, lastName }; 

 运行testModule.js文件,使用node src/testModule.js

node src/testModule.js

正常情况如下:

 如果出现以下错误:

 上图蓝色框中的内容就是解决该错误的方法,在package.json中添加"type": "module",以保证该功能正常运行。

 再次执行node src/testModule.js就可以了。

node src/testModule.js

let firstName = 'Michael'; 
let lastName = 'vicky'; 
// 列表导出
export { firstName, lastName }; 
// 重命名导出
export { firstName as first, lastName as last};
// 导出单个属性 
export let a = 3;	
// 导出一个函数
export function multiply(x, y) { return x * y; }; 
// 默认导出,一个模块只能有一个默认导出,不能使用 var、let 或 const 用于导出默认值 export default。
export default {}
export default function  foo(){}
let b = 1;
// 报错,因为没有提供对外的接口。应该export let b = 1; 或者export {b}
export b;	

需要特别注意的是,export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系,不能直接导出一个值。

import导入

静态的import 语句用于导入由另一个模块导出的绑定。

export default 命令: 使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载,但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出

export default function foo() { 
	console.log('foo'); 
}

其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。

import customName from './export-default'; 
customName(); // 'foo’
export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能对应一个方法或者对象。
// 导入整个模块内容
import * as person from './testExport.js'	
// 导入列表内的多个接口
import {firstName,lastName} from './testExport.js'
// 重命名导入
import {firstName as name} from './testExport.js'	
// 运行整个模块而不导入任何值
import './testExport.js';   
// 导入使用export default导出的模块
import myDefault from './testExport.js';  

测试导出和导入:

src目录下新建testExport.jstestImport.js两个文件,testExport.js文件里用来测试导出,testImport.js用来测试导入。

testExport.js内部代码

let firstName = 'Michael'; 
let lastName = 'vicky'; 
// 列表导出
export { firstName, lastName }; 

testImport.js内部代码

// 导入列表内的多个接口
import { firstName, lastName } from './testExport.js'
// 重命名导入
import { firstName as name } from './testExport.js'
console.log(firstName, lastName, name);
// 结果为 Michael vicky Michael

执行node src/testImport.js命令,输出结果。

node src/testImport.js

 

模块化开发的优点主要有这么几点:

  1. 使用模块化开发能解决文件之间的依赖关系。

    当你引入很多个JS文件的时候,很有可能会不清楚这些JS文件之间的依赖关系,从而导致加载顺序出错。使用模块化开发之后就能避免这个问题。

  2. 使用模块化开发可以避免命名的冲突。

    JS本身是没有命名空间的,为了减少命名冲突,经常使用对象或者闭包来减少命名冲突。对象只能减少命名冲突的概率,闭包的过多使用会造成内存泄漏。模块化开发之后,在模块内任何形式的命名都不会和其他模块的命名产生冲突,有效的解决了命名冲突的问题。

  3. 使用模块化开发能进行代码的复用。

    当我们想要实现某个功能的时候,如果某个模块正好有这个功能,我们就可以直接引用该模块,不必再写多余的代码,这样可以提高代码整体的效率,减少重复冗余的代码。

CJS模块化

CommonJS (CJS) 和 AMD 模块,都只能在运行时确定模块之间的依赖关系,以及输入输出的变量。比如,CommonJS 模块就是对象,输入时必须查找对象属性。

// CommonJS模块 
let { stat, exists, readfile } = require('fs');
// 等同于如下代码块
let _fs = require('fs'); 
let stat = _fs.stat;
let exists = _fs.exists; 
let readfile = _fs.readfile; 

上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。

CommonJS模块化规范,服务器端工作,NodeJS实现了。

模块对象

Node内部提供一个Module构建函数。所有模块都是Module的实例。每个模块内部,都有一个module对象,代表当前模块。它有以下属性,通过module.XXX来使用:

属性说明
id模块的识别符,通常是带有绝对路径的模块文件名
filename模块的文件名,带有绝对路径
loaded返回一个布尔值,表示模块是否已经完成加载
parent返回一个对象,表示调用该模块的模块
children返回一个数组,表示该模块要用到的其他模块
exports表示模块对外输出的值

exports导出

为了方便,Node为每个模块提供一个exports变量,指向module.exports。使用module.exports={}或者exports.XXXX=""来导出。这等同在每个模块头部,有一行这样的命令:

let exports = module.exports;

require导入

require函数是nodejs提供的内置函数,用于加载指定路径的模块或者是指定名称的模块。将加载的模块进行返回,使用如下:

返回一个对象

let path = require('fs');

测试导出和导入:

src下创建testExports.jstestRequire.js文件,testExports.js用来测试导出,testRequire.js用来测试导入。

testExports.js文件内容如下:

// 导出
module.exports = {
  firstName: 'Michael',
  lastName: 'vicky'
};

testRequire.js文件内容如下:

// 导入
const { firstName, lastName } = require('./testExports');
console.log(firstName, lastName);

执行node src/testRequire.js命令,输出结果。

node src/testRequire.js

  

差异

  • ES6 模块输出的是值的引用。 ES6 模块加载的不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。ES6 模块输出的是值的引用

  • CommonJS 模块输出的是一个值的拷贝 CommonJS加载的是一个对象,即module.exports属性,该对象只有在脚本运行完才能生成。CommonJS 模块输出的是值的拷贝

  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口

验证CommonJS导出的是值的拷贝:

//testCommonJS.js
let firstname = 'ren';
let lastname = 'terry';
setTimeout(() => {
  firstname = 'zhao';
}, 2000);
module.exports = {
  firstname: firstname,
  lastname: lastname
  // 对象属性的简写方式
  /* firstname,
  lastname */
};

//useCommonJS.js
let { firstname, lastname } = require('./testCommonJS.js');
console.log(firstname, lastname);
setTimeout(() => {
  console.log(firstname, lastname);
}, 4000)
node src/useCommonJS.js 
ren terry
ren terry

验证ES6导出的是值引用:

//testES6.js
let firstname = 'ren';
let lastname = 'terry';
setTimeout(() => {
  firstname = 'zhao';
}, 2000);
export { firstname, lastname };

//useES6.js
import { firstname, lastname } from './testES6.js'
console.log(firstname, lastname);
setTimeout(() => {
  console.log(firstname, lastname);
}, 4000);
node src/useES6.js
ren terry
zhao terry

拓展:(授课不做要求)

path模块

path 模块提供了一些工具函数,用于处理文件与目录的路径,使用如下方法引用:

var path = require(‘path’);

path.basename() 该方法返回一个参数路径的最后一部分

path.dirname() 该方法返回一个 path 的目录名

path.extname() 该方法返回 path 的扩展名,即从 path 的最后一部分中的最后一个 .(句号)字符到字符串结束。

path.isAbsolute() 该方法会判定 path 是否为一个绝对路径。

path.join() 该方法使用平台特定的分隔符把全部给定的 path 片段连接到一起,并规范化生成的路径

path.normalize() 该方法会规范化给定的 path,并解析 '..' 和 '.' 片段

path.delimiter 该属性提供平台特定的路径分隔符

querystring模块

querystring 模块提供了一些实用函数,用于解析与格式化 URL 查询字符串。使用如下方法引用

var querystring = require('querystring');

querystring.stringify(obj[, sep[, eq]]) 将对象转换为查询字符串

obj 要序列化成 URL 查询字符串的对象。

sep 用于界定查询字符串中的键值对的子字符串。默认为 '&'。

eq 用于界定查询字符串中的键与值的子字符串。默认为 '='。

querystring.parse(str[, sep[, eq]]) 将查询字符串转换为对象

url模块

url模块提供了一些实用函数,用于 URL 处理与解析。 可以通过以下方式使用

var url = require('url');

url.parse() 将一个url地址转换为一个对象

url.resolve() 该方法会以一种 Web 浏览器解析超链接的方式把一个目标 URL 解析成相对于一个基础 URL

url.resolve('/one/two/three', 'four'); // '/one/two/four'

url.resolve('Example Domain', '/one'); // 'http://example.com/one'

url.resolve('http://example.com/one', '/two'); // 'http://example.com/two'

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值