模板的发明是编程史上的一大里程碑,让我们摆脱了烦锁且易出错的字符串拼接,维护性大大提高。

都在JSP,ASP时代,人们已经学会使用include等语句,将多个页面片断拼接成一个页面。
此外,为了将数据库中的数据或业务中用到的变量输出到页面,我们需要将页面某个地方标记一下,将变量塞到里面去。
最后,出于方便循环输出一组数据,就需要将each语句从HTML里撕开一道口子,加上其他什么if语句,页面上其实变撕裂成两部分
一种是与后端语言相近的逻辑部分,一个是够为纯净的HTML部分,到最后,模板引擎就发展出来。

在jQuery王朝的后期,业务逻辑不断往前搬,前端模板也发明出来了。这些模板我统称为静态模板或字符串模板,特征是模板是放在
一个script标签或textarea标签里。静态模板的好处是统一管理,我们从script标签等抽取内容时,它是原汁原味,没有被窜改。
缺点是破坏原有的结构。MVVM时代,knockout, ember等率先发明动态模板,或叫DOM模块,特点是通过在元素节点上标记一些特殊属性,注明此元素里面会输出什么内容
或此元素的子元素是作用循环体要循环多少次,当然if等输出不输出很小儿科。缺点是,需要对文档的整体或某一区域进行扫描,这里耗时比静态模板多上几倍,并且定界符(用于输出变量的标记)可能离奇失踪。但这也没什么大不了,现在流行的两种定界符形式≈lt&;, %>{{ }}在IE10+或W3C浏览器活得好好的,IE6-9,我们只要避开大于小于号就行了。
此外动态模板与静态模板最大的不同在于,它是没有编译函数,而是通过扫描文档,根据节点上的定界符与绑定属性实现循环输出,填空等功能。

我们看一下avalon是怎么做的。大致分两块,定义VM,添加绑定。VM是我们操作的主体,绑定是将页面变成模板的关键。

VM的定义

avalon.define("test",function(vm){
vm.aaa = "司徒正美"
})

avalon.define是用来定义VM,第一个参数为VM的ID名,这是用于在页面圈定作用域的范围,对应的绑定属性是ms-controller。因为一个页面可能有多人负责,就存在多个VM了,而VM相当于一个数据据,它们都用于不同的区域,这里就需要用ID来区分了。

添加绑定,我们随便往body一塞就行了

<body>{{aaa}}</body>

这里的{{ }}是定界符,放在文本节点里。我们可以用过avalon.config({interpolate: ["{?", "?}"]})来设置定界符。

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"> </script>
<script>
avalon.define("test", function(vm){
vm.aaa = "司徒正美"
})
</script>
</head>
<body ms-controller="test">
<h3>{{aaa}}</h3>
{{aaa}}
</body>
</html>

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"> </script>

<script>
avalon.define("test", function(vm){
vm.aaa = "司徒正美"
})
</script>
</head>
<body ms-controller="test">
<h3>{{aaa}}</h3>
{{aaa}}
</body>
</html>

运行代码

但世界上没有这么简单的页面,比如我们要输出一个列表,是不是要这样干呢?

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
avalon.define("test", function(vm) {
vm.a = "2014预言:阿里死磕港交所"
vm.b = "马云进军游戏背后:恐失势电商"
vm.c = "支付宝信息泄露揭大公司管理困境"
vm.d = "盘点2013:智能手机开启的新场景"
})
</script>
</head>
<body ms-controller="test">
<ol>
<li>{{a}}</li>
<li>{{b}}</li>
<li>{{c}}</li>
<li>{{d}}</li>
</ol>
</body>
</html>

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
avalon.define("test", function(vm) {
vm.a = "2014预言:阿里死磕港交所"
vm.b = "马云进军游戏背后:恐失势电商"
vm.c = "支付宝信息泄露揭大公司管理困境"
vm.d = "盘点2013:智能手机开启的新场景"
})
</script>
</head>
<body ms-controller="test">
<ol>
<li>{{a}}</li>
<li>{{b}}</li>
<li>{{c}}</li>
<li>{{d}}</li>
</ol>
</body>
</html>

