require 模块化
1. 前言
本节课将会引导大家学习了解:
- 什么是模块化
- require/exports 怎样完结模块化
- import/export 怎样完结模块化
学习完本节课程后,应该具有:
- 运用 require/exports 完结模块化和加载不同依靠库的才能
- 用 import/export 完结模块化的才能
2. 模块化
模块化概念由来已久,跟着前端 JavaScript 脚本代码越来越杂乱、庞大,加之 Node.js 呈现,支撑 JavaScript 成为可用的服务端言语。JavaScript 适应模块化的呼声日益强大。
2.1 什么是模块化编程
模块化编程,就是将完结一个特定功用的代码,分解成若干个独立的、可替换的、具有预订功用的模块。通过调用不同的模块组合来完结不同的实践功用。
2.2 举例
在很多项目中,咱们都能看见一个归于该项目自身的东西库,一般都叫 util.js
或许 utils.js
。里边有很多不同的东西,例如:
- 运用正则校验金额的办法
- 运用正则校验手机号的办法
- 运用正则校验邮箱地址的办法
- 接口响应的格式规范办法
这种将一大堆东西放到某一个文件里集中管理,一起也供任何项目代码调用的思维,也是 模块化编程 思维的一种体现。
2.3 模块化编程有什么优势
模块化编程有以下优势:
- 有利于完结规划:较大较杂乱的问题不通过拆分很难找到完结功用的底子途径,将其运用模块化思维拆分红若干个小问题,咱们就能够从笼统的模块功用而非杂乱功用自身了解。使开发人员能专心于处理每一个小问题,而非被杂乱的大问题困扰。
- 进步开发功率:能够使有经验有才能的开发人员能够专心于中心模块开发,使得一个功用能够多人一起完结。
- 有利于问题排查:代码呈现异常时,能通过由后往前的代码截断来调查实践产生异常的代码段,再由相关人员修复。
- 易维护:需求呈现微调时,能够将微调涉及的模块挑出来调整,不需求修正其他函数,确保稳定性。
- 可复用:笼统出来的办法,能够不需求任何修正就能够用于另一个需求的开发中。
下面咱们来介绍 2 种现在干流的模块化完结方案:require/exports 和 import/export。
3. require/exports
3.1 require/exports 是什么
require/exports 是用作代码模块化的,选用的是 CommonJS 模块规范,也是 Node.js 10 现在仍首要支撑的一种模块化方案。
JavaScript 在 ES6 发布了另一种模块化规范 —— import/export。
这是现在干流的两种模块化方案。
3.2 代码示例
3.2.1 引证
const fs = require('fs');
3.2.2 导出
第一种:module.exports
module.exports = {
exportFn: function() {
console.log('this is the export function');
}
}
第二种:exports
function exportFn() {
console.log('this is the export function');
}
exports.exportFn = exportFn;
导出有两种写法,那么用 module.exports
和 exports
有什么区别呢?
先来看看能不能打印出来:
console.log(module.exports);
console.log(exports);
{}
{}
这么看感觉这两个东西很像,再多打印一个判断条件调查下:
console.log(module.exports === exports);
true
因为在 JavaScript 中,目标类型是 引证类型,参数实践指向的是存放变量的 内存地址。操作符===
只会对 内存地址 一致的变量回来true
。
再来看看赋值后的体现:
从头界说 module.exports
module.exports = {
fn: function() {
console.log('this is a export function');
}
}
console.log(module.exports);
console.log(exports);
console.log(module.exports === exports);
{ fn: [Function: fn] }
{}
false
插值到 module.exports
module.exports.fn = function() {
console.log('this is a export function');
}
console.log(module.exports);
console.log(exports);
console.log(module.exports === exports);
{ fn: [Function] }
{ fn: [Function] }
true
exports
function fn() {
console.log('this is a export function');
}
exports.fn = fn;
console.log(module.exports);
console.log(exports);
console.log(module.exports === exports);
{ fn: [Function] }
{ fn: [Function] }
true
从头界说 module.exports
和 exports
混合运用
function fn() {
console.log('this is a export function');
}
exports.fn = fn;
module.exports = {
fn1: function() {
console.log('this is a export function 1');
}
}
console.log(module.exports);
console.log(exports);
console.log(module.exports === exports);
{ fn1: [Function] }
{ fn: [Function] }
false
插值到 module.exports
和 exports
混合运用
function fn() {
console.log('this is a export function');
}
exports.fn = fn;
module.exports.fn1 = function() {
console.log('this is a export function 1');
}
console.log(module.exports);
console.log(exports);
console.log(module.exports === exports);
{ fn: [Function: fn], fn1: [Function] }
{ fn: [Function: fn], fn1: [Function] }
true
咱们能够总结出以下规矩:
-
exports
是module.exports
的引证,它们指向相同的 内存地址。 - 运用 从头界说
module.exports
导出时,运用了新开辟的 内存空间,使得旧的引证exports
失效,运用exports
导出的模块 和在此之前运用 插值到module.exports
导出的模块也会失效。
建议不要运用 从头界说 module.exports
的方式来导出,因为这样会丢失 exports
的正确引证,导致部分引证丢失的问题。
Tips:
module.exports
和exports
没有本质区别,exports
是 Node.js 为了便利导出而默认界说的引证。但是以module.exports
为准,因为他所用的 内存空间 为实践导出模块的 内存空间。
3.3 调用机遇
require 是运行时调用,归于动态加载,所以能够用在代码的任何一个当地,如:
if (1 + 1 == 2) {
const fs = require('fs');
}
3.4 工作机制
require 实践上就是在赋值,或许说浅仿制(引证类型只仿制内存地址,而不新开辟内存存值)。
因为 require 实践上是在赋值,所以就算咱们不需求运用该模块一切的导出函数,咱们都需求加载整个模块的函数,再提取出需求的函数,如:
const {readFileSync, writeFileSync} = require('fs');
这时,先会加载整个 fs 模块,生成 _fs 目标,再在这个目标中取出 readFileSync
和 writeFileSync
函数。
4. import/export
上文提到 import/export
在 ES6 成为模块化规范,这证明了 require/exports
只是 Node.js 私有的办法。
而在 Node.js 9.0 及以上版本,Node.js 官方支撑了用一种比较奇妙的办法运用 import/export
。
但是在笔者现在编撰文档的时空中,Node.js 发布的最新开发版为 V 13.11.0 import/export
的支撑仍在试验阶段。但 import/export
有或许成为若干版本后的 Node.js 运用的模块化东西,在这里也讲解一下 import/export
的内容。
4.1 import/export 怎样运用
因为这是试验阶段特性,本段内容有或许在日后的重大更新中失真
在 Node.js V 13.11.0 中,假如需求运用 import/export
作为模块化东西,则文件后缀名要改成 .mjs
来标识这是符合 ES 规范的模块。
4.2 代码示例
4.2.1 引进
// 引进默认模块
import fs from 'fs';
// 引进命名模块
import { readFileSync, writeFileSync } from 'fs';
// 引进一切模块
import * as fs from 'fs';
Tips:根据 ES6 的规则,
import
必须在文件开头引进,它前面不能够有任何逻辑代码。
但因为和 require
比较,失去了灵活性。所以 import
提供了 import()
函数来支撑 动态加载。
import()
函数回来一个 promise 目标。
if (1 + 1 == 2) {
import('fs')
.then(function(fs) {
const str = fs.readFileSync('./none_js.txt', 'utf8');
console.log(str);
})
}
4.2.2 导出
default
导出默认模块
export default {
name: 'none_js',
explainNode: function() {
console.log('Node.js is a backend runtime of javaScript');
}
}
导出命名模块
export function fn1() {
console.log('this is another function');
}
function fn2() {
// todo...
}
export { fn2 }
导出默认模块时假如没有命名,引进时能自界说一个名称。
4.3 调用机遇
和 require
不同,import
是编译时调用,归于静态加载,假如引进的模块不存在,将在编译时报错。
理论上 import
只能用在文件头部,如:
import fs from 'fs';
但 import
提供 import()
函数,来支撑动态加载,如:
if (true) {
import('fs')
.then(function(fs) {
// todo...
})
}
4.4 工作机制
import 实践上是在做解构操作,所以咱们不需求运用该模块一切的导出函数,咱们就不需求加载整个模块的函数,如:
import { readFileSync, writeFileSync } from 'fs';
这时,会只从 fs 模块中 readFileSync
和 writeFileSync
函数,其他函数不加载。
和 require
比较而言,import
对性能更加友爱。
5. 小结
本节课程咱们首要学习了 模块化编程、require/exports、import/export。
重点如下:
-
重点1
模块化编程在杂乱系统中十分重要,其优点在于:有利于完结规划、进步开发功率、有利于问题排查、易维护、可复用。
-
重点2
require/exports
归于动态加载,require
用于引进,exports
用于导出,是module.exports
的引证,最终以module.exports
实践指向的目标为准。 -
重点3
import/export
归于静态加载,是 ES6 提出的规范,Node.js 正在试验其落地的或许性,理论上,其性能要强于require/exports
。