3-工程化-模块化开发(02-02-01)

161 阅读4分钟

1. 模块化演变(第一阶段)

  • 具体做法就是将每个功能及其相关状态数据各自单独放到不同的文件中,
  • 约定每个文件就是一个独立的模块,
  • 使用某个模块就是将这个模块引入到页面中,然后直接调用模块中的成员(变量 / 函数)

缺点十分明显:

  • 所有模块都直接在全局工作,没有私有空间,
  • 所有成员都可以在模块外部被访问或者修改,
  • 而且模块一段多了过后,容易产生命名冲突,
  • 另外无法管理模块与模块之间的依赖关系

2. nodejs中必须遵循commonjs的规范

  • 一个文件就是一个模块
  • 每个模块都有单独的作用域
  • 通过modele.exports导出成员
  • 通过require函数载入模块
  • CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。
  • 只能在node环境中实现,浏览器不可以资源同步
  • nodejs是实现了commonjs的规范

3. AMD是一种规范

  • "Asynchronous Module Definition"的缩写,意思就是"异步模块定义"
  • require.js实现了AMD
  • 默认用define定义一个模块
  • require引入一个模块

4. 前端模块规范有三种:CommonJs,AMD和CMD。

CommonJs用在服务器端,AMD和CMD用在浏览器环境

  • CommonJs是规范,同步,实现nodeJs
  • AMD是规范,异步,实现requireJs。提前执行(异步加载:依赖先执行)+延迟执行
  • CMD是规范,异步,实现SeaJS。(运行到需加载,根据顺序执行)

浏览器遵循ESmodule 规范

Nodejs遵循commonjs 规范

5. ESmodule的特性

 通过给 script 添加 type = module 的属性,就可以以 ES Module 的标准执行其中的 JS 代码了
  <script type="module">
    console.log('this is es module')
  </script>
1. ESM 自动采用严格模式,忽略 'use strict'
2. 每个 ES Module 都是运行在单独的私有作用域中
3. ESM 是通过 CORS 的方式请求外部 JS 模块的
4. ESM 的 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>

6. ES Modules 导出

// module.js
var name = 'foo module'

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

class Person {}

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

7. ES Modules 导入

import { name } from './module.js'
import { name } from '/04-import/module.js'
import { name } from 'http://localhost:3000/04-import/module.js'
console.log(name)

import './module.js'
import * as mod from './module.js'
console.log(mod)

var modulePath = './module.js'
import { name } from modulePath
console.log(name)

if (true) {  
  import { name } from './module.js'
}

import('./module.js').then(function (module) {
   console.log(module)
})

8. ES Modules in Browser

ES modules已经在下面这些浏览器中实现

  • Safari 10.1.
  • Chrome 61.
  • Firefox 60.
  • Edge 16.

Polyfill 是一块代码(通常是 Web 上的 JavaScript),用来为旧浏览器提供它没有原生支持的较新的功能。

8. ES Modules in Nodejs

// 第一,将文件的扩展名由 .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'))

9. ES Modules in Nodejs 与CommonJs交互

  • ES Module 中可以导入 CommonJS 模块
  • CommonJS 不能导入ES Module模块
  • 不能直接提取成员,注意import不是解构导出对象
  • CommonJS模块始终只会导出一个默认成员
  • 不能在 CommonJS 模块中通过 require 载入 ES Module

10. ES Module 和 CommonJS差异

概念差异

  • ESM:import, import(), export, export default

  • CommonJS: require(), module.exports

  • commonJS模块输入的是一个值的拷贝,ES6模块输出的是值的引用(所以重新对其赋值)。

  • commonJS模块是运行时加载(同步),ES6模块是编译时输出接口(异步)。

  • ES Module:静态加载/编译时加载

  • CommonJS:运行加载

  • ESM效率要比CommonJS模块的加载方式高,动态绑定关系

  • import:静态执行,会提升,不可修改/只读

  • export default:指定模块的输出,输出一个叫做default的方法/变量,系统允许修改名称

  • import():动态加载模块,当需要按需加载时使用

用法的区别:

// commonJS导出一个对象
module.exports={ name:name,age:age }
let xx=require('./index')
//ES modules导出一个对象
export { name:name,age:age }
import { name,age } from './index'//不能任意命名,只能解构赋值

export default { name:name,age:age }
import xx from './index'//导入的对象可以任意取名