浅言模块化的理解及实践

261 阅读3分钟

演变过程

早期对于开发者对于'模块化'的实现的几种方式

  1. 通过文件划分方式
    • 所有模块都在全局工作,污染全局作用域
    • 命名冲突问题
    • 无法管理模块依赖关系
  2. 命名空间方式
    • 将每个模块包括成全局对象的方式实现,减小命名冲突的可能
    • 成员在外面还是可以被修改
  3. 立即执行函数,提供一个私有空间,对于提供给外部的成员,通过挂在到全局对象的方式去实现
    • 实现了私有成员的概念

模块化规范的出现

模块化标准 + 模块加载器

CommonJS规范

  • 一个文件就是一个模块
  • 每个模块都有独立的作用域
  • 通过module.exports 导出成员
  • 通过require 函数载入模块 CommonJS是以同步模式加载模块,所以在早期浏览器开发中,使用的是结合CommonJS设置的AMD规范,同期的还有淘宝出的 Sea.js + CMD ,设计初衷是为了尽量让写法接近于CommonJS,降低学习成本,后来也被require.js兼容了

AMD 使用起来相对复杂,模块JS文件请求频繁

模块化的最佳实践

  • 在Node中一般遵循 CommonJS规范
  • 在浏览器中遵循 ES Modules 规范
    • ES Modules 是ES6定义的一个最新的模块系统,是最近这几年才被定义的标准,所以存在各种的环境兼容问题
    • 随着Webpack 等打包工具的流行,这一规范才逐渐开始普及
    • 浏览器也开始逐步支持

深入讨论ES Modules

  • 作为规范到底约定了哪些特性和语法
  • 如何通过工具和规范在运行环境中兼容性带来的问题

约定的特性和语法

特性

  1. ESM 自动采用严格模式,忽略 'use strict'
  2. 每个ESM 都是运行在单独的私有作用域中
  3. ESM 是通过 CORS 的方式请求外部js 模块
  4. ESM 的script 标签会延迟执行脚本(网页渲染完成后,再去执行脚本)

语法

  • 导入和导出(import/export)的注意事项

    • 导出的成员并不是字面量对象,导入的时候与结构也是不相同,只是一个语法而已
    • ES Mudules 的导出并不是导出里面的值,而是导出存放的地址,
    • 在外部导入之后,并不能去修改
  • 导入的用法

    路径问题

    • import 导入的时候引入模块不能省略.js的拓展名import { name } from './module.js'
    • 也是不能去载入index,必须使用完整路径
    • ./ 的相对路径也不能使用,不写会被以为是第三方模块, 导入问题
    • import {} from 'module.js'只会提取这个模块,不会提取任何成员,简写为import './module.js'
    • import * as mod from 'module.js' 提取所有成员 import为导入模块的声明
    • 运行时就应该知道需要导入的路径,只能出现在最顶层作用域,并不能出现在判断中 动态导入模块
    • import('./module.js')返回的是Promise
       import('./module.js').then(function(module){
        console.log(module) // 模块中导出的成员
       })
    

    如果在一个模块中导出了一些命名成员,同时导出了一些默认成员

    // 两种写法
    import {name,age,default as title} from './module.js'
    import title, { name, age } from './module.js'
    

如何通过工具或方案解决在运行环境中兼容性问题

Polyfill —— Browser ES Module Loader

一个js文件,可以在网页中使用ES module browser-es-module-loader的github

如果IE还不支持Promise,在IE中还需要单独去引入命为Promise的Polyfill

promise-polyfill的github

支持ES Module 的浏览器会运行两遍的问题 通过script的属性去解决

<script nomodule>
// 只会在不支持module 的浏览器上去执行 
</script>

ESModule in Node

从Node8.5版本之后逐步支持

  • ESM 中可以导入CommonJS模块
  • CommonJS中不能导入ESM模块
  • CommonJS始终只会导出一个默认成员
  • 注意 import 不是解构导出解构对象