迷你MVVM框架 avalonjs 學習教程1、引入avalon


avalon是國內最強大的MVVM框架,沒有之一,雖然淘寶KISSY團隊也搞了兩個MVVM框架,但都無疾而終。其他的MVVM框架都沒幾個。也只有外國人與像我這樣閑的架構師才有時間鑽研這東西。我很早之前就預言,MVVM是前端的終極解決方案。我之前在盛大無線做盛大通行證就深有體會,一個業務邏輯對應十來個不同的界面,分層架構是必不可少的。因此雙向綁定作為解葯,結合很早就流行的MVC框架,衍生出MVVM這神器。

但這么牛叉的東西,為什么現在才流行起來呢?要不是谷歌振臂高呼,這個一直縮在flex, wps世界的MVVM就根本不可能在前端冒頭。要知道,微軟也搞了knockout, winjs等MVVM框架。原因之一,這東西非常難做。早些年,JS沒有后端語言那種監聽對象屬性變動的高級特性,屬性的變動如何同步視圖,這需要非常巧妙的依賴收集機制,綁定(或叫指令)需要把一個編譯器把VM的屬性分離出來,這也不是一般人能搞出來的。加之,前端本來就沒有幾個是專科出來的人,都是半路出家的,寫編譯器與玩轉jQuery不是同一個概念。knockout沒有依賴什么高級特性,但用戶體驗太差,因此也沒有流行起來。angular的缺點與優點也非常明顯,幸好google比較大牌。

目前使用avalon的公司名單 enter image description here

但牆的內外畢竟是兩個世界,這也是avalon存在的理由。avalon最早是模擬knockout為了解決盛大通行證這樣多界面的東東而研發出來的。為了不像knockout那樣別扭,它是使用IE8的Object.defineProperty劫持用戶對數據的操作,從而實現對視圖的同步。這種設計也比后來的angular的臟檢測優秀許多。但Object.defineProperty是缺陷的,兼容性不好,早期的標准瀏覽器需要用_defineGetter_, _defineSetter_, IE6,IE7,IE8(因為IE8的Object.defineProperty也是有缺陷的)需要用VBScript,為了弄懂VBScript,我還特意入了一本90塊錢的書。但這不是全部,兼容IE6是非常痛苦的,需要寫大量額外的代碼,因此存在avalon.jsavalon.modern.js兩個版本。

avalon.js的兼容性是最好的,支持IE6及非常老的標准瀏覽器。這里的標准瀏覽器特指W3C陣營中的safari, opera, firefox, chrome。avalon.js在最近幾個月的升級中,還對IE的VML,W3C方的SVG進行各種兼容處理。要知道,就是瀏覽器自身的API,也有各種問題。從這個層面來看,avalon.js的兼容能力比jQuery強多了。並且它可以與jQuery和平共處,享用其強大的AJAX,動畫, Deferred等功能。加之,avalon現在擁有全職的團隊幫它打造UI庫(OniUI),大家就不用自己去拼湊各種插件了。

<!DOCTYPE html>
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
        <script src="avalon.js"> </script>
    </head>
    <body>
        <div>TODO write content</div>
    </body>
</html>

上面就是一個范本,如果想引入avalon.modern.js,就是把上面script標簽的src改一改。

avalon.modern.js之前是叫做avalon.mobile.js,是打算用在移動端的,里面是用了許多高級API,因此性能比avalon.js高許多。由於也不算兼容舊式IE(avalon.modern.js是支持IE10及以上的新瀏覽器),許多兼容邏輯也刪掉了,因此體積少了許多,大概少了1000行代碼。

如果你想做移動端開發呢,這要用到觸屏事件,avalon的倉庫有一個mobile.js,你可以直接將它的源碼 拷貝到avalon.modern.js里最后一個花括號的前面,或者這樣引入:

<!DOCTYPE html>
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
        <script src="avalon.modern.js"></script>
        <script src="mobile.js"></script>
    </head>
    <body>
        <div>TODO write content</div>
    </body>
</html>

我建議使用拷貝方式,方便以后我們通過合並方式,把所有業務邏輯也統統合並成一個文件。

avalon.js本身是自帶加載器,它是符合AMD規范,因此它可以用requirejs項目的rjs進行合並。如果大家不想用avalon.js的自帶加載器,可以在緊接着的 script標簽里將它禁用。

<!DOCTYPE html>
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
        <script src="avalon.modern.js"></script><!--不兼容IE6到8,也不玩移動端就用這個-->
        <script>
            avalon.config({
                loader: false
            })
        </script>
    </head>
    <body>
        <div>TODO write content</div>
    </body>
