`
jindw
  • 浏览: 508510 次
  • 性别: Icon_minigender_1
  • 来自: 初到北京
社区版块
存档分类
最新评论

雏凤清音 -- 面向数据的前端编程方法

阅读更多

名词解释:

桐花万里丹山路,雏凤清于老凤声——唐·李商隐《韩冬郎既席为诗相送因成二绝》
作为一种有别传统的前端编程方法,雏风名之。
面向数据的编程方法,避开繁琐的ui代码,直接针对前端数据模型编程,你的程序就能更加清晰简单。清音名之。




从实例开始:

避免过于枯燥的陈述,我们从实例开始,我们以百度 WebIM分组管理为例。


 

这里面,我们需要对用户分组列表执行:添加、删除、修改、保存、取消、排序等六种功能。


这算是一个比较常见的需求,那么,我们通常在前段实现这种功能,一般如何设计?多少代码?多长时间?

思考时间............................
===================================================

好了现在我们来展示一中简单的实现方式:

 

var data = {
	"list" : [
		{"id" : 1,"name" : "我的分组1"},
		{"id" : 2,"name" : "我的分组2"},
		{"id" : 3,"name" : "我的分组3"},
		{"id" : 4,"name" : "我的分组4"},
		{"id" : 5,"name" : "我的分组5"},
		{"id" : 6,"name" : "我的分组6"}	]
}
var action = {
  sort:function(list, inc){
    $("sort").className = inc?"down":"up";
    list.sort(function(a, b){
      return (inc?1:-1)*a.name.localeCompare(b.name);
    });
    render(data);
  },
  create : function(name){
    data.list.push({id: +new Date(),name: name});
    render(data);
  },
  edit : function(id){
    each(data.list, function(value, i){
      data.list[i].state = value.id == id ? "edit" : "normal";
    });
    render(data);
  },
  del : function(id){
    each(data.list, function(value, i){
      if(value.id == id){
        data.list.splice(i,1);
      }
    })
    render(data);
  },
  save : function(id){
    each(data.list, function(value, i){
      if(value.id == id){
        value.name = $("g_" + id).value;
        value.state = "normal";
      }
    });
    render(data);
  },
  cancel : function(id){
    each(data.list, function(value, i){
      data.list[i].state = "normal";
    });
    render(data);
  }
}
function $(id){
  return document.getElementById(id);
}
function each(obj, fn){
  for (var i = 0; i < obj.length; i++) {
     fn.call(obj[i], obj[i], i); 
  }
}
function render(data){
  $("container").innerHTML = teamList(data);
}
 


模板代码:

<c:client id="teamList">
	<c:for items="${list}" var="item">
		<div class="${item.state}">
			<c:if test="${item.state == 'edit'}">
				<div class="cell1">
					<input id="g_${item.id}" value="${item.name}" />
				</div>
				<div class="cell2">
					<button onclick="action.submit(${item.id})">
						提交
					</button>
				</div>
				<div class="cell3">
					<button onclick="action.cancel(${item.id})">
						取消
					</button>
				</div>
			</c:if>
			<c:else>
				<div class="cell1">${item.name}</div>
				<div class="cell2">
					<img src="images/edit.gif"
						onclick="action.edit(${item.id})" />
				</div>
				<div class="cell3">
					<img src="images/delete.gif"
						onclick="action.del(${item.id})" />
				</div>
			</c:else>
		</div>
	</c:for>
</c:client>
 


以传统的编程方法不同,我们没有直接操作html,或者说文档对象模型,而是抽象出一个更加简单的数据模型--一个JavaScript 数组对象。

我们所有的操作都是从这个简单的原始数据开始,数据模型被修改后,调用模板渲染函数,重新前端局部刷新展现UI。操作即可完成。

这种方法能给我们简化多少代码呢?大家可以去翻翻那些比较出名的JavaScript图书,里面通常会花一个小节的篇幅去讲解一个话题,那就是表格排序。

对照这里的表格排序,7行代码,需要一个章节去解释吗?





核心思想:

* 一切从源头开始
传统的编程方法有一个问题,那就是他修改的对象是文档对象模型,但是,当您的程序足够复杂的时候,这种文档对象模型往往不够简单,有太多冗余,或者夹杂着 太多的静态展示逻辑,我们控制它非常麻烦。好了,我们没有必要迁就文档对象模型,我们要设计更加简单的没有冗余的前端模型。

一切从源头开始,不仅是为了操作上更加简单,更重要的一点是,避免数据不一致和错误累加的危害。
如果是传统的文档数据模型,不仅他本身带有大量冗余。我们前段操作代码也容易有大量的冗余。如上例,传统的方法中,初次展现的分组列表和后来编辑,添加的 分组,可能生成方法都不一样,分布在不同的代码中,一单涉及到展现风格的修改,都得多次同步编辑,这在编程上的大忌。而我们上面介绍的方法中,我们只有一 处展现逻辑,那就是完整覆盖。
仍外,传统方法是对UI做增量的修改操作,但是加加减减的多了,难免不出点小问题,而且,这种问题只能被累加起来,但是在我们新的编程方法中,因为我们每次都是整体覆盖,这样就不会有错误累加的问题了。

* 表现层没有上下文
既然我们有自己的数据模型,就不要随便在文档对象模型上保留任何状态了,任何修改,都反映到数据模型中去吧,状态改变了,同步数据模型,再从数据模型开始,完全覆盖前端展示。





性能的问题
上例中的联系人分组管理,能承受多大的数据呢?
我们得先解释一下我们的统计方法。
人的视觉停留时间是0.1秒,或者说100毫秒。也就是说,100毫秒间隔人类是无法感知的。而我们的网页操作。是一种延迟,不是间隔,人类能感知的时间延迟有多少呢?我们没有权威的数据,一般认为是300毫秒。300毫秒内的延迟,我们是无法感知的。
那么300毫秒内,我们能渲染多少数据呢?IE8,FF3(不打开Firebug,打开firebug能渲染400次)上能支持1000条左右,Chrome上能支持3000次左右。

另外,在某些情况下,面向数据的编程方法,性能可能还具有一定的优势。比如,百度工具栏widget列表的设计中,我们一次装载了较多的widget数 据,但是我们每次只展示一小部分,这样,面向数据边车呢个方法中DOM节点明显比传统方式少一个数量级。要知道,DOM节点是非常消耗内存的。。。。



说点不足

动画,对,我们不应该采用这种方式做网页动画,因为动画的刷性要求太高,模板这种重头开始的方式不再适用。

所以,真要使用js动画效果的时候,你还是应脚本库去辅助。
同时提一点个人看法,用户体验的提高并不是动画这样吸引 眼球的效果。更多考虑应该是让用户操作更方便,不要被扣分。




如何体验?


* 运行JSide调试服务器。
http://www.xidea.org/webstart/JSide.jnlp
一个webstart程序,JSide 启动之后会在窗口右侧出现一个彩色的浮层(无边框窗口,如果是Java6u10+,这个窗口会透明显示)



* 下载测试程序并解压。
http://lite.googlecode.com/files/Example-20091219.zip
下载之后随便解压缩到一个目录里面吧。

* 设置调试网站目录。
测试服务器就是为了测试方便而设计的,你只要把刚才解压缩的目标目录拖放到JSide浮层上,测试服务器自动切换网站目录。

* 查看测试程序。
每次切换网站目录后,程序会提示您打开网站首页,你打开就是了。首页默认是一个文件列表。选择你看着顺眼的文件点击吧(*.s.js是一种用JavaScript编写的服务端小程序,可以在JSide测试服务器上运行)

* 修改程序代码。
在JSide浮层上右键,浏览文件,修改吧。

  • 大小: 23 KB
分享到:
评论
16 楼 jindw 2010-04-29  
skyblue1984 写道
采用全部重新渲染的方式,当记录条数很多的情况下,是不是应该考虑下性能的问题?



性能问题不是想出来的。是测出来的。
性能优化也更多在于架构的调整,而不必在乎一两条语句的得失。

前端的性能最常见的有者两种:

1。 装载性能(主要是网络,阻塞。。)
2。 动画

而前端模板经常用于局部刷新操作,这种行为一般认为只要能在0.3妙内完成渲染,就不必优化,因为用户无法察觉(当能,如果您希望从节能环保的角度考虑,哪就是另外一回事了)。
15 楼 skyblue1984 2010-04-13  
采用全部重新渲染的方式,当记录条数很多的情况下,是不是应该考虑下性能的问题?
14 楼 jindw 2010-01-16  
Zehee 写道
你这个和我试验的JavaScript的MVC理念相同。将业务(Model)和DOM操作(View)分离开通过Controller连接渲染。

我这里有个小例子大家可以评一下:

http://zehee.iteye.com/blog/513818

很不错的小游戏,鉴定完毕。

joehe 写道
简单的事总要搞的这么复杂

看来太简单的大脑是不适合用来欣赏简单的。

baguo8899 写道
请叫个问题,这种方法前台的数据模型改变了,后台数据是否也相应发现改变?
是通过模板改变的,还是也要通过ajax请求改变后台相应的数据?

这里只是前端UI编程,没有涉及到后台数据存储的问题,需要的人自己补充。

13 楼 baguo8899 2010-01-15  
请叫个问题,这种方法前台的数据模型改变了,后台数据是否也相应发现改变?
是通过模板改变的,还是也要通过ajax请求改变后台相应的数据?
12 楼 joehe 2010-01-15  
简单的事总要搞的这么复杂
11 楼 Ihavegotyou 2010-01-14  
用GWT吧。  可以在没有server side的时候,使用dummy server. 直接用Json来作驱动开发。
10 楼 Zehee 2010-01-13  
你这个和我试验的JavaScript的MVC理念相同。将业务(Model)和DOM操作(View)分离开通过Controller连接渲染。

我这里有个小例子大家可以评一下:

http://zehee.iteye.com/blog/513818
9 楼 jindw 2010-01-12  
taocong810 写道
这种方法优点很多,省略500字,但是model和view的状态有时是不一致的。比如
  a。 重新生成的view有滚动条的位置需要记忆时怎么办?
  b。 用户选择了几个item,想同时操作,例如同时删除,在删除前想做一个排序,如何记忆那几个item被选择了?


恩,每一种方法都有自己的局限性。
我们也发现了不少不适合用模板的地方。基本的特点就是,连续动作。比如拖放,动画,就不适合了。

a. 内容本身的滚动条所有浏览器都会记录的,当能,如果你是内容内部元素的滚动条,那就不行了。
b. checkbox点击的时候同步数据就可以了。
8 楼 taocong810 2010-01-12  
这种方法优点很多,省略500字,但是model和view的状态有时是不一致的。比如
  a。 重新生成的view有滚动条的位置需要记忆时怎么办?
  b。 用户选择了几个item,想同时操作,例如同时删除,在删除前想做一个排序,如何记忆那几个item被选择了?
7 楼 hatedance 2010-01-12  
每次都是从Model生成View。的确费一点电,不过能接受就好。
问题是view层自己的状态就没有了吗?还都保存在model里?
6 楼 yeaha 2010-01-12  
我们也是这样的做法,不过我们把数据放到xml document里面,对于复杂关系的数据场合,xml document比js array更方便操作
xml document可以看作在客户端的一个数据库,xpath作为sql,数据的改变通过js写入xml document,同时js对dom进行相应改动,客户端的任何修改都不会和服务器交互,只有最后保存xml document时才和服务器打交道
5 楼 jindw 2010-01-12  
akandfxs 写道
非常不错。
hehe,按照js模板的需要生成数据。以前我们做B/S项目也是这么做的。后来也看过一个js后端的服务器,还实验过。很多方法都可以动态,不过没有在项目中体现出来究竟能省多少。担心js做后端影响项目的通用性。


js后端就算了吧。不靠谱。
前端模板也一直没有看见过真正实用的。


我认同的前端模板使用方式只有两种:
1. 在后端模板中嵌入前端模板,让后端模板作为前端模板编译的平台
2. 使用脚本托管机制(如JSI),开发时候可以实时编译,但是导出上线的时候,能后抛掉编译工作,轻装发布。
4 楼 Army 2010-01-12  
我感觉你的东西要开始真正的普及了。
3 楼 akandfxs 2010-01-11  
非常不错。
hehe,按照js模板的需要生成数据。以前我们做B/S项目也是这么做的。后来也看过一个js后端的服务器,还实验过。很多方法都可以动态,不过没有在项目中体现出来究竟能省多少。担心js做后端影响项目的通用性。
2 楼 bellstar 2010-01-11  
好复杂的实现,但不用渲染整个
var groupManagerCmp = {
      body: "#userGroup",
      viewTemplete: new Template( "<tr><td>${name}</td><input type='button' value='编辑' /><td></td><td><input type='button' value='删除' /></td></tr>"),
      editTemplate:   new Template( "<tr><td><input type='text' value='${name}'></td><td><input type='button' value='提交' /></td><td><input type='button' value='取消' /></td></tr>"),
      renderViewRow: function(id, name){
         var tr = this.viewTemplete.bind(id,name);
         $("input[0]",tr).click(function(){ //编辑
             groupManagerCmp.edit(tr, id, name);
         })
        $("input[1]",tr).click(function(){ //删除
             action.do("delete", {id: id}, function(){
                 groupManagerCmp.remove(tr);
             })
         })
         return tr;
      },
      renderEditRow: function(id, name){
           var tr =  this.editTemplate.bind(id,name);
           $("input[0]",tr).click(function(){ //提交
              action.do("put", {id: id, name: name}, function(group){
                  groupManagerCmp.update(tr, group.id, group.name);
             })
           });
          $("input[1]",tr).click(function(){ //取消
             groupManagerCmp.update(tr, id, name);
         })
        return tr;
      },
      append: function(id, name){
          $(this.body).appendChild(this.renderViewRow(id, name));
      },
      remove: function(trNode){
         $(this.body).removeChild(trNode);
      },
      edit: function(trNode, id, name){
          $(this.body).replaceNode(trNode, this.renderEditRow(id, name));
      },
      update: function(trNode, id, name){
         $(this.body).replaceNode(trNode, this.renderViewRow(id,name));
      }
}

var action = {
    url: {
      post: "group/new",
      put: "group/update",
      delete: "group/destroy"
    }
    do: function(action, params, callback){
       ajax(this.url[action], params, function(data){
          callback(decode(data))
      });
     }
}
1 楼 kimmking 2010-01-11  
不是面向数据,而且把模型丢到js里了。

相关推荐

    Digital-signal-processing.rar_汉明窗_清音和浊音

    在数字信号处理领域,"Digital-signal-processing.rar_汉明窗_清音和浊音"这一主题涵盖了几个关键概念,主要包括数字信号处理的基础、汉明窗的应用以及如何识别清音和浊音。以下是对这些知识点的详细阐述: 1. 数字...

    qingyin.rar_清音

    这个名为"qingyin.rar_清音"的压缩包文件显然与MATLAB中的语音处理相关,特别是涉及“清音”这一概念。清音在语音学中指的是没有声带振动的声音,比如在英语中的"sh", "s", "p"等发音。在音频处理中,对清音的理解和...

    (Sublime Text 3) 3.3103 流风清音中文优化版32位+64位

    Sublime Text 3是一款广受开发者喜爱的文本编辑器,版本号为3.3103的流风清音中文优化版是针对中国用户特别定制的,它在原版的基础上进行了汉化处理,使得国内用户在使用时可以更加便捷地理解和操作。这款编辑器不仅...

    hw1.rar_voiceless matlab_清音_清音浊音

    在本项目中,我们主要探讨的是使用MATLAB进行语音信号处理,特别是针对清音和浊音的分析。MATLAB是一款强大的数学计算软件,它在信号处理领域有着广泛的应用,包括语音信号的分析、处理和识别。 首先,我们要理解...

    浊音清音判定和激励信号

    在浊音/清音分类中,一种常用的方法是利用时域语音信号的零交点计数。零交点是指信号值从正变负或从负变正的点。浊音信号由于其周期性较强,零交点相对较少且规律;而清音信号因其随机性质,零交点数量较多且分布不...

    Speech-Processing---Detect-voice-and-unvoice:语音处理 - 检测浊音和清音

    在语音处理领域,检测浊音(voiced)和清音(unvoiced)是一项基本任务,对于语音识别、语音合成、噪声抑制等应用至关重要。在这个项目中,我们将探讨如何使用语音信号的过零率(Zero Crossing Rate, ZCR)和能量...

    一种面向智能家居的语音端点检测方法仿真.pdf

    标题和描述中提到了“面向智能家居的语音端点检测方法仿真”,这意味着本文讨论的是一种将语音识别技术应用于智能家居环境中的方法,具体而言是语音端点检测。语音端点检测是语音识别的前端处理过程,它涉及确定语音...

    qingyin-.rar_matlab浊音

    在语音信号处理领域,理解和应用轻音(清音)与浊音(重音)的识别技术至关重要。"qingyin-.rar_matlab浊音"这个压缩包文件包含了使用MATLAB编程环境实现的轻音浊音识别算法。MATLAB,全称矩阵实验室,是一款强大的...

    五十音图清音、浊音、拗音.docx

    五十音图是日语学习的基础,它包含了日语中的所有基本元音和辅音的组合,分为清音、浊音和拗音三类。 首先,我们来看清音表,它是日语发音的基本部分,共有五段十行。第一行是以"a"开头的假名,如"あいうえお",这...

    论文研究-基于短时能量和过零率分析的语音端点检测方法研究 .pdf

    语音端点检测是语音信号处理领域中的一个重要环节,它能够识别出语音信号的开始和结束位置,进而帮助提取出纯净的语音部分,为后续的语音识别、语音编码等工作提供准确的数据基础。端点检测的准确性直接关系到语音...

    基于Matlab编写的语音端点检测.doc

    - 学习MATLAB的基本语法和编程方法,包括变量、数组、函数调用和控制结构等,是进行语音处理的前提。 2. **语音信号处理**: - 语音信号处理是信号处理的一个分支,主要研究如何分析、变换、编码和合成语音信号。...

    电子功用-可屏蔽电磁干扰的高清音视频连接器

    标题中的"电子功用-可屏蔽电磁干扰的高清音视频连接器"揭示了本资料的核心内容,即一种能有效抵御电磁干扰(EMI)的高清音视频连接器。这种连接器的设计目的是确保在复杂电磁环境中传输的音视频信号保持高质量,不受...

    麦克风阵列前端语音信号处理

    麦克风阵列前端语音信号处理涉及众多技术和方法,通过对语音信号的特点及其处理技术的深入理解,可以显著提高语音通信和识别系统的性能。随着技术的进步,未来语音信号处理将在更多领域发挥重要作用。

    ADV7511技术手册

    ADV7511以其高性能和丰富的功能,可提供给各种家用电子设备高清音视频信号的传输需求,是构建现代家庭影院和娱乐系统的理想选择。通过其高级的HDMI 1.4标准特性,可以支持高清3D视频播放和高质量音频格式,而自动...

    日语50音表 罗马字表 清音 浊音

    ### 日语50音表与罗马字表:清音、浊音详解 #### 日语50音表概览 日语50音图,又称五十音图或五十音,是日语语音的基础,包含了平假名(ひらがな)和片假名(カタカナ),以及对应的罗马字(Rōmaji)。它分为五段...

    一种基于Matlab的语音信号端点检测方法

    本文提出了一种结合短时平均幅度与短时平均过零率的双门限算法来实现语音信号的端点检测,并通过Matlab编程验证了该方法的有效性和准确性。 #### 引言 在语音信号处理领域,语音端点检测技术是一项关键的技术环节...

    yy.rar_voiced energy_浊音、清音_清 浊音_清音_短时过零率

    提取短时能量,短时过零率,清音/浊音自相关函数

    vad.rar_vad_vad matlab_浊音、清音_清音浊音_过零率

    端点检测算法实现,使用短时能量和过零率区分浊音或清音,从而识别语音的开始与结束。

    日文输入法日文输入法日文输入法

    6. 促音:表示短促的音,输入时有两类方法:一是输入后一个音的双重,如小っ(suttu);二是直接输入ltu或xtu。 7. 小假名:如小ぁ、ぃ、ぅ、ぇ、ぉ,输入时需在字母前加"x"或"l",如la对应ぁ,li对应ぃ。 8. 特殊...

Global site tag (gtag.js) - Google Analytics