1 - 模块化概述
传统开发模式的主要问题:① 命名冲突,② 文件依赖
通过模块化解决上述问题:
- 模块化就是把单独的一个功能封装到一个模块(文件)中,模块之间相互隔离,但是可以通过特定的接口公开内部成员,也可以依赖别的模块
- 模块化开发的好处:方便代码的重用,从而提升开发效率,并且方便后期的维护
2 - 大一统的模块化规范 - ES6模块化
1. ES6模块化
ES6模块化出现之前,浏览器端和服务器端的模块化是不同的,如下:
- 浏览器端的模块化
① AMD(Asynchronous Module Definition,异步模块定义)
代表产品为:Require.js
② CMD(Common Module Definition,通用模块定义)
代表产品为:Sea.js
- 服务器端的模块化
服务器端的模块化规范是使用CommonJS规范:
① 使用require引入其他模块或者包
② 使用exports或者module.exports导出模块成员
③ 一个文件就是一个模块,都拥有独立的作用域
在ES6模块化规范诞生之前,Javascript社区已经尝试并提出了 AMD、CMD、CommonJS 等模块化规范,但是,这些社区提出的模块化标准,还是存在一定的差异性与局限性,并不是浏览器与服务器通用的模块化标准,例如:AMD 和 CMD 适用于浏览器端的 Javascript 模块化,CommonJS 适用于服务器端的 Javascript 模块化。
因此,ES6 语法规范中,在语言层面上定义了 ES6 模块化规范,是浏览器端与服务器端通用的模块化开发规范。
ES6模块化规范中定义:
- 每个 js 文件都是一个独立的模块
- 导入模块成员使用 import 关键字
- 暴露模块成员使用 export 关键字
2. Node.js 中通过 babel 体验 ES6 模块化
Node.js中默认支持CommonJS模块化规范,但是对于 ES6模块化支持的并不是很好,所以通常需要借助babel这个第三方插件,才能在Node中体验高级的ES6特性。
babel是一个语法转换工具可以把高级的有兼容性的js代码转换成低级的没有兼容性的js代码。
步骤:
-
cd
到项目目录,使用npm init -y
创建package.json文件 - 安装babel的依赖包:
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node
,安装完之后会创建node_modules文件夹和package-lock.json文件 - 安装polyfill插件:
npm install --save @babel/polyfill
- 项目根目录创建文件 babel.config.js文件,文件内容如下
- 创建index.js文件,文件写一行输出代码:
console.log("ok")
- 通过
npx babel-node index.js
执行代码(npx在高版本的npm中默认提供了,可以直接通过npx运行某些cli命令,这里就是通过npx运行babel-node命令,运行的文件是index.js) - 发现终端打印:ok,说明babel配置的没问题
给index.js运行babel-node命令,babel运行之前会先读取babel.config.js 文件内容,再做代码转换。
// 语法转换的数组,里面是语法转换时可能用到的语法转换插件
const presets = [
["@babel/env", {
targets: { // 转换完毕的代码最低要支持如下浏览器
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1"
}
}]
];
module.exports = { presets }; // 向外暴露出去,供babel使用
babel配置完成之后我们就可以在这个node项目下学习ES6模块化的一些高级特性,比如ES6的模块化导入。
3 - ES6 模块化的基本语法
1. 默认导出 与 默认导入
创建m1.js文件,代码如下:
// 定义私有成员 a 和 c
let a = 10
let c = 20
// 外界访问不到变量d,因为它没有被暴露出去
let d = 30
function show() {}
// 默认导出:将本模块中的私有成员暴露出去,供其他模块使用
export default {
a,
c,
show }
在index.js文件导入,代码如下:
// 默认导入:导入模块成员
import m1 from './m1.js'
console.log(m1) // { a: 10, c: 20, show: [Function: show] }
执行:npx babel-node index.js
命令,打印输出的结果为:{ a: 10, c: 20, show: [Function: show] }
- 每个模块中,只允许使用唯一的一次
export default
,否则会报错!如果在一个模块中没有向外暴露成员,其他模块引入该模块时将会得到一个空对象。 - 默认导出和默认导入都是对象,不能是其他东西,默认导出和默认导入最常用。
2. 按需导出 与 按需导入
m1.js文件代码如下:
// 向外按需导出变量 s1
export let s1 = 'aaa'
// 向外按需导出变量 s2
export let s2 = 'ccc'
// 向外按需导出方法 say
export function say = function() {}
在index.js中按需导入:
// 导入模块成员
import { s1, s2 as ss2, say } from './m1.js' // 把s2起了个别名ss2
console.log(s1) // 打印输出 aaa
console.log(ss2) // 打印输出 ccc
console.log(say) // 打印输出 [Function: say]
执行:npx babel-node index.js
命令,打印输出的结果如上。
注意:
- 每个模块中,可以多次使用按需导出,只能使用一次默认导出。
- 默认导出/导入是对象,按需导出/导入是成员。
- 默认导出和按需导出可以同时使用,如果同时使用,则需要按如下格式导入,其中m1中是默认导出的成员。
import m1, { s1, s2 as ss2, say } from './m1.js'
3. 直接导入并执行模块代码
有时候,我们只想单纯执行某个模块中的代码,并不需要得到模块中向外暴露的成员,此时,可以直接导入并执行模块代码。
m2.js文件代码如下:
// 在当前模块中执行一个 for 循环操作
for(let i = 0; i < 3; i++) {
console.log(i)
}
在index.js中导入:
// 直接导入并执行模块代码
import './m2.js'
终端执行如下命令:
npx babel-node ./index.js
// 打印:0 1 2