今天来聊聊前端,其实这个专题刚开始写起来的时候,是想和大家分享数据可视化的一些知识的。这也是图表君名头的由来,后来慢慢就写进了些前端的东西,再后来,因为图表君工作的转换,现在更多的做一些后台的工作,那么讨论的问题就更杂了,现在看起来算是我的一些工作和学习心得和感受吧。
好了,说了这么多。今天聊点什么呢?聊聊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这个的技术栈,我觉得理解的更加的清楚了,当然这仅仅是我的一点点小小的思考,欢迎大家一起讨论拍砖,后边逐步的和大家分享我的心得体会。