运行代码

当然不行,这要定义多少个变量啊!这时就需用到循环绑定,ms-repeat!

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
avalon.define("test", function(vm) {
vm.array = ["2014预言:阿里死磕港交所",
"马云进军游戏背后:恐失势电商",
"支付宝信息泄露揭大公司管理困境", "盘点2013:智能手机开启的新场景"]
})
</script>
</head>
<body ms-controller="test">
<ol>
<li ms-repeat="array">{{el}}</li>
</ol>
</body>
</html>

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
avalon.define("test", function(vm) {
vm.array = ["2014预言:阿里死磕港交所",
"马云进军游戏背后:恐失势电商",
"支付宝信息泄露揭大公司管理困境", "盘点2013:智能手机开启的新场景"]
})
</script>
</head>
<body ms-controller="test">
<ol>
<li ms-repeat="array">{{el}}</li>
</ol>
</body>
</html>

运行代码

ms-repeat相当于ms-each-el,后面的-el是可配置可省略。比如改成ms-repeat-elem,那么对应的位置要改成{{elem}}。

我们还可以输出2维数组

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
avalon.define("test", function(vm) {
vm.array = [[1, 2], [4, 5], [7, 8]]
})
</script>
</head>
<body ms-controller="test">
<table width="80%" border="1">
<tr ms-repeat-item="array">
<td ms-repeat="item">{{el}}</td>
</tr>
</table>
</body>
</html>

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
avalon.define("test", function(vm) {
vm.array = [[1, 2], [4, 5], [7, 8]]
})
</script>
</head>
<body ms-controller="test">
<table width="80%" border="1">
<tr ms-repeat-item="array">
<td ms-repeat="item">{{el}}</td>
</tr>
</table>
</body>
</html>

运行代码

输出对象数组

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
var model = avalon.define("test", function(vm) {
vm.array = [{name: 111}, {name: 222}, {name: 333}]
})
</script> </head>
<body ms-controller="test">
<ul>
<li ms-repeat-me="array">{{me.name}}</li>
</ul>
</body>
</html>

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
var model = avalon.define("test", function(vm) {
vm.array = [{name: 111}, {name: 222}, {name: 333}]
})
</script>

</head>
<body ms-controller="test">
<ul>
<li ms-repeat-me="array">{{me.name}}</li>
</ul>
</body>
</html>

运行代码

现在大家算是对ms-repeat算是有一个大体的了解吧。那么我们学一点高级的。avalon.define会返回一个VM对象,我们通过操作它就能实现页面的操作数据即操作DOM!!!,比如vmodel.array.push。另,我们想输出每个元素对应的索引值,可以使用$index这个变量。

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
var model = avalon.define("test", function(vm) {
vm.array = [{name: 111}, {name: 222}, {name: 333}]
})
var array = model.array
setInterval(function(){
array.push({name: Math.random().toString(32).substr(4,14)})
if(array.length > 10){
array.shift()
}
},500)
</script> </head>
<body ms-controller="test">
<ul>
<li ms-repeat="array">{{$index}}--{{el.name}}</li>
</ul>
</body>
</html>

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
var model = avalon.define("test", function(vm) {
vm.array = [{name: 111}, {name: 222}, {name: 333}]
})
var array = model.array
setInterval(function(){
array.push({name: Math.random().toString(32).substr(4,14)})
if(array.length > 10){
array.shift()
}
},500)
</script>

</head>
<body ms-controller="test">
<ul>
<li ms-repeat="array">{{$index}}--{{el.name}}</li>
</ul>
</body>
</html>

运行代码

之所以 能有这样神奇的效果,是因为avalon会将VM中的数组转换为监控数组,它拥有以下方法:

push, shift, unshift, pop, slice, splice, remove, removeAt, removeAll, clear, ensure, sort, reverse, set

