AMD的一道面試題

圖片描述

模塊化如今應該已經成爲了稍微複雜一點前端開發的標配了。在es6中,都已經支持了的模塊化。 前端

以前的面試中,一直感受模塊化AMD,CMD沒有什麼能夠問的,不過昨天面試忽然想到一個題目:
對於一個AMD的模式下git

文件d.js以下es6

define(function (require) {
    // ... 不少代碼
    require('a');
    // ... 不少代碼
    require(['b'], function (b) {});
    // ... 不少代碼
    require('c');
});

a.js,b.js,c.js 文件分別是何時加載的,如何加載的?github

題目不難面試

答案是a.jsc.js 是在加載完d.js後就加載。
b.js是在執行到這一行時異步加載的。瀏覽器

具體分析:異步

我沒有看過require.js的源碼,咱們使用的是esl.js(也是一個AMD的模塊加載器),可是他們的實現原理應該差很少。模塊化

我從esl.js的角度解讀一下:性能

同步加載,異步加載

首先你們須要知道AMD裏面一個同步加載和異步加載的概念。ui

從概念上面理解,同步就是當我執行到require('a');時,我須要同步的執行a.js裏面的內容,也就是須要在執行到這句話時a.js必須已經加載好了,這樣才能到達同步。

而對於 require(['b'], function (b) {});,我執行到這一步時,是異步的發出請求,而後異步等待b.js的返回+執行。

同步加載的實現原理

咱們從概念上面理解的同步加載的原理,如今看看esl.js的實踐。
這裏面須要處理兩個核心步驟

  1. 執行到require('a');時,a.js必須已經加載好了;

  2. a.js文件裏面的全部require('*'),也都必須加載好了,保證在執行a.js時,全部a.js依賴的同步文件都能同步執行;

對於第一步的實現,大概原理是這樣的,在加載好了d.js後,會正則匹配一次文件裏面的同步依賴require('*');,例如匹配出了 a.jsc.js,而後繼續加載a.jsc.js

對於第二部,其實就是一個遞歸處理,直到沒有下一步的依賴爲止。

同步加載另一種處理方法

上面有一部正則邏輯,可見若是使用這種方式,在執行代碼前,js須要所有正則一次全部模塊化代碼的。這樣性能是否是有一個無謂的耗損。

那麼咱們通常怎麼處理了?

你們通常都瞭解過打包編譯,例如在使用Requirejs時,線上環境的代碼會通過r.js處理一次。

那麼d.js文件應該會處理以下

define(
    'path/b',
    ['require', 'a', 'b'],
    function (require) {
        // ... 不少代碼
        require('a');
        // ... 不少代碼
        require(['b'], function (b) {});
        // ... 不少代碼
        require('c');
});

define方法會增長第一個和第二個參數

第一個參數是按照路徑生成一個具名id
第二個參數是此文件所依賴的同步文件

這時當模塊在解析這個b,js文件時,發現若是存在第二個參數,就會直接解析所需依賴部分,而省去了正則這一步。

咱們正則這一步轉換到了打包編譯中去分析,這樣就省掉了瀏覽器加載時去正則全部AMD文件這一步。

那麼爲何咱們不在開發環境中直接使用['require', 'a', 'b']方式,我理解目的是爲了提升開發便捷性,咱們不須要再增長一個require('*')都在中括號內配置一次,一樣刪除時也不用去刪掉配置。

由於這一步徹底能夠在編譯時處理。

打包編譯的延生

不知道你們有沒有看過編譯後的代碼和開發環境代碼的區別,對於這個d.js文件,編譯後應該是:

define(
    'path/a',
    ['require'],
    function (require) {
        // ... 不少代碼
});

define(
    'path/c',
    ['require'],
    function (require) {
        // ... 不少代碼
});

define(
    'path/b',
    ['require', 'a', 'b'],
    function (require) {
        // ... 不少代碼
        require('a');
        // ... 不少代碼
        require(['b'], function (b) {});
        // ... 不少代碼
        require('c');
});

上面可見,a.jsc.js這兩個文件被合併到了d.js中,全部文件都加上了具名id。並且這個id的生成規則是更具路徑生成的。

而咱們異步加載的b.js文件就沒有被打包進來。這是由於咱們指望b.js是懶加載的,當使用時在加載,這樣也能達到按需加載的目的。

公衆號

圖片描述

博客地址

http://tangguangyao.github.io/

相關文章
相關標籤/搜索