avalonJS

前端神器avalonJS入門(一)

avalonJS是司徒正美開發和維護的前端mvvm框架,能夠輕鬆實現數據的隔離和雙向綁定,相比angularJS等前端框架它有以下優點:javascript

1.壓縮後僅有60多kb,而angular的min版是2MB左右(無視其gzip版);html

2.兼容IE6+,符合天朝市場需求;前端

3.效率更高,跑起來比angular和knockout都要更快,在移動端上該優點會更大(avalon有移動端專版的avalon.modern.js)。關於其性能更詳細的介紹能夠看 這裏java

4.涵蓋了angular的大部分功能,且實現方式更爲便捷、上手更容易;node

5.有配套的UI庫(固然這個按需選擇便可),由司徒正美及其「去哪兒」團隊維護,有相關的中文文檔(下方會提到),除了在github提交issue,你也能夠加入正美的Q羣79641290 來交流問題或提交bug。git

(這位兄臺,谷歌送溫暖,開門查水錶)github

然而avalon也有本身的劣勢——知名度較低。不過畢竟國產的東西沒經BAT推廣,要像seaJS那樣馳名中外卻是不容易。後端

相關中文文檔api

正美其實私下寫了很多avalon的官方api和教程,你們能夠訪問以下地址:數組

GitHub (下載最新的avalon以及實例(examples文件夾裏),經過實例來掌握某些功能的實現是很好的學習途徑)

Avalon簡單介紹

Avalon快速入門 (比較快捷的入門課程,只用了幾篇文章來介紹了最經常使用的一些功能)

API文章(正美的博文,篇幅較大,涵蓋知識點不少,能夠看成API來查閱,只是正美的博客排版真的。。。看起來略吃力),也能夠在 這裏 查看更規範的API。

Avalon亂燉 (強烈推薦,用了20多篇文章較詳細地、漸進地介紹avalon,必讀的就是啦)

本系列初衷

雖然正美已經細心編寫了很多中文文檔,不過有的文章技術門檻有點高,不太適合初學者,另外做爲avalon的用戶,以用戶的角度來較詳細地介紹avalon或許會更合適些。

本系列相比正美的教程,會更側重於「怎麼用」,而非其機制或原理的介紹。

另外也但願本系列能爲推廣avalon出一份綿薄之力,但願能讓更多的前端愛好者開始接觸avalon,並喜歡上這個前端利器。

本系列技術需求

本系列除了avalonJS以外,還會搭配requireJS作輔助,特別是後面咱們會使用avalon的路由系統,來作一個單頁面站點(放到移動端就是SPA了),須要requireJS及其插件來按需加載腳本和樣式文件,故建議查閱本系列的朋友要多多少少會一點requireJS的知識。

我的仍是以爲avalon搭配requireJS的話,在前端可謂hold住全場了(咱忽略node及其框架...)~

 

咱們能夠在 這裏 獲取最新版本的avalonJS,而後將其引入頁面中(本章先不考慮搭配requireJS,僅僅先玩一玩、介紹下):

<!DOCTYPE html> <html> <head lang="en">  <meta charset="UTF-8">  <title>初玩阿瓦隆</title>  <script type="text/javascript" src="avalon.js"></script> </head> <body> <div></div> </body> </html>

接着,相似於ng的「ng-controller」,avalon的控制域屬性名叫作「ms-controller」,你能夠把它看成一個監聽器,把它綁定到一個容器後,avalon就能掃描和監聽這個容器內的全部 (綁定了avalon方法或帶有插值表達式的) 元素了。

咱們來給div加上這個監聽器,並在裏面寫一個 avalon插值表達式{{ XXX }} :

<div ms-controller="wrap">{{a}}</div>

你如今運行它的話,尚未任何效果,由於咱們尚未寫腳原本讓avalon工做起來。咱們能夠來這麼一段簡單的腳本:

