再談JavaScript模塊化

閱讀原文javascript

簡述

緣起

模塊一般是指編程語言所提供的代碼組織機制,利用此機制可將程序拆解爲獨立且通用的代碼單元。css

模塊化主要是解決代碼分割、做用域隔離、模塊之間的依賴管理以及發佈到生產環境時的自動化打包與處理等多個方面。html

javascript應用日益複雜,模塊化已經成爲一個迫切需求。可是做爲一個模塊化方案,它至少要解決以下問題:前端

  • 可維護性
  • 避免做用域污染
  • 代碼重用
  • 依賴管理

script

最原始的方式就是,每一個文件就是一個模塊,而後使用script的方式進行引入。vue

可是此方式有如下問題:java

  • 全局做用域污染
  • 無依賴管理,執行順序依賴script標籤的順序,若是採用異步加載,那會亂套,先下載的先執行

IIFE 和 模塊對象

爲了解決做用域污染的問題,就產生了當即執行函數 + 模塊對象模式:jquery

// app1.js
var app = {};
複製代碼
// app2.js
(function(){
  app.a = function(a, b) {
    // code
  }  
})();
複製代碼
// app3.js
(function(app){
  var temp = [ 1, 2];
  var a = app.a(temp)
})(app);
複製代碼

具體的能夠查閱阮一峯老師的博客Javascript模塊化編程(一):模塊的寫法webpack

在ES6以前,js沒有塊級做用域,因此採用此方式創建一個函數做用域。可是在ES6以後,可使用塊級做用域。git

因爲使用了IIFE,因此減小了全局做用域污染,但並非完全消除,由於還定義了一個appa模塊對象呢。github

因此這也僅僅只是減小了做用域污染,仍是會有其餘缺點。

CommonJS

後來,有人試圖將javascript引入服務端,因爲服務端編程相對比較複雜,就急需一種模塊化的方案,因此就誕生了commonjs,有require + module.exports實現模塊的加載和導出。

CommonJS採用同步的方式加載模塊,主要使用場景爲服務端編程。由於服務器通常都是本地加載,速度較快。

AMD 和 CMD

後來,隨着前端業務的日漸複雜,瀏覽器端也須要模塊化,可是commonjs是同步加載的,這意味着加載模塊時,瀏覽器會凍結,什麼都幹不了,這在瀏覽器確定是不行的,這就誕生了AMD和CMD規範,分別以requirejs和seajs爲表明。

這兩貨都採用異步方式加載模塊。

AMD

AMD(Asynchronous Module Defination)異步模塊加載機制。

define(
    [module_id,] // 模塊名字,若是缺省則爲匿名模塊
    [dependenciesArray,] // 模塊依賴 
    definition function | object // 模塊內容,能夠爲函數或者對象 ); 複製代碼

CMD

CMD(Common Module Defination)通用模塊加載機制

// 方式一
define(function(require, exports, module) { 
    // 模塊代碼
    var a = require('a')
});
// 方式二
define( 'module', ['module1', 'module2'], function( require, exports, module ){
    // 模塊代碼
} );
複製代碼

區別

  • 對於依賴的模塊,AMD 是提早執行( RequireJS 從 2.0 開始,也改爲能夠延遲執行),CMD 是延遲執行
  • CMD 推崇依賴就近,AMD 推崇依賴前置
  • AMD 的 API 默認是一個當多個用,CMD 的 API 嚴格區分,推崇職責單一

不完美

儘管以上方案解決了上面說的問題,可是也帶來了一些新問題:

  • 語法冗餘,全部東西都要封裝在define中
  • AMD中的依賴列表必須與函數的參數列表匹配,易錯且修改苦難

Browserify

因爲上述這些緣由,有些人想在瀏覽器使用 CommonJS 規範,但 CommonJS 語法主要是針對服務端且是同步的,因此就產生了Browserify,它是一個 模塊打包器(module bundler),能夠打包commonjs規範的模塊到瀏覽器中使用。

UMD

UMD(Universal Module Definition) 統一模塊定義。

AMD 與 CommonJS 雖然師出同源,但仍是分道揚鑣,關注於代碼異步加載與最小化入口模塊的開發者將目光投注於 AMD;而隨着 Node.js 以及 Browserify 的流行,愈來愈多的開發者也接受了 CommonJS 規範。使人扼腕嘆息的是,符合 AMD 規範的模塊並不能直接運行於 CommonJS 模塊規範的環境中,符合 CommonJS 規範的模塊也不能由 AMD 進行異步加載。

並且有這麼多種規範,若是咱們要發佈一個模塊供其餘人用,咱們不可能爲每種規範發佈一個版本,就算你蛋疼這樣作了,別人使用的時候還得下載對應版本,因此如今須要一種方案來兼容這些規範。

實現的方式就是在代碼前面作下判斷,根據不一樣的規範使用對應的加載方式。

// 以vue爲例
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global = global || self, global.Vue = factory());
}(this, function () { 
    // vue code ...
})
複製代碼

