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

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

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

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

但牆的內外畢竟是兩個世界,這也是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),你們就不用本身去拼湊各類插件了。java

<!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改一改。jquery

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

若是你想作移動端開發呢,這要用到觸屏事件,avalon的倉庫有一個mobile.js,你能夠直接將它的源碼 拷貝到avalon.modern.js裏最後一個花括號的前面,或者這樣引入:github

<!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>

我建議使用拷貝方式,方便之後咱們經過合併方式,把全部業務邏輯也通通合併成一個文件。chrome

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進行合併壓縮!

最近附上本章節的源碼

相關文章
相關標籤/搜索