js模块化规范

主流的模块化

目前流行的js模块化规范有CommonJS、AMD、CMD、ES6的模块系统。

CommonJS

CommonJS 的出发点:JS没有完善的模块系统,标准库较少,缺少包管理工具。NodeJS的兴起使得JS能在任何地方运行,特别是服务端,也达到了具备开发大型项目的能力,所以CommonJS应运而生。

Node.js是CommonJS规范的主要实践者,它有四个重要的环境变量为模块化的实现提供支持:module、exports、require、global。module.exports定义当前模块对外输出的接口(不推荐使用exports),用require加载模块。

不推荐使用exports的原因是:

1.如果要导出一个键值对象{},则可以使用exports; 2.如果要导出一个函数或数组,只能对module.exports赋值;

所以,任何时候导出模块都用module.exports即可

CommonJS通过同步的方式加载模块。在服务器,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。

CommonJS 规范

  • 一个文件就是一个模块,拥有单独的作用域

    如何实现单独的作用域?

    JavaScript是一种函数式编程语言,支持闭包。把一段JavaScript代码用一个函数包装起来,这段代码的所有“全局”变量就变成了函数内部的局部变量。

    //hello.js
    var s = 'Hello';
    var name = 'world';
    console.log(s + ' ' + name + '!');
    

    Node.js加载了hello.js后,它可以把代码包装一下,变成这样执行:

    (function(){
    var s = 'Hello';
     var name = 'world';
     console.log(s + ' ' + name + '!');
    })();
    
  • 普通方式定义的变量、函数、对象都属于该模块内

  • 通过require来加载模块 require(xxx)

  • 通过module.exports来暴露模块中的内容 module.exports=xxx

注意:

  1. 模块可以多次加载,但只会在第一次加载时候运行,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果
  2. 模块加载顺序,按照代码出现的顺序同步加载
  3. __dirname代表当前文件所在的文件夹路径
  4. __filename代表当前模块文件所在的文件夹路径+文件名

ES6模块化

ES6在语言标准的层面上,实现了模块功能,而且实现得相当简单,旨在成为浏览器和服务器通用的模块解决方案。其模块功能主要由两个命令构成:exportimport,另外还提供了export default 命令,为模块指定默认输出,对应的import语句不需要使用大括号,这也更趋近于AMD的引用写法。

ES6的模块不是对象,import命令会被JavaScript引擎静态分析,在编译时就引入模块代码,而不是在代码运行时加载,所以无法实现条件加载。也正因如此才使得静态分析成为可能。

  • export

export可以导出一个对象中包含的多个属性、方法。

export default 只能导出一个可以不具名的函数。

导出的内容可以使用import进行引用,也可以直接使用require进行引用。

  • import
//export导出方式的引用方式
import {fn} from 'xxx'   
//export default 导出方式的引用方式
import fn from './xxx/xxx'  

AMD

Asynchronous Module Definition,异步模块定义。它是一个在浏览器模块化开发的规范,不是原生js的规范,使用AMD规范进行页面开发需要用到对应的函数库,RequireJS

主要有两个JavaScript库实现了AMD规范:require.js和curl.js

AMD规范采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

使用RequireJS实现AMD规范的模块化:用require.config()指定引用路径等,用define()定义模块,用require()加载模块

//定义模块
define('moduleName',['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
    // some code here
})

//引入模块
require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
    // some code here
});

//自定义模块加载行为
require.config({
    baseUrl: "js/lib",
    paths: {
        "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min",
        "underscore": "underscore.min",
        "backbone": "backbone.min"
    }
});

//语法
define(id,dependencies,factory)
id 可选参数,模块标识,默认为脚本文件名
dependencies是依赖的模块名称数组
factory是函数,在模块的依赖加载完毕之后,该函数会被调用来定义该模块,因此该模块应该返回一个定义了本模块的object。依赖关系会以参数的形式注入到该函数上,参数列表与依赖名称列表一一对应。

require([module],callback);
第一个参数[module],是一个数组,表示所依赖的模块;
第二个参数callback,加载成功之后的回调函数,当前面指定的模块都加载成功之后,它会被调用。加载的模块以参数形式传入该函数,回调函数内部就可以使用这些模块。

RequireJS主要解决的问题

文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器,js加载的时候浏览器会停止页面渲染,加载文件越多,页面响应时间越长,所以需要异步前置加载

实现js文件的异步加载,避免网页失去响应

管理模块之间的依赖性,便于代码的编写和维护。

 

CMD

CMD 是另一种js模块化方案,它与AMD很类似,不同点在于:AMD推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。此规范是在SeaJS推广过程中产生的。

因为CMD推崇一个文件一个模块,所以经常就用文件名作为模块id;

CMD推崇依赖就近,所以一般不在define的参数中写依赖,而是在factory中写。

// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
// 此处略去 100 行
var b = require('./b') // 依赖可以就近书写
b.doSomething()
// ... 
})

AMD和CMD的区别?

1.AMD是RequireJS在推广过程中对模块定义的规范化产出,CMD是SeaJS在推广过程中对模块定义的规范化产出

2.对于依赖的模块,AMD是提前执行,CMD是延迟执行

3.CMD推崇依赖就近,AMD推崇依赖前置

 

UMD通用模块规范

一种整合了CommonJS和AMD规范的方法,希望能解决跨平台模块方案。

运行原理

UMD先判断是否支持Node.js的模块(exports是否存在),存在则使用Node.js模块模式。

再判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。

 

 

参考

Javascript模块化编程(三):require.js的用法

主流的模块化

Javascript模块化编程(二):AMD规范

主流的模块化方式及区别

 

posted @   qiao101  阅读(440)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示