一. UI层的松耦合
1. 将JavaScript从CSS中抽离
在IE8和早版本的浏览器有一个特性,CSS表达式:1
2
3#box{
width: expression(document.body.offsetWidth + "px");
}
浏览器会以高频率重复计算CSS表达式,严重影响性能,且很难维护,所以要避免使用CSS表达式;
值得庆幸的是IE9不再支持CSS表达式了。
2. 将CSS从JavaScript中抽离
可以同过脚本修改样式,最常见的是在js中修改DOM元素的style属性:1
2
3element.style.color = "red";
element.style.left = "10px";
element.style.top = "20px";
这样的方法带来的最大的问题就是可维护性问题,所以最佳的方法是操作CSS的className:1
2
3
4
5
6
7
8
9
10
11
12//css:
.default{
color:red;
left:10px;
top:10px;
}
//js:
//原生方法:
element.className += " default";
//jQuery:
$(element).addClass("default");
由于CSS的className可以成为CSS和JavaScript之间的通信桥梁,在页面中JavaScript可以随意添加或者删除元素的className。而className所定义的样式在CSS中,CSS中的样式是可以随时修改的,但是不需要更新JavaScript代码,实现了CSS的松耦合。
3. 将JavaScript从HTML中抽离
将脚本嵌入到HTML中运行:1
<button onclick="doSomething()">click me</button>
这种方法在之前是很流行的,但却是两个UI层(JavaScript和HTML)的深耦合;
缺点是:第一,在点击按钮的时候doSomething函数必须存在;第二,可维护性低。
最佳的做法是JavaScript代码包含在外部文件中,在页面中通过<script>标签来引用。
4. 将HTML从JavaScript中抽离
在JavaScript中可以通过HTML的innerHTML属性赋值:1
2var div = document.getElementById('test');
div.innerHTML = "<h3>Error></h3><p>Invalid email address.</p>";
这样做的缺点是:第一,它增加了跟踪文本和结构性问题的复杂度;第二,可维护性低。
降低HTML和JavaScript之间的耦合的方法:
1)从服务器加载:
将模板放置于远程服务器,使用XMLHttpRequest对象来获取外部标签。这种方法会对单页面应用带来更多的便捷。
例如,点击一个按钮弹出一个对话框:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function 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('dialog-holder');
div.innerHTML = xhr.responseText;
oncomplete();
} else {
//处理错误
}
}
}
2)简单客户端模板:
客户端模板是一些带“插槽”的标签片段,这些“插槽”会被JavaScript程序替换为数据以保证模板的完整可用。1
2
3
4
5
6
7
8
9
10
11<li><a href="%s">%s</a></li>
// %s为占位符,这个位置的文本会被程序替换;
function sprintf( text ){
var i =1, args = arguments;
return text.replace( /%s/g, function(){
return ( i < args.length ) ? args[i+1] : "";
});
}
// 用法
var result = sprintf( templateText, "/item/4", "Fourth item" );
通常我们将模板定义在其他标签之间,直接存放在HTML中,这样就可以被JavaScript读取,用以下两种方法之一即可做到:
方法一: 是在HTML注释中包含模板文本1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<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>
function addItem( url, text ){
var mylist = document.getElementById("mylist"),
templateText = mylist.firstChild.nodeValue,
result = sprintf( template, url, text );
div.innerHTML = result;
mylist.insertAduacentHTML( "beforeend", result );
}
// 用法
addItem( "/item/4", "fourth item");
方法二: 是使用一个带有自定义type属性的<script>元素1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<script type="text/x-my-template" id="list-item">
<li><a href="%s">%s</a></li>
</script>
function addItem( url, text ){
var mylist = document.getElementById("mylist"),
script = document.getElementById('list-item'),
templateText = script.text,
result = sprintf( template, url, text ),
div = document.createElement("div");
div.innerHTML = result.replace( /^\s*/, "");
mylist.appendChild( div.firstChild );
}
// 用法
addItem( "/item/4", "fourth item");
3)复杂客户端模板:
例如handlebars提供的解决方案:
handlebars是专为浏览器JavaScript设计的完整的客户端模板系统;
首先必须将Handlebars类库引入到页面,这个类库会创建一个名为Handlebars的全局变量,用来将模板文本编译为一个函数。
1 | <script type="text/x-handlebars-template" id="list-item"> |
Handlebars 模板还支持一些简单的逻辑和循环。
[参考资料]:
编写可维护的JavaScript,Nicholas C. Zakas 著,李晶 郭凯 张散集 译, Copyright 2012 Nicholas Zakas,978-7-115-31008-8