谈谈Js前端模块化范例

抛出题目:

  • 在开辟中在导入模块时常常运用requireimport
  • 导出模块时运用module.exports/exports或许export/export default;
  • 有时刻为了援用一个模块会运用require奇怪的是也可以运用import????它们之间有何区分呢?

因而有了菜鸟解惑的搜喽历程。。。。。。

追溯泉源,来到Js模块化范例

1、CommonJS范例(同步加载模块)

  • 许可模块经由过程require要领来同步加载所要依靠的其他模块,然后经由过程exports或module.exports来导出须要暴露的接口。
  • 运用体式格局:
// 导入
require("module");
require("../app.js");
// 导出
exports.getStoreInfo = function() {};
module.exports = someValue;
    • 长处:

      1. 简朴轻易运用
      2. 服务器端模块便于复用
    • 瑕玷:

      1. 同步加载体式格局不适合在浏览器环境中运用,同步意味着壅塞加载,浏览器资本是异步加载的
      2. 不能非壅塞的并行加载多个模块
    • 为何浏览器不能运用同步加载,服务端可以?

      • 因为模块都放在服务器端,关于服务端来讲模块加载时
      • 而关于浏览器端,因为模块都放在服务器端,加载的时刻还取决于网速的快慢等要素,假如须要等很长时刻,悉数运用就会被壅塞。
      • 因而,浏览器端的模块,不能采纳”同步加载”(CommonJs),只能采纳”异步加载”(AMD)。
    • 参照CommonJs模块代表node.js的模块体系

    AMD(异步加载模块)

    • 采纳异步体式格局加载模块,模块的加载不影响背面语句的运转。一切依靠模块的语句,都定义在一个回调函数中,比及加载完成今后,回调函数才实行。
    • 运用实例:
    // 定义
    define("module", ["dep1", "dep2"], function(d1, d2) {...});
    // 加载模块
    require(["module", "../app"], function(module, app) {...});
    
    • 加载模块require([module], callback);第一个参数[module],是一个数组,内里的成员就是要加载的模块;第二个参数callback是加载胜利今后的回调函。
    • 长处:

      1. 适合在浏览器环境中异步加载模块
      2. 可以并行加载多个模块
    • 瑕玷:

      1. 提高了开辟本钱,代码的浏览和誊写比较难题,模块定义体式格局的语义不顺畅
      2. 不符合通用的模块化头脑体式格局,是一种让步的完成
    • 完成AMD范例代表require.js

    RequireJS对模块的立场是预实行。因为 RequireJS 是实行的 AMD 范例, 因而一切的依靠模块都是先实行;即RequireJS是预先把依靠的模块实行,相称于把require提早了

    • RequireJS实行流程:
    1. require函数搜检依靠的模块,依据配置文件,猎取js文件的现实途径
    2. 依据js文件现实途径,在dom中插进去script节点,并绑定onload事宜来猎取该模块加载完成的关照。
    3. 依靠script悉数加载完成后,调用回调函数

    CMD范例(异步加载模块)

    • CMD范例和AMD很类似,简朴,并与CommonJS和Node.js的 Modules 范例坚持了很大的兼容性;在CMD范例中,一个模块就是一个文件。
    • 定义模块运用全局函数define,其吸收 factory 参数,factory 可以是一个函数,也可以是一个对象或字符串;
    • factory 是一个函数,有三个参数,function(require, exports, module):

      1. require 是一个要领,接收模块标识作为唯一参数,用来猎取其他模块供应的接口:require(id)
      2. exports 是一个对象,用来向外供应模块接口
      3. module 是一个对象,上面存储了与当前模块相干联的一些属性和要领
    • 实例:
    define(function(require, exports, module) {
      var a = require('./a');
      a.doSomething();
      // 依靠就近誊写,什么时刻用到什么时刻引入
      var b = require('./b');
      b.doSomething();
    });
    • 长处:
    1. 依靠就近,耽误实行
    2. 可以很轻易在 Node.js 中运转
    • 瑕玷:
    1. 依靠 SPM 打包,模块的加载逻辑着重
    • 完成代表库sea.js:SeaJS对模块的立场是懒实行, SeaJS只会在真正须要运用(依靠)模块时才实行该模块

    AMD 与 CMD 的区分

    1. 关于依靠的模块,AMD 是提早实行,CMD 是耽误实行。不过 RequireJS 从2.0最先,也改成了可以耽误实行(依据写法差别,处理体式格局差别)。CMD 推重 as lazy as possible.
    2. AMD推重依靠前置;CMD推重依靠就近,只需在用到某个模块的时刻再去require。
    // AMD
    define(['./a', './b'], function(a, b) {  // 依靠必需一最先就写好  
       a.doSomething()    
       // 此处略去 100 行    
       b.doSomething()    
       ...
    });
    // CMD
    define(function(require, exports, module) {
       var a = require('./a')   
       a.doSomething()   
       // 此处略去 100 行   
       var b = require('./b') 
       // 依靠可以就近誊写   
       b.doSomething()
       // ... 
    });

    UMD

    • UMD是AMD和CommonJS的糅合
    • AMD 以浏览器第一准绳生长异步加载模块。
    • CommonJS 模块以服务器第一准绳生长,挑选同步加载,它的模块无需包装。
    • UMD先推断是不是支撑Node.js的模块(exports)是不是存在,存在则运用Node.js模块形式;在推断是不是支撑AMD(define是不是存在),存在则运用AMD体式格局加载模块。
    (function (window, factory) {
        if (typeof exports === 'object') {
        
            module.exports = factory();
        } else if (typeof define === 'function' && define.amd) {
        
            define(factory);
        } else {
        
            window.eventUtil = factory();
        }
    })(this, function () {
        //module ...
    });

    ES6模块化

    • ES6 在言语规范的层面上,完成了模块功用,而且完成得相称简朴,完全可以庖代 CommonJS 和 AMD 范例,成为浏览器和服务器通用的模块解决方案。
    • ES6 模块设想头脑:只管的静态化、使得编译时就可以肯定模块的依靠关联,以及输入和输出的变量(CommonJS和AMD模块,都只能在运转时肯定这些东西)。

      • 运用体式格局:
    // 导入
    import "/app";
    import React from “react”;
    import { Component } from “react”;
    // 导出
    export function multiply() {...};
    export var year = 2018;
    export default ...
    ...
    • 长处:
    1. 轻易举行静态剖析
    2. 面向未来的 EcmaScript 规范
    • 瑕玷:
    1. 原生浏览器端还没有完成该规范
    2. 全新的敕令字,新版的 Node.js才支撑。

    回到题目“require与import的区分”

    • require运用与CommonJs范例,import运用于Es6模块范例;所以二者的区分本质是两种范例的区分;
    • CommonJS:
    1. 关于基础数据类型,属于复制。即会被模块缓存;同时,在另一个模块可以对该模块输出的变量从新赋值。
    2. 关于庞杂数据类型,属于浅拷贝。因为两个模块援用的对象指向同一个内存空间,因而对该模块的值做修正时会影响另一个模块。
    3. 当运用require敕令加载某个模块时,就会运转悉数模块的代码。
    4. 当运用require敕令加载同一个模块时,不会再实行该模块,而是取到缓存当中的值。也就是说,CommonJS模块不管加载多少次,都只会在第一次加载时运转一次,今后再加载,就返回第一次运转的效果,除非手动消灭体系缓存。
    5. 轮回加载时,属于加载时实行。即剧本代码在require的时刻,就会悉数实行。一旦涌现某个模块被”轮回加载”,就只输出已实行的部份,还未实行的部份不会输出。
    • ES6模块
    1. ES6模块中的值属于【动态只读援用】。
    2. 关于只读来讲,即不许可修正引入变量的值,import的变量是只读的,不论是基础数据类型照样庞杂数据类型。当模块碰到import敕令时,就会天生一个只读援用。比及剧本真正实行时,再依据这个只读援用,到被加载的谁人模块内里去取值。
    3. 关于动态来讲,原始值发生变化,import加载的值也会发生变化。不论是基础数据类型照样庞杂数据类型。
    4. 轮回加载时,ES6模块是动态援用。只需两个模块之间存在某个援用,代码就可以够实行。
    • 末了:require/exports 是必要通用且必需的;因为事实上,现在你编写的 import/export 终究都是编译为 require/exports 来实行的。
    • 参考
    1. AMD与CMD范例详解
    2. CommonJS模块和ES6模块的区分
    3. Js模块范例
    4. 明白Js模块化
    5. Require与import的区分

    “积跬步、行千里”—— 不时的分享更新中~,喜好留下个赞哦!

        原文作者:keywords
        原文地址: https://segmentfault.com/a/1190000015991869
        本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
    点赞