<body> <div ms-controller="wrap">{{a}}</div> <script type="text/javascript">  var abc = avalon.define({ //abc是隨便起的一個名字,用做該Model的載體   $id: "wrap", //告訴avalon這個Model是做用於哪一個ms-controller的   a: "你好啊" //定義一個avalon對象屬性「a」,其值是「你好啊」  });  avalon.scan(); //要在最後加這一句,讓avalon掃描文檔,從而執行起來 </script> </body>

在avalon中,咱們用 avalon.define({ ...  }) 的形式來定義一個Model實例(其參數能夠看作一個avalon數據對象),其中的  $id  是內置屬性,對應所要掃描和監控的控制域名。

咱們還在內部定義了一個屬性"a",故在對應的控制域 (如這段代碼對應的域是綁定ms-controller="wrap"的div標籤) 裏,咱們使用avalon插值表達式{{a}}的話,能夠自動綁定其值「你好啊」。

上述代碼運行效果以下:

固然,avalon有着更相似ng的寫法:

avalon.define("wrap", function(vm) { ...... })

但我的以爲不必,仍是下方的寫法來的簡單(本系列後續的實例也將聽從該寫法):

var vm = avalon.define({
    $id:wrap, ...... })

下面這段代碼能夠幫你更好了解avalon的控制域:

<body> <div ms-controller="wrap">{{a}}</div> <div ms-controller="wrap2">  {{a}}  <span>{{b}}</span> </div> <script type="text/javascript">  var abc = avalon.define({   $id: "wrap",   a: "你好啊"  });  var def = avalon.define({   $id: "wrap2",   a: "你們好",   b: "哈哈哈"  });  avalon.scan(); </script> </body>

執行效果以下:

兩個做用域(ms-controller)之間能夠互相訪問彼此的數據,還記得咱們給avalon.define的前面定義了一個載體麼(varXX= avalon.define),利用它就能輕鬆獲取:

<body> <div ms-controller="wrap">{{a}}</div> <div ms-controller="wrap2">  {{a}}  <span>{{b}}</span> </div> <script type="text/javascript">  var abc = avalon.define({   $id: "wrap",   a: "你好啊"  });  var def = avalon.define({   $id: "wrap2",   a: "你們好",   b: abc.a //獲取第一個Model裏的屬性值  });  avalon.scan(); </script> </body>

執行效果:

數據和視圖同步

上方咱們實現了很是簡單的數據綁定,將一個avalon對象屬性a綁定到DOM元素上。不過avalon更有趣和實用的地方是它實現了數據與視圖的同步,說的簡單點,咱們用腳本修改了a的值,那麼DOM上綁定的數據也會跟着改變(固然反過來也是同樣的):

<body> <div ms-controller="wrap">  <span>{{a}}</span>  <input ms-duplex="a" /> </div> <script type="text/javascript">  var abc = avalon.define({   $id: "wrap",   a: "你好啊"  });  avalon.scan(); </script> </body>

注意咱們這裏增長了一個 <inputms-duplex="a"/> ,其中的 ms-duplex 是avalon的雙工綁定屬性,它除了負責將VM中對應的值(如本例是a)放到表單元素的value中,還對元素偷偷綁定一些事件,用於監聽用戶的輸入從而自動刷新VM。

執行以下:

實例

利用avalon數據-視圖同步的特性,咱們能夠更便捷地、更少代碼地實現某些功能。舉個例子,咱們來實現一個選項卡的功能:

如上圖的選項卡你會如何實現呢?可能你會寫兩個ul來對應下方兩個選項卡列表,每一個ul裏都寫上4個li(或者讓後端人員經過後端框架來寫loop,從而動態生成li),而後你再把第二個ul隱藏了,接着寫個方法,讓鼠標移到第二個選項卡標題時,第一個ul隱藏,第二個ul顯示,對吧。

還有右上角的 「更多XX」 的鏈接,也能夠經過隱藏-顯示的方式來實現

你的DOM代碼多是這樣的:

<div> <span id="gg">公告</span><span id="bd">媒體報道</span> <a id="more_gg" href="#!/gg">更多公告</a><a id="more_bd" href="#!/bd">更多報道</a> <ul id="gg_list">  <li><a href="#!/gg/1" title="公告文章標題1">公告文章標題1</a></li>  <li><a href="#!/gg/2" title="公告文章標題2">公告文章標題2</a></li>  <li><a href="#!/gg/3" title="公告文章標題3">公告文章標題3</a></li>  <li><a href="#!/gg/4" title="公告文章標題4">公告文章標題4</a></li> </ul> <ul id="bd_list">  <li><a href="#!/bd/1" title="媒體報道文章標題1">媒體報道文章標題1</a></li>  <li><a href="#!/bd/2" title="媒體報道文章標題2">媒體報道文章標題2</a></li>  <li><a href="#!/bd/3" title="媒體報道文章標題3">媒體報道文章標題3</a></li>  <li><a href="#!/bd/4" title="媒體報道文章標題4">媒體報道文章標題4</a></li> </ul> </div>

但使用avalon的話,一切都更簡單:

<div ms-controller="list">  <span ms-mouseover="changeUl(gg)">公告</span>  <span ms-mouseover="changeUL(bd)">媒體報道</span>  <a ms-href="'#!/'+ more_name">{{more_text}}</a>  <ul>   <li ms-repeat="infoList">    <a ms-href="'#!/'+ more_name + '/' + el.id" ms-title="el.title">{{el.title}}</a>   </li>  </ul> </div>

首先它只有一個「更多XX」的<a>標籤,並且只有一個<ul>,並且不存在任何後端標籤的介入就能實現數據循環綁定。

它的優點在數據越多的時候會越明顯(例如每一個ul要顯示100條li)。

咱們來看看avalon的腳本應當怎麼寫:

<body> <script type="text/javascript">  var gg=[{"id":"1","title":"公告文章標題1"},{"id":"2","title":"公告文章標題2"},{"id":"3","title":"公告文章標題3"},{"id":"4","title":"公告文章標題4"}];  var bd=[{"id":"1","title":"媒體報道文章標題1"},{"id":"2","title":"媒體報道文章標題2"},{"id":"3","title":"媒體報道文章標題3"},{"id":"4","title":"媒體報道文章標題4"}]; </script> <div ms-controller="list">  <span ms-mouseover="changeUl(1)">公告</span>  <span ms-mouseover="changeUl(0)">媒體報道</span>  <a ms-href="'#!/'+ more_name">{{more_text}}</a>  <ul>   <li ms-repeat="infoList">    <a ms-href="'#!/'+ more_name + '/' + el.id" ms-title="el.title">{{el.title}}</a>   </li>  </ul> </div> <script type="text/javascript">  var vm = avalon.define({   $id: "list",   more_name: "gg",   more_text: "更多公告",   gg:gg,   bd:bd,   infoList:gg,   changeUl:function(flag){    if(flag){ //鼠標移過「公告」選項卡頭部     vm.more_name = "gg";     vm.more_text = "更多公告";     vm.infoList = vm.gg;    }else{ //鼠標移過「媒體報道」選項卡頭部     vm.more_name = "bd";     vm.more_text = "更多報道";     vm.infoList = vm.bd;    }   }  });  avalon.scan(); </script> </body>

執行效果:

咱們來逐步分析下上方的代碼。首先看第一段腳本:

<script type="text/javascript"> var gg=[{"id":"1","title":"公告文章標題1"},{"id":"2","title":"公告文章標題2"},{"id":"3","title":"公告文章標題3"},{"id":"4","title":"公告文章標題4"}]; var bd=[{"id":"1","title":"媒體報道文章標題1"},{"id":"2","title":"媒體報道文章標題2"},{"id":"3","title":"媒體報道文章標題3"},{"id":"4","title":"媒體報道文章標題4"}]; </script>

這裏的 gg 表示「公告」的列表JSON數據,bd 則是「媒體報道」的列表JSON數據,你可讓後端的朋友直接在此處提供JSON數據過來。咱們後續會利用avalon把這些數據綁定到頁面視圖上。

咱們再看DOM結構:

<div ms-controller="list">  <span ms-mouseover="changeUl(1)">公告</span>  <span ms-mouseover="changeUl(0)">媒體報道</span>  <a ms-href="'#!/'+ more_name">{{more_text}}</a>  <ul>   <li ms-repeat="infoList">    <a ms-href="'#!/'+ more_name + '/' + el.id" ms-title="el.title">{{el.title}}</a>   </li>  </ul> </div>

<span>中的 ms-mouseover 是avalon的「onmouseover」方法,其值 changeUl(X) 是咱們在最後的avalon腳本中定義的一個事件方法,而後好比當鼠標移到「媒體報道」的span上,會觸發綁定是 changeUl(0) 事件。

咱們再看看 <ams-href="'#!/'+ more_name">{{more_text}}</a>  這裏的 ms-href 天然也是avalon中的「href」屬性,能夠植入avalon對象屬性(如這裏的more_name),也能夠加上字符串(如這裏的'#!/'),但要用引號括起來,否則會被看成avalon對象屬性處理。

接着是最重要的部分:

<li ms-repeat="infoList"> <a ms-href="'#!/'+ more_name + '/' + el.id" ms-title="el.title">{{el.title}}</a> </li>

咱們使用了 ms-repeat="XX" 屬性來綁定要重複顯示的哈希數據,同時會生成一個代理VM對象,該代理對象擁有el,$index, $first, $last, $remove 等屬性(點這裏查看詳細),其中咱們用到的 el 表示指向當前的數據元素,從而能夠經過 el.id ,el.title 來獲取infoList數組對象的具體元素。

最後咱再看看avalon腳本:

<script type="text/javascript">  var vm = avalon.define({   $id: "list",   more_name: "gg",   more_text: "更多公告",   gg:gg, //獲取公告JSON數據   bd:bd, //獲取媒體報道JSON數據   infoList:gg, //infoList缺省值爲公告JSON數據   changeUl:function(flag){    if(flag){ //鼠標移過「公告」選項卡頭部     vm.more_name = "gg";     vm.more_text = "更多公告";     vm.infoList = vm.gg; //infoList變爲公告JSON數據    }else{ //鼠標移過「媒體報道」選項卡頭部     vm.more_name = "bd";     vm.more_text = "更多報道";     vm.infoList = vm.bd; //infoList變爲媒體報道JSON數據    }   }  });  avalon.scan(); </script>

這裏要注意的是咱們用了

gg:gg,   //獲取公告JSON數據 bd:bd, //獲取媒體報道JSON數據

來獲取和存儲「公告/媒體報道」的JSON數據到avalon對象的屬性中(左側的gg和bd是avalon對象屬性,右側的gg和bd是全局變量),這樣作的緣由是後續的回調事件changeUl(flag)要經過參數來判斷和修改vm.infoList的值,而其值應同爲avalon對象屬性。若是把代碼改成這樣會出錯(剛用avalon的朋友可能就會這樣寫):

<script type="text/javascript">  var vm = avalon.define({   $id: "list",   more_name: "gg",   more_text: "更多公告",   infoList:gg, //infoList缺省值爲公告JSON數據   changeUl:function(flag){    if(flag){ //鼠標移過「公告」選項卡頭部     vm.more_name = "gg";     vm.more_text = "更多公告";     vm.infoList = gg;    }else{ //鼠標移過「媒體報道」選項卡頭部     vm.more_name = "bd";     vm.more_text = "更多報道";     vm.infoList = bd;    }   }  });  avalon.scan(); </script>

執行效果以下:

是的,鼠標第一次移上去的時候是無誤的,但再移到其它選項卡的時候就不按常理出牌了,這是爲何?

我我的認爲,在咱們第一次獲取全局變量以後,avalon就會把該變量變爲一個avalon對象,致使該變量的值就這麼被改變了,你能夠這樣來調試:

var vm = avalon.define({
  $id: "list",   more_name: "gg",   more_text: "更多公告",   gg:gg,   bd:bd,   infoList:gg,   changeUl:function(flag){    console.table(gg); //console出「公告」變量的數據信息    if(flag){     vm.more_name = "gg";     vm.more_text = "更多公告";     vm.infoList = vm.gg;    }else{     vm.more_name = "bd";     vm.more_text = "更多報道";     vm.infoList = vm.bd;    }   }  });  avalon.scan();

結果(第一次回調事件的gg是正常的,但第二次開始就改變了):

所以咱們要記得,若存在外部引入的數據,應用一個avalon對象屬性保存起來。

第一篇入門文章就到這裏,下一篇咱們開始以配合requireJS的形式來使用avalon,共勉~

相關文章
相關標籤/搜索