如何在前端編碼時(shí)實(shí)現(xiàn)人肉雙向編譯
如何在前端編碼時(shí)實(shí)現(xiàn)人肉雙向編譯
React+flux是目前最火的前端解決方案之一,但flux槽點(diǎn)頗多,例如store比較混亂,使用比較繁瑣等,于是出現(xiàn)了很多第三方的基于flux優(yōu)化的架構(gòu)。
有人統(tǒng)計(jì)了目前主流的flux實(shí)現(xiàn)方案,感興趣的可以看這里:Which Flux implementation should I use?
其中redux是目前github上star最多的一個(gè)方案,該方案完全獨(dú)立于react,意味著這套理念可以作為架構(gòu)層應(yīng)用于其他的組件化方案。同時(shí)官方也提供了react-redux庫(kù),幫助開發(fā)者直接使用react+redux快速開發(fā)。
個(gè)人理解它的主要特性體現(xiàn)在以下幾點(diǎn):
- 
    
強(qiáng)制使用一個(gè)全局的
store,store只提供了幾個(gè)簡(jiǎn)單的api(實(shí)際上應(yīng)該是4個(gè)),如subscribe/dispatch(訂閱、發(fā)布),getState,replaceReducer。 - 
    
store負(fù)責(zé)維護(hù)一個(gè)唯一的叫做state樹的對(duì)象,其中state存儲(chǔ)了應(yīng)用需要用到的所有數(shù)據(jù)。 - 
    
store和頂層組件使用connect方法綁定,并賦給props一個(gè)dispatch方法,可以直接在組件內(nèi)部this.props.dispatch(action)。 簡(jiǎn)單一點(diǎn)說(shuō),就是去掉了flux中組件和store的unbind/bind環(huán)節(jié)。當(dāng)state變化時(shí),自動(dòng)更新components,不需要手動(dòng)操作。 - 
    
提供了
applyMiddleware方法用于異步的action,并且提供了加入中間件的能力,例如打印日志追蹤應(yīng)用的所有狀態(tài)變化。 - 
    
對(duì)全局的數(shù)據(jù)
state的操作,由多個(gè)reducer完成。每個(gè)reducer都是一個(gè)純函數(shù),接收兩個(gè)參數(shù)state和action,返回處理后的state。這點(diǎn)類似管道的操作。 
接下來(lái)我們可以回答標(biāo)題的問(wèn)題了,即:如何在前端編碼時(shí)實(shí)現(xiàn)人肉雙向編(zi)譯(can)。
其實(shí)就是使用coffee來(lái)編寫react+redux應(yīng)用。
我們來(lái)寫個(gè)簡(jiǎn)單的hello world玩玩。
view部分
這部分和redux/flux無(wú)關(guān),純粹react的實(shí)現(xiàn),使用jsx的話,render部分的代碼大概長(zhǎng)這樣:
- render:function(){
 - return (
 - <div>
 - <div class="timer">定時(shí)器:{interval}</div>
 - <div>{title}</div>
 - <input ref="input"><button>click it.</button>
 - </div>
 - )
 - }
 
那如何使用coffee寫這段代碼呢? 我們需要先將jsx編譯這類似這樣的js代碼,請(qǐng)注意是用大腦編譯:
- render:function(){
 - return React.createElement('div',null,
 - React.createElement('div',{className:'timer'},'定時(shí)器'+this.props.interval),
 - React.createElement('div',null,this.props.title),
 - React.createElement('input',{ref:'input'}),
 - React.createElement('button',null,'click it.')
 - );
 - }
 