因爲目前ES6瀏覽器支持還不夠好,因此不少第三方庫都採用了這種方式。

ESModule

ES6引入了ESModule規範,主要經過export + import來實現,最終一統江湖。但是現實很骨感,一些瀏覽器並不支持(IE,說的就是你),因此還不能直接在瀏覽器中直接使用。

經常使用的兩種方案

  • 在線編譯:requirejs/seajs/sytemjs

在頁面上加載一個AMD/CMD模塊格式解釋器。這樣瀏覽器就認識了define, exports,module這些東西,也就實現了模塊化。

SystemJS 是一個通用的模塊加載器,它能在瀏覽器或者 NodeJS 上動態加載模塊,而且支持 CommonJS、AMD、全局模塊對象和 ES6 模塊。經過使用插件,它不只能夠加載 JavaScript,還能夠加載 CoffeeScript 和 TypeScript。配合jspm也是不錯的搭配。

  • 預編譯:browserify/webpack

相比於第一種方案,這個方案更加智能。因爲是預編譯的,不須要在瀏覽器中加載解釋器。你在本地直接寫JS,無論是AMD/CMD/ES6風格的模塊化,它都能認識,而且編譯成瀏覽器認識的JS。

注意: browerify只支持Commonjs模塊,如需兼容AMD模塊,則須要plugin轉換

CommonJS

前身爲ServerJS。 咱們能夠理解爲代碼會被以下內建輔助函數包裹:

(function (exports, require, module, __filename, __dirname) {
    // ...
    // Your code
    // ...
});
複製代碼

加載

經過require加載模塊。

const a = require('a')
複製代碼

導出

經過exportsmodule.exports進行模塊導出。

  • exportsexportsmodule.exports的一個引用,一個模塊可使用屢次,可是不能直接對exports從新賦值,只能經過以下方式使用
exports.a = function(){
    // code...
}
複製代碼
  • module.exports:一個模塊只能使用一次
module.exports = function(){
    // code...
}
複製代碼

特色

  • 同步加載,定位於服務端,不適合瀏覽器

requirejs

特色

  • 支持同步和異步兩種方式
  • 大多數第三方庫都有兼容AMD,即時不兼容也能夠經過配置使其可用
  • 異步加載,不阻塞瀏覽器
  • 依賴前置,能夠很清楚看到當前模塊的依賴

引入

在引入requirejs的script標籤上添加data-main屬性定義入口文件,該文件會在requirejs加載完後當即執行。

若是baseUrl未單獨配置,則默認爲引入require的文件的路徑。

<script src="./assets/lib/requirejs/require.js" data-main="./assets/lib/requirejs/config"></script>
複製代碼

config

requirejs.config({
    // 爲模塊加上query參數,解決瀏覽器緩存,只在開發環境使用
    urlArgs: 'yn-course=' + (new Date()).getTime(),
    // 配置全部模塊加載的初始路徑,全部模塊都是基於此路徑加載
    baseUrl: './', 
    // 映射一些快捷路徑,至關於別名
    paths: {
        '~': 'assets',
        '@': 'components',
        'vue': 'assets/lib/vue/vue',
        'vueRouter': 'assets/lib/vue-router/vue-router',
        "jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery"]
    },
    // 對於匹配的模塊前綴,使用一個不一樣的模塊ID來加載該模塊
    map: {
        'layer': {
            'jquery': 'http://libs.baidu.com/jquery/2.0.3/jquery'
        }
    },
    // 從CommonJS包(package)中加載模塊
    packages:{},
    // 加載上下文
    context:{},
    // 超時,默認7S
    waitSeconds: 7,
    // 定義應用依賴的模塊,在啓動後會加載此數組中的模塊
    deps: [],
    // 在deps加載完畢後執行的函數
    callback:function(){},
    // 用來加載非AMD規範的模塊,以瀏覽器全局變量注入,此處僅做映射,須要在須要時手動載入
    shim: { 
        // 'backbone': {
        // deps: ['underscore', 'jquery'], // 模塊依賴
        // exports: 'Backbone' // 導出的名稱
        // }
    },
    // 全局配置信息,可在模塊中經過module.config()訪問
    config:{
        color:'red'
    },
    // 若是設置爲true,則當一個腳本不是經過define()定義且不具有可供檢查的shim導出字串值時,就會拋出錯誤
    enforceDefine:false,
    // 若是設置爲true,則使用document.createElementNS()去建立script元素
    xhtm: false,
    //指定RequireJS將script標籤插入document時所用的type=""值
    scriptType:'text/javascript'
});
複製代碼