现在我们跳前一步,学一下ms-on-*绑定,实现一个更复杂的效果。ms-on-*的*对应一个事件名,属性值为VM中一个函数名,与元素onkeypress, onclick一样,它的第一个参数默认是事件对象,this指向元素节点,不同的是我们已经对IE6-8下的事件对象做了兼容处理。

<!DOCTYPE HTML>
<html>
<head>
<title>ms-repeat</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
avalon.define("test", function(vm) {
vm.array = ["1", "2", "3", "4"]
"push,unshift,remove,ensure".replace(/\w+/g, function(method) {
vm[method] = function(e) {
if (this.value && e.which == 13) {//this为input元素
vm.array[method](this.value);
this.value = "";
}
}
}) vm.removeAt = function(e) {
if (isFinite(this.value) && e.which == 13) {//this为input元素
var a = ~~this.value
vm.array.removeAt(a)
this.value = "";
}
}
"pop,shift,sort,reverse".replace(/\w+/g, function(method) {
vm[method] = function(e) {
vm.array[method]();
}
})
}); </script>
</head>
<body ms-controller="test">
<p>监控数组拥有以下方法,我们可以操作它们就能同步对应的区域</p>
<blockquote>
push, shift, unshift, pop, slice, splice, remove, removeAt, removeAll, clear, ensure, sort, reverse, set
</blockquote>
<ul>
<li ms-repeat="array">数组的第{{$index+1}}个元素为{{el}}</li>
</ul>
<p>对数组进行push操作,并回车<input ms-on-keypress="push"></p>
<p>对数组进行unshift操作,并回车<input ms-on-keypress="unshift"></p>
<p>对数组进行ensure操作,并回车<input ms-on-keypress="ensure"><br/>
(只有数组不存在此元素才push进去)</p>
<p>对数组进行remove操作,并回车<input ms-on-keypress="remove"></p>
<p>对数组进行removeAt操作,并回车<input ms-on-keypress="removeAt"></p>
<p><button type="button" ms-on-click="sort">对数组进行sort操作</button></p>
<p><button type="button" ms-on-click="reverse">对数组进行reverse操作</button></p>
<p><button type="button" ms-on-click="shift">对数组进行shift操作</button></p>
<p><button type="button" ms-on-click="pop">对数组进行pop操作</button></p>
<p>当前数组的长度为<span style="color:red">{{array.size()}}</span>,注意 我们无法修改数组length的固有行为,因此它无法同步视图,需要用size方法。</p>
</body>
</html>

<!DOCTYPE HTML>
<html>
<head>
<title>ms-repeat</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
avalon.define("test", function(vm) {
vm.array = ["1", "2", "3", "4"]
"push,unshift,remove,ensure".replace(/\w+/g, function(method) {
vm[method] = function(e) {
if (this.value && e.which == 13) {//this为input元素
vm.array[method](this.value);
this.value = "";
}
}
})

vm.removeAt = function(e) {
if (isFinite(this.value) && e.which == 13) {//this为input元素
var a = ~~this.value
vm.array.removeAt(a)
this.value = "";
}
}
"pop,shift,sort,reverse".replace(/\w+/g, function(method) {
vm[method] = function(e) {
vm.array[method]();
}
})
});

</script>
</head>
<body ms-controller="test">
<p>监控数组拥有以下方法,我们可以操作它们就能同步对应的区域</p>
<blockquote>
push, shift, unshift, pop, slice, splice, remove, removeAt, removeAll, clear, ensure, sort, reverse, set
</blockquote>
<ul>
<li ms-repeat="array">数组的第{{$index+1}}个元素为{{el}}</li>
</ul>
<p>对数组进行push操作,并回车<input ms-on-keypress="push"></p>
<p>对数组进行unshift操作,并回车<input ms-on-keypress="unshift"></p>
<p>对数组进行ensure操作,并回车<input ms-on-keypress="ensure"><br/>
(只有数组不存在此元素才push进去)</p>
<p>对数组进行remove操作,并回车<input ms-on-keypress="remove"></p>
<p>对数组进行removeAt操作,并回车<input ms-on-keypress="removeAt"></p>
<p><button type="button" ms-on-click="sort">对数组进行sort操作</button></p>
<p><button type="button" ms-on-click="reverse">对数组进行reverse操作</button></p>
<p><button type="button" ms-on-click="shift">对数组进行shift操作</button></p>
<p><button type="button" ms-on-click="pop">对数组进行pop操作</button></p>
<p>当前数组的长度为<span style="color:red">{{array.size()}}</span>,注意 我们无法修改数组length的固有行为,因此它无法同步视图,需要用size方法。</p>
</body>
</html>

