js模块化发展史与commonJs,ES6模块化规范介绍

JavaScript 模块化发展史

第一阶段

在 JavaScript 语言刚刚诞生,js只用于实现小的效果,js代码通常只有几百行,专业的前端工程师还没有出现,都是后端顺带完成前端工作
大事件

  • 1996年,NetScape将JavaScript语言提交给欧洲的一个标准制定阻止ECMA(欧洲计算机制造商协会)

  • 1998年,NetScape在与微软浏览器IE的竞争中失利,宣布破产

第二阶段

随着ajax的出现,改变了 JavaScript 在浏览器中扮演的角色,js可以与服务器进行交互,优化了用户体验。
js代码逐渐增加,前端逻辑逐渐复杂。
但是前端规模没有进一步扩大,主要由于以下几个原因

  • 浏览器解释执行JS的速度太慢
  • 用户端的电脑配置不足
  • 更多的代码带来了全局变量污染、依赖关系混乱等问题
    大事件
  • IE浏览器制霸市场后,几乎不再更新
  • ES4.0流产,导致JS语言10年间几乎毫无变化
  • 2008年ES5发布,仅解决了一些 JS API 不足的糟糕局面

第三阶段

到了2008年,谷歌的 V8 引擎发布,将JS的执行速度推上了一个新的台阶,甚至可以和后端语言媲美。
摩尔定律持续发酵,个人电脑的配置开始飞跃(摩尔定律指的是当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍,性能也将提升一倍。)
同时,nodejs诞生,对模块化的需求进一步加深,

经过社区的激烈讨论,最终,形成了一个模块化方案,即鼎鼎大名的CommonJS,该方案,彻底解决了全局变量污染和依赖混乱的问题

该方案一出,立即被nodejs支持,于是,nodejs成为了第一个为JS语言实现模块化的平台,为前端接下来的迅猛发展奠定了实践基础
大事件

  • 2008年,V8发布
  • IE的市场逐步被 firefox 和 chrome 蚕食,现已无力回天
  • 2009年,nodejs发布,并附带commonjs模块化标准

第四阶段

随着后端模块化的诞生,前段模块化也相继出现AMD,CMD,终于在2015年,ES6发布,它提出了官方的模块化解决方案 —— ES6 模块化
前端模块化的出现使得一些框架相继诞生

既然JS也能编写大型应用,那么自然也需要像其他语言那样有解决复杂问题的开发框架

  • Angular、React、Vue等前端开发框架出现
  • Express、Koa等后端开发框架出现
  • 各种后端数据库驱动出现

要开发大型应用,自然少不了各种实用的第三方库的支持

  • npm包管理器出现,实用第三方库变得极其方便
  • webpack等构建工具出现,专门用于打包和部署

总结

由于ajax的出现,js代码量激增,同时也完成了更多的任务,但是由于代码量的增多,js暴露出来三个问题(1.js执行速度太慢2.用户电脑配置不高3.代码增多导致全局变量污染),v8引擎的出现与摩尔定律的发酵,解决了前两个问题,nodejs和commonJs的出现,也将前端模块化推上日程,于是出现了三种前端模块化解决方案(AMD,CMD,ES6),前端模块化的出现也给框架的诞生提供了基础

扫描二维码关注公众号,回复: 10275697 查看本文章

模块化规范介绍

由于nodejs刚刚发布的时候,前端没有统一的、官方的模块化规范,因此,它选择使用社区提供的CommonJS作为模块化规范

在学习CommonJS之前,首先认识两个重要的概念:模块的导出和模块的导入

何为导出

要理解模块的导出,首先要理解模块的含义

什么是模块?

模块就是一个JS文件,它实现了一部分功能,并隐藏自己的内部实现,同时提供了一些接口供其他模块使用

模块有两个核心要素:隐藏和暴露

     隐藏的,是自己内部的实现
     暴露的,是希望外部使用的接口

任何一个正常的模块化标准,都应该默认隐藏模块中的所有实现,而通过一些语法或api调用来暴露接口

何为导入

当需要使用一个模块时,使用的是该模块暴露的部分(导出的部分),隐藏的部分是永远无法使用的。

当通过某种语法或api去使用一个模块时,这个过程叫做模块的导入

nodejs对CommonJS的实现

