前言

在Web开发中,UI被定义为以下三个层的组合:

  1. HTML: 结构化需要显示的数据.
  2. CSS: 视觉上的渲染.
  3. JavaScript: 增强页面的交互性.

通常的UI布局是这样的, HTML层作为基础, CSS, JavaScript层在他的上面:

  事实是, 某些应用中Javascript并不依靠于css, 存在这样的应用: 只有JS和Html; 或者只有Html和Css. 这都是有可能的, 故Javascript和css的关系应该是平等的!例如: 尽管JavaScript和Css之间存在某些相互作用(如动态改变元素的className等.), JavaScript也能消除和CSS之间的依赖关系而独立运作.

  HTML, CSS, JavaScript之间的关系经常被定义得非常的紧密(紧密耦合), 以至于带来的后果是: 改变一个层不得不改变另外的一个或者两个层; 比如说: 你想修改一段HTML代码(改一个类名字), 发现不仅需要到CSS里面去修改还需要去JS文件中寻找是否这个元素上绑定有操作事件! 因此在较大规模的web应用中, 或者是团队开发过程中这种布局存在很大的问题.

松耦合

  很多设计模式设计的初衷都是为了解决以上三层之间存在的紧密耦合关系, 如果两个模块紧密耦合, 意味着修改一个必然依赖于另一个模块的改变! 比如: 在你的网站中有一个error的类名, 当你有一天想把它换成warning的时候, 由于HTML和CSS是紧密耦合的, 所以你需要同时编辑HTML文件(改变所有类名叫error的元素)和CSS文件! 再往多了想, 当你面对几十个或者几百个模块的时候, 如此的修改是不能接受的!

  所以松耦合的目的就是: 当你改变一个单一模块的时候不必去修改别的任何模块! 松耦合适用于团队开发. 因为当多人贡献于一个项目的时候, 你希望的是一个开发者修改的某个代码部分不会影响到其它代码部分的开发者!

  有一点是需要注意的: 在一个系统中, 不存在完全没有耦合的两个模块! 在任何系统中, 任两个模块之间都会存在一定的信息共享. 我们的目的仅仅是保证一个模块的修改不会导致像基本布局一样带来的太多地方的改动.

  松耦合的UI很容易调试, 文本或者结构的问题可以直接定位到HTML层, 样式问题肯定出现在CSS层, 其它的像一些调试器上出现的error我们会直接去JS文件中寻找(代码的剥离在下面有介绍).

CSS中不要出现JS代码

CSS expressions: 在IE8及早期版本中, 允许在CSS中直接插入JS代码.

.box {width: expression(document.body.offsetWidth + "px");
}

JS代码被放在expression()函数里面, 接受任何的JS代码, CSS expression()每次都会被浏览器重新计算导致性能上的问题!

  抛开性能上的问题, 由于JS代码是直接嵌入到CSS的, 给维护带来了很大的问题! 比如说有时候出现了一个js错误, 你的第一直觉肯定是去js文件中寻找错误, 但是不幸的是等你找遍了所有的js文件后还是没能发现问题, 最后当你一气之下剥离掉所有js代码之后发现错误并没有消失....当你绝望的上下滚动鼠标时, 惊奇的发现CSS文件里面还有一段js代码, 最终出错的就是这里的时候, 你一定会气得砸掉电脑....

  IE9去掉了CSS expression(). 不过CSS3中添加了特殊的属性, 功能类似与expression, 但里面支持的仅仅是数学表达式:

/* 元素的居中 */
.box{width: 500px;height: 300px;margin-left: calc((100% - 500) / 2); /* +,-前后必须有空格才能被解释, *, /后可以没有空格 */
}

去掉JS中的CSS代码

使用最频繁的就是利用DOM元素的style属性, style属性是一个包含了可读可写的css属性的对象, 如修改文本的颜色:

// Bad
element.style.color = 'blue';

或者是批量修改样式:

// Bad
element.style.color = "red";
element.style.left = "10px";
element.style.top = "100px";
element.style.visibility = "visible";

甚至是通过cssText属性一次性解决:

// Bad
element.style.cssText = "color: red; left: 10px; top: 100px; visibility: hidden";

这些修改样式的方法都会带来维护上的问题: 当页面的样式出现差异的时候肯定首先去css文件中查找, 会走很多的弯路!

最好的解决办法就是, 把所有的样式定义都放在css文件中, 当需要修改某个节点的样式的时候只需为它加上相应的className即可!

如下的css:

.reveal {color: red;left: 10px;top: 100px;visibility: visible;
}

用js为元素添加类名:

// Good - Native
element.className += " reveal";// Good - HTML5
element.classList.add("reveal");// Good - jQuery
$(element).addClass("reveal");

把className看作是CSS和JS之间的联系, JS能很容易的通过改变元素的类名就改变了元素的样式. 当需要修改样式效果的时候也只需修改对应的css文件即可!

剥离出HTML中的JS代码

对于一些事件响应, 我们通常喜欢把js嵌入到html文件中. 通过为元素添加一个属性(如: onclick):

<!-- Bad -->
<button onclick="doSomething();" id="action-btn">Click Me</button>

尽管这样在执行效果上没什么问题, 事实是HTML层和JS层紧密耦合, 带来了几个问题:

  1. 必须保证在button点击以前, doSomething()函数是存在的! 某些情况下, doSomething()或许是在html文档后面才加载的! 因此当你点击button的时候就会发生一个js error, 相应地按钮什么也不做!
  2. 再一个就是维护问题, 试想一下当你想改变函数名的时候, 你还需要去改变html中所有调用这个函数的地方为他们修改名字(或许你可以通过查找替换轻松的实现, 但要是你忘记了替换呢?)!

因此, 在html文件中尽量不要通过on属性来绑定一个事件, 而是通过js的方法来给一个元素绑定监听事件(考虑到兼容性).

