前端开发之模块化开发中的规范讲解--commonjs、AMD、CMD、ES6

commonjs、AMD、CMD、ES6有什么区别?是什么意思?前端开发为何要模块化?由于听朋友说面试时被问道,不是很清楚,因此想在此阐述一下。

复制代码

一、为何前端使用模块化开发?

       模块化是指把一个复杂的系统分解到多个模块以方便编码。就像积木,咱们能够能够重复的利用这些积木组成不一样的形状。一个模块就是实现特定功能的文件,有了模块,咱们就能够更方便地使用别人的代码,想要什么功能,就加载什么模块。模块开发须要遵循必定的规范,不然就都乱套了。javascript

二、commonjs、AMD、CMD、ES6是什么?

commonjs、AMD、CMD是一种规范也是一种标准。

  2.1 AMD

AMD 是RequireJs在推广过程当中对模块化定义的规范化产出。(异步模块)AMD 规范主要是为了解决针对 浏览器环境的模块化问题。

AMD异步加载模块。它的模块支持对象 函数 构造器 字符串 JSON等各类类型的模块。 
前端

AMD 的优势java

  • 可在不转换代码的状况下直接在浏览器中运行
  • 可加载多个依赖
  • 代码可运行在浏览器环境和 Node.js 环境下

AMD 的缺点node

  • JavaScript 运行环境没有原生支持 AMD,须要先导入实现了 AMD 的库后才能正常使用。

  用法:jquery

// 定义模块,三个参数
define(id?, deps?, factory)

// 执行模块,两个参数 依赖前置
require(deps, factory)
复制代码

例子:
// 定义一个模块  经过数组引入依赖 ,回调函数经过形参传入依赖 
define('a', [], function () {
    return 'a';
});
define('b', ['a'], function (a) {
    return a + 'b';
});
// 导入和使用
require(['b'], function (b) {
    console.log(b);
});
复制代码

例子分析:nginx

参数说明:git

id:指定义中模块的名字,可选;若是没有提供该参数,模块的名字应该默认为模块加载器请求的指定脚本的
名字。若是提供了该参数,模块名必须是“顶级”的和绝对的(不容许相对名字)。

依赖deps:是一个当前模块依赖的,已被模块定义的模块标识的数组字面量。
依赖参数是可选的,若是忽略此参数,它应该默认为["require", "exports", "module"]。
然而,若是工厂方法的长度属性小于3,加载器会选择以函数的长度属性指定的参数个数调用工厂方法。

工厂方法factory,模块初始化要执行的函数或对象。若是为函数,它应该只被执行一次。
若是是对象,此对象应该为模块的输出值。复制代码

经过define来定义一个模块,a 和 b,由于require用到了b,全部须要依赖前置,先定义b,而b中又用到了aes6

经过deps参数,很容易知道该文件依赖的脚本是b.js,而后去加载依赖的脚本,加载完以后执行一遍而且将执行的结果做为回调函数factory的参数,而后执行该匿名的回调函数便可。github

ps:面试

require API 介绍:github.com/amdjs/amdjs…

AMD规范中文版:github.com/amdjs/amdjs…

原理:

let factories = {};
function define(modName, dependencies, factory) {
    factory.dependencies = dependencies;
    factories[modName] = factory;
}
function require(modNames, callback) {
    let loadedModNames = modNames.map(function (modName) {
        let factory = factories[modName];
        let dependencies = factory.dependencies;
        let exports;
        require(dependencies, function (...dependencyMods) {
            exports = factory.apply(null, dependencyMods);
        });
        return exports;
    })
    callback.apply(null, loadedModNames);
}
复制代码

2.2 cmd

CMD 是 SeaJS 在推广过程当中对模块定义的规范化产出。该规范明确了模块的基本书写格式和基本交
互规则。该规范是在国内(淘宝)发展出来的。同步概念。复制代码

在 CMD 规范中,一个模块就是一个文件。

代码的书写格式以下:

define(factory);复制代码

define 是一个全局函数,用来定义模块

define 接受 factory 参数,factory 能够是一个函数,也能够是一个对象或字符串。

factory 为对象、字符串时,表示模块的接口就是该对象、字符串。好比能够以下定义一个 JSON 数据模块:

define({ "foo": "bar" });复制代码

也能够经过字符串定义模板模块:

define('I am a template. My name is {{name}}.');复制代码

factory 为函数时,表示是模块的构造方法。执行该构造方法,能够获得模块向外提供的接口。factory 方法在执行时,默认会传入三个参数:requireexportsmodule

define(function(require, exports, module) {

  // 模块代码

});复制代码

define define(id?, deps?, factory)

define 也能够接受两个以上参数。字符串 id 表示模块标识,数组 deps 是模块依赖。好比:

define('hello', ['jquery'], function(require, exports, module) {

  // 模块代码

});复制代码

iddeps 参数能够省略。省略时,能够经过构建工具自动生成。

注意:带 iddeps 参数的 define 用法不属于 CMD 规范,而属于 Modules/Transport 规范。

CMD具体规范: github.com/seajs/seajs…

sea.js如何使用?

  • 1.- 引入sea.js的库
  •  2.- 如何变成模块? - define - 
  • 3.-如何调用模块?  -exports -sea.js.use 
  • 4.-如何依赖模块? -require
