天天看點

如何在前端編碼時實作人肉雙向編譯

如何在前端編碼時實作人肉雙向編譯

react+flux是目前最火的前端解決方案之一,但flux槽點頗多,例如store比較混亂,使用比較繁瑣等,于是出現了很多第三方的基于flux優化的架構。

如何在前端編碼時實作人肉雙向編譯

其中<code>redux</code>是目前<code>github</code>上<code>star</code>最多的一個方案,該方案完全獨立于<code>react</code>,意味着這套理念可以作為架構層應用于其他的元件化方案。同時官方也提供了<code>react-redux</code>庫,幫助開發者直接使用<code>react+redux</code>快速開發。

個人了解它的主要特性展現在以下幾點:

強制使用一個全局的<code>store</code>,<code>store</code>隻提供了幾個簡單的api(實際上應該是4個),如<code>subscribe</code>/<code>dispatch</code>(訂閱、釋出),<code>getstate</code>,<code>replacereducer</code>。

<code>store</code>負責維護一個唯一的叫做<code>state</code>樹的對象,其中<code>state</code>存儲了應用需要用到的所有資料。

<code>store</code>和頂層元件使用<code>connect</code>方法綁定,并賦給<code>props</code>一個<code>dispatch</code>方法,可以直接在元件内部<code>this.props.dispatch(action)</code>。 簡單一點說,就是去掉了<code>flux</code>中元件和<code>store</code>的<code>unbind/bind</code>環節。當<code>state</code>變化時,自動更新<code>components</code>,不需要手動操作。

提供了<code>applymiddleware</code>方法用于異步的<code>action</code>,并且提供了加入中間件的能力,例如列印日志追蹤應用的所有狀态變化。

對全局的資料<code>state</code>的操作,由多個<code>reducer</code>完成。每個<code>reducer</code>都是一個純函數,接收兩個參數<code>state</code>和<code>action</code>,傳回處理後的<code>state</code>。這點類似管道的操作。

接下來我們可以回答标題的問題了,即:如何在前端編碼時實作人肉雙向編(zi)譯(can)。

<code>其實就是使用coffee來編寫react+redux應用。</code>

我們來寫個簡單的hello world玩玩。

view部分

這部分和redux/flux無關,純粹react的實作,使用jsx的話,render部分的代碼大概長這樣:<code></code>

render:function(){ 

    return ( 

        &lt;div&gt; 

            &lt;div class="timer"&gt;定時器:{interval}&lt;/div&gt; 

            &lt;div&gt;{title}&lt;/div&gt; 

            &lt;input ref="input"&gt;&lt;button&gt;click it.&lt;/button&gt; 

        &lt;/div&gt; 

    ) 

那如何使用coffee寫這段代碼呢? 我們需要先将jsx編譯這類似這樣的js代碼,請注意是用大腦編譯:<code></code>

    return react.createelement('div',null, 

        react.createelement('div',{classname:'timer'},'定時器'+this.props.interval), 

        react.createelement('div',null,this.props.title), 

        react.createelement('input',{ref:'input'}), 

        react.createelement('button',null,'click it.') 

    ); 

然後将js代碼逆向編譯為coffee。

這裡我們可以用<code>$</code>代替<code>react.createelement</code>簡化代碼(終于可以用jquery的坑位了),得益于<code>coffee</code>的文法,借助<code>react.dom</code>可以用一種更簡單的方式實作:

<code>{div,input,button,span,h1,h2,h3} = react.dom</code>

這裡就不單獨放render部分,直接看完整代碼:<code></code>

{component,proptypes} = react = require 'react' 

$ = react.createelement 

{div,input,button} = react.dom 

class app extends component 

    clickhandle:-&gt; 

        dom = this.refs.input.getdomnode() 

        this.props.actions.change(dom.value) 

        dom.value = '' 

    render:-&gt; 

        {title,interval} = this.props 

        div classname:'timer', 

            div null,'定時器:' + 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,并在大腦裡自動編譯成js代碼再到jsx代碼,恭喜你。

連接配接store

這個環節的作用,主要是實作view層和store層的綁定,當store資料變化時,可自動更新view。

這裡需要使用redux提供的createstore方法建立一個store,該方法接受2個參數,reducer和初始的state(應用初始資料)。

store.coffee的代碼如下:<code></code>

{createstore} = require 'redux' 

reducers = require './reducers' # reducer 

state = require './state' # 應用初始資料 

module.exports = createstore reducers,state 

然後我們在應用的入口将store和app綁定,這裡使用了redux官方提供的react-redux庫。<code></code>

{provider,connect} = require 'react-redux' 

store = require './store' 

mapstate = (state)-&gt; 

    state 

rootcomponent = $ provider,store:store,-&gt; 

    $ connect(mapstate)(app) 

react.render rootcomponent,document.body 

可能有人會問,mapstate和provider是什麼鬼?

mapstate提供了一個類似選擇器的效果,當一個應用很龐大時,可以選擇将<code>state</code>的某一部分資料連接配接到該元件。我們這裡用不着,直接傳回<code>state</code>自身。

provider是一個特殊處理過的react component,官方文檔是這樣描述的:<code></code>

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元件内部使用this.props.dispatch()方法了。

添加action和reducer

最後我們添加一個按鈕點選的事件和定時器,用于觸發action,并編寫對應的reducer處理資料。

在前面的<code>app</code>内部已經添加了<code>this.props.actions.change(dom.value)</code>,這裡看下action.coffee的代碼:<code></code>

module.exports = 

    change:(title)-&gt; 

        type:'change' 

        title: title 

    timer:(interval)-&gt; 

        type:'timer' 

        interval:interval 

再看reducer.coffee<code></code>

module.exports = (state,action)-&gt; 

    switch action.type 

        when 'change' 

            object.assign {},state,title:'hello ' + action.title 

        when 'timer' 

            object.assign {},state,interval:action.interval 

        else 

            state 

至此,代碼寫完了。

一些其他的東西

這裡隻介紹一個中間件的思想,其他的特性例如異步action,或者dispatch一個promise等原理基本類似:

dispatch = store.dispatch 

store.dispatch = (action)-&gt; 

    console.log action # 列印每一次action 

    dispatch.apply store,arguments 

作者:yisbug

來源:51cto

繼續閱讀