facebook的react的框架提出了一个基于唯一状态来渲染前端组件的想法,什么是唯一状态,采用唯一状态渲染到底有什么好处。希望大家看到这篇文章以后不用任何框架也可以写出基于唯一状态渲染的前端组件。
基于唯一状态的组件的开发模式就是组件内部永远只存在一份数据来表示组件的状态,并且更新组件时只只使用这一份数据。
这种开发模式的好处,主要体现在以下两个方面
- 减少事件与Dom元素的联系
- 便于保存和恢复组件的状态
减少事件与Dom元素的联系
我们先来看一段传统开发页面交互逻辑时写的代码:
<span id="click_count">0</span>
<input id="color">
<button id="btn">递增</button>
<script type="text/javascript">
function $(id){
return document.getElementById(id);
}
$('color').onkeyup = function(){
var countDom = $('click_count');
countDom.style.backgroundColor = this.value;
}
$('btn').onclick = function(){
var countDom = $('click_count');
var oldClickCount = parseInt(countDom.innerHTML);
var newClickCount = oldClickCount+1;
countDom.innerHTML = newClickCount;
}
</script>
如果我们采用基于唯一状态的组件开发模式以后的代码
<span id="click_count">0</span>
<input id="color">
<button id="btn">递增</button>
<script type="text/javascript">
function $(id){
return document.getElementById(id);
}
//这里就是组件的唯一状态
var state = {
count:0,
color:''
};
function render(){
var countDom = $('click_count');
countDom.style.backgroundColor = state.color;
countDom.innerHTML = state.count;
}
$('color').onkeyup = function(){
state.color = this.value;
render();
}
$('btn').onclick = function(){
state.count++;
render();
}
</script>
通过这个比较简单的事例我们可以看出代码已经简洁了很多。但是这样写到底有什么好处呢,我们通过图形的方式来比较下
第一种方式的图例
第二种方式的图例
通过这个简单的事例,感觉好处不是太明显;但是我们在平常的业务大部分时候要比这复杂的多,会有很多的Event和很多的Element,我们如果采用第一种方式的写法,图例如下
但是如果我们采用第二种方式的写法,图例如下
假如我们有M个Event,有N个Element,如果采用第一种方式的写法,一共会有(M*N)中连接;如果我们采用第二种方式的写法,在Event这里会有M个state的连接,从state到Element会有N个链接,一共的会有(M+N)个链接;这样写是不是简化了很多。
便于记录和恢复组件的状态
我们在开发复杂表单应用,或者操作状态比较多的场景,页面刷新时需要把当前的状态保存到localStorate,服务器端或者文件中,都需要把当前页面作为一个唯一状态保存起来。
上面的这样一个界面,当我们与页面交互时,点击添加,上一条,下一条这些按钮时,所需要做的就是更新这个唯一状态,保存当前状态,重新更新红色的区域,再次刷新页面时还会恢复原来的状态,像下面的代码这样
更新状态
$('add').onclick = function(){
state.count++;
state.items.push('item'+state.count);
state.pos=state.count;
saveState();
render();
}
$('prev').onclick = function(){
if(state.pos==1) return;
state.pos--;
saveState();
render();
}
$('next').onclick = function(){
if(state.pos == state.count) return;
state.pos++;
saveState();
render();
}
保存状态
把state序列化为字符串后,保存到localStorage中
function saveState(){
localStorage.setItem('state',JSON.stringify(state));
}
渲染组件
获取最新的state,拼装html字符串,然后使用innerHTML重绘整个组件
function render(){
state = JSON.parse(localStorage.getItem('state'))||state;
var items = '';
var itemData = state.items;
for (var i = 0,l=itemData.length; i < l; i++) {
var bg = '';
var oneItem = itemData[i];
if(state.pos==i+1){
bg = 'style="background-color:red"';
}
items+='<li '+bg+'>'+oneItem+'</li>';
};
var html = '<span>列表数'+state.count+'</span>'+
'<ul>'+
items+
'</ul>'+
'<span>当前条目的位置'+state.pos+'</span>';
$('container').innerHTML = html;
}
采用innerHTML重绘组件,每次交互时都会重新渲染整块区域[没有更新的部分也会重新渲染],使得页面的reflow,repaint的次数急剧增加,这样就会造成的页面你的性能下降。
有没有办法解决这个问题呢?答案当然是有的,下篇文章中我们会重点分析解决这个问题。