小巧玲瓏的react框架(第二彈)正式命名--aomini

前戲

曾經我覺得已通過了看偶像劇的年紀,後來無心中看了泰版浪漫滿屋,而後控寄不住寄己的體內的洪荒之力,繼續追完了泰版的惡做劇之吻,--有顏+演技時刻在線(雖然仍是偶像劇狗血套路,不過不影響我被圈粉)。我突然發現,原來熱情還在,只是如今大多國內的流量小生小花們已經挑逗不到我了。看看國內如今的電視劇,生硬的臺詞,僵硬的表情,像一盆盆冷水一點點澆涼我對劇的熱情,有時候不由會想,難道沒人對本身的做品負責嗎?哦,你們並無進錯房間,這不是在聊電視劇,咱這仍是技術文,因此我就不繼續吐槽置評了,我曾經下定過決心,只要一天還在出做品,就要對本身的東西負點責任。情懷,是個好東西,得留着點。回到咱們的主題,咱這個小框架再不濟也是個框架,得起個名字先。我被這兩部泰劇圈粉主要是由於女主aom可愛又認真的戲,乾脆就叫aom了,並且我但願這是個方便任意玩耍,輕量迷你的小框架,因此,我給它起個名字叫作--aomini。你們不要被我帶跑偏了,我安利的不是這兩個泰劇,是介紹個人aomini,😄。
先回顧上一篇我只是用最簡單的代碼介紹了一下aomini的思路,那樣的破磚爛瓦不修整一番怎麼能用呢?好了,接下來我就帶着你們一塊兒來修整一番。vue

激情

若是你們不記得上一篇講的啥破磚爛瓦,能夠戳這裏回去看看。上一篇徹底是以糊塗流水帳的形式蓋的破瓦房,房屋結構啥的都是一股腦疊上去的,毫無結構可言,接下來咱們來將這個破房子分解一下。首先,底層結構跟上層結構得先拆開,底層結構屬於libs,上層結構就是業務邏輯,也就是咱們肉眼可見的那一層,入口文件,以及那幾個模塊文件確定都屬於業務邏輯了,至於store,Provider和connect函數確定屬於libs範疇了。拆解後文件結構以下所示,react


相信使用過react-redux的同窗對libs下的這三個名稱應該很熟悉了,不過咱們這裏跟react-redux中仍是有很大區別,雖然思路仍是相似的,閱讀過react-redux源碼的同窗可能會比較清楚一點,那接下來就對咱們的框架的實現思路進行深刻講解一下。雖然並無那麼複雜,可是爲了照顧不那麼熟悉的同窗,分析以前,咱們先搞清楚這三個東西的具體指責是什麼,其中Store主要能夠說是state的大總管;Provider能夠說是入口,store會從這個入口props的方式傳入,經過react context 上下文的方式進行全局定義,方便業務下的其餘組件獲取調用,這種方式就是爲了解決任意組件之間通訊非相關組件須要感知的問題;connect實際上是個HOC高階組件。好了,再讓咱們回顧一下咱們簡單的框架模型,store<=>UI,對比一下react原生的框架模型是這樣的state<=>UI,對比這兩個模型能夠得出一個簡單的Store的設計思路,store管理了state,而且當UI觸發Store中狀態更新時,由Store來調用組件setState進行模塊的UI更新。根據這麼個直白的思路設計的Store對象代碼以下,與以前出入不大:

let store = {
    //目標組件對象
    contexts:[],
    //state數據
    state:{
        m1Var:"111"
    },
    //註冊綁定組件與狀態數據,用於setState
    bindContext(_context){
        this.contexts.push(_context);
        return this.state;
    },
    unbindContext(_context){
        for(let i = 0; i < this.contexts.length; i++){
            if(this.contexts[i] === _context){
                this.contexts[i] = null;
                console.log(_context);
            }
        }
    },
    setState(bs_state,context){
        let newState = Object.assign(this.state,bs_state);
        console.log(".......",newState)
        // this.state = {};
        this.contexts.map((item)=>{
            if(!item) return;
            React.Component.prototype.setState.call(item,bs_state);
        })

    }
}複製代碼

store沒有作太大變化,只是完善了一下,組件裝載時,調用bindContext將組件的上下文關係添加到contexts列表,當數據中心狀態變化時,調用store的setState方法,該方法實質是將全部綁定的組件進行setState調用。如此,便簡單實現了狀態變化與UI的數據流關係。
每一個組件都須要接收store對象,所以就須要將store放入頂層父組件的context中,也就是Provider這個入口組件,該組件代碼以下:git

class Provider extends React.Component{
    getChildContext(){
        return {
            store:this.props.store
        }
    }
    constructor(props){
        super(props);
    }
    render(){
        return React.Children.only(this.props.children);
    }
}
Provider.childContextTypes = {
    store:PropTypes.object
}複製代碼

該組件與redux中同樣,子組件只接受單個元素。而後就是將props中傳入的store元素進行變量的全局化,固然這裏暫時實現比較粗暴,咱尚未作到像redux同樣過多考慮個性化擴展,但說白了redux實現核心也就是這麼個玩意兒。
接下來咱們再回過頭來觀察一下上一篇的組件代碼,每個模塊都有一堆樣板代碼,store.bindContext(this);尤爲以前只是單文件簡單的合併寫法,模塊拆成獨立文件後,store又是須要從上下文中引用的,所以樣板代碼至少又這些:
1,組件裝載的時候github

