模块化开发

397 阅读2分钟

2-2-1 模块化开发

1) 模块化演变过程

  • 基于文件的划分模块的方式
    • 污染全局作用域
    • 命名冲突问题
    • 无法管理模块依赖关系

  • 命名空间方式

每个模块只暴露一个全局对象,所有模块成员都挂载到这个对象上

  • IIFE 立即执行函数

  • 利用 IIFE 参数作为依赖声明使用

  • AMD 规范 (require.js)
    • AMD 使用起来相对复杂
    • 模块JS文件请求频繁
  • Sea.js+CMD(淘宝推出)

2) 模块化标准规范

  • CommonJs in Node.js
  • ES Module in browser

3) ES Moudle 基本特性

  • 自动采用严格模式,忽略‘use strict’
  • 每个ESM模块都是单独的私有作用域
  • ESM是通过CORS去请求外部JS模块的
  • ESM的script标签会延迟执行脚本
<!-- 通过给 script 添加 type = module 的属性,就可以以 ES Module 的标准执行其中的 JS 代码了 -->
  <script type="module">
    console.log('this is es module')
  </script>

  <!-- 1. ESM 自动采用严格模式,忽略 'use strict' -->
  <script type="module">
    console.log(this)
  </script>

  <!-- 2. 每个 ES Module 都是运行在单独的私有作用域中 -->
  <script type="module">
    var foo = 100
    console.log(foo)
  </script>
  <script type="module">
    console.log(foo)
  </script>

  <!-- 3. ESM 是通过 CORS 的方式请求外部 JS 模块的 -->
  <!-- <script type="module" src="https://unpkg.com/jquery@3.4.1/dist/jquery.min.js"></script> -->

  <!-- 4. ESM 的 script 标签会延迟执行脚本 -->
  <script defer src="demo.js"></script>
  <p>需要显示的内容</p>

4) ES Module 导出导入

// 导出
// export var name = 'foo module'

// export function hello () {
//   console.log('hello')
// }

// export class Person {}

var name = 'foo module'

function hello () {
  console.log('hello')
}

class Person {}

// export { name, hello, Person }

// export {
//   // name as default,
//   hello as fooHello
// }

// export default name

// var obj = { name, hello, Person }

export { name, hello, Person }


// 导入
// import { default as fooName } from './module.js'
// console.log(fooName)

import { name, hello, Person } from './module.js'
console.log(name, hello, Person)

5) ES Module 浏览器环境 Polyfill(兼容)

6)ES Module in Node.js

  • 使用要求node版本8以上
  • 文件改为 .mjs 后缀
// 第一,将文件的扩展名由 .js 改为 .mjs;
// 第二,启动时需要额外添加 `--experimental-modules` 参数;

import { foo, bar } from './module.mjs'

console.log(foo, bar)

// 此时我们也可以通过 esm 加载内置模块了
import fs from 'fs'
fs.writeFileSync('./foo.txt', 'es module working')

// 也可以直接提取模块内的成员,内置模块兼容了 ESM 的提取成员方式
import { writeFileSync } from 'fs'
writeFileSync('./bar.txt', 'es module working')

// 对于第三方的 NPM 模块也可以通过 esm 加载
import _ from 'lodash'
_.camelCase('ES Module')

// 不支持,因为第三方模块都是导出默认成员
// import { camelCase } from 'lodash'
// console.log(camelCase('ES Module'))

  • 与 CommonJs 交互
    • CommonJs 模块始终只会导出一个默认成员(相当于ES Module 中的export default)
    • import 不是解构导出对象
    • ES Module 中可以导入 CommonJs 模块
    • CommonJs 中不能导入 ES Module 模块
  • 与 CommonJs 差异
// esm.mjs
// ESM 中没有模块全局成员了

// // 加载模块函数
// console.log(require)

// // 模块对象
// console.log(module)

// // 导出对象别名
// console.log(exports)

// // 当前文件的绝对路径
// console.log(__filename)

// // 当前文件所在目录
// console.log(__dirname)

// -------------

// require, module, exports 自然是通过 import 和 export 代替

// __filename 和 __dirname 通过 import 对象的 meta 属性获取
// const currentUrl = import.meta.url
// console.log(currentUrl)

// 通过 url 模块的 fileURLToPath 方法转换为路径
import { fileURLToPath } from 'url'
import { dirname } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
console.log(__filename)
console.log(__dirname)

// cjs. js
// 加载模块函数
console.log(require)

// 模块对象
console.log(module)

// 导出对象别名
console.log(exports)

// 当前文件的绝对路径
console.log(__filename)

// 当前文件所在目录
console.log(__dirname)