<script type="text/javascript"> define(function (require,exports,module) { //exports : 对外的接口 //requires : 依赖的接口 require('./test.js');//若是地址是一个模块的话,那么require的返回值就是模块中的exports }) </script> 复制代码

2.3 commonjs

CommonJS是服务器端模块的规范,Node.js采用了这个规范。Node.JS首先采用了js模块化的概念。核心思想是经过
require方法来同步地加载依赖的其余模块,经过 module.exports 导出须要暴露的接口。复制代码

一、根据CommonJS规范,一个单独的文件就是一个模块。

二、每个模块都是一个单独的做用域,也就是说,在该模块内部定义的变量,没法被其余模块读取,除非定义为global对象的属性。

三、模块能够屡次加载,但只会在第一次加载的时候运行一次,而后运行结果就被缓存了,之后再加载,就直接读取缓存结果;模块的加载顺序,按照代码的出现顺序是同步加载的;

四、__dirname表明当前模块文件所在的文件夹路径,__filename表明当前模块文件所在的文件夹路径+文件名;

五、require(同步加载)基本功能:读取并执行一个JS文件,而后返回该模块的exports对象,若是没有发现指定模块会报错;

六、模块内的exports:为了方便,node为每一个模块提供一个exports变量,其指向module.exports,至关于在模块头部加了这句话:var exports = module.exports,在对外输出时,能够给exports对象添加方法,PS:不能直接赋值(由于这样就切断了exports和module.exports的联系);

七、npm root -g:查看npm全局包安装位置,建议在nvm目录下新建npm\node_modules目录,而后设置npm的全局包安装位置:npm config set prefix "",而后将该路径添加到环境变量中;

八、npm init -y:初始化一个package.json文件,加上-y就会默认生成该文件,无需一步一步填写;npm docs 包名:查看包的文档;npm install:安装package.json中dependencies属性中全部依赖的包

九、因为npm的服务器是国外的,因此若是你没有和谐工具是下载不了的,这里推荐使用淘宝NPM镜像:http://npm.taobao.org/,与官方NPM的同步频率目前为10分钟一次;安装命令:npm install -g cnpm --registry=https://registry.npm.taobao.org,安装包:cnpm install 包名(其它命令基本一致);

十、若是你不想下载cnpm,npm还提供了一个镜像源管理工具:npm install -g nrm,经过:nrm ls,查看镜像源列表 ,经过:npm use 镜像源,来切换;

十一、NPM的模块加载机制:

若是require的是绝对路径文件,查找不会去遍历每一个node_modules目录,其速度最快

  1).从module.paths数组中(由当前执行文件目录到磁盘根目录)取出第一个目录做为查找基准

  2).直接从目录中查找该文件,若是存在则结束查找,若是不存在则进行下一条查找

  3).尝试添加.js、.node、.json后缀以后查找,若是存在文件则结束查找,若是不存在则进行下一条查找

  4).尝试将require的参数做为一个包来进行查找,读取目录下的package.json文件,取得Main参数指定的文件

  5).尝试查找该文件,若是存在则结束查找,若是不存在则进行第3条查找

  6).若是继续失败,则取出module.paths数组中的下一目录做为基准查找,循环第1-5个步骤

  7).若是继续失败,循环第1-6个步骤,直到module.paths中的最后一个值

  8).若是继续失败,则抛出异常


CommonJS定义的模块分为:

{模块引用(require)} {模块定义(exports)} {模块标识(module)}

require()用来引入外部模块;

exports对象用于导出当前模块的方法或变量,惟一的导出口;module对象就表明模块自己。

用法

采用 CommonJS 导入及导出时的代码以下:

// 导入
const someFun= require('./moduleA');
someFun();

// 导出
module.exports = someFunc;复制代码


 原理

a.js

let fs = require('fs');
let path = require('path');
let b = req('./b.js');
function req(mod) {
    let filename = path.join(__dirname, mod);
    let content = fs.readFileSync(filename, 'utf8');
    let fn = new Function('exports', 'require', 'module', '__filename', '__dirname', content + '\n return module.exports;');
    let module = {
        exports: {}
    };

    return fn(module.exports, req, module, __filename, __dirname);
}
复制代码

b.js

console.log('bbb');
exports.name = 'Jones';复制代码

2.4 ES6 模块化

ES6 模块化是ECMA提出的JavaScript模块化规范,它在语言的层面上实现了模块化。浏览器厂商和Node.js 都宣布要原生支持该规范。它将逐渐取代CommonJSAMD`规范,成为浏览器和服务器通用的模块解决方案。 采用 ES6 模块化导入及导出时的代码以下

// 导入
import { name } from './person.js';
// 导出
export const name = 'Jones';复制代码

三、区别

commonJS规范:服务端的模块化规范,不适用于浏览器环境。适用于nodejs,终极目标是提供一个相似Python,Ruby和Java标准库。

AMD、CMD;浏览器端模块化开发的规范,都是模块加载器,倡导模块化开发理念,核心价值是让 JavaScript 的模块化开发变得简单天然。区别:CMD对模块的态度是懒执行(同步), 而AMD对模块的态度是预执行(异步)ps:AMD目前也在同步。

commonjs\AMD、CMD是es5中退出的。

es6中的:import|export 


本文内容参考网上内容,若有雷同和错误,请与我联系。

相关文章
相关标签/搜索