为了实现CommonJS规范,nodejs对模块做出了以下处理

  1. 为了保证高效的执行,仅加载必要的模块。nodejs只有执行到require函数时才会加载并执行模块(这叫做依赖延迟声明)

  2. 为了隐藏模块中的代码,nodejs执行模块时,会将模块中的所有代码放置到一个函数中执行,以保证不污染全局变量。

     (function(){
         //模块中的代码
     })()
    
  3. 为了保证顺利的导出模块内容,nodejs做了以下处理

    1. 在模块开始执行前,初始化一个值module.exports = {}
    2. module.exports即模块的导出值
    3. 为了方便开发者便捷的导出,nodejs在初始化完module.exports后,又声明了一个变量exports = module.exports
     (function(module){
         module.exports = {};
         var exports = module.exports;
         //模块中的代码
         return module.exports;
     })()
    
  4. 为了避免反复加载同一个模块,nodejs默认开启了模块缓存,如果加载的模块已经被加载过了,则会自动使用之前的导出结果

commonJs的导出

module.export.xxx = xxx;
module.export.xxx = {
	xxx: xxx
}

commonJs的导入

var xxx = require('./xxx.js'); // 路径必须为相对路径

es6的官方模块化标准

es6模块化标准通过script标签来引入文件,使用type="module"来声明此文件作为一个模块来执行

es6模块化特点如下

  1. 使用依赖依赖预声明的方式导入模块
  2. 灵活的多种导入导出方式
  3. 规范的路径表示法:所有路径必须以./或…/开头

es6模块化的基本导入导出
导出
es6的基本导出可以有多个,每个都必须是声明表达式,或具名符号

export const a = 1;
export const b = 2;
// 这样是不可以的↓
const c = 3;
export c;

es6的模块化支持简写

const a = 1;
const b = 2;
const c = 3;
export {a, b, c}; 
// 导出时支持重命名
export {a as a1, b as b2, c as c3}; 

导入

import {a, b} from './xxx.js'; // 只需要导入需要的变量,c不需要就可以不导入
// 同样的导入时也支持重命名导入数据
import {a as a1, b as b2} from './xxx.js';

es6模块化的默认导入导出

导出

export default 默认导出的数据

或者

export {默认导出的数据 as default}

由于每个模块仅允许有一个默认导出,因此,每个模块不能出现多个默认导出语句

导入

import xxx from './xxx.js'; // 由于默认导出没有给数据命名,所以不存在重命名

导入导出混合写法

一个模块里可以同时存在基本导入导出与默认导入导出
导出

const a = 1;
const b = 2;
const c = 3;
export {a, b, c};
export default {d: 4}

导入

import d, {a, b, c} from './xxx.js';
// d为默认导出,abc为正常导出

import * as data from './xxx.js';
// data中有一个属性叫做default,这个属性值即为默认导出数据
// 想导入所有正常导出,也可以使用*

es6模块化的其他细节

  1. 尽量导出不可变值

尽量使用const定义导出的数据,确保它在本模块内也是一个常量。
因为,虽然导入后,无法更改导入内容,但是在导入的模块内部却有可能发生更改,这将导致一些无法预料的事情发生

  1. 可以使用无绑定的导入用于执行一些初始化代码

如果我们只是想执行模块中的一些代码,而不需要导入它的任何内容,可以使用无绑定的导入:

import "模块路径"
  1. 可以使用绑定再导出,来重新导出来自另一个模块的内容

有的时候,我们可能需要用一个模块封装多个模块,然后有选择的将多个模块的内容分别导出,可以使用下面的语法轻松完成

export {绑定的标识符} from "模块路径"

例如a.js需要依赖b.js,b.js需要依赖c.js。
b.js需要原封不动的导出c.js里的变量c。
那么,b.js可以这样写

export { c } from './c.js';
 

依赖延迟声明与依赖预声明的区别

  1. 依赖延迟声明
    1. 优点:某些时候可以提高效率(某些情况见下方例子)
    2. 缺点:无法在一开始确定模块依赖关系(比较模糊)
  2. 依赖预声明
    1. 优点:在一开始可以确定模块依赖关系
    2. 缺点:某些时候效率较低

nodejs执行在本地,文件从本地读取,速度快,所以commonJs使用依赖延迟声明
而浏览器读取文件需要请求,速度较慢,使用依赖预声明
es6模块化标准的导入,应该写在js最上面,即使不写在最上面,浏览器在预编译的时候,也会将导入模块置于顶部

if (Math.random() > 0.5) {
	// 导入A模块
} else {
	// 导入B模块
}

当这种情况出现的时候,依赖延迟声明效率较高

发布了7 篇原创文章 · 获赞 38 · 访问量 434

猜你喜欢

转载自blog.csdn.net/qq_45516476/article/details/104349903