然后將js代碼逆向編譯為coffee。
這里我們可以用$代替React.createElement簡(jiǎn)化代碼(終于可以用jQuery的坑位了),得益于coffee的語(yǔ)法,借助React.DOM可以用一種更簡(jiǎn)單的方式實(shí)現(xiàn):
{div,input,button,span,h1,h2,h3} = React.DOM
這里就不單獨(dú)放render部分,直接看完整代碼:
- {Component,PropTypes} = React = require 'react'
 - $ = React.createElement
 - {div,input,button} = React.DOM
 - class App extends Component
 - clickHandle:->
 - dom = this.refs.input.getDOMNode()
 - this.props.actions.change(dom.value)
 - dom.value = ''
 - render:->
 - {title,interval} = this.props
 - div className:'timer',
 - div null,'定時(shí)器:' + interval
 - div null,title
 - input ref:'input'
 - button onClick:@clickHandle.bind(this),'click it.'
 - App.propTypes =
 - title: PropTypes.string
 - actions: PropTypes.object
 - interval: PropTypes.number
 - module.exports = App
 
如果你能看到并看懂這段coffee,并在大腦里自動(dòng)編譯成js代碼再到j(luò)sx代碼,恭喜你。
連接store
這個(gè)環(huán)節(jié)的作用,主要是實(shí)現(xiàn)view層和store層的綁定,當(dāng)store數(shù)據(jù)變化時(shí),可自動(dòng)更新view。
這里需要使用redux提供的createStore方法創(chuàng)建一個(gè)store,該方法接受2個(gè)參數(shù),reducer和初始的state(應(yīng)用初始數(shù)據(jù))。
store.coffee的代碼如下:
- {createStore} = require 'redux'
 - reducers = require './reducers' # reducer
 - state = require './state' # 應(yīng)用初始數(shù)據(jù)
 - module.exports = createStore reducers,state
 
然后我們?cè)趹?yīng)用的入口將store和App綁定,這里使用了redux官方提供的react-redux庫(kù)。
- {Provider,connect} = require 'react-redux'
 - store = require './store'
 - $ = React.createElement
 - mapState = (state)->
 - state
 - rootComponent = $ Provider,store:store,->
 - $ connect(mapState)(App)
 - React.render rootComponent,document.body
 
可能有人會(huì)問(wèn),mapState和Provider是什么鬼?
mapState提供了一個(gè)類似選擇器的效果,當(dāng)一個(gè)應(yīng)用很龐大時(shí),可以選擇將state的某一部分?jǐn)?shù)據(jù)連接到該組件。我們這里用不著,直接返回state自身。
Provider是一個(gè)特殊處理過(guò)的react component,官方文檔是這樣描述的:
- This makes our store instance available to the components below.
 - (Internally, this is done via React undocumented “context” feature,
 - but it’s not exposed directly in the API so don’t worry about it.)
 
所以,放心的用就好了。
connect方法用于連接state和App,之后即可在App組件內(nèi)部使用this.props.dispatch()方法了。
添加action和reducer
***我們添加一個(gè)按鈕點(diǎn)擊的事件和定時(shí)器,用于觸發(fā)action,并編寫對(duì)應(yīng)的reducer處理數(shù)據(jù)。
在前面的App內(nèi)部已經(jīng)添加了this.props.actions.change(dom.value),這里看下action.coffee的代碼:
- module.exports =
 - change:(title)->
 - type:'change'
 - title: title
 - timer:(interval)->
 - type:'timer'
 - interval:interval
 
再看reducer.coffee
- module.exports = (state,action)->
 - switch action.type
 - when 'change'
 - Object.assign {},state,title:'hello ' + action.title
 - when 'timer'
 - Object.assign {},state,interval:action.interval
 - else
 - state
 
至此,代碼寫完了。
一些其他的東西
這里只介紹一個(gè)中間件的思想,其他的特性例如異步action,或者dispatch一個(gè)promise等原理基本類似:
- dispatch = store.dispatch
 - store.dispatch = (action)->
 - console.log action # 打印每一次action
 - dispatch.apply store,arguments
 
由于時(shí)間關(guān)系,redux的一些特性和設(shè)計(jì)原理沒(méi)有展現(xiàn)出來(lái),以后有時(shí)間再單獨(dú)講,完整的項(xiàng)目代碼,感興趣的同學(xué)可以看這里:請(qǐng)點(diǎn)我,或者直接找我聊。
項(xiàng)目用到了fis3作為構(gòu)建工具,使用fis3 release即可在本地查看效果。
















 
 
 











 
 
 
 