概述

前端发展之初存在的问题

  1. 浏览器解释执行js的速度太慢
  2. 用户端的电脑配置不足
  3. 更多的代码带来了全局变量污染,依赖关系混乱等问题

大事件

  1. 2008年,谷歌V8引擎发布,解决浏览器解释执行js的速度太慢问题
  2. 摩尔定律持续发酵,个人电脑的配置开始飞跃
  3. 2009年,nodejs发布,并附带CommonJs模块化标准,CommonJs运用到浏览器困难
  4. 很快,AMD规范出炉,它解决的问题和CommonJS一样,但是可以更好的适应浏览器环境
  5. CMD规范出炉,它对AMD规范进行了改进
  6. 2015年,ES6发布,它提出了官方的模块化解决方案 —— ES6 模块化
  7. 从此以后,模块化成为了JS本身特有的性质,这门语言终于有了和其他语言较量的资本,成为了可以编写大型应用的正式语言

JS运用

  • 既然JS也能编写大型应用,那么自然也需要像其他语言那样有解决复杂问题的开发框架
    • Angular、React、Vue等前端开发框架出现
    • Express、Koa等后端开发框架出现
    • 各种后端数据库驱动出现
  • 要开发大型应用,自然少不了各种实用的第三方库的支持
    • npm包管理器出现,实用第三方库变得极其方便
    • webpack等构建工具出现,专门用于打包和部署
  • 既然JS可以放到服务器环境,为什么不能放到其他终端环境呢?
    • Electron发布,可以使用JS语言开发桌面应用程序
    • RN和Vuex等技术发布,可以使用JS语言编写移动端应用程序
    • 各种小程序出现,可以使用JS编写依附于其他应用的小程序
    • 目前还有很多厂商致力于将JS应用到各种其他的终端设备,最终形成大前端生态

CommonJS规范

CommonJS概述

  1. CommonJS使用exports导出模块,require导入模块
  2. exports是一个空的对象
  3. require是一个函数,传入模的路径即可返回该模块导出的整个内容
  4. nodejs中导入模块,使用相对路径,并且必须以./或../开头

nodejs对CommonJS的实现

  1. 为了保证高效的执行,仅加载必要的模块。nodejs只有执行到require函数时才会加载并执行模块
  2. 为了隐藏模块中的代码,nodejs执行模块时,会将模块中的所有代码放置到一个立即执行函数中执行,以保证不污染全局变量。
  3. 为了保证顺利的导出模块内容,nodejs做了以下处理
    • 在模块开始执行前,初始化一个值module.exports = {}
    • module.exports即模块的导出值
    • 为了方便开发者便捷的导出,nodejs在初始化完module.exports后,又声明了一个变量exports = module.exports
  1. (function(module){
  2. module.exports = {};
  3. var exports = module.exports;
  4. //模块中的代码
  5. return module.exports;
  6. })()
  • 为了避免反复加载同一个模块,nodejs默认开启了模块缓存,如果加载的模块已经被加载过了,则会自动使用之前的导出结果

CommonJS的工作原理

  • 当使用require(模块路径)导入一个模块时,node会做以下两件事情(不考虑模块缓存):
    1. 通过模块路径找到本机文件,并读取文件内容
    2. 将文件中的代码放入到一个函数环境中执行,并将执行后module.exports的值作为require函数的返回结果
  • CommonJS是同步的,必须要等到加载完文件并执行完代码后才能继续向后执行
  • 当浏览器遇到CommonJS
    1. 浏览器要加载JS文件,需要远程从服务器读取,而网络传输的效率远远低于node环境中读取本地文件的效率。由于CommonJS是同步的,这会极大的降低运行性能
    2. 如果需要读取JS文件内容并把它放入到一个环境中执行,需要浏览器厂商的支持,可是浏览器厂商不愿意提供支持,最大的原因是CommonJS属于社区标准,并非官方标准

新的规范

  • 要在浏览器中实现模块化,只要能解决上面的两个问题就行了
  • 解决办法其实很简单:
    1. 远程加载JS浪费了时间?做成异步即可,加载完成后调用一个回调就行了
    2. 模块中的代码需要放置到函数中执行?编写模块时,直接放函数中就行了
  • 基于这种简单有效的思路,出现了AMD和CMD规范,有效的解决了浏览器模块化的问题。

AMD和CMD

AMD

全称是Asynchronous Module Definition,即异步模块加载机制
require.js实现了AMD规范
在AMD中,导入和导出模块的代码,都必须放置在define函数中

CMD

全称是Common Module Definition,公共模块定义规范
sea.js实现了CMD规范
在CMD中,导入和导出模块的代码,都必须放置在define函数中

ES6模块化

ES6模块化特点

  • 使用依赖预声明的方式导入模块
    • 依赖延迟声明
      • 优点:某些时候可以提高效率
      • 缺点:无法在一开始确定模块依赖关系(比较模糊)
  • 依赖预声明
    • 优点:在一开始可以确定模块依赖关系
    • 缺点:某些时候效率较低
  • 灵活的多种导入导出方式
  • 规范的路径表示法:所有路径必须以./或../开头

模块的引入

浏览器使用以下方式引入一个ES6模块文件

  1. <script src="入口文件" type="module">

基本导入导出

基本导出

export 声明表达式 或 export {具名符号}

基本导入

  • import {导入的符号列表} from “模块路径”
  • import {name as name2} from “模块路径”
  • 注意:
    1. 导入时,可以通过关键字as对导入的符号进行重命名
    2. 导入时使用的符号是常量,不可修改
    3. 可以使用星号导入所有的基本导出,形成一个对象,使用*号必须要重命名

默认导入导出

默认导出

  • 每个模块除了允许有多个基本导出之外,只允许有一个默认导出
  • export default 默认导出的数据 或 export {默认导出的数据 as default}

默认导入

  • import 接收变量名 from “模块路径”
  • 如果使用*号,基本导出和默认导出会聚合到一个对象中,默认导出会作为属性default存在

ES6模块化细节

  1. 尽量导出不可变值
  • 使用const声明
  1. 可以使用无绑定的导入用于执行一些初始代码
  • import “模块路径”
  1. 可以使用绑定再导出,来重新导出来自另一个模块的内容
  • export {绑定的标识符} from “模块路径”,用一个模块封装多个模块,然后有选择的将多个模块的内容分别导出