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.mzph.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…

综合时如何插入scan_三综合环境试验箱维修时如何做出正确判断?

三综合环境试验箱维修时如何做出正确判断?三综合环境试验箱在试验的过程中&#xff0c;可以根据需要设定不同的温度情况&#xff0c;以便于为各种测试要求提供便利的条件。在测试一些材料结构或复合材料的时候&#xff0c;主要是利用其在瞬间高温情况或者是在极低温的连续环境…

mysql 判断字段为null表示 false 其它为true_日拱一卒,MySQL数据库 常用SQL优化技巧 十一式...

本文中所提到的SQL优化技巧均是基于Mysql 索引 BTree类型 。将从以下几个方面介绍常用的SQL优化技巧&#xff1a;避免在 WHERE 子句中使用 ! 或 <> 操作符。避免在 WHERE 子句中对索引列使用 %前缀模糊查询。避免在 WHERE 子句中对索引列使用 OR 来连接条件。避免在 WHER…

数字图像处理

题目&#xff1a;大规模图像中的目标检测与分类方法 在进行图像目标识别与跟踪时&#xff0c;摄像机所采集的图像&#xff0c;在成像、数字化以及传输过程中&#xff0c;难免会受到各种各样噪声的干扰&#xff0c;图像的质量往往会出现不尽人意的退化&#xff0c;影响了图像的视…

内容可编辑_让PDF像WORD一样自由编辑,好用的PDF编辑工具推荐

在日常工作中&#xff0c;我们经常要和PDF文件打交道。以往编辑PDF文件&#xff0c;比如修改文字等&#xff0c;需要下载专门的PDF编辑软件&#xff0c;通常编辑器都会超过200M&#xff0c;下载安装很麻烦&#xff0c;还会挤压电脑的储存空间&#xff0c;影响运行速度。当迅读P…

会返回两次_嫦娥五号为何用独特的半弹道式返回方式?原来有更深远的考虑……...

更多战史及装备评说&#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;微信小游戏也曝光了另外一项新能…

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…

Linux系统初级优化

系统参数优化和怎样增强系统安全性&#xff0c;系统默认的一些参数都是比较保守的&#xff0c;所以我们可以通过调整系统参数来提高系统内存、CPU、内核资源的占用&#xff0c;通过禁用不必要的服务、端口&#xff0c;来提高系统的安全性&#xff0c;更好的发挥系统的可用性。通…

【原创】SQlServer数据库生成简单的说明文档小工具(附源码)

这是一款简单的数据库文档生成工具&#xff0c;主要实现了SQlServer生成说明文档的小工具&#xff0c;目前不够完善&#xff0c;主要可以把数据库的表以及表的详细字段信息&#xff0c;导出到Word中&#xff0c;可以方便开发人员了解数据库的信息或写技术说明文档。技术上主要采…

封装成vla函数_第四章:Python之函数

第一节&#xff1a;函数入门与定义函数理解函数所谓函数&#xff0c;就是为一段实现特定功能的代码“取”个名字&#xff0c;以后即可通过该名字来执行(调用)这段代码从逻辑上看&#xff0c;函数相当于一个黑匣子定义函数的语法定义函数的三条铁律函数需要几个关键的、需要动态…

allegro大十字光标设置方法

使用大十字光标&#xff0c;在摆放元器件时&#xff0c;容易对齐。在allegro中&#xff0c;可以通过设置实现大十字光标&#xff0c;其具体方法如下&#xff1a; &#xff11;、选择Setup->User Perferences,即可出现如下图所示界面&#xff1a; &#xff12;、选择Display-…

基于.NET平台常用的框架整理(收藏)

目录 分布式缓存框架 日志记录异常处理 关于NoSQL数据库 自动任务调度框架 依赖注入IOC容器框架 常用的几个ORM框架 格式和数据类型转换 反射和动态语言 跨平台和运行时解决方案 WEB开发和设计 移动互联网和云计算 网络通信和网络协议 图形和图像处理框架 桌面应用程序框架 测试…

界址点号_界址点及四至优化

先对文中提到的面做一个解释&#xff0c;他可以指地块&#xff0c;宗地。一、界址点先说一下对界址点优化的情况&#xff0c;之前的方法主要是只要考虑了对坐标排序的问题&#xff0c;对于比较规整的面出的效果还是挺好&#xff0c;但往往现实中的面都比较复杂&#xff0c;像下…

java程序 输入10个数字并求和

课程作业&#xff1a; 模仿JavaAppArguments.java示例&#xff0c;编写编写一个程序&#xff0c;此程序从命令行接受多个数字&#xff0c;求和之后输出结果。 设计思想&#xff1a; 先从命令行读出数字&#xff0c;然后计算各个数字之和。求出结果。 流程图&#xff1a; 程序源…

是先打工还是直接创业?答案让我惊呆了!

第一问&#xff1a;成功路上&#xff0c;您是自己乱走&#xff0c;还是有老师指导更好&#xff1f;人生路上&#xff0c;因为有父母&#xff0c;才有生命&#xff1b;成功路上&#xff0c;因为有老师&#xff0c;才有方向。父母优秀&#xff0c;才可以培养出优秀的孩子。优秀的…