</html>

或者直接在源碼里改,我在公司里就是直接改源碼:

avalon.ready = function(fn) {
    if (innerRequire) {
        innerRequire("ready!", fn)
    } else if (fireReady === noop) {
        fn(avalon)
    } else {
        readyList.push(fn)
    }
}

avalon.config({
    loader: false
})

或者直接在源碼里AMD加載器這個模塊刪掉,這樣可以減少300行代碼

  
   /*********************************************************************
     *                      AMD加載器                                   *
     **********************************************************************/

    var innerRequire
    var modules = avalon.modules = {
        "ready!": {
            exports: avalon
        },
        "avalon": {
            exports: avalon,
            state: 2
        }
    }
……
……
        innerRequire.checkDeps = checkDeps
    }
    /*********************************************************************
     *                           DOMReady                               *
     **********************************************************************/

如果你也用require.js,那么avalon自帶的DOMReady模塊也可以省掉。這時,你們可以引用avalon.shim.js。此JS是基於avalon.js改造而來,你也可以模仿一下改造avalon.mobile.js。

如果你只支持最新的chrome瀏覽器,比如chrome36,那么你可以使用基於Promise, Object.observe 高級API冶造的avalon.observe.js,它使用全新的編譯器與監聽機制,其性能是目前所有MVVM框架之首!

最后我們結合require.jsdomReady.jstext.jscss.jsjQuery.js做一個簡單的項目吧。

我們建立一個新項目,結構如下:

enter image description here

其中modules文件是放置不同的業務模塊,可能不同的模塊由不同的人來說,每個人管好自己的js、html、 css,因此我們才需要requirejs的text、css插件。vendor是放置第三方JS庫、CSS庫什么的,main.js為入口文件,特意與index.html放在醒目的位置。

注意,我們需要禁用avalon自帶的加載器。

index.html的內容如下:

<!DOCTYPE html>
<html>
    <head>
        <title>第一個avalon項目</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
        <script src="vendor/require/require.js" data-main="main.js"></script>
        <style>
            .ms-controller{
                visibility: hidden;
            }
        </style>

    </head>
    <body ms-controller="root">
        <div>{{header | html}}</div>
        <div ms-include-src="page"></div>
        <div>{{footer}}</div>
    </body>
</html>

里面有許多奇怪的屬性,不要慌,這是avalon的綁定屬性,后面的章節我們慢慢講。之於requirejs的用法,自己到官網看。

然后是main.js,它大體分為三大塊:

require.config({//第一塊,配置
    baseUrl: '',
    paths: {
        jquery: 'vendor/jquery/jquery-2.1.1',
        avalon: "vendor/avalon/avalon",//必須修改源碼,禁用自帶加載器,或直接刪提AMD加載器模塊
        text: 'vendor/require/text',
        domReady: 'vendor/require/domReady',
        css: 'vendor/require/css.js'
    },
    priority: ['text', 'css'],
    shim: {
        jquery: {
            exports: "jQuery"
        },
        avalon: {
            exports: "avalon"
        }
    }
});


require(['avalon', "domReady!"], function() {//第二塊,添加根VM(處理共用部分)
    avalon.log("加載avalon完畢,開始構建根VM與加載其他模塊")
    avalon.templateCache.empty = " "
    avalon.define({
        $id: "root",
        header: "這是根模塊,用於放置其他模塊都共用的東西,比如<b>用戶名</b>什么的",
        footer: "頁腳消息",
        page: "empty"
    })
    avalon.scan(document.body)

    require(['./modules/aaa/aaa'], function() {//第三塊,加載其他模塊
        avalon.log("加載其他完畢")
    });

});

然后每一個模塊里都有其JS文件與模板文件(CSS的引入以后再說)

aaa.html

<div ms-controller="aaa">
    <input ms-duplex="username"/>{{username}}
</div>

aaa.js

define(["avalon", "text!./aaa.html"], function(avalon, aaa) {

    avalon.templateCache.aaa = aaa
    avalon.define({
        $id: "aaa",
        username: "司徒正美"
    })
    avalon.vmodels.root.page = "aaa"

})

然后大家運行服務器,就能看到效果(推薦用netBeans,可以直接右鍵運行)

enter image description here

注意,這不是一個簡單的玩具級helloworld!這是一個工業級的項目的種子原型,以后我們所有項目都可以根據它進行改造,最后用rjs進行合並壓縮!

最近附上本章節的源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM