1 ES Module 规范
ES Module 是目前使用较多的模块化规范,在 Vue、React 中大量使用,大家应该非常熟悉。TypeScript 中的模块化与 ES 类似。
1.1 导出模块
导出模块有两种方式:按需导出 和 默认导出。
按需导出是使用 export 关键字,将需要导出的成员(变量、函数、对象等)依次导出:
export const xxx = ''export const xxx = () => {}
一个模块中可以有多个按需导出,但只能有一个默认导出。假设默认导出 A 模块,当 B 模块直接导入模块 A 时,其导入的值就是模块 A 默认导出的值。
export default {}
1.2 导入模块
导入按需导出的模块:
import { xxx } from 'xxx'
上面语法中,花括号 {} 中的内容必须与导出的名称一致。
如果按需导出的成员较多,可以使用 as 一次性导入:
import * as xxx from 'xxx'
导入默认导出的模块:
import xxx from 'xxx'
也可以直接导入一个模块,并直接执行该模块的代码:
import 'xxxxx'
2 使用 Webpack 编译 ES Module2.1 初始化项目
创建 modules 目录,里面存放两个模块 module1.js 和 module2.js。入口文件与 modules 目录同级,名为 index.js。文件和目录结构如下:
05_ESM/ |- modules/ |- module1.js |- module2.js |- index.js |- index.html
2.2 实现两个模块
module1.js 使用按需导出变量 str1 和函数 fun1,默认导出 user 对象:
console.log('in module1')export const str1 = 'hello module1'export const fun1 = (msg) => { return `module1:${msg}`}const user = { name: 'zhangsan', age: 30}export default user
module2.js 使用默认导出,导出一个对象,这个对象包括属性 str2 和方法 fun2:
console.log('in module2')const str2 = 'hello module2'const fun2 = (msg) => { return `module2:${msg}`}export default { str2, fun2}
2.3 实现入口文件
在入口文件 index.js 中导入两个模块。由于 module1.js 是按需导出,故导入时需要使用 {}; module2.js 是默认导出,故此处可以直接导入:
import { str1, fun1 } from './modules/module1'import m2 from './modules/module2'console.log(str1)console.log(fun1('程序员优雅哥'))console.log(m2.str2)console.log(m2.fun2('youyacoder'))
2.4 入口 HTML
创建 index.html 文件,使用 script 标签导入 index.js :
在浏览器中访问 index.html 文件,控制台会提示如下错误:
Uncaught SyntaxError: Cannot use import statement outside a module (at index.js:1:1)
这是由于浏览器不认识 ESM 语法。可以使用 babel 将 ES6 语法编译为 ES5 的语法,然后使用 browserify 进行打包;也可以使用 webpack 打包。此处我使用 webpack 5。
2.5 使用 Webpack 打包
使用 npm 或 yarn 初始化项目:
yarn init -y
安装 webpack、webpack-cli 为开发依赖:
yarn add webpack webpack-cli -D
使用 webpack 打包:
npx webpack ./index.js -o ./dist/ --mode development
上面的打包命令直接在命令中配置参数,省略了额外的配置的文件。该命令指定了打包的入口文件为:index.js;输出的目录为 dist 目录,打包模式为 development。关于 webpack 5 的使用,有兴趣的可以看优雅哥的 webpack 5 系列文章。
执行完打包命令后,会生成 dist 目录,并且在该目录中有个 main.js 文件。
在 index.html 中删除之前引入的 index.js,替换为 dist/main.js:
重新在浏览器中访问 index.html, 控制台输出如下:
3 ES Module 总结
导出模块:
- 默认导出:export default xxx
- 按需导出 export const xxx
导入模块:
默认导入: import xxx from ‘xxx’
按需导入 import { xxx } from ‘xxx’
4 JS 模块化 4 大规范总结
前面优雅哥依次写了模块化的发展史,模块化的规范(可进主页查看每个规范详细版本),现进行一个大汇总方便大家查阅和总结:
01- 模块化前传
02 – Common JS 规范
03 – AMD 规范(Require JS 实现)
04 – CMD 规范 (Sea JS 实现)
05 – ESM 规范
模块化相关 demo 源码可以 github 搜索关键词 js-module-demo 或联系 程序员优雅哥 获取。
源码目录如下:
js-module-demo/|- 01_Histry/模块化发展史|- 02_CommonJS/CommonJS 规范|- 03_AMD/ADM 规范|- 04_CMD/CMD 规范|- 05_ESM/ESM 规范
各个模块化规范有相似之处,也有差异,模块定义与模块加载的语法如下:
4.1 Common JS 规范
定义模块的语法:
// 暴露函数module.exports = function () {}// 暴露对象module.exports = { xxx: () => {}}exports.xxx = {}exports.xxx = function() {}
加载模块的语法:
const xxx = require('xxxx')
4.2 AMD 规范
定义模块的语法:
define(id?, dependencies?, factory)
加载模块的语法:
require([module], callback)
4.3 CMD 规范
定义模块的语法:
// 定义模块define(function(require, exports, module) {// 使用 exports 导出模块exports.xxx = xxx//也可以使用 return 导出模块// return xxx})
加载模块的语法:
// 同步加载模块const m1 = require('../xxx')// 异步加载模块require.async('../xxx', function (m2) {})
4.4 ESM 规范
导出模块:
// 按需导出export const xxx = ''export const xxx = () => {}// 默认导出export default xxx
导入模块:
import { xxx, yyy } from 'xxx'import * as xxx from 'xxx'import xxx from 'xxx'import 'xxx'
感谢你阅读本文,如果本文给了你一点点帮助或者启发,还请三连支持一下,点赞、关注、收藏,作者会持续与大家分享更多干货