1.模块化
1-1.什么是模块化:
将一大段代码按照规则拆分成一个个代码模块,模块内部的变量等都是私有的。通过向外暴露一些接口来进行通信。
1-2.没有模块化带来的问题
1-2-1.全局污染
1-2-2.依赖管理混乱
2.CommonJS
2-1.CommonJS基本使用
2-1-1.CommonJS使用环境:
node环境或者webpack打包工具对CommonJS进行支持和转换
2-1-2.exports、module.exports、require
exports 和 module.exports 可以负责对模块中的内容进行导出;
require 函数可以帮助我们导入其他模块(自定义模块、系统模块、第三方库模块)中的内容;
// main.js
// 使用另一个模块导出的对象,就需要导入 require
// const { name, age, foo } = require("./part.js")
const bar = require("./part.js")
console.log('bar', bar, bar.money)
// foo()
// console.log(name, age)
// part.js
const name = 'zhangsan'
const age = 21
function foo(){
console.log('执行foo')
}
const money = '¥237847364738984'
//1.导出方案 module.exports
module.exports = {
name,
age,
foo,
money
}
console.log('modulemodule', module)
2-2.CommonJS实现原理
我们可以在 Commonjs 规范下每一个 js 模块上直接使用符合CommonJS规范的变量,如exports、module.exports、require、__filename 和 __dirname 变量。
module记录当前模块信息。require引入模块的方法。exports、module.exports当前模块导出的属性
2-2-1.exports 和 module.exports
exports 就是传入到当前模块内的一个对象,本质上就是 module.exports。
exports可以理解成 exports = module.exports
但是,当在另外一个模块中导入当前模块中的导出的数据时,require返回的是module.exports,所以,exports={} 直接赋值一个对象时,被另外一个模块导入的不是这个新对象,而是原来的module.exports的值。
当对一个模块中的数据进行导出时,若 exports 和 module.exports 同时存在,则可能出现覆盖的情况。
exports.name = 'alien' // 此时 exports.name 是无效的
// 由于最终导出的值以 module.exports 为准,这里对module.exports进行了重新赋值,而不是修改其属性,所以exports.name 是无效的
module.exports = {
name:'《React进阶实践指南》',
author:'我不是外星人',
say(){
console.log(666)
}
}
2-2-2.require
2-2-2-1.require加载的文件种类(按来源):
nodejs底层的核心模块- 我们编写的文件模块
- 我们通过
npm下载的第三方自定义模块
const fs = require('fs') // ①核心模块
const sayName = require('./hello.js') //② 文件模块
const crypto = require('crypto-js') // ③第三方自定义模块
require接受参数作为标志符- 像
fs,http,path等标识符,会被作为nodejs的核心模块 ./和../作为相对路径的文件模块,/作为绝对路径的文件模块- 非路径形式也非核心模块的模块,将作为自定义模块
2-2-2-2.node核心模块处理:
核心模块的优先级仅次于缓存加载,在 Node 源码编译中,核心模块已被编译成二进制代码。
2-2-2-3.路径形式文件模块处理:
以 ./ ,../ 和 / 开始的标识符,会被当作文件模块处理。在首次加载后会产生缓存,后续加载相同模块会直接从缓存中读取。
没有后缀名时,查找顺序:文件X->X.js->X.json->X.node
没有找到当以上文件时,将X作为一个目录,查找目录中的index文件(index.js->index.json->index.node)
2-2-2-4.第三方自定义模块处理:
第三方自定义模块的查找遵循以下规则:
在当前目录下的node_module目录下查找,若没找到,则在父级目录的node_module文件目录下查找若没找到,依次向上查找,知道找到该模块或者到达根目录的node_module目录。
2-2-2-5.require模块加载与处理
CommonJS模块同步加载并执行模块中的代码,在执行模块代码的过程中分析模块的依赖关系。采用深度优先搜索策略。
例如:
main.js文件中,require('./a.js')
a.js文件中,require('./b.js')
b.js文件中,require('./a.js')
此时执行main.js文件,那么b.js文件中的代码会先执行完,但是此时b.js不能使用a.js文件导出的数据等。
3.ESModule
3-1.ESModule基本使用
<!-- html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 注意1. -->
<!-- 当直接使用script标签引入一个js文件时,该js文件不会被当成一个模块,此时不允许该js文件中使用import -->
<!-- 所以,必须在script标签中写上type属性 -->
<!-- 注意2. -->
<!-- 使用模块化代码时,需要使用liveServer来打开页面。 -->
<!-- 当直接打开本地html文件时,浏览器链接的协议是file,无法正确加载模块 -->
<!-- 当使用live Server打开文件时,浏览器链接的协议不是file,而是http\https等,此时才能正确加载模块 -->
<script src="./main.js" type="module"></script>
</body>
</html>
// main.js
import { name ,num } from "./part1.js"
console.log('name', name)
console.log('num', num)
// part1.js
export const name = 'part1'
export const num = 1
3-2.其他使用方法
<!-- html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./main.js" type="module"></script>
</body>
</html>
3-2-1.三种导入方式:普通导入,起别名导入、将导出的所有内容放到一个标识符中
export声明语句导出
// main.js
// 普通导入
import { name ,num, sayName, Part1 } from "./part1.js"
console.log('name', name)
console.log('num', num)
sayName()
// main.js
// 导入方式2:起别名
import { name as name1 ,num as num1, sayName as sayName1 } from './part1'
console.log(name1, num1)
sayName1()
// main.js
// 将导出的所有内容放到一个标识符中
import * as part1 from './part1'
console.log(part1.name)
3-2-2.三种导出方式:起别名导入,声明和export导出分开,导出时起别名
// part1.js
// 导出方式:export 声明语句
export const name = 'part1'
export const num = 1
export function sayName() {
console.log('My name is part1')
}
export class Part1 {}
// part1.js
// 声明和export导出分开
const name = 'part1'
const num = 1
function sayName() {
console.log('My name is part1')
}
class Part1 {}
export {
name,
num,
sayName,
Part1
}
// part1.js
// 导出时起别名
const name = 'part1'
const num = 1
function sayName() {
console.log('My name is part1')
}
class Part1 {}
export {
name as part1Name,
num as part1Num,
sayName as part1SayName,
Part1 as part1Part1
}
3-3.结合使用
<!-- html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- utils文件夹中,index.js作为其他所有工具变量的统一出口 -->
<script src="./main.js" type="module"></script>
</body>
</html>
文件目录:
// main.js
import { timeFormat, priceFormat } from "./utils/index.js";
import { add, sub } from './utils/index.js'
console.log(timeFormat())
console.log(priceFormat())
console.log(add(2, 1))
console.log(sub(2, 1))
// index.js
// 此文件为utils文件夹下所有工具函数中变量的统一出口
// 第一种导出方式
// import { add, sub } from './math.js'
// import { timeFormat, priceFormat } from './format.js'
// export {
// add,
// sub,
// timeFormat,
// priceFormat
// }
// 第二种导出方式
// export { add, sub} from './math.js'
// export { timeFormat, priceFormat } from './format.js'
// 第三种导出方式
export * from './math.js'
export * from './format.js'
// format.js
function timeFormat() {
return '20220104'
}
function priceFormat() {
return 1122334455667788
}
export {
timeFormat,
priceFormat
}
// math.js
function add(num1, num2) {
return num1 + num2
}
function sub(num1, num2) {
return num1 - num2
}
export {
add,
sub
}
3-4.default的使用
<!-- html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./main.js" type="module"></script>
</body>
</html>
// main.js
import part1Name from './part1'
import { sayName } from './part1'
console.log(part1Name)
sayName()
// 默认导出default
const name = 'part1'
function sayName() {
console.log('我是part1')
}
// 方式一:
// export {
// name as default,
// sayName
// }
// 方式二:
export default name
export { sayName }
// 注意:
// 1.一个文件中只能有一个默认导出
3-5.import函数
// main.js
// 1.从part1.js中导入了值时,part1会先解析完,再执行当前文件之后的代码
// import { name, sayName } from './part1'
// console.log('开始执行main中的代码')
// console.log(name)
// sayName()
// 2.import函数:不让导入的文件阻塞当前文件中代码的执行
// import函数的返回值是一个promise,then中的就是part1文件中导出的值
import('./part1').then(res => {
console.log('res', res)
})
// 3.import有一个mate属性,mate是一个对象,mate.url是当前文件的的路径
console.log(import.mate, import.mate.url)
// part1.js
console.log('开始执行part1中的代码')
const name = 'part1'
function sayName() {
console.log('我是part1')
}
export { name, sayName}
参考文献: