今天来聊聊前端,其实这个专题刚开始写起来的时候,是想和大家分享数据可视化的一些知识的。这也是图表君名头的由来,后来慢慢就写进了些前端的东西,再后来,因为图表君工作的转换,现在更多的做一些后台的工作,那么讨论的问题就更杂了,现在看起来算是我的一些工作和学习心得和感受吧。
好了,说了这么多。今天聊点什么呢?聊聊React。其实这也并不是什么新东西了。2014年,图表君就知道有这么个东西,听着几位业界大牛聊,说这东西有多么多么的好,当时也不是特别的理解,跟了一遍官方的Tutorial,也没有特别的感受。应该是因为当时前端的经验特别的浅(虽然现在也不敢说深,捂脸),没有什么特别的感受吧。
Angular的痛
后来做了几个项目,使用的是Angular,刚开始觉得双向数据绑定好牛逼啊,好好用,好好用。但是随着项目逐渐的一点点变大,感觉$scope上的东西是越来越多,各个controller里各有各的$scope,再加上$scope是继承的,当项目一复杂就越来越难以管理和控制。还有自己要封装一个组件在angular里得用directive吧,好吧看看directive的文档你就得晕了,link,compile,controller都是什么鬼。好了,今天不是吐槽angular的时间,但是angular得设计和使用的确是太复杂了。
前端到底是在干什么
好了,让我们先暂时跳出框架的讨论,来思考一下,前端的工作到底是干什么的?其实可以简单的说就是将数据到View的一个映射上,也就是说无论什么框架解决的基本问题就是讲数据展示到View上, 然后将讲用户的操作最后再反应到数据的变化上来。
Data --> Whatever FrameWork --> View
Data <-- Whatever FrameWork <-- View
再想清楚这个问题之后,React是怎么做的呢?React并不是一个完整的前端框架,只是一个专注于渲染View的library,在看了React的文档之后,我们会发现他的api是很简单的。一个典型的react的组件
class ShoppingList extends React.Component {
render() {
return (
<div className="shopping-list">
<h1>Shopping List for {this.props.name}</h1>
<ul>
<li>Instagram</li>
<li>WhatsApp</li>
<li>Oculus</li>
</ul>
</div>
);
}
}
// Example usage: <ShoppingList name="Mark" />
即使你没有React的经验,看这样的代码也不会有什么特别的问题。好了,今天图表君不打算安利React,并不想写一个hello world出来。这样的文章太多,看看React的官方例子会比图表君写的好很多。那么今天说什么呢?
React Thinking - 状态机
看这部分之前,图表君强烈建议你可以看一看React的官方tutorialtutorial,很好的一个例子,也不长。一个小时就能看完,自己上手写一写,感受会更深。好了现在假设你看完了这个tutorial有什么感觉?
图表君的最大的感受是,最后将State,function都定义到了Game的这个Root级别的Component上了,再把所有的数据和function都传进自己的子Component里,需要的地方直接调用就好了。这样就使得我们上边说把Data的操作逻辑都被提出来,并集中在一起了,一下就清晰了,明确了。App管理从此变得一下简单了。反复品味这样的设计,忽然有个东西,进入了我的思维里。这东西不就是一个有限状态机呀。
有限状态机
有限状态机是个十分有用的模型,可以用来模拟世界上大部分的事物,其有三个特征:
- 状态总数(state)是有限的。
- 任一时刻,只处在一种状态之中。
- 某种条件下,会从一种状态转变(transition)到另一种状态。
我们再来看看例子中的代码
class Game extends React.Component {
constructor(){
super();
this.state={
history:[{
squares: Array(9).fill(null)
}],
stepNumber: 0,
xIsNext: true
}
}
handleClick(i){
const history = this.state.history;
const stepNumber = this.state.stepNumber
const current = history[history.length - 1];
const squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext? 'X':'O';
this.setState(
{
history: history.concat([{
squares: squares
}]),
stepNumber: stepNumber + 1,
xIsNext: !this.state.xIsNext,
}
)
}
jumpTo(step){
const newHistory = this.state.history.slice(0,step+1)
console.log(newHistory);
this.setState({
history: newHistory,
stepNumber:step,
xIsNext: (step % 2) ? false: true,
})
}
render() {
const history = this.state.history;
const current = history[this.state.stepNumber];
const winner = calculateWinner(current.squares);
let status;
if(winner){
status = 'Winner Is :' + winner;
}else{
status = 'Next player: ' + (this.state.xIsNext ? 'X':'O');
}
const moves = history.map((step,move) => {
const desc = move ? 'Move #' + move : 'Game Start';
return(
<li key={move}>
<a href="#" onClick={() => this.jumpTo(move)}>{desc}</a>
</li>
)
});
return (
<div className="game">
<div className="game-board">
<Board squares={current.squares} onClick={(i) => this.handleClick(i)} />
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol>
</div>
</div>
);
}
}
构造方法,constructor - 定义了APP的初始状态。
handleClick - 定义了在棋盘里点击事件后的APP状态的变化。
jumpTo - 定义点击历史记录中某一步后APP的状态变化。
render- 描述如何在View上来展示当前的状态。
这样精巧的设计,facebook果然聚集了当今世界一流的工程师。然后我看了阮一峰的这篇介绍有限状态机的文章,看到这段代码。
var menu = {
// 当前状态
currentState: 'hide',
// 绑定事件
initialize: function() {
var self = this;
self.on("hover", self.transition);
},
// 状态转换
transition: function(event){
switch(this.currentState) {
case "hide":
this.currentState = 'show';
doSomething();
break;
case "show":
this.currentState = 'hide';
doSomething();
break;
default:
console.log('Invalid State!');
break;
}
}
};
有没有似曾相识的感觉,Redux里是不是就是这么干的?然后再想想Redux,到底干了一件什么事?帮我们做了这样的一个状态机啊,我们开发者只要定义Action,Reducer,他把我们的APP组织成了一个状态机。
从这样的角度再来看React,Redux这个的技术栈,我觉得理解的更加的清楚了,当然这仅仅是我的一点点小小的思考,欢迎大家一起讨论拍砖,后边逐步的和大家分享我的心得体会。