knockoutJS學習筆記01:從拼接字符串到編寫模板引擎

開篇

  關于knockout的文章,園里已經有很多大神寫過了,而且都寫得很好。其實knockout學習起來還是很容易的,看看官網的demo和園里的文章,練習練習就可以上手了(僅限使用,不包含研究源碼)。之所以想寫這個系列,主要是想記錄自己的學習和應用過程,也希望能給初學者一點幫助。

  既然是學習過程就一步一步來,從最開始的解決方案,到優化過程,到最后的實現方案。有了思考和對比,才會更加明白這個東西有什么好處,為什么使用它、什么情況要使用它。ok, 官網學習鏈接為?:knockoutJS

準備例子  

  過程是這樣的:前臺發送ajax請求,后臺返回json字符串,前臺生成html,插入到dom。這個過程我們再熟悉不過了,接下來我們就用多種方式完成這個例子。

  先用jquery簡單寫一個發送請求的方法,如下:

window.Tester = {callback: function(fn) {$.ajax({url: "../Handlers/GetCourse.ashx",success: function(data) {data = $.parseJSON(data);fn(data);}});}
}

  后臺對應的實體對象,如下:

    public class CourseInfo{public string CourseID { get; set; }public string IconPath { get; set; }public string CourseName { get; set; }public string TeacherName { get; set; }public string CreatedDate { get; set; }public int StudyNumber { get; set; }}

  html如下:

        <ul id="course"><li><a href="/Default.aspx?courseID=001"><div class="course-img"><img src="../Image/1.jpg" /></div><div class="course-info"><div class="names"><span>jquery源碼解析</span><span class="fr">李老師</span></div><div class="pros"><span>2015-08-08</span><span class="fr">100人學習</span></div></div></a></li></ul>

  界面效果:

一、拼接字符串

  相信很多人開始都用過拼接字符串來生成dom元素,然后越寫越多,越寫越亂...,寫到自己都看不太懂了,最后干脆揮揮手留給別人去看。我們都不希望這樣做,有代碼潔癖的朋友,看到這些應該會發狂。  

  我們來看一下實現上面的效果,用拼接字符串是怎么樣的,代碼如下:

    Tester.callback(function(data) {for (var i = 0; i < data.length; i++) {var courseImg = "<div class='course-img'><img src='" + data[i].IconPath + "' alt='" + data[i].CourseName + "'/></div>";var names = "<div class='names'><span>" + data[i].CourseName + "</span><span class='fr'>" + data[i].TeacherName + "</span></div>";var pros = "<div class='pros'><span>" + data[i].CreatedDate + "</span><span class='fr'>" + data[i].StudyNumber + "人學習</span></div>";var item = "<li><a target='_blank' href='Default.aspx?courseID=" + data[i].CourseID + "'>" + courseImg + "<div class='course-info'>" + names + pros + "</div></a></li>";$("#course").append(item);}});

  可以很快得出下面幾點:1.拼接寫起來很麻煩? 2.不能給人清晰的dom結構 3.到處都是字符串修改起來很麻煩。實際項目中,我們應該盡量避免這種情況。

二、clone dom

  為了解決上面的缺點,我們可以把html模板先寫好,并隱藏。等到需要時,再clone一份,生成html。代碼如下:  

    <div id="tmp" class="noen"><ul><li id="tmpItem"><a><div class="course-img"><img/></div><div class="course-info"><div class="names"><span></span><span class="fr"></span></div><div class="pros"><span></span><span class="fr"></span></div></div></a></li></ul></div>
    Tester.callback(function(data) {for (var i = 0; i < data.length; i++) {var item = $("#tmpItem").clone();item.find("a").attr("href", "Default.aspx?CourseID=" + data[i].CourseID);item.find(".course-img>img").attr({ "src": data[i].IconPath, "alt": data[i].CourseName });item.find(".names>span:eq(0)").text(data[i].CourseName);item.find(".names>span:eq(1)").text(data[i].TeacherName);item.find(".pros>span:eq(0)").text(data[i].CreatedDate);item.find(".pros>span:eq(1)").text(data[i].StudyNumber + "人學習");$("#course").append(item);}});