运行代码

有了批量输出的ms-repeat及通过调用监控数组的方法就能实现对应节点的删除添加排序,那么实现grid简直易如反掌。

<!DOCTYPE HTML>
<html>
<head>
<title>ms-repeat</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
if (!Date.now) {
Date.now = function() {
return new Date - 0;
}
}
avalon.define('test', function(scope) {
scope.selected = "name"
scope.options = ["name", "size", "date"]
scope.trend = 1
scope.data = [
{name: "aaa", size: 213, date: Date.now() + 20},
{name: "bbb", size: 4576, date: new Date - 4},
{name: "ccc", size: 563, date: new Date - 7},
{name: "eee", size: 3713, date: 9 + Date.now()},
{name: "555", size: 389, date: Date.now() - 20}
];
scope.$watch("selected", function(v) {
var t = parseFloat(scope.trend)
scope.data.sort(function(a, b) {
var ret = a[v] > b[v] ? 1 : -1
return t * ret
})
})
scope.$watch("trend", function(t) {
var v = scope.selected, t = parseFloat(t)
scope.data.sort(function(a, b) {
var ret = a[v] > b[v] ? 1 : -1
return t * ret
})
})
}); </script>
</head>
<body ms-controller="test">
<p>
<select ms-duplex="selected">
<option ms-repeat="options">{{el}}</option>
</select>
<select ms-duplex="trend">
<option value="1">up</option>
<option value="-1">down</option>
</select>
</p>
<table width="500px" border="1">
<tbody >
<tr ms-repeat="data">
<td>{{el.name}}</td> <td>{{el.size}}</td> <td>{{el.date}}</td>
</tr>
</tbody>
</table>
</body>
</html>

<!DOCTYPE HTML>
<html>
<head>
<title>ms-repeat</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
if (!Date.now) {
Date.now = function() {
return new Date - 0;
}
}
avalon.define('test', function(scope) {
scope.selected = "name"
scope.options = ["name", "size", "date"]
scope.trend = 1
scope.data = [
{name: "aaa", size: 213, date: Date.now() + 20},
{name: "bbb", size: 4576, date: new Date - 4},
{name: "ccc", size: 563, date: new Date - 7},
{name: "eee", size: 3713, date: 9 + Date.now()},
{name: "555", size: 389, date: Date.now() - 20}
];
scope.$watch("selected", function(v) {
var t = parseFloat(scope.trend)
scope.data.sort(function(a, b) {
var ret = a[v] > b[v] ? 1 : -1
return t * ret
})
})
scope.$watch("trend", function(t) {
var v = scope.selected, t = parseFloat(t)
scope.data.sort(function(a, b) {
var ret = a[v] > b[v] ? 1 : -1
return t * ret
})
})
});

</script>
</head>
<body ms-controller="test">
<p>
<select ms-duplex="selected">
<option ms-repeat="options">{{el}}</option>
</select>
<select ms-duplex="trend">
<option value="1">up</option>
<option value="-1">down</option>
</select>
</p>
<table width="500px" border="1">
<tbody >
<tr ms-repeat="data">
<td>{{el.name}}</td> <td>{{el.size}}</td> <td>{{el.date}}</td>
</tr>
</tbody>
</table>
</body>
</html>

运行代码

这里用到了ms-duplex, $watch,大家可以到《入门教程》看看,都是很简单的东西。

