# why ?

不同功能封装成独立的文件
只开放部分功能性接口

代码复用
避免命名冲突
解耦(可管理)

# 开发模块管理引擎

市场上的 模块管理引擎:
AMD require.js
CMD sea.js
COMMONJS NODE.js
UMD

## (手写) 简单的 模块管理

  • moduleList - 容器、管理模块
  • define - 定义模块
  • @param {String[]} modules 依赖模块
    let module = (function() { 
      const moduleList = {} ; // 容器
      /** * @description 模块管理 * @param {String} name 名字 * @param {String[]} modules 依赖的模块 * @param {Function} action 执行的方法 * @return 开放接口 */
      function define(name, modules, action) { // 初始化
        modules.forEach((m,i)=> {
          modules[i] = moduleList[m]
        });
        moduleList[name] = action.apply(null,modules) ;// 往容器里面压模块
      }
      return {define} ;
    })();
    module.define('hd', [], function() { // 导出
      return {
        first(arr) {
          return arr[0] ;
        },
        max(arr, key) {
          return arr.sort((a, b) => b[key]-a[key])[0];
        }
      };
    })
    module.define('lesson', ['hd'], function(hd) { // 导入
      let data = [
        {name:'js', price: 99, click: 4} ,
        {name:'mysql', price: 222, click: 2} ,
        {name:'vue.js', price: 300, click: 3} ,
      ]
      let max = hd.max(data, 'price') ;
      console.log(max)
    })

# 使用:export / import … from …

通过 以下命令,就实现了 模块的 定义、导出、导入

module.js 文件
export 指定 导出哪些接口

let title = '后盾热' ;
let url = 'www.baidu.com'
export {title}

.html 文件

import 指定 导入哪些接口
from 指定从那里导入 (必须指定明确的路径,否则不能识别。只有到了用打包工具的时候,打包工具才能比较智能的识别)

  <script type="module"> import {title} from './module.js' ; console.log(title) </script>

注意:
script 标签需要 加上 type='module'

<script type="module"> ... </script>

# 模块的延时加载

只要 script 加了 type=‘module’
那么这个标签就是延时加载的

即便脚本放在了body上面,仍然能找到body 内容(看下面)

let a = document.querySelector('button') ;
console.log('module' , a) ; 
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<script type="module"> let a = document.querySelector('button') ; console.log('html' , a) ; import {} from "./module.js" </script>
<body>
  <button>后盾人</button>
</body>
</html>

# 默认是 严格模式

模块默认是 严格模式 'use strict'

<script type="module"> site = 'aa' // 非严格模式下,是不报错的 </script>

# 模块的 作用域

非模块标签内的变量是定义在全局的(下图)

模块标签的作用只在模块标签内部(下图)

# 模块的(预解析)的必要性

模块的导入,只有在 第一次 导入的时候才会解析数据。
避免了重复初始化

(下图)

  <script type="module"> import {} from "./module.js" ; import {} from "./module.js" ; import {} from "./module.js" ; import {} from "./module.js" ; import {} from "./module.js" ; </script>

module.js

class Common {
  init() {
    console.log("初始化")
  }
}
new Common().init() ;

同样的,如皋导入的模块导入已导入的模块,也不会再次出发编译(绕?就对了)
(下图)

  <script type="module"> import {} from "./module.js" ; import {} from "./module1.js" ; </script>

module.js

class Common {
  init() {
    console.log("初始化")
  }
}
new Common().init() ;

module1.js

import {} from "./module.js" ; // 导入 上面模块
console.log('module-1')

(下图)可以看到,module.js 模块只初始化一次

# 具名导出、导入

具名导入(具体名字导入)

直接在属性、方法前加 export 导出

或者 在最后具名导出

也是能用的

# 批量导入与建议

  <script type="module">
    import * as api from "./module.js" ;
    console.log(api)
  </script>
let site = 'www.baidu.com' ;
function show() {
  console.log('show')
}
class Common {
  init() {
    console.log("初始化")
  }
}
new Common().init() ;


export {site, show , Common}

建议:
使用具名导入

  1. 导入体积小
  2. 代码清晰

# 别名

  <script type="module"> import {site as s} from "./module.js" ; // 导入时候别名 let site = 'www.bilibili.com' ; console.log(s) </script>
let site = 'www.baidu.com' ;
function show() {
  console.log('show')
}
class Common {
  init() {
    console.log("初始化")
  }
}
new Common().init() ;


export {site, show as s , Common} // 导出时候别名

# 默认导出 default

默认导出,import 就不用加花括号 {...}

<mark>但是,只能有一个</mark>

  <script type="module"> import s from "./module.js" ; // 或者 import {default as s, site } from "./module.js" // 或者 import s , {site} from "./module.js" ; s() </script>

导出可以是(下图)

let site = 'www.baidu.com' ;
function show() {
  console.log('show')
}
class Common {
  init() {
    console.log("初始化")
  }
}
new Common().init() ;


export {site, show as default , Common} // 默认导出 show

也可以是(下图)

let site = 'www.baidu.com' ;
export default function  show() { // 默认导出 show
  console.log('show')
}
class Common {
  init() {
    console.log("初始化")
  }
}
new Common().init() ;


export {site, show  , Common}


如果批量导出 , 用 default 的 默认导出 需要这样调用(下图)

# 规范 - 推荐

导出 名尽量和 文件名 相关

# 模块合并导出

为了管理方便,弄一个专门的模块作为入口
管理其他模块

m13.2.js

let url = 'www.baidu.com-2'
function show()  {
  console.log(url) ;
}
class User {

}
export {url, show, User as default}

m13.1.js

let url = 'www.baidu.com-1'
function show()  {
  console.log(url) ;
}
class User {

}
export {url, show, User as default}

m13.index.js

import * as m131 from "./m13.1.js"
import * as m132 from "./m13.2.js"
export{ m131, m132}
  <script type="module"> import * as api from "./m13.index.js" ; api.m131.show() ; api.m132.show() ; console.log(api.m131.default) console.log(api) </script>

# 按需动态加载 - import()、Promise 对象

之前 import ... from ... 的模式 , 必须在顶层

否则报错(下图)

## Promise 对象

ES6 标准

import() 函数 返回一个 Promise 对象

Promise 对象有个 then 方法,当模块加载完成,进行回调
(如下)

./m13.1.js

let url = 'www.baidu.com-1'
function show()  {
  console.log(url) ;
}
class User {

}
export {url, show, User as default}

使用 import

    import('./m13.1.js').then(module => {
      console.log(module)
    })

接收的 module 对象就是 模块对象

## 按需加载 【实现】

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <button>点击,后加载</button>
</body>
  <script type="module"> document.querySelector('button') .addEventListener('click',() => { import('./m13.1.js').then(({url, site}) => { console.log(site,url) }); }) </script>
</html>

m13.1.js 模块

console.log("按需加载")
let site = 'www.baidu.com'
let url = 'www.bilibili.com'
export {site,url}