Js-CommonJs模块化&ES6模块化
- 将程序文件依据一定的规则拆分成多个文件,这种编码方式就是模块化的编码方式
- 拆分出来的每个文件就是一个模块,模块中的数据都是私有的,模块之间互相隔离
- 主流的模块化规范:ES6模块化、CommonJs模块化
- 模块化的核心思想就是:模块直接是隔离的,通过导入和导出的方式进行数据和功能的共享
- 导出:使模块公开其内部的一部分,使这些内容可以被其他模块使用
- 导入:模块引用和使用其他模块导出的内容
CommonJs模块化-在node js环境下
CommonJS 主要是为服务端设计的。在 Node.js 中,每个.js 文件被视为一个 CommonJS 模块。
Node.js 有自己的模块加载系统,它会自动识别
require
语句并加载对应的模块。当你在 Node.js 中使用require
时,它会按照一定的规则在本地文件系统中查找模块文件
CommonJs主要用于服务端的Js运行环境(node的默认模块化规范,新版的node已经支持es6的模块化,但它默认任然使用的是CommonJs)
导出(暴露)
每个模块内部的this、exports、modules.exports在初始化时,都指向一个空对象,该对象就是当前模块导出的数据
-
使用exports导出
const info = "info" const test = "test" function getInfo() { return info } // 导出(暴露当前模块的指定属性方法) 导出自定义属性、方法,值是指定的值、方法 // exports默认是一个空对象 exports.info = info exports.t = test exports.getInfo = getInfo
-
使用module.exports导出
module.exports.info = info // 等同于exports.info = info
// 可以直接赋值一个对象 module.exports = { info, test, getInfo }
-
使用this (node js)
this.info = info
无论如何修改导出对象,最终导出的都是module.exports的值
exports是对module.exports的初始引用,仅为了方便给导出对象添加属性,所以不能使用exprots = {}的方式导出数据
引入
require
是用于导入其他模块的函数。在 Node.js 环境下,一个文件就是一个 CommonJS 模块,通过require
可以将其他模块的功能引入到当前模块中使用
// require跟要引入的路径,data是一个对象,包含引入的数据、方法
const data = require('./a.js')
// 调用
data.info()
// 直接解构
const {info,test} = require('./a.js')
// 调用
info()
数据隔离原理
在 CommonJS 中,每个文件被视为一个独立的模块,模块拥有自己独立的作用域。这就好比每个模块都被装在一个独立的盒子里,盒子里的变量、函数等元素不会自动暴露在盒子外面,也不会和其他盒子里的内容相互干扰
可以理解每个文件被自动封装到一个函数里面,每个模块有自己的作用域,然后有module等参数,将要暴露的对应数据传入到module中,在其他模块使用module中的数据
CommonJS 采用同步加载模块的方式。当一个模块(比如
moduleA
)通过require
加载另一个模块(比如moduleB
)时,会暂停moduleA
的执行,先去加载并执行moduleB
。在这个过程中,moduleB
的变量和函数定义都在自己的作用域内进行,不会影响moduleA
已经定义的变量和函数
CommonJS 有模块缓存机制。当一个模块第一次被加载后,其导出的对象(通过
module.exports
)会被缓存起来。再次加载同一个模块时,直接从缓存中获取,而不是重新执行模块的代码。这种缓存机制不仅提高了性能,还进一步加强了模块间的数据隔离
浏览器端运行
CommonJs简称为CJS,最初是给服务端使用的,NodeJs默认支持CJS,但是浏览器端默认不支持,可以借助browserify编译,支持浏览器端运行
-
安装browserify
npm i browserify -g
-
编译文件
# a.js是要编译的目标文件,-o生成到当前目录, build_a.js 是编译后要生成的文件名 browserify a.js -o build_a.js
ES6模块化
es6模块化规范是一个官方标准,在语言标准的层面上实现了模块化功能,浏览器、服务端均支持该规范
基本使用
-
暴露
const info = "info" // 在想暴露出去的数据、方法前 写export关键字 export const test = "test" export function getInfo() { return info }
-
引入
// import 表示导入 // * 代表所有, as data 表示全部数据赋值给一个名字data的对象 // from 后跟对应模块路径 import * as data from './a.js' // 调用 data.test
-
在标签中
<!-- type="module" 属性,表明引入的 b.js 文件是作为一个 ES 模块 --> <!-- 当type="module"时,JavaScript 代码以模块的形式存在,每个模块都有自己独立的作用域。这就好比每个模块是一个独立的房间,房间里的变量和函数不会自动暴露到外面的 “全局空间” 中 --> <script type="module" src="./b.js"></script>
在node中运行ES6模块
第一种方式:
模块文件后缀修改为.mjs,引入什么都是.mjs
第二种方式:
在package.json文件中配置
{"type":"module"}
导出数据
分别导出
每个需要导出的数据前 + exports ,就是分别导出
统一导出
// export后面是包含当前模块所有需要导出的内容
export {info,test,getInfo}
默认导出
一个文件中只能有一个默认导出
// default默认导出,后面直接跟value 相当于{default:1000}
export default 1000
// {default:"aaaa"},info是当前文件变量,值是aaaa
export default info
// 默认导出一个对象,{default:{name:'aaaa',count:1000}}
export default {
name: info,
count: 1000
}
多种导出方式混用
// 分别导出
export function getInfo() {
return info
}
// 统一导出
export {info}
// 默认导出
export default test
导入数据
-
导入全部-适用于全部导出方式
// * 代表所有 import * as data from './a.js'
-
命名导入-适用于分别导出、统一导出
// 接收的key和需要暴露的key一致,分别接收对应的数据 import {info,test} as data from './a.js'
// 如果不想使用原先的key,可以使用as关键字 起一个别名 import {info as infos ,test} as data from './a.js'
-
默认导入-适用于默认导出
// 使用任意一个变量接收默认导出的数据 import aaaaa as data from './a.js'
-
混合导入
// 先接收default, import aaa,{info,test} from './a.js'
-
import可以不接收任何数据
// a.js中有调用的都会执行 import './a.js'