let store = context.store;
store.bindContext(this);複製代碼

2,組件卸載的時候須要移除被卸載的組件綁定關係web

componentWillUnmount(){
    this.store.unbindContext(this);
}複製代碼

每一個組件都要在邏輯中如此調用,須要封裝一下,何況,業務組件最好只關注業務點,這種非業務邏輯的樣板代碼不應出如今業務組件中,那這種通用邏輯調用的狀況下,咱們的高階組件就須要派上用場了。相似redux中的connect組件代碼以下:redux

let connect = function(wrappedCompo){
    class ConnectHoC extends React.Component{
        constructor(props,context){
            super();
            this.store = context.store;
            this.store.bindContext(this);
        }
        render(){
            return React.createElement(wrappedCompo,{store:this.store});
        }
        componentWillUnmount(){
            this.store.unbindContext(this);
        }
    }
    ConnectHoC.contextTypes = {
        store:PropTypes.object
    }

    return ConnectHoC;
}複製代碼

這個connect高階組件接收一個實際業務組件爲參數,返回一個包含該組件的新組件,並實現了store經過props的傳遞,實際業務組件獲取store直接從props便可,也許你會問store已經存在context中,直接去取就是了,爲啥還要經過props傳入,仍是同樣的道理,那樣的話業務組件會多一些獲取全局變量的樣板代碼,而且違背了業務組件只須要關注業務的原則。好了,如今這麼一來,咱們的業務組件只須要關注本身的業務點以及數據在哪裏獲取,無需過分關心其餘。
而且就跟react-redux中使用是同樣的,組件都使用connect進行包裝便可,僞代碼以下:bash

class Module extends React.Component{
    constructor(){
        super();
    }
    render(){

    }
}
let ModuleHoC = connect(Module);複製代碼

所以,Provider組件下的全部通過connect包裝過的組件裝載都會自動進行上下文綁定,並使用store集中管理數據狀態,當狀態改變,全部綁定的組件進行setState操做,即完成了UI的更新。架構

高潮

很明顯,這個設計思路就是來源於react-redux的設計思想,react-redux是個好東西,只是我在實踐中發覺,react自己其實已經至關好用了,只是有些地方好比狀態管理部分只用原生react的話,開發過程會變得十分不優雅,既然如此,那咱們或許只須要將react中不那麼優雅的東西適當摺疊修整一番,就能變得更好用了。react-redux中強調邏輯與視圖之間徹底分離,這是很好的思想,可是不管flux或者redux的設計初衷,其實都是爲了更合理地管理狀態而來的,react的設計本來就並未將UIrender與邏輯徹底割裂,並不是像vue中將template徹底進行了剝離。何況,邏輯與UI的分離並不是必須分到兩個不一樣的文件中去纔是分離,這樣反而從編碼層面上帶來很多的干擾,缺乏碼代碼的快感。
不過目前來講,aomini雖然實現了功能,可是絕對比不上react-redux在總體設計上的優雅。react-redux是基於redux的動做訂閱模式,使用了監聽者模式,每個模塊的設計都是根據單一職責的原則,UI中觸發state的更新必須經過派發一個action,經過監聽器進行監聽每個action,不過這裏的監聽器是弱化了的,並無針對每個action進行細分監聽,這裏你們須要注意,其實這個設計是很巧妙且又合理的,由於react是基於狀態變化的,而非咱們以前熟悉的命令式開發模式,一切都是圍繞總體的狀態,而非某個動做,所以不管action如何指示,最終只是轉化成總體狀態變化的讀取。reducers進行狀態計算,並使用connect組件的參數mapStateToProps函數做爲狀態選擇器,進行子狀態select,完美地解決了特定狀態的讀取,以上處理就優雅地完成了UI=>store=>action=>reducer=>UI這樣一個完整閉環。aomini上有flux和react-redux的影子,可是本質上來講,到目前爲止,aomini跟後二者並非同一種東西,flux是一套完整的web架構思想,react-redux是從flux發展而來的比較完善的開發框架,而aomini是從二者之中取出的部分思想,變得更加靈活,儘管不算是一個完整的框架,不過我從實際開發中感覺到,配合本來已經足夠完美的react,已經夠用了。app

過後--結語

框架上的東西沒有絕對的好與很差,殺雞用牛刀何嘗不可,只是這把刀不可過重,否則殺只雞比宰一頭牛還累,虧本買賣。一個小工程,總共業務幾句代碼,非得上一堆牛逼哄哄的框架,最後出來幾大m的代碼量,反而拖累頁面效率,這不就是得不償失。以上對aomini的核心代碼設計思路介紹了一下,目前只是一些核心骨幹代碼,後續接下來就是須要繼續完善其中的細節,代碼已上傳github,並實時進行更新,有興趣的同窗們就敬請關注了。但願跟你們一塊兒交流。
須要的同窗,能夠戳這裏簽出代碼:react-aomini
會繼續實時更新,歡迎各位拍磚!框架

相關文章
相關標籤/搜索