avalon通過幾年之後,已成爲國內一個舉足輕重的框架。它提供了多種不一樣的版本,知足不一樣人羣的須要。好比avalon.js支持IE6等老舊瀏覽器,讓許多靠政府項目或對兼容性要求夠高的公司也能享受MVVM的樂趣。avalon.modern.js支持IE10以上版本,優先使用新API,性能更優,體積更少。avalon.mobile.js在avalon.modern的基礎提供了觸屏事件的支持,知足你們在移動開發的需求。此外,它們分別存在avalon.xxx.shim版本,指無自帶加載器版,avalon.xxx.min版本,指上線壓縮版本。javascript
avalon早期嚴重受到angular與knockout的影響,API與它們很相近,通過多年的發展,漸漸摸索出本身一套模式。騰訊百度阿里等大公司都有部門在使用avalonjs,這有力促進avalon的發展。avalon1.5是一個里程碑的版本,它帶來許多全新的特性,讓咱們編寫代碼更加爽快。css
avalon1.5的下載地址: https://github.com/RubyLouvre/avalon/tree/1.5html
avalon與jQuery最大的一個區別是,思惟的不一樣。jQuery要操做一個元素,老是設法找到此元素,想象這個元素是否有ID,有某個類名,存在某個特定的標籤下,是父節點的第幾個孩子,諸如此類,最後拼湊出一個CSS表達式,而後$(expr)找到元素,而後再進行操做,因而JS代碼裏滿屏$。維護代碼的人,老是要對着頁面來看看,這表達式是對應某某元素,若是隻有ID,類名還好,新手非常寫出很長的CSS表達式,致使你最後崩潰掉。前端
avalon要操做某個元素,就直接在HTML爲它添加一些指令,這些指令或者以ms-開頭的元素屬性,或是標籤之間的4個花括號。指令裏面存在某些變量,這些變量最後在JS彙集成一個對象,這就叫作VM(View Model, 視圖模型)。咱們只要操做這個VM的數據變更就好了,頁面上就會自動變化。有了這一層的分離,咱們在代碼量就少能許多操做DOM的代碼,專致於業務自己。好比說:java
<p>{{aaa}}</p>
至關於jQuery的如下代碼:node
$(function(){ $("p").text(aaa) })
那咱們看看怎麼定義一個VM吧。avalon在1.5以前存在兩種定義方式,如今1.5只支持新風格,即git
var vm = avalon.define({ $id: "test", a: 1, b: 2, c: { d: 1 }, onClick: function(e){ e.preventDefault() }, arr: [1,2,3] })
avalon.define是一個很是重要的方法,要求傳入一個對象,對象裏面必須有$id屬性,它是用於指定其在頁面的做用範圍。github
avalon.define會返回一個新對象,它除了以前咱們定的屬性與方法,還添加了$watch, $events, $fire, $model等屬性與方法。web
當咱們以vm.a = 4來從新賦值時,頁面上用到a的地方會天然做出反應,這個行爲稱之爲綁定,有的屬性會使用ms-duplex指令綁定到表單元素上,這時反應是雙向的,input,select, textarea的值被用戶改動時,會天然反應到VM上,而咱們對VM上的操做也會反應到表單元素上,這叫作雙工綁定ajax
有的東西,你壓底只有它只做用一次,如大表格的數據展現,之後沒有任何互動交互,那咱們有幾種方式:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="avalon.js"></script> <script> var vm = avalon.define({ $id: "test", a: 1, $b: 2, $skipArray: ["a"], c: 3 }) setTimeout(function () { vm.a = 100 vm.$b = 100 vm.c = 100 }, 3000) </script> </head> <body> <div ms-controller="test"> <p>{{a}}不會變</p> <p>{{$b}}不會變</p> <p>{{::c}}不會變</p> <p>{{c}}會變</p> </div> </body> </html>
VM是一個對象,它除了包含一些必須的方法與屬性外,其餘的東西就分爲兩大類,非監控屬性與監控屬性。
非監控屬性,就是咱們上面指的以$開頭,或是名字定義在$skipArray數組的東西,此外,當某屬性的值的類型爲函數或元素節點,文本節點,註釋節點,文檔對象,文檔碎片與window時,它們也沒法監控。
監控屬性則分爲4類:
在1.5以前的版本,還有一個叫監控函數的東西,即裏面包含了某些監控方法。但如今咱們不建議這樣用,由於在將來的版本,咱們打算像angular那樣經過純靜態詞法分析,就能獲得此指令所依賴的全部監控屬性。而監控方法則須要使用動態的依賴檢測實現。動態依賴檢測雖然很是強大,但也很是耗性能。在1.5以前,avalon是徹底經過動態依賴檢測實現綁定的,1.5是結合靜態詞法分析與動態依賴檢測,將來會一點點改成純靜態詞法分析。
var vm = avalon.define({ $id: "test", a: 1, $b: 2, $skipArray: ["a"], c: 3, //監控屬性 d: { //這是子VM dd: { ddd: 3 }, dd2: 4 }, arr: [1, 2, 3, 4], //監控數組 $computed: { c: {//計算屬性c get: function () { return this.a + " " + this.c }, set: function (val) { var arr = val.split(" ") this.a = arr[0] this.b = arr[1] } }, e: {//計算屬性e get: function () { return this.a + 100 } } } })
爲了方便協做開發的需求,咱們引入了做用域的概念。由於一個頁面可能很大,分爲N個模塊,每一個模塊交同不一樣的人來編寫。這個在移動端的SPA應用中尤其明顯。 對於JS,咱們能夠拆分爲N個JS文件,每一個JS文件都有本身的VM。頁面也是拆成一塊塊,這能夠經過PHP或nodejs的模板貼合起來。而在這以前,咱們先爲它們加上ms-controller!
ms-controller爲一個指令,其值爲一個VM的$id,如ms-controller="test",它就會在avalon.vmodels中找到該VM,而後這個元素下方用到的全部指令中的變量,都應該位於此VM。
但若是一個功能模塊特別複雜,它用到的字段特別多,意味着這個VM也要定義許多許多屬性,而這些屬性的某一部分也在其餘頁面或模塊用到,這時咱們就須要對它進行拆分,方便重用。拆分後的兩個對象或N個對象,avalon容許咱們以ms-controller套ms-controller的形式,實現做用域間的數據共享。換言之,若是某變量在當前的VM換不到,它就會往上找,在上面的VM中查找此屬性,一直找到爲止。這有點像JS的對象屬性查找,其實,它像CSS的做用域查找,由於咱們還引入了ms-important。ms-important的寓意就是CSS中的important!符號,就在此做用域查找,不往外找!
此外,還有些地方,你不想avalon來處理它們,如script標籤的內容,style標籤的內容,文章的語法高亮部分,引用別人文章的部分,這個可使用ms-skip指令來繞開這些無用的區域。
至少,咱們學習了ms-controller, ms-important, ms-skip, 更詳細能夠到新官網上學習
avalon能實現VM與視圖之間的互動,最關鍵的東西就是這個。在有的MVVM框架,這也叫作編譯(compile),意即,將視圖的某一部分的全部指令所有抽取出來,轉換爲一個個視圖刷新函數,而後放到一個個數組中,當VM的屬性變更時,就會執行這些數組的函數。固然數組裏面的東西不定是函數,也多是對象,但裏面確定有個視圖刷新函數。這是MVVM框架的核心機制,但怎麼抽取出來,每一個框架的方式都不同。avalon將這個過程稱之爲掃描。掃描老是從某個節點開始。在avalon內部,已經默認進行了一次掃描,從body元素開始描。若是咱們爲頁面插入了什麼新內容,而這個區域裏面又包括了avalon指令,那麼咱們就須要手動掃描了。
avalon.scan是avalon第二重要的API,它有兩個參數,第一個是元素節點,第二個是數組,裏面爲一個個VM。固然這兩個參數是可選的。但當你手動掃描時,最好都會進去,這樣會加快掃描速度,並減小意外。由於全部指令,都掃描後就變移除掉,這包括指定VM用的ms-controller,ms-important!
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="avalon.js"></script> <script> avalon.ready(function () { var div = document.createElement("div") div.innerHTML = "{{aaa}}" div.setAttribute("ms-controller", "eee") document.body.appendChild(div) var vm = avalon.define({ $id: "eee", aaa: 111 }) avalon.scan(div, vm) }) </script> </head> <body> </body> </html>
指令是指寫在HTML中的特殊符號,包括如下幾種,ms-開頭的綁定屬性,寫在innerText裏面的{{}}的插值表達式,相似data-duplex-xxx的輔助指令(data-後面跟着的綁定屬性的名字,它們必須與綁定元素定義在同一元素),還有新添加的自定義標籤(它們必須帶有:號)
新手們或從angular過來的人很容易犯一個錯誤,就是直接在屬性值裏面加一個{{}},覺得就能綁定了,卻不知avalon爲了性能優化,會跳過全部非ms-*屬性。
這裏擁有全部指令的一覽圖
這裏提供ms-text, ms-html兩種指令,其餘ms-text擁有{{expr}}這個變體,ms-html擁有{{expr|html}}這個變體。當大家頁面也使用後端模板拼湊而成時,可能 後端會佔用了{{}}界定符,咱們能夠經過如下配置方式從新指定界定符
avalon.config({ interpolate:["{%","%}"] })
而且咱們能夠經過avalon.config.openTag, avalon.config.closeTag獲得「{%」,"%}"。注意,界定符裏面千萬別出現<, >,由於這存在兼容性問題。這兩個界定符也不能同樣,最好它們的長度都大於1。
<script> avalon.define({ $id: "test", text: "<b> 1111 </b>" }) </script> <div ms-controller="test"> <div><em>用於測試是否被測除</em>xxxx{{text}}yyyy</div> <div><em>用於測試是否被測除</em>xxxx{{text|html}}yyyy</div> <div ms-text="text"><em>用於測試是否被測除</em>xxxx yyyy</div> <div ms-html="text"><em>用於測試是否被測除</em>xxxx yyyy</div> </div>
插值表達式{{}}在綁定屬性的使用,只限那些能返回字符串的綁定屬性,如ms-attr、ms-css、ms-include、ms-class、 ms-href、 ms-title、ms-src等。一旦出現插值表達式,說明這個整個東西分紅可變的部分與不可變的部分,{{}}內爲可變的,反之亦然。 若是沒有{{}}說明整個東西都要求值,又如ms-include="'id'",要用兩種引號強制讓它的內部不是一個變量。
ms-include指令是ms-html的有效補充。咱們知道ms-html是將VM中某個符合HTML結構的字符串,放到某元素底下解析爲節點。但若是這個字符串很大,放在VM上就不合算,這時咱們就想到將它到頁面的某個位置上(如script, noscript, textarea等能放大片內容的特殊標籤)或乾脆獨立成一個HTML文件。因而前者叫作內部模板,由於是放在頁面的內部,後者叫作外部模板。對於前者,咱們使用ms-include=「expr」來引用,後者,咱們是使用ms-include-src="expr"來引用。src表示一個路徑,所以其值每每是一個URL地址,爲了你們方便拼接URL,咱們容許ms-include-src的值可使用插值表達式。如ms-include-src="aaa/{{bbb}}.html"。因爲咱們加載外部模板時是用AJAX實現的,所以你們在調試代碼時,必須打開WEB服務器。
<html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <script src="avalon.js"></script> <script> avalon.define({ $id: "test", xxx: "引入內部模板" }) </script> </head> <body > <script type="avalon" id="tpl"> here, {{ 3 + 6 * 5 }} </script> <div ms-controller="test"> <p>{{xxx}}</p> <div ms-include="'tpl'"></div> </div> </body> </html>
注意,ms-include的值要用引號括起,表示這只是一個字符串,這時它就會搜索頁面的具備此ID的節點,取其innerHTML,放進ms-include所在的元素內部。不然這個tpl會被當成一個變量, 框架就會在VM中檢測有沒有此屬性,有就取其值,重複上面的步驟。若是成功,頁面會出現here, 2的字樣。
若是你們想在模板加載後,加工一下模板,可使用data-include-loaded來指定回調的名字。
若是你們想在模板掃描後,隱藏loading什麼的,可使用data-include-rendered來指定回調的名字。
因爲ms-include綁定須要定義在一個元素節點上,它的做用僅僅是一個佔位符,提供一個插入位置的容器。 若是用戶想在插入內容後,去掉這容器,可使用data-include-replace="true"。
avalon在使用ms-include-src 加載外部模板時,會將它們存放到avalon.templateCache對象中,所以咱們能夠搞出一種架構出來,在上線前,將全部要遠程加載的模板所有打包到avalon.templateCache對象中,這樣它在發出請求前,先查找此對象,發現存在就不會發出請求了。
注意,不管是ms-include仍是ms-include-src都會在其值變化時,請空原元素的全部子孫節點,致使原有數據丟失,裏面用到的全部組件從新生成,若是保持原來的節點,可使用data-include-cache="true"輔助指令。
下面是一個經典的後臺系統框架!
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="avalon.js"></script> <script> avalon.templateCache = { aaa: "<div>這裏是很是複雜的HTML結構1</div>", bbb: "<div>這裏是很是複雜的HTML結構2</div>" ccc: "<div>這裏是很是複雜的HTML結構3</div>" ddd: "<div>這裏是很是複雜的HTML結構4</div>" } var vm =avalon.define({ $id:"root", tabs:["aaa","bbb", "ccc","ddd"],//全部標籤頁的名字 curTab: "aaa", switchTab: function(el){ vm.curTab = el }, showLoading: function(){}, hideLoading:function(){} }) </script> </head> <body ms-controller="root"> <table> <tr> <td> <ul> <li ms-repeat="tabs" ms-click="switchTab(el)">{{el}}</li> </ul> </td> <td> <!--主內容顯示區--> <div ms-include-src="curTab" data-include-loaded="showLoading" data-include-rendered="hideLoading" > </div> </td> </tr> </table> </body> </html>
更詳細的內容可見新官網
avalon1.5如今只支持新風格,即ms-class="aaa: true"這種形式,此綁定屬性的值以冒號分爲兩部分,前面爲類名,後面表示添加或移除。 ms-class="aaa bbb ccc: toggle",當toggle在VM中爲true時,它會爲元素同時添加aaa, bbb, ccc三個類名。冒號及其後面的東西也不是必須的, 如ms-class="aaa1 bbb2",表示老是爲元素添加aaa1,bbb2這兩個類名。前面的部分也可使用插值表達式動態生成,如ms-class="{{className}}:true", className在VM是什麼,就會爲元素添加什麼類名。若是你想爲元素添加多個類名綁定,可使用ms-class-1="aaa: true" ms-class-2="bbb:toggle"來添加。
ms-hover, ms-active與ms-class的用法相同,但它們一個只在鼠標掠過元素上方時添加類名,移走時移除;另外一個則在元素得到焦點時(好比點擊)添加類名,失去焦點時移除。
更詳細的內容可見新官網
咱們能夠經過ms-on-*爲元素綁定各類事件,好比ms-on-click=fn,表示爲當前元素綁定點擊事件,fn爲VM的一個函數。默認地,咱們會爲fn傳入一個參數event,咱們已經爲它作了兼容處理,所以你在IE下也能使用preventDefault, stopPropagation, pageX, pageY, target, timeStamp, which等標準屬性與方法。若是你還想傳其餘參數,還想用事件對象,能夠用$event佔位。ms-on-click=fn(aaa, bbb, $events)。此外,咱們爲全部經常使用事件作了快捷處理,所以大家還能夠這樣用,ms-click=fn2, ms-mouseover=fn3, ms-mouseleave=fn4。 注意,事件綁定的值的第一個單詞必須是VM中的函數名字,你不能在其值裏面使用加減乘除,如 ms-click="aaa+bbb",這樣是不對的。若是 你想同時綁定多個點擊事件,用法與ms-class,ms-hover同樣,在後面加數字就好了。ms-click-1=fn1 ms-click-2=fn2 ms-click-3=fn3。
<!DOCTYPE HTML> <html> <head> <title>ms-on</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <script src="../avalon.js" ></script> <script> var model = avalon.define({ $id: "test", firstName: "司徒", array: ["aaa", "bbb", "ccc"], argsClick: function(e, a, b) { alert([].slice.call(arguments).join(" ")) }, loopClick: function(a, e) { alert(a + " " + e.type) }, status: "", callback: function(e) { model.status = e.type }, field: "", check: function(e) { model.field = this.value + " " + e.type }, submit: function() { var data = model.$model if (window.JSON) { setTimeout(function() { alert(JSON.stringify(data)) }) } } }) </script> </head> <body> <h3 style="text-align: center">ms-on-*</h3> <fieldset ms-controller="test"> <legend>有關事件回調傳參</legend> <div ms-mouseenter="callback" ms-mouseleave="callback">{{status}}<br/> <input ms-on-input="check"/>{{field}} </div> <div ms-click="argsClick($event, 100, firstName)">點我</div> <div ms-each-el="array" > <p ms-click="loopClick(el, $event)">{{el}}</p> </div> <button ms-click="submit">點我</button> </fieldset> </body> </html>
<!DOCTYPE HTML> <html> <head> <title>ms-on</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <script src="../avalon.js" ></script> <script> var count = 0 var vm = avalon.define({ $id: "multi-click", str1: "1", str2: "2", str3: "3", click0: function () { vm.str1 = "xxxxxxxxx" + (count++) }, click1: function () { vm.str2 = "xxxxxxxxx" + (count++) }, click2: function () { vm.str3 = "xxxxxxxxx" + (count++) } }) </script> </head> <body> <fieldset> <legend>一個元素綁定多個同種事件的回調</legend> <div ms-controller="multi-click"> <div ms-click="click0" ms-click-1="click1" ms-click-2="click2" >請點我</div> <div>{{str1}}</div> <div>{{str2}}</div> <div>{{str3}}</div> </div> </fieldset> </body> </html>
一個綜合了ms-click, ms-class, 監控數組的例子:
<!doctype hmtl> <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <title>attribute</title> <script type="text/javascript" src="avalon.modern.js"></script> <script> var vm = avalon.define({ $id: "test", selected: [], array: avalon.range(0, 12), onClick: function (i) { if (vm.selected.indexOf(i) === -1) { vm.selected.push(i) } else { avalon.Array.remove(vm.selected, i) } } }) </script> <style> b{ background: gray; -moz-border-radius: 15px; /* Gecko browsers */ -webkit-border-radius: 15px; /* Webkit browsers */ border-radius:15px; /* W3C syntax */ width:30px; height:30px; display: inline-block; line-height: 30px; text-align: center; } b.toggle{ background: red; } </style> </head> <body ms-controller="test"> <p ms-each="array"> <b ms-click="onClick($index)" ms-class="toggle: selected.indexOf($index)!==-1">{{$index}}</b> </p> </body> </html>
更詳細的內容可見新官網
ms-visible="expr"是經過改變元素的style.display值來控制元素的顯示隱藏。
在1.5中結合動畫指令還可使用動畫效果。
<!DOCTYPE HTML> <html> <head> <title>ms-visible</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <script src="../avalon.js" ></script> <script> avalon.define({ $id: "test", a: true }) </script> <style> .rect{ width:100px; height:100px; background: red; display: none; border:1px solid red; text-align: center } </style> </head> <body> <h3>ms-visible</h3> <div ms-controller="test"> <p>點我隱藏或顯示下面的方塊<input ms-duplex-checked="a" type="radio"></p> <div class="rect" ms-visible="a" >visible</div> </div> </body> </html>
更詳細的內容可見新官網
ms-if 與 ms-visible的效果很像,都是不顯示元素,但ms-if更進一步,將元素移出原來的位置藏到別處。而且它還會延遲其內容元素的掃描與綁定,所以相對於ms-visible性能更好。若是你想ms-if 與ms-repeat用在同一元素,讓某個不合格的數組元素表明的那個地方不顯示出來,那你請務必使用ms-if-loop!
在1.5中結合動畫指令還可使用動畫效果。
<!DOCTYPE HTML> <html> <head> <title>ms-if</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <script src="../avalon.js" ></script> <style> .active{ background: blueviolet; } </style> <script> var vm = avalon.define({ $id: "test", data: { toggle: true }, toggle: function () { vm.data.toggle = !vm.data.toggle } }); </script> </head> <body> <div class="avalonHide">測試avalonHide</div> <div ms-controller="test"> <div ms-if="data.toggle" class="active">能動態添加移除</div>{{data.toggle}} <p><button type='button' ms-click='toggle'>點我</button></p> </div> </body> </html>
更詳細的內容可見新官網
MVVM框架中最重要的功能之一,惟一一個能讓視圖同步VM的指令。值得注意的是,它綁定的元素只是 表單元素,input, textarea, select。
在avalon1.5中,ms-duplex只支持duplex2.0。所謂的duplex2.0是指, 咱們能夠從表單元素的checked屬性,value屬性抽取數據,轉變成所須要的字符串類型,數值類型,布爾類型或數組類型,或添加更多攔截器進行更多操做。
ms-duplex實際上是默認使用了string 攔截器,即至關於ms-duplex-string。
其餘內置攔截器分別是ms-duplex-number, ms-duplex-boolean, ms-duplex-checked
名字 | 應用元素 | 效果 |
ms-duplex-checked | radio, checkbox | 將其checked值同步到VM |
ms-duplex-string | 全部表單元素 | 將其value值同步到VM, 對於checkbox, select[multiple=true]會進一步轉換爲數組 |
ms-duplex-number | 全部表單元素 | 將其value值同步到VM, 對於checkbox, select[multiple=true]會進一步轉換爲數組; 咱們還能夠經過data-duplex-number='strong|medium|weak'輔助指令來轉換非數值 爲strong時,不能轉的都轉換爲0; 爲medium時,空字符串會忽略轉換,不然轉換0或其餘數字; 爲weak時,不是數值形式的值不作轉換 |
ms-duplex-boolean | 全部表單元素 | 將其value值同步到VM, 對於checkbox, select[multiple=true]會進一步轉換爲數組; 轉換方式爲,當其值爲「true」轉換爲true,其餘都轉換爲false |
默認地,對於文本域,文本區,密碼域,框架是使用oninput事件進笨監聽,即用戶每改動一個字符都會同步到VM。若是想在失去焦點時同步VM,可使用 data-duplex-event="change"輔助指令來調整。
另,咱們還能夠爲元素添加一個輔助指令, data-duplex-changed="fn", fn爲VM中的一個函數。當元素的值 改動時,它會爲回調傳入當前的值及回調內部的this指向當前表單元素。
咱們還能夠在avalon.duplexHooks上添加本身的攔截器。
<!DOCTYPE html> <html> <head> <title>ms-duplex</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <script src="../avalon.js" ></script> <script> var model = avalon.define({ $id: "test", text: "xxx", textcb: "", textchange: function(a) { model.textcb = a }, radio: "radio", radiocb: "", radiochange: function(a) { model.radiocb = a }, select: "2222", selectcb: "", selectchange: function(a) { model.selectcb = a } }) </script> </head> <body ms-controller="test"> <h3>data-duplex-changed回調</h3> <div><input ms-duplex="text" data-duplex-changed="textchange">{{textcb}}</div> <div><input ms-duplex-checked="radio" type="radio" data-duplex-changed="radiochange">{{radiocb}}</div> <div> <select ms-duplex="select" data-duplex-changed="selectchange"> <option>1111</option> <option>2222</option> <option>3333</option> <option>4444</option> </select> {{selectcb}}</div> </body> </html>
咱們能夠經過如下方式定義一個攔截器,在avalon.duplexHooks上添加一個對象,此對象擁有init, get, set這三個方法,此三個方法不必定要寫全,只要某一個就好了。init在初始化時調用,get方法是同步到VM時調用,set方法是同步視圖時調用,這些方法都有兩個參數,第一個參數是當前值,第二個參數是綁定對象。
有了攔截器,咱們就能夠實現各類表單驗證,而且一個ms-duplex是能夠帶N多攔截器的,如ms-duplex-number-notempty-gt10
<!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> <script> var model = avalon.define({ $id: "test", a: 111 }); avalon.duplexHooks.add100 = { get: function (val) { return val }, set: function (val) { return val + 100 } } </script> </head> <body> <div ms-controller="test"> <input ms-duplex-number-add100="a" >{{a}} </div> </body> </html>
avalon的oniui的驗證組件也是基於攔截器機制構建的,你們能夠參考一下,方便本身設計本身的表單驗證。
更詳細的內容可見新官網
用法爲ms-css-name="value"。其值可使用插值表達式, 如ms-css-width=」prop」(會自動補px),ms-css-height=」{{prop}}%」, ms-css-color=」prop」, ms-css-background-color=」prop」, ms-css-background-image=」url({{imageUrl}})」, ms-css-font-size=」{{prop}}px」
注意:屬性值不能加入CSS hack與important!
<!DOCTYPE html> <html> <head> <title>by 司徒正美</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script src="../avalon.js"></script> <script> avalon.define({ $id: "test", o: 0.5, bg: "#F3F"// 不能使用CSS hack,如 bg : "#F3F\9\0" }) </script> <style> .outer{ width:200px; height: 200px; position: absolute; top:1px; left:1px; background: red; z-index:1; } .inner{ width:100px; height: 100px; position: relative; top:20px; left:20px; background: green; } </style> </head> <body ms-controller="test" > <h3>在舊式IE下,若是父元素是定位元素,但沒有設置它的top, left, z-index,那麼爲它設置透明時, 它的全部被定位的後代都沒有透明</h3> <div class="outer" ms-css-opacity="o" ms-css-background-color="bg" > <div class="inner"></div> </div> </body> </html>
更詳細的內容可見新官網
爲當前元素添加data-*屬性。能夠參見ms-attr的用法。
更詳細的內容可見新官網
其用法爲ms-attr-name="value", value可使用插值表達式。當value爲undefined, null, false,會移除此屬性,不然會添加此屬性。
<!DOCTYPE html> <html> <head> <title>ms-attr-*</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width"> <script src="../avalon.js"></script> <script> var vm = avalon.define({ $id: "test", aaa: true, bbb: "@@@", ccc: "&&&", active: "active", click: function(){ vm.aaa = !vm.aaa } }) </script> <style> .active { background: goldenrod; } .readonly{ border:1px solid blueviolet; } </style> </head> <body> <form method="get" action="aaa.html" ms-controller="test"> <input ms-enabled="aaa" name="a1" value="12345"/> <input ms-disabled="aaa" name="a2" value="67890"/> <input ms-readonly="aaa" name="a3" ms-class="readonly: aaa" value="readonly" /> <input ms-duplex-radio="aaa" type="checkbox" value="checkbox" name="a4"/> <select name="a5"> <option>222</option> <option ms-selected="aaa">555</option> </select> <p> <input ms-attr-value="其餘內容 {{ccc}}" name="a6" value="改"/> <input ms-attr-value="'其餘內容 '+ccc" name="a7" value="改" /> <input ms-value="其餘內容 {{ccc}}" name="a8" value="改"/> </p> <button type="button" ms-click="click" ms-attr-class="active"> 點我 </button> <input type="submit" value="提交" /> <svg width="100" height="100"> <circle ms-attr-cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" /> </svg> </form> </body> </html>
更詳細的內容可見新官網
這三個東西都很相像,見下表
名字 | 循環的類型 | 循環的範圍 |
ms-repeat | 用於循環數組與對象 | 循環當前元素 |
ms-each | 用於循環數組 | 循環當前元素的內部 |
ms-with | 用於循環對象 | 循環當前元素的內部 |
當咱們用ms-each, ms-repeat循環數組時,能夠爲元素指定其別名,如ms-each-item="array1", ms-repeat-elem="array2", 若是你不指定別名,則默認爲el。(注意,別名不能出現大寫,由於屬性名在HTML規範中,會所有轉換爲小寫,詳見這裏)。監控數組擁有原生數組的全部方法,而且比它還多了set, remove, removeAt, removeAll, ensure, pushArray與 clear方法 。詳見這裏。
注意,ms-each, ms-repeat會生成一個新的代理VM對象放進當前的vmodels的前面,這個代理對象擁有el, $index, $first, $last, $remove, $outer等屬性。另外一個會產生VM對象的綁定是ms-widget。
咱們還能夠經過data-repeat-rendered, data-each-rendered來指定這些元素都插入DOM被渲染了後執行的回調,this指向元素節點, 有一個參數表示爲當前的操做,是add, del, move, index仍是clear
若是循環的是對象,那麼代理VM就有$val, $key, $index, $outer等屬性。$key, $val分別引用鍵名,鍵值。另咱們能夠經過指定data-with-sorted回調,規定只輸出某一部分建值及它們的順序。 注意,此綁定已經不建議使用,它將被ms-repeat代替,ms-repeat裏面也可使用data-with-sorted回調。
<!DOCTYPE html> <html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script src="avalon.js"></script> <style> .id2013716 { width: 200px; float:left; } </style> <script> var a = avalon.define({ $id: "array", array: ["1", "2", "3", "4"] }) setTimeout(function() { a.array.set(0, 7) }, 1000); var b = avalon.define({ $id: "complex", array: [{name: "xxx", sex: "aaa", c: {number: 2}}, {name: "yyy", sex: "bbb", c: {number: 4}}]// }); setTimeout(function() { b.array[0].c.number = 9 b.array[0].name = "1000" }, 1000) setTimeout(function() { a.array.push(5, 6, 7, 8, 9) }, 1000) setTimeout(function() { a.array.unshift("a", "b", "c", "d") }, 2000) setTimeout(function() { a.array.shift() b.array[1].name = 7 }, 3000) setTimeout(function() { a.array.pop() }, 4000) setTimeout(function() { a.array.splice(1, 3, "x", "y", "z") b.array[1].name = "5000" }, 5000) </script> </head> <body> <fieldset class="id2013716" ms-controller="array"> <legend>例子</legend> <ul ms-each="array"> <li >數組的第{{$index+1}}個元素爲{{el}}</li> </ul> <p>size: <b style="color:red">{{array.size()}}</b></p> </fieldset> <fieldset class="id2013716" ms-controller="complex"> <legend>例子</legend> <ul > <li ms-repeat-el="array">{{el.name+" "+el.sex}}它的內容爲 number:{{el.c.number}}</li> </ul> </fieldset> </body> </html>
更詳細的內容可見新官網
avalon1.5新增的功能,容許咱們用avalon.effect方法定義一個動畫,而後在頁面上存在ms-if,ms-repeat,ms-include,ms-visible的元素添加ms-effect="effectName"實現動畫效果。此動畫可使用CSS3與JS實現。
更詳細的內容可見新官網
經過自定義標籤聲明使用某個組件,這是avalon1.5引入的最重要特性。
早在20年前,JAVA也想經過標籤庫實現堆積木般的開發。但鑑於當時JAVAer的技術力量,這東西沒有作成;如今谷歌有了本身的瀏覽器,實現了一套WEB Components規範,開源polymer,也是往這條路子走下去。由於以標籤的形式聲明組件比起在JS裏以類的形式建立組件,來得更簡單明瞭。
<oni:buttonset> <oni:button color="danger" type="icon" icon-position="left" icon="\&\#xf04c;"></oni:button> <oni:button color="danger" type="icon" icon-position="left" icon="\&\#xf04b;"></oni:button> <oni:button color="danger" type="icon" icon-position="left" icon="\&\#xf074;"></oni:button> </oni:buttonset>
作這套東西有幾個難點:
爲了讓一個頁面使用多個UI庫,avalon引入了命名空間的概念,這是來自早期XML的設計。一個標籤名由冒號隔開,前面是命名空間,後面是標籤名。
avalon.library("oni", {})//這是聲明一個命名空間,也是算聲明一個UI庫
在定義一個UI組件,最少有3個文件,JS文件(引用其餘兩個文件或更多子組件的JS),HTML文件(組件的模塊),CSS文件(它能夠由SASS更高級的語言生成)。
define(["avalon", "text!./avalon.pager.html", "css!../chameleon/oniui-common.css", "css!./avalon.pager.css" ], function (avalon, template) { var _interface = function () { } avalon.component("oni:pager", { regional: {}, //@config {Object} 默認語言包 perPages: 10, //@config {Number} 每頁包含多少條目 showPages: 10, //@config {Number} 中間部分一共要顯示多少頁(若是兩邊出現省略號,即它們之間的頁數) currentPage: 1, //@config {Number} 當前選中的頁面 (按照人們平常習慣,是從1開始),它會被高亮 _currentPage: 1, //@config {Number} 跳轉檯中的輸入框顯示的數字,它默認與currentPage一致 totalItems: 200, //@config {Number} 總條目數 totalPages: 0, //@config {Number} 總頁數,經過Math.ceil(vm.totalItems / vm.perPages)求得 pages: [], //@config {Array} 要顯示的頁面組成的數字數組,如[1,2,3,4,5,6,7] ellipseText: "…", //@config {String} 省略的頁數用什麼文字表示 prevText: "<", //@config {String} 「下一頁」分頁按鈕上顯示的文字 nextText: ">", //@config {String} 「上一頁」分頁按鈕上顯示的文字 firstPage: 0, //@config {Number} 當前可顯示的最小頁碼,不能小於1 $ready: function(){ //.... } //.... }) })
avalon.component是用來定義一個組件,第一個參數爲自定義標籤的名字(包括命名空間),第二個是配置對象,裏面定義這個組件用到的屬性,狀態與方法。
更詳細的內容可見新官網
咱們也能夠到這裏直接看pager, button, checkboxlist是怎麼定義。
將來與avalon配套使用的OniUI一點點所有改爲自定義標籤的形式,更但願你們一塊兒爲avalon設計更多的UI組件。
avalon1.5是無比強大,並如今只有VM第一層有$watch, $fire方法,並去掉$unwatch方法。$watch方法會返回一個方法,調用它就會去掉此回調。
早期$watch只能監聽當前層的屬性,不能監聽其子對象的屬性,尤爲在數組中,沒法監聽元素的屬性變更。所以在avalon1.5中,引入通配符及多級監聽功能。
var vm = avalon.define({ $id: "test", array: [1, 2, 3], arr: [{a: 1}, {a: 2}, {a: 3}], obj: { a: 1, b: 2 }, a: { b: { c: { d: 33 } } } }) vm.$watch("array.*", function (a, b) { expect(a).to.be(6) expect(b).to.be(2) }) vm.$watch("arr.*.a", function (a, b) { expect(a).to.be(99) expect(b).to.be(1) }) vm.$watch("obj.*", function (a, b, c) { expect(a).to.be(111) expect(b).to.be(1) }) vm.$watch("a.*.c.d", function (a, b, c) { expect(a).to.be(88) expect(b).to.be(33) }) setTimeout(function () { vm.array.set(1, 6) vm.arr[0].a = 99 vm.obj.a = 111 vm.a.b.c.d = 88 }, 100)
注意,*通配任只能出現一次
var unwatch = vm.$watch("array.*", function (a, b) { expect(a).to.be(6) expect(b).to.be(2) }) unwatch() //移除當前$watch回調
若是在跨模塊通訊,因爲全部VM都儲存在avalon.vmodels中,咱們能夠遍歷它們,找到目標VM,而後$fire。
avalon多級聯動例子:
<!DOCTYPE html> <html> <head> <title>三級聯動</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="avalon.js"></script> <script> var map = { "中國": ["江南四大才子", "初唐四傑", "戰國四君子"], "日本": ["日本武將", "日本城堡", "幕府時代"], "歐美": ["三大騎士團", "三大魔幻小說", "七大奇蹟"], "江南四大才子": ["祝枝山", "文徵明", "唐伯虎", "周文賓"], "初唐四傑": ["王勃", "楊炯", "盧照鄰", "駱賓王"], "戰國四君子": ["楚國春申君黃歇", "齊國孟嘗君田文", "趙國平原君趙勝", "魏國信陵君魏無忌"], "日本武將": ["織田信長", "德川家康", "豐臣秀吉"], "日本城堡": ["安土城", "熊本城", "大阪城", "姬路城"], "幕府時代": ["鎌倉", "室町", "豐臣", "江戶"], "三大騎士團": ["聖殿騎士團", "醫院騎士團", "條頓騎士團"], "三大魔幻小說": ["冰與火之歌", "時光之輪", "荊刺與白骨之王國"], "七大奇蹟": ["埃及胡夫金字塔", "奧林匹亞宙斯巨像", "阿爾忒彌斯月神殿", "摩索拉斯陵墓", "亞歷山大港燈塔", "巴比倫空中花園", "羅德島太陽神巨像"] } var vm = avalon.define({ $id: "test", first: ["中國", "日本", "歐美"], second: [], third: [], firstSelected: "日本", secondSelected: "日本武將", thirdSelected: "織田信長" }) vm.second = map[vm.first[1]].concat() vm.third = map[vm.second[0]].concat() vm.$watch("firstSelected", function (a) { vm.second = map[a].concat() vm.secondSelected = vm.second[0] }) vm.$watch("secondSelected", function (a) { vm.third = map[a].concat() vm.thirdSelected = vm.third[0] }) </script> </head> <body> <div ms-controller="test"> <h3>下拉框三級聯動</h3> <select ms-duplex="firstSelected" > <option ms-repeat="first" ms-attr-value="el" >{{el}}</option> </select> <select ms-duplex="secondSelected" > <option ms-repeat="second" ms-attr-value="el" >{{el}}</option> </select> <select ms-duplex="thirdSelected" > <option ms-repeat="third" ms-attr-value="el" >{{el}}</option> </select> </div> </body> </html>
avalon從angular中抄來管道符風格的過濾器,但有點不同。 它只能用於{{}}插值表達式。若是不存在參數,要求直接跟|filter,若是存在參傳,則要用小括號括起,參數要有逗號,這與通常的函數調用差很少,如|truncate(20,"……")
avalon自帶如下幾個過濾器
decimals 可選,規定多少個小數位。 dec_point 可選,規定用做小數點的字符串(默認爲 . )。 thousands_sep 可選,規定用做千位分隔符的字符串(默認爲 , ),若是設置了該參數,那麼全部其餘參數都是必需的。
'yyyy': 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) 'yy': 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) 'y': 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) 'MMMM': Month in year (January-December) 'MMM': Month in year (Jan-Dec) 'MM': Month in year, padded (01-12) 'M': Month in year (1-12) 'dd': Day in month, padded (01-31) 'd': Day in month (1-31) 'EEEE': Day in Week,(Sunday-Saturday) 'EEE': Day in Week, (Sun-Sat) 'HH': Hour in day, padded (00-23) 'H': Hour in day (0-23) 'hh': Hour in am/pm, padded (01-12) 'h': Hour in am/pm, (1-12) 'mm': Minute in hour, padded (00-59) 'm': Minute in hour (0-59) 'ss': Second in minute, padded (00-59) 's': Second in minute (0-59) 'a': am/pm marker 'Z': 4 digit (+sign) representation of the timezone offset (-1200-+1200) format string can also be one of the following predefined localizable formats: 'medium': equivalent to 'MMM d, y h:mm:ss a' for en_US locale (e.g. Sep 3, 2010 12:05:08 pm) 'short': equivalent to 'M/d/yy h:mm a' for en_US locale (e.g. 9/3/10 12:05 pm) 'fullDate': equivalent to 'EEEE, MMMM d,y' for en_US locale (e.g. Friday, September 3, 2010) 'longDate': equivalent to 'MMMM d, y' for en_US locale (e.g. September 3, 2010 'mediumDate': equivalent to 'MMM d, y' for en_US locale (e.g. Sep 3, 2010) 'shortDate': equivalent to 'M/d/yy' for en_US locale (e.g. 9/3/10) 'mediumTime': equivalent to 'h:mm:ss a' for en_US locale (e.g. 12:05:08 pm) 'shortTime': equivalent to 'h:mm a' for en_US locale (e.g. 12:05 pm)
例子:
生成於{{ new Date | date("yyyy MM dd:HH:mm:ss")}}
生成於{{ "2011/07/08" | date("yyyy MM dd:HH:mm:ss")}}
生成於{{ "2011-07-08" | date("yyyy MM dd:HH:mm:ss")}}
生成於{{ "01-01-2000" | date("yyyy MM dd:HH:mm:ss")}}
生成於{{ "03 04,2000" | date("yyyy MM dd:HH:mm:ss")}}
生成於{{ "3 4,2000" | date("yyyy MM dd:HH:mm:ss")}}
生成於{{ 1373021259229 | date("yyyy MM dd:HH:mm:ss")}}
生成於{{ "1373021259229" | date("yyyy MM dd:HH:mm:ss")}}
值得注意的是,new Date可傳的格式類型很是多,但不是全部瀏覽器都支持這麼多,詳看這裏
多個過濾器一塊兒工做
<div>{{ prop | filter1 | filter2 | filter3(args, args2) | filter4(args)}}</div>
若是想自定義過濾器,能夠這樣作
avalon.filters.myfilter = function(str, args, args2){//str爲管道符以前計算獲得的結果,默認框架會幫你傳入,此方法必須返回一個值 /* 具體邏輯 */ return ret; } //默認過濾器 avalon.filters.default = function(str, defaultStr){ return str || defaultStr } //性別過濾器 avalon.filters.sex = function(str){ return str == '0' ? '男': '女' }
avalon1.5新添加的API,avalon.directive, 用於快速建立一種全新的指令!
過去avalon自定義綁定比較麻煩,很是影響avalon推廣,也不利於業務上各類功能的開發,新的定義方式將變得很是簡單
avalon.directive(name, { init: function(binding){ //在這裏處理binding.expr屬性 }, update: function(value, oldValue){ //value是binding.expr通過解析獲得的當前VM屬性的值 //oldValue是它以前的值 }, })
拿最簡單的data綁定來講吧:
avalon.directive("data", { priority: 100, update: function (val) { var elem = this.element var key = "data-" + this.param if (val && typeof val === "object") { elem[key] = val } else { elem.setAttribute(key, String(val)) } } })
咱們發現它沒有init回調,init主要是對binding.expr進行加工,或綁定事件的,它這些都不須要, 就會直接返回原始值.好比說,<div ms-data-xxx="yyy"< 這裏的屬性會抽取成下面一個對象:
var binding = { name: "ms-data-xxx", expr: "yyy", type: name, element: DIVElement, param: "xxx", oneTime: false, uuid: //框架會在這裏生成一個UUID給它 priority://框架會根據上面的定義,計算出來,大概是1000+ ; }
假如此時vm.yyy = 999; 那麼第一次update時,value與oldValue分別爲 999, undefined
對於用戶來講,priority通常不須要設置, 其計算公式爲
//詳見 scanAttr priority: (directives[type].priority || type.charCodeAt(0) * 10) + (Number(param.replace(/\D/g, "")) || 0)
此外,若是你的綁定須要綁定一些事件來監聽用戶行爲,那麼你得在init或update添加一個roolback回調,在裏面解綁定事件.
update方法中的this就是init的傳參binding
那麼咱們作一下簡單的例子吧:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="avalon.js"> </script> <script> avalon.directive("foo", { init: function (binding) { var elem = binding.element var vmodels = binding.vmodels var remove = avalon(elem).bind("click", function () { elem.innerHTML = new Date - 0 for (var i = 0, v; v = vmodels[i++]; ) { if (v.hasOwnProperty(binding.expr)) { v[binding.expr] = elem.innerHTML break } } }) binding.roolback = function () { avalon(elem).unbind("click", remove) } }, update: function (value, oldValue) { this.element.innerHTML = value } }) var vm = avalon.define({ $id: "test", aaa: 111 }) vm.$watch("aaa", function (a, b) { console.log(a, b) }) </script> </head> <body> <div ms-controller="test"> <div ms-foo="aaa">點我</div> </div> </body> </html>
別看例子簡單,其實它是除duplex外又一個新的雙工綁定!
avalon自帶加載器,它是使用業界最通用的AMD規範。
若是你想禁用自帶加載器,有兩種辦法:
avalon.config({ loader:false })
至於avalon是怎麼與jQuery, requirejs怎麼搭配,怎麼打包,能夠詳看這裏。
avalon自己沒提供AJAX模塊,你們可使用jQuery或 mmRequest
require(["mmRequest/mmRequest"], function() { var vm = avalon.define("test", function(vm) { vm.loadScript = function() { avalon.getScript("test.js", function() { console.log("success") vm.time = + new Date() }) } vm.loadAJAX = function() { avalon.ajax({ url: "test.js", dataType: "script", data: { page: 1, name: 1 }, success: function() { console.log("success") vm.time = + new Date() } }) } vm.jsonData = [] vm.time = "" }) avalon.scan() })
mmRequest覆蓋原jQuery全部的功能,其文檔地址在這裏。
若是咱們想將VM提交到後臺,因爲VM存在許多方法與其餘東西,直接提交數據量很大,所以咱們能夠經過下面方法提交純數據
$("button").keyup(function(){ $.post("demo_ajax_gethint.asp", JSON.parse(JSON.stringify(vm.$model)),function(result){ alert("提交成功") }); });
avalon提供了mmRouer與mmState兩個路由器,但mmState也是基於mmRouter開發的,引入mmState就會自動引入mmRouter。
這裏兩個加載器均可以在這裏下載到,包括(mmPromise, mmHIstory, mmRouter, mmState)
其文檔放在這裏,有不懂的地方直接在此倉庫提ISSUE
因爲IE6下沒有console.log,若是又不想用VS等巨無霸IDE,能夠本身定義如下方法
if(!window.console){ window.console = {} console.log = function(str){ avalon.ready(function() { var div = document.createElement("pre"); div.className = "mass_sys_log"; div.innerHTML = str + ""; //確保爲字符串 document.body.appendChild(div); }); } }
上線後,將.mass_sys_log{ display: none; }
若是是高級瀏覽器,avalon會在控制檯上打印許多調試消息,若是不想看到它們,能夠這樣屏蔽它們:avalon.config({debug: false})
avalon是經過Object.defineProperty來實現屬性監聽,所以不像angular,每次都VM的全部屬性都比較一遍,而是用戶一改動屬性就當即觸發視圖變更。
這是avalon的優點,但隱藏一個很差的地方。用戶必須在avalon.define的配置對象將全部監聽的對象預先寫好,不然沒法監聽到。
var vm = avalon.define({ $id: "test", a: "aaa", //這個能夠監聽,改變它會同步視圖 $b:"ccc" //這個是非監控屬性,改變它不會同步視圖 }) vm.c = '111' //這是avalon.define後才添加的,改變它不會同步視圖 //在IE6-8還會拋錯
avalon在IE6-8是使用VBScript實現,不支持隨便添加新屬性,好比上例就會拋錯
VBScript還帶一個問題,它是不區分大小寫,aaa與AAA是認爲同樣的,這時它會報重複定義的問題。
某些屬性因爲是VBscript的關鍵字,也不能做爲屬性或方法,不然在IE6-8也拋錯,如type, err, erm, me
因爲avalon是經過Object.defineProperty來實現屬性監聽,它會轉換其底下的每個屬性,每個數組,每個子對象,所以對象的層次不太深,不然會帶來性能問題。最好不超過3層,數組的層次也宜過長。而且儘可能使用$開頭或放在$skipArray減小無謂的屬性監聽。
avalon能夠經過如下手段撕開不能監聽新屬性的限制,就是重寫子對象!
var vm = avalon.define({ $id: "test", a: {} }) vm.a = { b: 2,//如今a.b, a.c, a.d能夠與視圖互相同步了 c: 3 d: 4 }
若是你想重寫數組的某一個元素,能夠其set方法,若是交互兩個數組元素,請使用splice方法或深拷貝原數組再賦值。
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <script src="avalon.js"></script> <script> var vm = avalon.define({ $id: "test", items: [ { name: 'First lady' }, { name: 'Second boy' }, { name: 'Third guy' } ]; }); var temp = vm.items.splice(2, 1); vm.items.splice(1, 0, vm[0].$model);//若是是簡單數據類型,就不須要.$model </script> </head> <body> <ul ms-controller="test"> <li ms-repeat="items">{{ el.name }}</li> </ul> </body> </html>
<!DOCTYPE html> <html> <head> <title>avalon入門</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script src="avalon.js" type="text/javascript"></script> <script> var model = avalon.define({ $id: 'test', arr: [{ 'value': '0', 'text': 'zero' }, { 'value': '1', 'text': 'one' }, { 'value': '2', 'text': 'two' }, { 'value': '3', 'text': 'three' }], click: function () { var aa = avalon.mix(true, [], model.$model.arr) //交換one和three aa[1] = aa[3] aa[3] = aa[1] //控制檯顯示交換成功 console.log(aa[1]) console.log(aa[3]) model.arr = aa } }) </script> </head> <body> <div ms-controller="test"> <button type="button" ms-click="click">xxx</button> <ul> <li ms-repeat="arr" > {{el.text}} </li> </ul> </div> </body> </html>
與mmState搭配使用時,頁面上的組件重複生成的解決方法:在ms-view所在元素上添加一屬性data-view-cache="true", 。但若是兩個視圖有相同的dialog(就是ms-include-src='路徑.html')相互以前切換的話dialog vmodels會被刪除。就把調用公用dialog的html移除到ms-view以外解決了。
有問題請到GITHUB提ISUUE,或到avalon專門論壇提問。
avalon在慕課網也有相關視頻教程, 前端亂燉上有系列長文剖析avalon的原理
本人也承受北京地區的avalon上門培訓!!!