默認requirejs會根據baseUrl+paths配置去查找模塊,可是以下狀況例外:

  • 路徑以.js結尾,好比lib/hello.js、hello.js
  • 以「/」開始
  • 包含url協議:如"http:"、"https"

設置baseURl的方式有以下三種:

  • requirejs.config指定;
  • 如指data-main,則baseUrl爲data-main所對應的js的目錄
  • 若是上述均未指定,則baseUrl爲運行RequireJS的HTML文件所在目錄

map配置對於大型項目很重要:若有兩類模塊須要使用不一樣版本的"foo",但它們之間仍須要必定的協同。 在那些基於上下文的多版本實現中很難作到這一點。並且,paths配置僅用於爲模塊ID設置root paths,而不是爲了將一個模塊ID映射到另外一個。

requirejs.config({
    map: {
        'some/newmodule': {
            'foo': 'foo1.2'
        },
        'some/oldmodule': {
            'foo': 'foo1.0'
        }
    }
});
複製代碼

define

經過define來定義模塊,推薦依賴前置原則,固然也可使用require動態按需加載。

define(
    [module_id,] // 模塊名字,若是缺省則爲匿名模塊
    [dependencies,] // 模塊依賴 
    definition function | object // 模塊內容,能夠爲函數或者對象 ); 複製代碼
// 若是僅僅返回一個鍵值對,能夠採用以下格式,相似JSONP
define({
    color: "black",
    size: "unisize"
})
//若是沒有依賴
define(function () {
    return {
        color: "black",
        size: "unisize"
    }
})
// 有依賴
define(["./a", "./b"], function(a, b) {
    
})
// 具名模塊
define("name",
    ["c", "d"],
    function(cart, inventory) {
    //此處定義foo/title object
    }   
)
複製代碼

如要在define()內部使用諸如require("./a/b")相對路徑,記得將"require"自己做爲一個依賴注入到模塊中:

define(["require", "./a/b"], function(require) {
    var mod = require("./a/b");
});
複製代碼

或者使用以下方式:

define(function(require) {
    var mod = require("./a/b");
})
複製代碼

標識

require加載的全部模塊都是單例的,每一個模塊都有一個惟一的標識,這個標識是模塊的名字或者模塊的相對路徑(如匿名模塊)。

模塊的惟一性與它們的訪問路徑無關,即便是地址徹底相同的一份JS文件,若是引用的方式與模塊的配置方式不一致,依舊會產生多個模塊。

// User.js
define([], function() {
    return {
        username : 'yiifaa',
        age       : 20
    };
});
複製代碼
require(['user/User'], function(user) {
    //  修改了User模塊的內容
    user.username = 'yiifee';
    //  em/User以baseUrl定義的模塊進行訪問
    //  'user/User'以path定義的模塊進行訪問
    require(['em/User', 'user/User'], function(u1, u2) {
        //  輸出的結果徹底不相同,u1爲yiifaa,u2爲修改後的內容yiifee
        console.log(u1, u2);
    })
})
複製代碼

依賴

requirejs推薦依賴前置,在define或者require模塊的時候,能夠將須要依賴的模塊做爲第一個參數,以數組的方式聲明,而後在回調函數中,依賴會以參數的形式注入到該函數上,參數列表須要和依賴數組中位置一一對應。

define(["./a", "./b"], function(a, b) {
    
})
複製代碼

導出

在requirejs中,有3中方式進行模塊導出:

  • 經過return方式導出,優先級最高(推薦);
define(function(require, exports, module) {
    return {
        a : 'a'
    }
});
複製代碼
  • 通關module.exports對象賦值導出,優先級次之;
define(function(require, exports, module) {
    module.exports = {
        a : 'a'
    }
});
複製代碼
  • 經過exports對象賦值導出,優先級最低;
define(function(require, exports, module) {
    exports.a = 'a'
});
複製代碼

require

requirejs提供了兩個全局變量requirerequirejs供咱們加載模塊,這兩者是徹底等價的。

// 此處require 和 define 函數僅僅是一個參數(模塊標識)的差別,
// 通常require用於沒有返回的模塊,如應用頂層模塊
require(
    [dependencies,] // 模塊依賴 
    definition function // 模塊內容 ); 複製代碼

require是內置模塊,不用在配置中定義,直接進行引用便可。

define(['require'], function(require) {
    var $ = require('jquery');
})
複製代碼

requirejs支持異步(require([module]))和同步(require(module))兩種方式加載,即require參數爲數組即爲異步加載,反之爲同步。

同步加載

在requirejs中,執行同步加載必須知足兩點要求:

  • 必須在定義模塊時使用,亦即define函數中;
  • 引用的資源必須是以前異步加載過的(沒必要在同一個模塊),換句話說,同步載入的模塊是不會發網絡請求的,只會調取以前緩存的模塊;
  • define(function(require, exports, module) { })中能夠同步加載模塊

應用場景

  • 明確知道模塊的前後順序,確認此模塊已經被加載過,例如系統通用模塊,在載入完成後,以後的模塊就能夠進行同步的引用,或者在Vue等前端技術框架中,在應用模塊同步加載vue模塊。
  • 引用的資源列表太長,懶得回調函數中寫一一對應的相關參數
//  假定這裏引用的資源有數十個,回調函數的參數一定很是多
define(['jquery'], function() {
    return function(el) {
        //  這就是傳說中的同步調用
        var $ = require('jquery');
        $(el).html('Hello, World!');
    }
})
複製代碼
  • 能夠減小命名或者命名空間衝突,例如prototype與jquery的衝突問題
define(['jquery', 'prototype'], function() {
    var export = {};
    export.jquery = function(el) {
        //  這就是傳說中的同步調用
        var $ = require('jquery');
        $(el).html('Hello, World!');
    }

    export.proto = function(el) {
        //  這就是傳說中的同步調用
        var $ = require('prototype');
        $(el).html('Hello, World!');
    }
    return export;
})
複製代碼
  • 處女座專用,代碼顯得更整潔漂亮了,硬是把回調函數寫出了同步的感受

異步加載

  • define([],function()):依賴數組中的模塊會異步加載,全部模塊加載完成後混執行回調函數
  • require([]):傳入數組格式即表示須要異步加載
require === requirejs //=> true
複製代碼

require.toUrl("./a.css"): 獲取模塊url

模塊卸載

只要頁面不刷新,被requirejs加載的模塊只會執行一次,後面會一直緩存在內存中,即時從新引入模塊也不會再進行初始化。 咱們能夠經過undef卸載已加載的模塊。

require.undef("moduleName") // moduleName是模塊標識
複製代碼

其餘

插件

  • css:加載css
  • text:加載HTML及其餘文本
  • domReady

模塊加載錯誤

  • Module name has not been loaded yet for context: _

此錯誤表示執行時模塊還未加載成功,通常爲異步加載所致,改爲同步加載便可。

藉助類解決模塊間的相互干擾

//C模塊
define([],function(){
 
 // 定義一個類
 function DemoClass()
 {
	var count = 0;
	this.say = function(){
		count++;
		return count;
	};
 }
 
 return function(){
	//每次都返回一個新對象
	return new DemoClass();
 };
 
});
 
// A模塊
require(['C'],
  function(module) {
         cosole.log(module().say());//1
});
 
// B模塊
require(['C'],
  function(module) {
		cosole.log(module().say());//1
});
複製代碼

文檔:官方文檔中文版

seajs

Sea.js 追求簡單、天然的代碼書寫和組織方式,具備如下核心特性:

  • 簡單友好的模塊定義規範:Sea.js 遵循 CMD 規範,能夠像 Node.js 通常書寫模塊代碼。
  • 天然直觀的代碼組織方式:依賴的自動加載、配置的簡潔清晰,可讓咱們更多地享受編碼的樂趣。

經過exports + require實現模塊的加載與導出。

引入

<script src="assets/lib/seajs/sea.js"></script>
<script src="assets/lib/seajs/seajs.config.js"></script>
<script type="text/javascript">
    seajs.use('app');
</script>
複製代碼

config

//seajs配置
seajs.config({

    //1.頂級標識始終相對 base 基礎路徑解析。
    //2.絕對路徑和根路徑始終相對當前頁面解析。
    //3.require 和 require.async 中的相對路徑相對當前模塊路徑來解析。
    //4.seajs.use 中的相對路徑始終相對當前頁面來解析。

    // Sea.js 的基礎路徑  在解析頂級標識時,會相對 base 路徑來解析   base 的默認值爲 sea.js 的訪問路徑的父級
    base: './',

    // 路徑配置  當目錄比較深,或須要跨目錄調用模塊時,可使用 paths 來簡化書寫
    paths: {
        gallery: "https://a.alipayobjects.com/gallery"
        /*
            var underscore = require('gallery/underscore');
            //=> 加載的是 https://a.alipayobjects.com/gallery/underscore.js
         */
    },

    // 別名配置  當模塊標識很長時,可使用 alias 來簡化(至關於 base 設置的目錄爲基礎)
    //Sea.js 在解析模塊標識時, 除非在路徑中有問號(?)或最後一個字符是井號(#),不然都會自動添加 JS 擴展名(.js)。若是不想自動添加擴展名,能夠在路徑末尾加上井號(#)。
    alias: {
        'seajs-css': '~/lib/seajs/plugins/seajs-css',
        'seajs-text': '~/lib/seajs/plugins/seajs-text',
        '$': '~/lib/zepto/zepto'
    },

    // 變量配置  有些場景下,模塊路徑在運行時才能肯定,這時可使用 vars 變量來配置
    vars: {
        //locale: "zh-cn"
        /*
            var lang = require('./i18n/{locale}.js');
            //=> 加載的是 path/to/i18n/zh-cn.js
         */
    },

    // 映射配置  該配置可對模塊路徑進行映射修改,可用於路徑轉換、在線調試等
    map: [
        //[".js", "-debug.js"]
        /*
            var a = require('./a');
            //=> 加載的是 ./js/a-debug.js
        */
    ],

    // 預加載項  在普通模塊加載前,提早加載並初始化好指定模塊  preload 中的配置,須要等到 use 時才加載
    preload: ['seajs-css','seajs-text'],

    // 調試模式  值爲 true 時,加載器不會刪除動態插入的 script 標籤。插件也能夠根據 debug 配置,來決策 log 等信息的輸出
    debug: true,

    // 文件編碼  獲取模塊文件時,<script> 或 <link> 標籤的 charset 屬性。 默認是 utf-8   還能夠是一個函數
    charset: 'utf-8'
});

複製代碼

模塊標識

  • 相對標識: 相對標識以 . 開頭,只出如今模塊環境中(define 的 factory 方法裏面)。相對標識永遠相對當前模塊的 URI 來解析。
  • 頂級標識:頂級標識不以點(.)或斜線(/)開始, 會相對模塊系統的基礎路徑(即 Sea.js 的 base 路徑)來解析。
  • 普通路徑:除了相對和頂級標識以外的標識都是普通路徑。普通路徑的解析規則,和 HTML 代碼中的 同樣,會相對當前頁面解析

use

用來在頁面中加載一個或多個模塊。seajs.use 理論上只用於加載啓動,不該該出如今 define 中的模塊代碼裏。在模塊代碼裏須要異步加載其餘模塊時,推薦使用 require.async 方法。

// 加載一個模塊
seajs.use('./a');

// 加載一個模塊,在加載完成時,執行回調
seajs.use('./a', function(a) {
  a.doSomething();
});

// 加載多個模塊,在加載完成時,執行回調
seajs.use(['./a', './b'], function(a, b) {
  a.doSomething();
  b.doSomething();
});
複製代碼

define

// 方式一
define(function(require, exports, module) { 
    // 模塊代碼
    var a = require('a')
});
// 方式二,此方法嚴格來講不屬於CMD規範
define( 'module', ['module1', 'module2'], function( require, exports, module ){
    // 模塊代碼
});
// 若是模塊內容僅是對象或者字符串
define({ "foo": "bar" });
define('I am a template. My name is {{name}}.');
複製代碼

require

require 是一個方法,接受 模塊標識做爲惟一參數,用來獲取其餘模塊提供的接口。

同步執行

此方式,require 的參數值 必須 是字符串直接量。

var a = require('./a');
複製代碼

異步回調執行

require.async 方法用來在模塊內部異步加載模塊,並在加載完成後執行指定回調。callback 參數可選。 此時,參數值能夠是動態的,以實現動態加載。

define(function(require, exports, module) {

  // 異步加載一個模塊,在加載完成時,執行回調
  require.async('./b', function(b) {
    b.doSomething();
  });

  // 異步加載多個模塊,在加載完成時,執行回調
  require.async(['./c', './d'], function(c, d) {
    c.doSomething();
    d.doSomething();
  });

});
複製代碼

require.resolve 使用模塊系統內部的路徑解析機制來解析並返回模塊絕對路徑。

define(function(require, exports) {

  console.log(require.resolve('./b'));
  // ==> http://example.com/path/to/b.js

});
複製代碼

exports

exports 是一個對象,用來向外提供模塊接口,也可使用return或者module.exports來進行導出

define(function(require, exports) {

  // 對外提供 foo 屬性
  exports.foo = 'bar';
  // return 
  return {
    foo: 'bar',
    doSomething: function() {}
  };
  // module.exports
  module.exports = {
    foo: 'bar',
    doSomething: function() {}
  };
});
複製代碼

module

module 是一個對象,上面存儲了與當前模塊相關聯的一些屬性和方法。

  • module.id 模塊的惟一標識
  • module.uri 根據模塊系統的路徑解析規則獲得的模塊絕對路徑
  • module.dependencies 表示當前模塊的依賴
  • module.exports 當前模塊對外提供的接口

其餘

插件

  • seajs-css
  • seajs-preload
  • seajs-text
  • seajs-style
  • seajs-combo
  • seajs-flush
  • seajs-debug
  • seajs-log
  • seajs-health

文檔:官方文檔

ESModule

簡介

在 ES6 以前,社區制定了一些模塊加載方案,最主要的有 CommonJS 和 AMD 兩種。前者用於服務器,後者用於瀏覽器。ES6 在語言標準的層面上,實現了模塊功能,並且實現得至關簡單,徹底能夠取代 CommonJS 和 AMD 規範,成爲瀏覽器和服務器通用的模塊解決方案。

嚴格模式

ES6 的模塊自動採用嚴格模式,無論你有沒有在模塊頭部加上"use strict";

嚴格模式主要有如下限制:

  • 變量必須聲明後再使用
  • 函數的參數不能有同名屬性,不然報錯
  • 不能使用with語句
  • 不能對只讀屬性賦值,不然報錯
  • 不能使用前綴 0 表示八進制數,不然報錯
  • 不能刪除不可刪除的屬性,不然報錯
  • 不能刪除變量delete prop,會報錯,只能刪除屬性delete global[prop]
  • eval不會在它的外層做用域引入變量
  • eval和arguments不能被從新賦值
  • arguments不會自動反映函數參數的變化
  • 不能使用arguments.callee
  • 不能使用arguments.caller
  • 禁止this指向全局對象
  • 不能使用fn.caller和fn.arguments獲取函數調用的堆棧
  • 增長了保留字(好比protected、static和interface)

export 命令

定義模塊的對外接口。 一個模塊就是一個獨立的文件。該文件內部的全部變量,外部沒法獲取。若是你但願外部可以讀取模塊內部的某個變量,就必須使用export關鍵字輸出該變量。 如下是幾種用法:

//------輸出變量------
export var firstName = 'Michael';
export var lastName = 'Jackson';
//等價於
var firstName = 'Michael';
export {firstName}; //推薦,能清除知道輸出了哪些變量
//------輸出函數或類------
export function multiply(x, y) {
  return x * y;
};
//------輸出並as重命名------
var v1 = 'Michael';
function v2() { ... }
export {
  v1 as streamV1,
  v2 as streamV2
};
//------輸出default------
export default function () { ... }
複製代碼

注意:export default在一個模塊中只能有一個。

import 命令

使用export命令定義了模塊的對外接口之後,其餘 JS 文件就能夠經過import命令加載這個模塊。 如下是幾種用法,必須和上面的export對應:

//------加載變量、函數或類------
import {firstName, lastName} from './profile.js';
//------加載並as重命名------
import { lastName as surname } from './profile.js';
//------加載有default輸出的模塊------
import v1 from './profile.js';
//------執行所加載的模塊------
import 'lodash';
//------加載模塊全部輸出------
import  * as surname from './profile.js';
複製代碼

複合寫法

若是在一個模塊之中,先輸入後輸出同一個模塊,import語句能夠與export語句寫在一塊兒。

export { foo, bar } from 'my_module';

// 等同於
import { foo, bar } from 'my_module';
export { foo, bar };
複製代碼

不完美

  • 只能出如今模塊頂層,不能在其餘語句中
  • 沒法動態加載,其實這點主要是爲了保證靜態分析,全部的模塊都要在解析階段肯定它的依賴
相關文章
相關標籤/搜索