接着我们再看看如何循环输出对象吧,它也是用ms-repeat,不过里面的变量为$key, $val。不用多言,看例子。

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
var model = avalon.define("test", function(vm) {
vm.object = {
grape: "葡萄",
coconut: "椰子",
pitaya: "火龙果",
orange: "橙子"
} }) </script> </head>
<body ms-controller="test">
<ul>
<li ms-repeat="object">{{$key}}--{{$val}}</li>
</ul>
</body>
</html>

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
var model = avalon.define("test", function(vm) {
vm.object = {
grape: "葡萄",
coconut: "椰子",
pitaya: "火龙果",
orange: "橙子"
}

})

</script>

</head>
<body ms-controller="test">
<ul>
<li ms-repeat="object">{{$key}}--{{$val}}</li>
</ul>
</body>
</html>

运行代码

好了,循环输出就到这里。我们最后看一下如何实现其他模板引擎的if语句。它的名字为ms-if,如果值为真就输出,否则不输出。

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
var model = avalon.define("test", function(vm) {
vm.toggle = true
vm.click = function(){
vm.toggle = !vm.toggle
}
vm.text = "捉迷藏" }) </script> </head>
<body ms-controller="test">
<p ms-if="toggle">{{text}} </p>
<button type="button" ms-on-click="click">点我{{toggle ? '隐藏' : "显示"}}</button>
</body>
</html>

<!DOCTYPE html>
<html>
<head>
<title>avalon</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="http://files.cnblogs.com/rubylouvre/avalon20130929.js"></script>
<script>
var model = avalon.define("test", function(vm) {
vm.toggle = true
vm.click = function(){
vm.toggle = !vm.toggle
}
vm.text = "捉迷藏"

})

</script>
</head>
<body ms-controller="test">
<p ms-if="toggle">{{text}} </p>
<button type="button" ms-on-click="click">点我{{toggle ? '隐藏' : "显示"}}</button>
</body>
</html>

运行代码

{{}}, ms-repeat, ms-if这就是动态模板相对静态模板的所有功能了,但由于动态模板在扫描之后,得到所有要处理的节点的引用,这也意味着,以后我们要做一小部分的更新,不用像静态模板那样大规模替换,而是细化到每一个元素节点,特性节点或文本节点。这就是所谓的“最小化刷新”技术。一般的,只有ms-if等少量绑定才影响到元素节点那一层面,更多的时候, 我们是在刷新特性节点的value值,文本节点的data值,这也意味着,我们的刷新不会引起reflow。加之,能得到元素节点本上,我们就可以轻松实现绑定事件,操作样式,修改属性等功能。这也是为什么大多数MVVM框架选择动态模板的缘故,jQuery原来可以做的,我们全部通过绑定属性或定界符在HTML里搞定。 这也意味着,我们实现了完美的分层架构,JS里面是纯粹的模型层(包括model与viewmodel),HTML里是学习成本与维护成本极低的视图层。这已经不是多了一个模板引擎这么简单的事,我们抢到了 一直以来属性于后端的禁脔——分层架构