function addListener(target, type, handler) {if (target.addEventListener) {target.addEventListener(type, handler, false);// for IE} else if (target.attachEvent) {target.attachEvent("on" + type, handler);// 对于DOM Level 0(这只是不同的版本, 就像html4和html5一样, 目前基本是DOM Level 3)} else {target["on" + type] = handler;}
}

如给一个id为btn的元素绑定一个事件:

function doSomething() {
// code
}var btn = document.getElementById("btn");// js方法
addListener(btn, "click", doSomething);// jQuery
$("#btn").on("click", doSomething);

还有另一种方法是在html文件中通过script标签嵌入js代码:

<!-- Bad -->
<script>doSomething();
</script>

总之: 最好让js文件和html文件分离! 分离的好处是调试方便.

不要在JS中插入html代码

使用最多的就是在js中使用innerHTML属性来为某个元素动态添加内容.

// Bad
var div = document.getElementById("my-div");
div.innerHTML = "<h3>Error</h3><p>Invalid e-mail address.</p>"; 

在js中嵌入html串有几个方面的问题:

  1. 就像上面常提到的, 出错了找错不方便, 或许会走很多不必要的弯路.
  2. 维护问题.

由于很多的网站应用都是动态的, JS通常用于改变网页的UI, 因此用js来插入或者操作网页中的节点是非常有必要的,  要实现html和js间的松耦合有一下几种方法.

从服务器上动态或者页面信息

对于单个页面的动态加载非常的方便:

function loadDialog(name, oncomplete) {var xhr = new XMLHttpRequest();xhr.open("get", "/js/dialog/" + name, true);
  xhr.onreadystatechange = function() {    if (xhr.readyState == 4 && xhr.status == 200) {var div = document.getElementById("dlg-holder");div.innerHTML = xhr.responseText;oncomplete();} else {// handle error}  };xhr.send(null);
}

jQuery的异步加载方式:

// jQuery
function loadDialog(name, oncomplete) {$("#dlg-holder").load("/js/dialog/" + name, oncomplete);
}

当你想插入大量的html的时候异步加载是不错的选择, 但是从性能的角度讲, 把大量没用的节点放在内存或者是DOM中是值得考虑的! 对于最少化标签量的原则, 可以考虑模板引擎.

简单的模板

例如有如下的HTML模板:

<!--%s是需要动态改变的参数, 这里我们通过给js函数传递参数实现-->
<div id="template"><li><a href="%s">%s</a></li>
</div>

然后我们针对这一个模板, 写一个js函数来动态替换标签a中的内容(主要是链接地址和链接的名字):

/*@param {Array} text 这里接受的参数形式是一个数组, 第一个是需要替换的元素后面的参数是顺序替换的内容@return {string} text 返回替换完成后的文本, 再把这文本注入到DOM元素中 
*/
function sprintf( text ) {var i = 1,args = arguments,len = args.length;return text.replace(/%s/g, function() {return ( i < len ) ? args[ i++ ] : '';});
} 

接下来是在DOM中根据实际参数替换模板中的内容:

window.onload = function() {var template= document.getElementById("template"),     templateText = template.innerHTML,result = sprintf( templateText, "/item/4", "Fourth item" );
  template.innerHTML = result; // 注入修改后的内容
}

实际修改后HTML内容为:

<div id="template"><li><a href="/item/4">Fourth item</a></li>
</div> 

  上面这种模板带来的问题是: 当同一地方也定义了相同的模板以后, 导致JS会两个模板一起渲染, 在js传参不确定的情况, 某些模板将会解释出错. 达不到预期的效果, 所以理想的情况是: 把模板放在一个相对固定的地方, 能通过js精确的渲染这个模板

以下是一个利用HTML注释(也是DOM节点, 能通过js操作)作为模板的例子:

<ul id="mylist"><!--<li id="item%s"><a href="%s">%s</a></li>--><li><a href="/item/1">First item</a></li><li><a href="/item/2">Second item</a></li><li><a href="/item/3">Third item</a></li>
</ul>

接下来为js添加一个方法来处理它:

function addItem( item, url, text ) {var mylist = document.getElementById("mylist"),templateText = mylist.firstChild.nodeValue, // 获取注释节点result = sprintf( templateText, item, url, text );/*More About insertAdjacentHTML: https://developer.mozilla.org/zh-CN/docs/DOM/element.insertAdjacentHTML */mylist.insertAdjacentHTML("beforeend", result);
}

当你需要向ul#mylist里动态添加一个项目的时候, 只需要传递变化部分的参数即可!

window.onload = function() {addItem( "4", "/item/4", "Fourth item" );
}

利用script标签的type属性创建模板

type属性: script的type属性通常是"text/javascript", 但是你完全可以自定义自己的type属性(目的就是要让浏览器不知道这个type是做什么的):

<script type="text/x-my-template" id="list-item"><li><a href="%s">%s</a></li>
</script>

然后修改一下前面的addItem()方法:

function addItem( item, url, text) {var doc = document, // 做好缓存mylist= doc.getElementById("mylist"),script= doc.getElementById("list-item"),templateText= script.text, // 取回模板中的文本result= sprintf(templateText, item, url, text), // 根据参数渲染模板div= doc.createElement("div");div.innerHTML = result.replace(/^\s*/, "");mylist.appendChild(div.firstChild); // 动态插入
}

注意: 为什么在向div插入结果的时候需要截取掉开始的空格?

  因为在我们的script模板中, 真正的模板文件在第二行, 当用text取得模板内容的时候空格也被带进来了, 因此插入div的时候会产生一个空的文本节点(什么都没有, 但确实是一个文本节点), 这样再向mylist中插入项目的时候将会插入一个空的文本节点, 结果是什么也没有! 因此在插入div以前, 要确保一开始的内容就是我们想要的而非空格.

跟上面同样的使用方法:

window.onload = function() {addItem( "4", "/item/4", "Fourth item" );
}

复杂的模板

目前网上有很多现成的模板库, 大家可以根据自己的需求集成到自己的应用中.如HandleBars, 以及它的教程.

转载于:https://www.cnblogs.com/Poised-flw/archive/2013/05/05/3061665.html

JavaScript----UI的松耦合相关推荐

  1. 案例学习BlazeDS+Spring之八InSync06“松耦合”UI同步事件通知

    InSync06:增加"松耦合"UI同步事件通知 一.运行DEMO: 1.运行程序:http://localhost:8400/spring-flex-testdrive/insy ...

  2. MEF实现设计上的“松耦合”(一)

    1.什么是MEF 先来看msdn上面的解释:MEF(Managed Extensibility Framework)是一个用于创建可扩展的轻型应用程序的库. 应用程序开发人员可利用该库发现并使用扩展, ...

  3. IoC控制反转设计原则——实现松耦合

    IoC控制反转设计原则--实现松耦合 1.IoC设计原则 1.1.控制程序流 1.2.控制依赖对象的创建 2.从典型的n层体系架构来理解IoC IOC是一种设计原则(虽然,有很多人将它当成是一种设计模 ...

  4. C#进阶系列——MEF实现设计上的“松耦合”(一)

    前言:最近去了趟外地出差,介绍推广小组开发的框架类产品.推广对象是本部门在项目上面的同事--1到2年工作经验的初级程序员.在给他们介绍框架时发现很多框架设计层面的知识他们都没有接触过,甚至没听说过,这 ...

  5. 与应用程序松耦合的报表开发组织

    在软件项目中,报表模块经历了一个和其他模块从紧耦合到松耦合的发展过程. 早期的报表,和软件项目的其他功能模块一样,都是由同样的开发工具和语言编写的.从PB.Delphi到asp.php,再到目前的C# ...

  6. Webix 1.5发布:一个强大的JavaScript UI组件库

    日前,XB公司发布了新版的Webix 1.5,较之前的版本,Webix 1.5增加了一些新的特性,并进行了一些改进.Webix是一个跨浏览器的JavaScript UI组件库,可以构建跨平台的HTML ...

  7. 松耦合和紧耦合的架构设计、性能对比

    在最近的一次大数据技术讨论会上,有一家公司的技术高管谈到松耦合和紧耦合的性能表现的话题.正好Laxcus大数据管理系统的设计,从0.x.1.x到2.x版本,也经历了从紧耦合到松耦合的发展过程.做为亲历 ...

  8. Spring松耦合的个人理解和代码实例

    Spring松耦合的个人理解和代码实例 理解Spring的松耦合概念,那么我们先来看看一个不使用Sring的实例代码 先看一下整个测试项目案例的结构 正常方式 创建一个接口,这个接口指定车辆的行驶速度 ...

  9. Spring松耦合的实现

    HelloShiyanlou 与松耦合的实现 一.实验介绍 1.1 实验内容 本节实验将带你入门简单的 Maven 项目创建和如何实现松耦合. 1.2 实验知识点 Maven 介绍 Spring 松耦 ...

最新文章

  1. Virtex-6器件的时钟资源、混合模式时钟管理器(MMCM)
  2. T-Sql(七)用户权限操作(grant)
  3. html 模板引擎 热部署,springboot系列四、配置模板引擎、配置热部署
  4. NEC使用C+L EDFA在超过1.1万公里的海底光缆中首次实现50Tb传输
  5. C++在构造函数中调用构造函数
  6. Spring Cloud开发实践 - 04 - Docker部署
  7. [Java 安全]加密算法
  8. C#操作快捷方式(获取快捷方式属性、创建快捷方式)
  9. C# datagridview、datagrid、GridControl增加行号
  10. JAVA中vector是否存在数据_如何找出std :: vector中是否存在项目?
  11. linux bash环境,Win10系统怎样启用Linux Bash环境
  12. Springboot在线电影系统实战开发
  13. Spark--安装和配置遇到的所有问题
  14. 【JZOJ4762】【NOIP2016提高A组模拟9.7】千帆渡
  15. 数字图像处理(冈萨雷斯)学习 第二章 数字图像基础
  16. linux环境使用c语言获取系统时间,并拼接成字符串
  17. 装系统:主分区、扩展分区、逻辑分区,引导(启动)分区、系统分区、活动分区
  18. Linux查看mpp数据库地址,Linux环境搭建DM8 MPP双节点集群
  19. 浙商银行计算机专业笔试考什么,浙商银行笔试题目汇总
  20. 发布本人整理的面试问题大全,为准备找工作的同行们尽一份力 希望大家多补充或回答

热门文章

  1. IDea项目名修改后,后面有个中括号原来的项目名
  2. php数组元素转字符串,php怎么把数组转成字符串?
  3. Shader Forge实现聚光灯舞台效果
  4. windows下玩linux--MetroPipe VPM
  5. 计算机二级ms office必背知识点,计算机二级MS Office 函数必背考点资料
  6. 十 iOS 之UIVIew动画 和 核心动画的区别
  7. 一个优秀的管理者,一看专业能力,二看管理技巧,三看……
  8. 我的世界服务器自定义怪物怎么用,我的世界自定义怪物属性的方法
  9. 【Spring】万字详解使用注解来存取对象
  10. 教育大数据总体解决方案(7)