AMD and CMD are dead之KMD.js之懶

【當耐特】發表於2014-06-30

緣由

“懶”在軟體設計中,有著重大的意義。最常見的兩種“懶”,便是:

懶得計算

懶得載入

“懶得計算”常見於伺服器端:

比如Multiplayer Online Role-PlayingGame,客戶端主動計算,遊戲伺服器平滑過渡,在效能、遊戲同步性找一個合適恰當的點。其目的是節約伺服器端CPU、記憶體等的消耗,把許多消耗效能的計算分佈在玩家電腦上;

比如cache,任何cache的目的都是:懶得重新計算,因為我已經計算過了。

比如web應用的表單校驗,在資料提交給伺服器前進行資料有效性校驗,當然這樣的目的不是為了省去伺服器端的校驗,客戶端與伺服器雙校驗是必須的,只是過濾一部分正常手段提交錯了的格式資訊,從而偷一定程度的懶;

“懶得載入”常見於瀏覽器端:比如圖片、視訊、音訊、css、js等的載入。

拿最常見的焦點圖滾動來說:焦點圖什麼時候初始化完畢?等所有圖片載入完成還是第一張圖片載入完成?這裡可以好好考慮延遲載入。

比如一三屏的網頁,使用者開啟時候處於第一屏?第二屏和第三屏的圖片是否要載入?還是根據使用者所處的viewport去載入viewport內部的圖片?

OK,上面說的都跟本文無關,本文主要講的是js按需載入。

舉個場景

有這樣一個頁面:

image

如你所見,使用者開啟網站,頁面有一個a標籤,點選可跳轉到美女網站,頁面還有一個按鈕,點選可產生一個運動的小球。如下圖所示:

image

所以專門正對小球抽象出一個物件Ball.js:

define("Ball", {
    init: function (x,y,r,vx,vy,text) {
        this.x = x;
        this.y = y;
        this.r = r;
        this.d = 2 * r;
        this.vx = vx;
        this.vy = vy;
        this.text = text;
        this.element = document.createElement("div");
        this.element.innerHTML = text;
        this.element.style.cssText = "text-align:center;position:absolute; -moz-border-radius:" + this.d + "px; border-radius: " + this.d + "px; width: " + this.d + "px; height: " + this.d + "px;background-color:green;line-height:" + this.d + "px;color:white;";
        document.body.appendChild(this.element);
        var self = this;
        this.loop = setInterval(function () {
            self.tick();
        }, 15)
    },
    tick: function () {
        this.x += this.vx;
        this.y += this.vy;
        this.element.style.left = this.x + "px";
        this.element.style.top = this.y + "px";
    }
})

後來,網站架構師經過隨機抽樣統計,發現開啟這個頁面的10000人當中,其中有9999人點選了美女網站直接跳轉去看美女了,而僅剩的1人還是個女同胞,開啟該頁面後立馬直接關閉,生怕自己老公發現這塊寶地。最後統計的結果就是:那個create a ball 的就根本沒有人按過。

既然create a ball從未有人點選,那麼這個Ball.js就白白載入了,浪費了使用者頻寬。

這種場景在各種網站中太多了,比如youku登入相關的彈出層的js,彈出層裡表單驗證的js。使用者進入頁面的時候可能僅僅只是想要看視訊。所以登入相關的js可以延遲到使用者點選登入之後再進行載入和執行。當然這個是js非常小,影響甚微,但是如果某一專案板塊特別大粒度,按需載入執行就特別重要。

解決方案

KMDjs作為JS工程化終極解決方案,以Kill AMD和CMD為己任,肯定會提供相關的解決方案。

先說一下,KMDjs的懶執行。這點KMDjs開銷真的是太小了,因為大部分邏輯都在init才真正執行,所以KMDjs模組ready的開銷僅僅是建立類。

KMDjs又是怎麼解決懶載入的呢?不賣關子,直接上碼:

var crtBtn = document.getElementById("crtBtn");
var balls = [];
crtBtn.onclick = function () {
    kmdjs.get("HelloKMD.Ball", function (Ball) {
        var ball = new Ball(100, 100, 28, 1, 2, "KMD.js");
        balls.push(ball);
    });        
}

當然,也支援 promise style:

kmdjs.get("HelloKMD.Ball").then(function (Ball) {
    var ball = new Ball(100, 100, 28, 1, 2, "KMD.js");
    balls.push(ball);
});

初衷受挫

其實,上面不是我最初想要的lazy方式。我最初想要的是這樣的結果:

kmdjs.config({
    name:"HelloKMD",
    baseUrl: "js",
    classes: [
        { name: "HelloKMD.Ball", lazy:true },
        { name: "Util.Bom",url:"Util" }
    ]

});
define("Main",["Util"], {
    init: function () {

        var crtBtn = document.getElementById("crtBtn");
        var balls = [];
        crtBtn.onclick = function () {
            var ball = new Ball(100, 100, 28, 1, 2, "KMD.js");
            balls.push(ball);
        }
        var vp = Bom.getViewport();
        setInterval(function () {
            for (var i = 0, len = balls.length; i < len; i++) {
                var ball = balls[i];
                (ball.x + ball.r * 2 > vp[2] || ball.x < 0) && (ball.vx *= -1);
                (ball.y + ball.r * 2 > vp[3] || ball.y < 0) && (ball.vy *= -1);
            }          
        }, 100)
    }
})

可以看到程式碼沒有任何更改,只不過在config裡稍微配置了一番,但這幾乎要把wind.js整合進來,把

var ball = new Ball(100, 100, 28, 1, 2, "KMD.js");

變成

var ball=$await(new Ball(……..));

而且要去分析ast,還要去分析current scope,還要遍歷scope tree,還要去考慮build的問題,還要去…

最終我放棄!選擇了這種kmdjs.get的還不錯的方式。如果你有更好的想法、建議或意見,請第一時間告訴我,我會將其糅合進0.0.3版本當中。

地址

https://github.com/kmdjs/kmdjs

目前還是0.02版本,kmdjs.get將在0.0.3出現…

相關文章