AMD
Asynchronous module definition
AMD 的代表肯定就是大名鼎鼎的 RequireJS
James Burke 觉得 CMJ 很好,但是在浏览器里玩不转,所以自己提出了一个 AMD 规范
AMD Usage
1define(id?, depencies?, factory);23define('foo', ['utils', 'bar'], function(utils, bar) {4 utils.add(1, 2);5 return {6 name: 'foo'7 }8})
实现一个符合 AMD 的 rj.js
只是核心能力作为实现,具体:https://requirejs.org/docs/api.html
1.可以直接配置依赖路径
1rj.config({ paths: {2 'jquery': 'https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js'3} });45rj(['jquery'], function(jquery) {6 // ....7})
2.加载模块
1// RequestJs('')2rj(['moduleA'], function(moduleA) {});
3.定义模块
1rj('moduleA', [], function() {2 return 'hello zhuawa!';3})
行为
1// RequireJS2define('a', function () {3 console.log('a load')4 return {5 run: function () { console.log('a run') }6 }7})89define('b', function () {10 console.log('b load')11 return {12 run: function () { console.log('b run') }13 }14})1516require(['a', 'b'], function (a, b) {17 console.log('main run') // 🔥18 a.run()19 b.run()20})2122// a load23// b load24// main run25// a run26// b run
记录一下: 1.require 的时候加载了依赖的模块
一些可以用来测试的 CDN 地址
systemjs: https://cdn.bootcdn.net/ajax/libs/systemjs/6.8.3/system.min.js
lodash: https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js
CMD
Common Module Definition
玉伯 Seajs
Usage
1// sea.js2define('a', function (require, exports, module) {3 console.log('a load')4 exports.run = function () { console.log('a run') }5})67define('b', function (require, exports, module) {8 console.log('b load')9 exports.run = function () { console.log('b run') }10})1112define('main', function (require, exports, module) {13 console.log('main run')14 var a = require('a')15 a.run()16 var b = require('b')17 b.run()18})1920seajs.use('main')2122// main run23// a load24// a run25// b load26// b run
COMMONJS
文件是一个模块,私有。内置两个变量 module require (exports = module.exports)
一个引入一个导出,就构成了通信的基本结构
需要注意的两个问题
1.缓存,require 会缓存一下,所以
1// a.js2var name = 'morrain'3var age = 184exports.name = name5exports.getAge = function(){6 return age7}8// b.js9var a = require('a.js')10console.log(a.name) // 'morrain'11a.name = 'rename'12var b = require('a.js')13console.log(b.name) // 'rename'
2.引用拷贝还是值拷贝的问题(CMJ 是值拷贝)
1// a.js2var name = 'morrain'3var age = 184exports.name = name5exports.age = age6exports.setAge = function(a){7 age = a8}9// b.js10var a = require('a.js')11console.log(a.age) // 1812a.setAge(19)13console.log(a.age) // 18
3.运行时加载
ESM
我们所说的esModule其实就是es6推出的javascript模块规范。在这之前由于没有规范所以社区推出了CommonJS规范、require.js等。esModule的语法是静态的、导出是绑定的
什么是静态? 静态的语法意味着可以在编译时确定导入和导出,更加快速的查找依赖,可以使用lint工具对模块依赖进行检查,可以对导入导出加上类型信息进行静态的类型检查
什么是导出绑定? 由于使用import导入的模块是运行在严格模式下的,且均为只读的(即无法被赋值。但是可以更改属性),且均为引用传递,无关类型,均是与原变量的引用。
编译时加载(多阶段,异步)
导出 export
1// ep.js2// 最常用的大概就是声明命名导出了3export const name = 'bababa' // var let const function class 均可4// 默认导出5export default constant6export default function() {}7export { constant as default } // 重命名默认导出8// 命名导出9export { ep1, ep2, ep3 }10export { ep1 as _ep1, ep2 as _ep2, ep3 as _ep3 } // 重命名导出11// 重定向导出12export * from 'action' // 重定向导出(导出不包含模块内的default的所有),重定向的命名并不能在本模块使用,只是搭建一个桥梁。13// 下面是几个错误范例14export 115const $ep = 116export $ep
导入 import
1// 命名导入2import { ep1, ep2, ep3 } from './ep'3// 重命名导入4import { ep1 as _ep1, ep2 as _ep2, ep3 as _ep3 } from './ep'5// 混合导入6import * as ep from './ep'7import default, { ep1, ep2, ep3 as $ep3 } from './ep'8// effect 执行 多次引入也只会执行一次,有点像php里的include_once9import './ep'10// 动态导入11import('./ep').then()1213// 下面是几个错误范例14import { 'ep1' + 'ep2' } from './ep' // import是编译阶段,所以不能动态加载