?  看起來比拼接字符串好多了。這里我們提到了“模板”的概念,但它還不是真正意義上的模板,所謂模板應該是:基礎內容準備好了,就差數據,只要把數據傳遞過來,就可以生成完整內容。可以看到,我們上面還是自己去解析數據,然后生成內容,而不是自動化的過程。如果可以這樣生成html就最好了:var html = template("#tmpID",data); tmpID 表示模板的id,data 是數據,這樣生成html,不用自己去for遍歷。沒錯,這就是大多數模板引擎的實現思路。

三、模板引擎

  關于js模板引擎有很多,我也會在下一篇文章單獨介紹。不過在這里我不想馬上就用現成的,我們自己先實現試試看!

3.1 基礎版

  首先我們需要找到字符串中真實數據的位置,這通常是通過“占位符”來實現的,例如:${ $};然后再將占位符替換為真實的數據。查找占位符可以用正則表達式實現,替換占位符用字符串操作即可。

  例如字符串:my name is ${name$}, i am ${year$} years old。 數據為:{name : "tom", year : 18}。我們希望生成最后的結果是: my name is tom, i am 18 years old。

  先編寫匹配占位符的正則表達式:/\${((?:.(?!\$}))*.)?\$}/g (說明:正則水平一般,卡了好久...,厲害的朋友在回復寫出更好的!)。實現代碼如下:

    var reg = /\${((?:.(?!\$}))*.)?\$}/g; var str = "my name is ${name$}, i am ${year$} years old";var data = {name : "tom",year : 18}var match;while (match = reg.exec(str)) {str = str.replace(match[0], data[match[1]]);}console.log(str);//my name is tom, i am 18 years old 

  簡單解釋一下:核心是exec方法,它返回的是一個數組,包括匹配到字符串的值,和其位置等。match[0] 是占位符;match[1] 是占位為內的內容(如name)。這樣通過一個循環,就可以將所有匹配找到。

?3.2 改進版

  上面例子實在太簡單了,看一個稍微復雜點的結構。字符串是:my name is ${name$}, i am ${info.age$} years old。數據為:{name: "tom", info: {age:18}}。按上面的做法就不能得到正確的結果了,因為匹配后 match[1] 為 “info.age”,而 data["info.age"] 顯然不能獲取到18。如果可以在字符串里寫js呢,例如:this.name或this.info.age,運行時this由我們傳遞并執行,這樣問題就解決了。這里有兩個問題:1. 如何在字符串里寫js代碼?? 2.this 如何動態決定?

  要在字符串里寫代碼執行,Function 就可以實現。Function接收字符串類型的參數,前面的是函數的參數,最后一個是函數的執行體。例如:var fn = new Function("arg1","arg2","return arg1 + arg2;"); fn 就是一個函數,接收兩個參數。可以執行得到結果:console.log(fn(1,2)); //3。那么 this 如何由我們動態決定呢?答案就是:對象冒充。js 的 call, apply 就是用來實現對象冒充的。

  解決了這兩個問題,實現起來就輕松多了,如下:

    var code = "return 'my name is ' + this.name + ', i am ' + this.info.age + ' years old';";var fn = new Function(code).apply(data);console.log(fn);

  這里我們創建一個函數,函數執行體就是code,this指向了data對象。注意,這里 this.name 不能加'',否則就作為普通字符串進行拼接了。字符串拼接太麻煩了,在網上看到一種很好的做法,通過數組實現,代碼如下:

    var code = "var result = [];"code += "result.push('my name is ');";code += "result.push(this.name);";code += "result.push(' i am ');";code += "result.push(this.info.age);";code += "result.push(' years old');";    code += "return result.join('');";     var fn = new Function(code).apply(data);console.log(fn());