迷你MVVM框架 avalonjs 沉思录 第3节 动态模板的更多相关文章

  1. 迷你MVVM框架 avalonjs 沉思录 第2节 DOM操作的三大问题

    jQuery之所以击败Prototype.js,是因为它自一开始就了解这三大问题,并提出完善的解决方案. 第一个问题,DOM什么时候可用.JS不像C那样有一个main函数,里面的逻辑不分主次.但JS是 ...

  2. 迷你MVVM框架 avalonjs 沉思录 第1节 土耳其开局

    #cnblogs_post_body p{ text-indent:2em; margin-top: 1em; } 正如一切传说的开端那样,有一远古巨神开天辟地,然后就是其他半神喧宾夺主.我们对最巨贡 ...

  3. 迷你MVVM框架 avalonjs 0.95发布

    迷你MVVM框架 avalonjs 0.95发布 本版本最主要的改进是ms-with 深层绑定的实现,至少,avalon1.0所有重要的feature已经开发完毕,之后就是小补小漏,性能优化了. ms ...

  4. 迷你MVVM框架 avalonjs 0.85发布

    迷你MVVM框架 avalonjs 0.85发布 本版本对循环绑定做了巨大改进,感谢@soom, @limodou, @ztz, @Gaubee 提供的大量测试文件. fix scanNodes, 在 ...

  5. 迷你MVVM框架 avalonjs 0.82发布

    迷你MVVM框架 avalonjs 0.82发布 本版本最大的改进是启用全新的parser. parser是用于干什么的?在视图中,我们通过绑定属性实现双向绑定,比如ms-text="fir ...

  6. 迷你MVVM框架 avalonjs 1.3.9发布

    本次升级,avalon改进了许多内部方法,大大提升性能,并且带来异步刷新视图的新功能. ms-html内部不再使用异步 head元素中的avalon元素加入ms-skip指令 重构计算属性,现在超级轻 ...

  7. 迷你MVVM框架 avalonjs 1.3.8发布

    avalon1.3.8主要是在ms-repeat. ms-each. ms-with等循环绑定上做重大性能优化,其次是对一些绑定了事件的指令添加了roolback,让其CG回收更顺畅. 重构ms-re ...

  8. 迷你MVVM框架 avalonjs 1.3.7发布

    又到每个月的15号了,现在avalon已经固定在每个月的15号发布新版本.这次发布又带来许多新特性,让大家写码更加轻松,借助于"操作数据即操作DOM"的核心理念与双向绑定机制,现在 ...

  9. 迷你MVVM框架 avalonjs 1.3.6发布

    本版本是一次重要的升级,考虑要介绍许多东西,也有许多东西对大家有用,也发到首页上来了. 本来是没有1.36的,先把基于静态收集依赖的1.4设计出来后,发现改动太多,为了平缓升级起见,才减少了一部分新特 ...

随机推荐

  1. NodeJS反序列化漏洞利用

    原文来自:http://www.4hou.com/web/13024.html node.js是一个服务器端的运行环境,封装了Google V8引擎,V8引擎执行JavaScript速度非常快,性能非 ...

  2. L192 Virgin Galactic Completes Test of Spaceship to Carry Tourists

    Virgin Galactic says its spacecraft designed to launch tourists into space completed an important te ...

  3. 最终还是迁移到github

    作为全球最大的程序员同性交友社区,github pages 吸引了我 为了有一个更好的博客的写作环境 将会把内容逐渐迁移到 github.io 地址 zongxiao.github.io 新的文章也会 ...

  4. Nginx 静态资源缓存配置

    示例 # Media: images, icons, video, audio, HTC location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|m ...

  5. springboot将项目打成war包

    1. 将项目的打包方式改为war包 <groupId>com.cc</groupId> <artifactId>aaaaaa</artifactId> ...

  6. streamsets origin 说明

    origin 是streamsets pipeline的soure 入口,只能应用一个origin 在pipeline中, 对于运行在不同执行模式的pipeline 可以应用不同的origin 独立模 ...

  7. MySQL的WHERE语句中BETWEEN与IN的用法和他们的区别

    MySQL BETWEEN 用法 not可以对between...and取反. 1.数值型 BETWEEN 运算符用于 WHERE 表达式中,选取介于两个值之间的数据范围.BETWEEN 同 AND ...

  8. staltStack安装配置

    http://www.cnblogs.com/kevingrace/p/5570290.html

  9. LCD RGB 控制技术讲解 — 时钟篇(上)

    时序图 下面是LCD RGB 控制的典型时序图  天啊,一下就上这玩意,怎么看??? 其实要解释上面的时序图,我们还需要了解一些LCD的显示过程.所以现在只是有个印象,稍后我们详细讲解. LCD显示流 ...

  10. 【详解】Linux的文件描述符fd与文件指针FILE*互相转换

    使用系统调用的时候用文件描述符(file descriptor,简称fd)的时候比较多,但是操作比较原始.C库函数在I/O上提供了一些方便的包装(比如格式化I/O.重定向),但是对细节的控制不够. 如 ...