JS:commonJS AMD/requireJS ES6模块化开发

commonJS

commonJS 定义了一套 JavaScript 模块化的规范,Node 中的模块系统即参照 commonJS 规范实现,webpack 也对 commonJS 提供原生支持。根据该规范,每一个文件即一个模块,其内部定义的变量是属于这个模块的,不会对外暴露,也就是说不会污染全局变量。commonJS 使用同步方式加载模块,适用于服务端而不适合客户端。

机制

当 commonJS 第一次加载某个模块时,Node 会缓存该模块。每当再次加载该模块时,就直接从缓存取出该模块的 module.exports 属性。模块被引入时的值是其输出值在缓存中的拷贝。一旦输出一个值,模块内部的变化就影响不到这个值。

使用示例

引入:

 const $ = require("lib/js/jquery-1.11.3.min.js"); 

导出

module.exports = function(para){
	console.dir(para);
}

入口文件

一般都会有一个主文件(入口文件),在index.html中加载这个入口文件,然后在这个入口文件中加载其他文件。可以通过在package.json中配置main字段来指定入口文件。

AMD / requireJS

AMD 全称 Asynchronous Module Definition,即异步模块加载机制。AMD规范完整描述了模块的定义,依赖关系,引用关系以及加载机制。AMD规范的作者亲自实现了符合AMD规范的requireJS。

机制

在requireJS内部,会使用head.appendChild()将每一个模块依赖加载为一个script标签。requireJS等待所有的依赖加载完毕,计算出模块定义函数正确调用顺序,然后依次调用它们。

使用示例

HTML中的script标签在加载和执行过程中会阻塞网页的渲染,所以通常会将script标签放置在body元素的底部,或者通过async异步方式加载js文件。

<script data-main="js/main" src="js/require.js" defer asyn="true">script>

require.js加载时会检查data-main属性,当requireJS加载执行后,会继续异步加载data-main指向的main.js文件,并作为当前网页逻辑入口。理想情况下,整个网页只需要这一个script标记,利用requireJS加载依赖的其它文件。

注意 main.js中的脚本都是异步加载的,如果在页面中配置了其它JS加载,则不能保证它们所依赖的JS已经加载成功。

define方法:define(module_name, dependencies_array, module);
define方法:
  module_name: 模块名,可选。
  dependencies_array: 依赖的模块,可选。
  module: 模块的实现本身,一个对象或一个函数。如果是一个函数则必须返回一个对象作为输出,该函数将在其依赖的模块加载成功后被调用。依赖的模块会以参数形式传入该函数,在其内部可以使用这些模块。

// alpha.js
define(["beta"], function (b) {
    return {a: "alpha",b: b};
});
// beta.js
define(function () {
    return "beta";
});

require方法:require(dependencies_array, module);
require 参数:
dependencies_array: 依赖的模块。
module: 模块的实现本身,一个函数。

// main.js
require(["alpha"], function (a) {
    console.dir(a);
});

注意1 define() 定义的模块可以被调用,require() 定义的模块不可以被调用。入口文件不需要被别的模块调用,用 require() 或 define() 都行,其他模块需要被调用,只能用 define()。

注意2 requireJS 默认在没有 data-main 和 config 的情况下,把当前页面所在目录作为 baseUrl;在有 data-main 的情况下,把 data-main 所在目录作为 baseUrl;在有 config 的情况下,以 config 配置的为准。

在上面的示例中,入口文件 main.js 依赖于 alpha.js,直接写成[“alpha”],则意味着此时 alpha.js 和 main.js 在同一个目录。

上面 alpha.js 代码也可以对象形式导出,写作:

// alpha.js
define(["beta"], {
	a: "alpha",
	b: function (b) {
    	return b;
	}
});

上面 main.js 代码也可以加入 config 以适应需求,写作:

// main.js
require.config({
    baseUrl: 'modules'
});
require(["alpha"], function (a) {
    console.dir(a);
});

此时,可以把 alpha.js 和 beta.js 都放在 modules 目录下,方便管理。如果有模块的存放路径较深,可以这样设置:

// main.js
require.config({
    baseUrl: 'modules',
    paths: {
		'jq':'jquery/min/pc/jquery-1.11.3.min'
	}
});
require(["jq","alpha"], function ($,a) {
	console.dir($);
    console.dir(a);
});

此时, modules/jquery/min/pc/jquery-1.11.3.js 则可以直接使用 jq 来引入。

注意 paths字段可以指定各个模块的位置,上面代码中 jq 的实际路径为 baseUrl + ‘/’ + paths.jq + ‘.js’,在 paths 中定义时是以 baseURL 为起始路径进行查找的。

除此之外, config 还可以进行以下设置:

require.config({
    baseUrl: 'modules',
    paths: {
		jq: 'jquery/min/pc/jquery-1.11.3.min',
		// 一个模块可定义多个 paths 备用,加载失败时自动加载备用 paths
		vue: [
			// paths 可以使用相对路径、绝对路径和 url 
			'https://cdn.vue.org/v2/development/vue-2.5.8.js',
			'vue/dev/vue-2.5.8'
		]
	},
	// 可以通过 module.config() 调取 config 数据
	config: {
		// 即 module.config().index.apiKey 
        'index': {
            apiKey: '211_ronnie_985'
        },
        // 将目标设置为包的主模块来把config传给包
        'login/index': {
            apiKey: '211_ronnie_985'
        }
    },
    // 从CommonJS包(package)中加载的模块
    packages: [
        {
            name: 'login',
            // 主模块
            main: 'index'
        }
    ],
    // 为指定模块(module)加载指定paths的模块(foo)以供调用
    map: {
    	// 即,当 new/moudule.js 调用 require('foo') 时,加载 foo-2.0.js,该特性仅适用于调用了 define 并将其注册为匿名模块的真正 AMD 模块
        'new/module': {
            'foo': 'foo-2.0'
        },
        'old/module': {
            'foo': 'foo-1.0'
        }
    },
    // 可以对于非AMD规范的库进行依赖和导出需要通过shim进行配置
	shim: {
        // 外部调用时使用 Backbone 指代该模块
        'backbone': {
        	// 改模块依赖于 underscore , jquery 
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        },
        // 外部调用时使用 _ 指代该模块
        'underscore': {
            exports: '_'
        },
    }
});

注意 shim配置仅设置了代码的依赖关系,想要实际加载shim指定的或涉及的模块,仍然需要一个常规的require/define调用。设置shim本身不会触发代码的加载。

注意 请仅使用其他"shim"模块、无依赖关系且在 define 之前定义了全局变量(如jQuery或lodash)的 AMD 库作为 shim 脚本的依赖。如果使用了一个AMD模块作为一个 shim 配置模块的依赖,在 build 之后 AMD 模块可能在 shim 托管代码执行之前都不会被执行。终极的解决方案是将所有 shim 托管代码都升级为含有可选的 AMD define 调用。

懒加载

commonJS规范加载模块是同步的,只有加载完成,才能执行后续操作。AMD规范则是异步加载模块,允许指定回调函数,在回调函数中执行操作。由于Node.js主要用于服务器编程,模块文件通常存储于本地硬盘,加载速度快,不需要异步加载模块。但在浏览器环境中,模块文件需要从服务器端请求加载,所以必须采用异步加载模式。AMD规范保留了 commonJS 中的 require、exprots、module这三个功能。可以在代码中用require来引入依赖模块,而不需罗列在dependencies数组中。

上面的代码可以这样实现懒加载:

// main.js
require.config({
    baseUrl: 'modules',
    paths: {
		'jq':'jquery/min/pc/jquery-1.11.3.min'
	}
});
require(["jq"], function ($) {
	console.dir($);
	require(['alpha'],function(a){
        console.dir(a);
   });
});

此时,alpha.js 在代码执行到 require 时才加载,而不是页面加载时就加载,如果 require 在一个 click 事件中,那么当触发 click 事件时才会加载。

CMD / sea.js

CMD 全称 Common Module Definition, 即模块定义规范。该规范明确了模块的基本书写格式和基本交互规则,并在 AMD 基础上增加了异步加载API。 sea.js 实现了 CMD 规范模块化。

和 AMD 的懒加载方式不同,CMD 定义了诸如 require.sync() 这样的方法来实现异步加载。

包装 commonJS 模块

虽说Node遵循commonJS的规范,但是相比也是做了一些取舍,填了一些新东西的。浏览器不兼容commonJS的根本原因,在于缺少四个nodeJS环境的变量,即 module、exports、require 和 global。只要能够提供这四个变量,浏览器就能加载 commonJS 模块。AMD规范允许输出的模块兼容commonJS规范,这时define方法需要写成下面这样:

define(function(require, exports, module) {
    var a = require('a'),
        b = require('b');
    return function () {};
    exports.explode = function(){}
});

Web Worker 支持

从版本0.12开始,requireJS可在Web Worker中运行。可以通过在web worker中调用importScripts()来加载requireJS(或包含require()定义的JS文件),然后调用require就好了。

ES6 模块化方案规范

在ES6中,模块化方案规范得到了实现,功能较之于社区的方案更为强大。由于ES6目前无法在浏览器中执行,所以只能通过 babel 编译为当前受到广泛支持的方案。

ES6使用 import 关键字引入模块,使用 export 关键字导出模块。

// 单一输出模块导入
import axios from '../assets/js/request'
// 多个输出模块分别导入 和 重命名
import {mapState as mapStatus, mapMutations, mapActions} from 'vuex'
// 多个输出模块整体导入
import * as util from '../utils/js/util.js'

// 导出变量
export let name = "ronnie";
export const age = 25;
// 导出类
export class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }
}
// 导出函数
export function minus (num1, num2){
    return num1 - num2;
}
// 导出对象(引用)
function multiply(num1, num2) {
    return num1 * num2;
}
export {multiply}
// 导出重命名 配置
function plus(num1, num2){
    return num1 + num2;
}
export {plus as sum}
// 导出默认值配置
export default {
  created () {
    this.getClassify();
    this.RESET_VALUE();
    console.log('created' ,new Date().getTime());
  }
}

你可能感兴趣的:(JS)