?  同樣,數據部分不能加''。這種方式很巧妙,fn 執行時,會從 var? result = []; 開始執行,this 就是 data 對象,最后生成字符串返回。這里我們簡單封裝一下:

    var str = "my name is ${this.name$}, i am ${this.info.age$} years old";var data = {name: "tom",info: {age:18}}function template(html, data) {if (!html) {return;}var reg = /\${((?:.(?!\$}))*.)?\$}/g;var cursor = 0;var code = "var result = [];\n";var match;while (match = reg.exec(html)) {code += "result.push('" + html.substring(cursor, match.index) + "');\n";code += "result.push(" + match[1] + ");\n";cursor = match.index + match[0].length;}code += "result.push('" + html.substring(cursor) + "');\n";code += "return result.join('')";        //console.log(code);return new Function(code.replace(/\n/g,"")).apply(data);}console.log(template(str, data));  

3.3 最終版

  許多時候后臺返回的是json數組字符串,這時需用使用邏輯判斷和循環來處理。這里需要一個正則:/(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g 用來匹配判斷循環關鍵字。需要注意的是,當遇到這些關鍵字的時候,就不能push到數組里了,而應該是作為程序的一部分執行,例如:

  var result = [];

  for(var i=0;i<10;i++){

    result.push(this.name);

  }

  ...

  結合上面的,封裝一個最終版,如下:

    function template(id, data) {if (!id) {throw new Error("模板id不能為空!");}var jTmpl = $(id);if(jTmpl.length <= 0){throw new Error("找不到id為:"+id+"的模板");}var html = jTmpl.html();if(!html){return html;}html = html.replace(/\"/g,"\\\"");var reg = /\${((?:.(?!\$}))*.)?\$}/g;              var logicReg = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g;var cursor = 0;var code = "var result = [];\n";        var match;var key;        while (match = reg.exec(html)) {code += "result.push('" + html.substring(cursor, match.index) + "');\n";            code += match[1].match(logicReg) ? match[1] : "result.push(" + match[1] + ");";            code += "\n";cursor = match.index + match[0].length;}code += "result.push('" + html.substring(cursor) + "');\n";code += "return result.join('')";        //console.log(code.replace(/\n/g, ""));return new Function(code.replace(/\n/g, "")).apply(data);}

?  我們試著用這個模板完成上面拼接字符串和clone dom 相同的功能。先定義模板:

<script type="text/tmpl" id="courseTmpl">${for(var i=0,length=this.length;i<length;i++){$}<li>    <a href="Default.aspx?courseID=${this[i].CourseID$}"><div class="course-img"><img src="${this[i].IconPath$}" alt="${this[i].CourseName$}"/></div><div class="course-info"><div class="names"><span>${this[i].TeacherName$}</span><span class="fr">${this[i].CourseName$}</span></div><div class="pros"><span>${this[i].CreatedDate$}</span><span class="fr">${this[i].StudyNumber$}人學習</span></div></div></a></li>${}$}
</script>

?  模板定義好后,執行代碼就只有一行了!如下:

    Tester.callback(function(data) {$("#course").html(template("#courseTmpl",data));});

  通過使用模板引擎,我只需要定義好模板,傳遞數據,渲染工作就由模板引擎自動完成了。

  這里還有一個小知識點,script的type屬性設置為:text/tmpl,這個屬性是瀏覽器不認識的。如果script的type是瀏覽器支持的(如text/javascript),就會當做腳本執行或通過src屬性請求下載腳本再執行,如果是瀏覽器不支持的,就會忽略。所以這里可以用來存儲數據,大多數模板也都是定義在這個地方。

四、總結

  上面的模板引擎很簡單,只有30行左右,但它其實已經可以解決一些簡單的問題了。實際它還有許多問題沒考慮,書寫起來還是比較復雜的,也不可能針對多變的需求都適用,所以還是建議用于簡單的應用或學習。很好的是,它讓我們明白了整個解決思路和模板運行的過程。

  實際上現成的模板引擎已經很多了,接下來一篇就將介紹其中一個。

轉載于:https://www.cnblogs.com/4littleProgrammer/p/4807180.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/272933.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/272933.shtml
英文地址,請注明出處:http://en.pswp.cn/news/272933.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

新鄉臺達服務器驅動器維修,臺達DELTA伺服驅動器維修

與數控裝置的接口電路無關。檢查測量系統電纜連接正確、可靠&#xff0c;排除了電纜連接的問題。利用示波器檢查位置測量系統的前置放大器EXE601/5-F的Ual和Ua*Ua1和Ua2輸出波形&#xff0c;發現Ua1相無輸出。進一步檢查光柵輸出(前置放大器EXE601/5-F的輸入)信號波形&#xff…

60度斜坡怎么計算_【測繪】南方CASS土方計算方法—方格網法

01概述在我們的日常工作中&#xff0c;遇到大量的土方修正算的相關咨詢&#xff0c;為什么CASS的方格網土方修正算&#xff0c;方格設定為10米和20米&#xff0c;修正算結果有很大差異呢&#xff1f;從軟件計算原理、數據質量等方面進行分析&#xff0c;讀了這篇文章&#xff0…

NSMutableArray 排序

NSMutableArray *array1[NSMutableArray arrayWithObjects:"1","3","2", nil];//NSLog("%",array1);/*結果:(1,3,2 )*/NSLog("%",array1);NSArray *array2 [array1 sortedArrayUsingSelector:selector(compare:)];//注意 這…

綜合時如何插入scan_三綜合環境試驗箱維修時如何做出正確判斷?

三綜合環境試驗箱維修時如何做出正確判斷?三綜合環境試驗箱在試驗的過程中&#xff0c;可以根據需要設定不同的溫度情況&#xff0c;以便于為各種測試要求提供便利的條件。在測試一些材料結構或復合材料的時候&#xff0c;主要是利用其在瞬間高溫情況或者是在極低溫的連續環境…

mysql 判斷字段為null表示 false 其它為true_日拱一卒,MySQL數據庫 常用SQL優化技巧 十一式...

本文中所提到的SQL優化技巧均是基于Mysql 索引 BTree類型 。將從以下幾個方面介紹常用的SQL優化技巧&#xff1a;避免在 WHERE 子句中使用 ! 或 <> 操作符。避免在 WHERE 子句中對索引列使用 %前綴模糊查詢。避免在 WHERE 子句中對索引列使用 OR 來連接條件。避免在 WHER…

b樣條和三次樣條_樣條曲線

最近在學習軌跡規劃中的軌跡生成&#xff0c;涉及到樣條曲線方面的知識&#xff0c;總結一下。二次樣條三次樣條曲線平滑曲線的平滑性和相應的平滑性的評判準則相關&#xff0c;在[1]中&#xff0c;作者采用曲率的平方和曲率導數的平方作為評判準則其中 是路徑點的方向角。最小…

數字圖像處理

題目&#xff1a;大規模圖像中的目標檢測與分類方法 在進行圖像目標識別與跟蹤時&#xff0c;攝像機所采集的圖像&#xff0c;在成像、數字化以及傳輸過程中&#xff0c;難免會受到各種各樣噪聲的干擾&#xff0c;圖像的質量往往會出現不盡人意的退化&#xff0c;影響了圖像的視…

2015年秋季個人閱讀計劃

10月閱讀計劃&#xff1a;《軟件需求模式》 10月12日23:59前發表第一篇讀書筆記。 10月22日23:59前發表第二篇讀書筆記。 10月31日23:59前發表第三篇讀書筆記。 11月閱讀計劃&#xff1a;需求模式——軟件建模與分析 11月12日23:59前發表第一篇讀書筆記。 11月22日23:59前發表第…

內容可編輯_讓PDF像WORD一樣自由編輯,好用的PDF編輯工具推薦

在日常工作中&#xff0c;我們經常要和PDF文件打交道。以往編輯PDF文件&#xff0c;比如修改文字等&#xff0c;需要下載專門的PDF編輯軟件&#xff0c;通常編輯器都會超過200M&#xff0c;下載安裝很麻煩&#xff0c;還會擠壓電腦的儲存空間&#xff0c;影響運行速度。當迅讀P…

DHL 快遞跟蹤查詢

思路描述&#xff1a;主要使用正則表達式解析。 返回一個跟蹤步驟列表。 public class TrackingData { public string time { get; set; } public string context { get; set; } } public class DHLExpressTrackingHelper { private static string urlFormat "http://web…

會返回兩次_嫦娥五號為何用獨特的半彈道式返回方式?原來有更深遠的考慮……...

更多戰史及裝備評說&#xff0c;請移步公眾號asiavikin&#xff08;轉載請注明出處&#xff09;24日凌晨4時30分&#xff0c;嫦娥五號在文昌航天發射場由長征五號火箭成功送入地月轉移軌道&#xff0c;22時6分完成第一次軌道修正&#xff0c;可喜可賀。這是人類44年來首度去月球…

【轉】VS2013中如何解決error C4996: 'fopen'問題

原文網址&#xff1a;http://jingyan.baidu.com/article/ce436649fd61543773afd32e.html 今天編寫控制臺應用程序時出現如下錯誤 error C4996: fopen: This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_…

中關鍵字 表示空類型_C語言數據類型

程序在運行時要做的內容就是處理數據。程序要解決復雜的問題&#xff0c;就要處理不同的數據。不同的數據都是以自己本身的一種特定形式存在的&#xff0c;不同的數據類型占用不同的存儲空間。C語言中有多種不同的數據類型&#xff0c;其中包括幾個大的方向&#xff1a;基本數據…

理解inode

。 理解inode 一、inode是什么&#xff1f; 理解inode&#xff0c;要從文件儲存說起。 文件儲存在硬盤上&#xff0c;硬盤的最小存儲單位叫做"扇區"&#xff08;Sector&#xff09;。每個扇區儲存512字節&#xff08;相當于0.5KB&#xff09;。 操作系統讀取硬盤的時…

幀同步_微信小游戲接入“熊孩子噩夢”健康系統 幀同步能力上線

3月31日&#xff0c;微信小游戲官方公眾號“做個小游戲”發文宣布全新面向未成年人保護的健康系統已經上線&#xff0c;該系統聯動“成長守護平臺”的功能&#xff0c;可以更好助力家長群體對于未成年人游戲行為的監管。另外就在昨天&#xff0c;微信小游戲也曝光了另外一項新能…

【js】獲得項目路徑

1 var curWwwPathwindow.document.location.href; 2 //獲取主機地址之后的目錄&#xff0c;如&#xff1a; uimcardprj/share/meun.jsp 3 var pathNamewindow.document.location.pathname; 4 var poscurWwwPath.indexOf(pathName); //獲取主機地址&#xff0c;如&…

寫一個python程序、求解使得npv值為零的折現率_計算題專題:凈現值NPV分析與習題...

凈現值(NPV)是反映投資方案在計算期內獲利能力的動態評價指標。投資方案的凈現值是指用一個預定的基準收益率(或設定的折現率)i&#xff0c;分別把整個計算期間內各年所發生的凈現金流量都折現到投資方案開始實施時的現值之和。今天的一分錢要比明天的一分錢值錢NPV—計算公式和…

Myeclipse 安裝Aptana3.2 插件

轉自&#xff08;http://www.cnblogs.com/yinger/archive/2011/08/29/2157193.html&#xff09; 安裝步驟&#xff1a; 1、下載aptana3.2 Eclipse Plugin插件. 下載地址&#xff1a;http://update1.aptana.org/studio/3.2/024747/index.html 2、在java文件夾下新建文件夾plugin…

python語言的取余運算符_Python 中用于整數除法取余的運算符是()_學小易找答案...

【填空題】隱球菌病多由()傳播。【單選題】下列選項中,哪一個符號是管道符號。 ( )【多選題】可采用分批法計算產品成本的企業有( )【填空題】現有字符串 s”1234567” ,則 s[::2] 得到子串 , s[-1:0:-2] 得到子串【單選題】關于 Python 格式控制,下列說法正確的是()【單選題】…

Linux系統初級優化

系統參數優化和怎樣增強系統安全性&#xff0c;系統默認的一些參數都是比較保守的&#xff0c;所以我們可以通過調整系統參數來提高系統內存、CPU、內核資源的占用&#xff0c;通過禁用不必要的服務、端口&#xff0c;來提高系統的安全性&#xff0c;更好的發揮系統的可用性。通…