[react-control-center tutorial 1] 啟動cc

正楷發表於2019-01-16

[react-control-center tutorial 1] cc.startup

注:本教程針對的有一定react基礎知識的使用者,如無任何react只是瞭解或者開發經驗,可以先通過create-react-app快速跑起來一個應用並結合官網的知識介紹,再來閱讀此文, 對於react開發者可以執行起來quick-start專案做更深的瞭解

quick-start demo: github.com/fantasticso…

startup,讓我們把cc啟動起來

  • cc的啟動非常容易,且對第三方包依賴極少,目前僅僅依賴了couuid,react15和16均能夠使用

cc和redux最大的不同之一就是,redux需要在你的頂層App元件之外包裹一個Provider元件,用於全域性注入和管理redux的上下文context,對於cc來說只需要在你定義好cc的啟動指令碼,然後在你的程式碼入口檔案的第一行裡引用改指令碼,就可以完成cc的啟動工作了,所以使用cc並不會對你現有的程式碼造成任何入侵,你可以漸進式的在已有專案裡區域性使用cc,嘗試cc的有趣且強大的功能

後續會放出and-design-pro的cc版本,改動的程式碼不超過100行,就完美將其狀態管理框架redux遷移到cc

  • cc支援兩種啟動方式

cc支援以模組化的方式和非模組的方式啟動起來,如果以非模組的方式啟動,cc的store只會有兩個內建的模組存在,即$$global$$default模組,如果以模組化的方式啟動,則需要使用者顯示的劃分好模組並作為配置引數交個cc啟動讓cc按照使用者的規劃理念啟動起來。 啟動起來之後:

  • cc將cc的store繫結到了window.sss下。
  • cc將cc的頂層api繫結到window.cc下。
  • cc將cc的上下文管理物件繫結到window.ccc下和window.CC_CONTEXT下
    使用者可以在console裡通過sss可以檢視當前狀態樹的最新狀態,通過cc直接呼叫cc提供給開發者的頂層api與各個cc元件產生有趣的互動,這是cc讓使用者能夠體會到cc的強大和有趣的入口之一。
/**-----------------[引入cc啟動指令碼,讓整個專案能夠使用cc的所有介面]--------*/
/**  code in index.js */
import './startup-cc';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

/**-----------------[不寫入任何引數,直接執行cc啟動]-----------------------*/
/**  code in  startup-cc.js */
import cc from 'react-control-center';
cc.startup();
複製程式碼
  • 以上示例裡,我們呼叫了cc.startup(),啟動了cc,在正式介紹cc的各種啟動方式和區別之前,我們先來了解一下startup函式的簽名介紹

cc.startup(startupOption?:StartupOption);

  • @param StartupOption.isModuleMode 是否以模組化方式啟動cc,預設是false,cc強烈建議使用者設定此項為true,方便使用者定義更多的模組
  • @param StartupOption.store 為cc配置store
  • @param StartupOption.reducer 為cc配置reducer,reducer是一堆按模組劃分的函式集合,可以是普通函式、生成器函式、async函式,每一cc例項上都可以通過this.$$dispatch派發action物件呼叫reducer裡的函式,修改響應模組裡的值
  • @param StartupOption.computed 為cc配置computed,這裡配置的是模組級別的computed,在cc例項裡通過this.$$moduleComputed取到計算後的值
  • @param StartupOption.init 為cc配置init,通常是需要從後端獲取後再次賦值給store才需要配置此項
  • @param StartupOption.sharedToGlobalMapping 為cc配置sharedToGlobalMapping,使用者需要把一些模組的值對映到$$global模組時,需要配置此項
  • @param StartupOption.moduleSingleClass 為cc配置moduleSingleClass,標記哪些模組只能註冊生成一個ccClass,預設cc允許一個模組註冊生成多個cc類moduleSingleClass是一個物件,key為moduleName,值為布林值,true就表示這個模組只允許註冊一個cc類
    大家可以先對這些這些引數有個印象,閱讀後面的講解再逐步理解透這裡面每一個引數的具體作用
  • 不管是模組話啟動還是非模組啟動,對於cc來說都存在這模組的概念

非模組化啟動,cc會內建兩個模組$$default$$global
一個模組一定包含statereducerinitcomputed是可選項,根據使用者的實際情況考慮是否配置

[react-control-center tutorial 1] 啟動cc

  • 非模組化模式啟動cc,直接啟動

非模組方式通常適用於小規模的應用,狀態劃分簡單,邊界清晰,智慧元件較少,開發時對狀態的修改都比較清晰,業務上這些元件的領域分類不是很明顯,例如基於react寫一個表單提交(當然這只是舉例,通常一個表單就不需要寫成一個單頁面應用了,但是如果是寫一個生成通用表單的平臺,為了方便維護和擴充套件,享受現代js開發ui帶來的友好體驗,開發者通常還是會選擇一個ui庫和狀態管理庫)

/**-----------------[引入cc啟動指令碼,讓整個專案能夠使用cc的所有介面]--------*/
/**  code in index.js */
import './startup-cc';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

/**-----------------[不寫入任何引數,直接執行cc啟動]-----------------------*/
/**  code in  startup-cc.js */
import cc from 'react-control-center';

// 預設引數啟動cc,cc會預設生成兩個模組$$default、$$global
cc.startup();
// 在console裡輸入sss, 可以檢視到狀態樹形如: {$$default:{}, $$global:{}}

複製程式碼
  • 非模組化模式啟動cc,配置$$global模組和$$default模組啟動

通過上面我們知道以非模組化方式啟動cc時,cc會自動建立2個模組$$default$$global,但是cc也允許使用者顯示的申明兩個模組的值, 為StartupOption.store配置一個初始化的狀態樹
當使用者顯示的把store的key寫為global或者default任意之一時,cc把store的key當做模組名,將global或者default對應的物件賦值給cc對應global模組或者default模組的狀態樹,其他多餘的key會被cc警告使用者使用模組化方式啟動才能識別,但是cc只會警告使用者,然後忽略這key的值啟動起來

/**-----------------[規劃模組引數啟動cc]----------------------------------*/
/**  code in  startup-cc.js */
import cc from 'react-control-center';

// 通過上面我們知道以非模組化方式啟動cc時,cc會自動建立2個模組$$default、$$global
// 但是cc也允許使用者顯示的申明兩個模組的值, StartupOption.store 為cc初始化一個狀態樹
// 當使用者顯示的把store的key寫為$$global或者$default任意之一時,cc把store的key當做
// 模組名,將$$global或者$$default對應的物件賦值給cc對應$$global模組或者$$default
// 模組的狀態樹,其他多餘的key會被cc警告使用者使用模組化方式啟動才能識別,但是cc只會警告
// 使用者,然後忽略這key的值啟動起來

cc.startup({
  store:{
    $$global:{
      themeColor:'pink',
      module:'pink',
    },
    $$default:{
      foo:'foo',
      bar:'bar'
    },
    thisModuleWillBeenIgnored:{// 這對於cc來說是一個無效的模組宣告
      foo:'foo',
    }
  }
});


// 如果store直接賦值一個普通物件,不包含任何名字為$$default、$$global的key,cc預設
// 將這個物件處理為$$default模組的物件
cc.startup({
  store:{
    themeColor:'pink',
    module:'pink',
  }
});
複製程式碼
  • 非模組化模式啟動cc,配置$$global模組和$$default模組啟動,為$$global模組配置reducerinitcomputed
/**  code in  startup-cc.js */
import cc from 'react-control-center';
cc.startup({
  store:{
    $$global:{
      themeColor:'',
      module:'pink',
      bonus:0,
      recommendedLink:'',
    },
    $$default:{
      foo:'foo',
      bar:'bar'
    }
  },
  reducer:{
    $$global:{
      changeThemeColor:function* ({payload:{userId, color}}){
        // 修改主題色,使用者獲得積分
        const bonus = yield api.changeThemeColor({userId, color});
        return {bonus};
      },
      recoverOriginalThemeColor:async function({payload:{userId}, dispatch}){
        // 恢復最初的主題色,各一個推薦連結
        const recommendedLink = yield api.recoverOriginalThemeColor({userId});
        dispatch({reducerModule:'whatever',type:'trackUser', payload:'wow wow'});
        return {recommendedLink};
      }
    },
    // 注意此處申明瞭whaterver當做模組值,但是whaterver並沒有在store裡宣告過,cc是允許使用者這樣做的,因為cc認為recuder可以有自己的模組劃分定義,實際上當使用者在cc例項裡呼叫dispatch時,
    // 會形如this.$$dispatch({reducerModule:'whatever',type:'trackUser',payload:'cool'})這樣,
    // cc會找到對應reducer模組whatever的type為trackUser的函式去執行資料修改邏輯,
    // this.$$dispatch裡不指定reducerModule,預設會找Action物件裡指定的module當做reducerModule,
    // Action對物件裡沒有指定module,會把當前cc例項所屬的module當做reducerModule
    whaterver:{
      trackUser: function*(){
        // ... ...
      }
    }
  },
  init:{
    $$global:setState=>{
      api.getInitThemeColor(themeColor=>{
        setState({themeColor});
      })
    }
  },
  computed:{
    $$global:{
      themeColor(themeColor){// 當themeColor發生變化時,計算新的值,cc例項裡的this.$$globalComputed.themeColor可以取到
        return {spanBorder:`2px solid ${themeColor}`, pBorder:`8px solid ${themeColor}`};
      }
    },
    $default:{
      foo(foo){// 反轉foo字串, cc例項裡this.$$moduleComputed.foo可以取到改計算值
        return foo.split('').reverse().join('');
      }
    }
  }
});
複製程式碼
  • 模組化模式啟動cc,

需要在StartOption顯示的設定isModuleMode為true,其他方式和上面的非模組的方式一樣,唯一不同的是cc允許你使用其他名字作為模組名了,還允許你自定義StartOption.sharedToGlobalMapping將某些模組裡的某些key起個別名對映到$$global模組裡.
cc提供sharedToGlobalMapping是因為在cc世界裡,一個cc類只能觀察註冊的所屬模組的狀態變化(即一個cc類直屬於一個模組),但是所有cc類都能夠觀察global模組的轉態變化,當cc類需要觀察其他模組的某些key的狀態變化時,需要那個模組先將它的這些key對映到$$global裡,然後cc類觀察對映到$$global裡的這些key,就達到了一個cc類可以觀察多個模組變化的目的

/**  code in  startup-cc.js */
import cc from 'react-control-center';
cc.startup({
  store:{
    $$global:{
      themeColor:'',
      module:'pink',
      bonus:0,
      recommendedLink:'',
    },
    $$default:{
      foo:'foo',
      bar:'bar'
    },
    foo:{
      f1:'f1',
      f2:'f2',
    },
    bar:{
      f1:'f1',
      f2:'f2',
    }
  },
  //其他配置略 .......

  // 對映時注意命名衝突
  sharedToGlobalMapping:{
    // 以下配置將foo模組的f1、f2欄位對映到$$global裡,因為$$global沒有名字為f1、f2的欄位,這裡就不再起別名了
    foo:{
      f1:'f1',
      f2:'f2',
    },
    // 以下配置將bar模組的f1、f2欄位對映到$$global裡分別為bf1、bf2,因為$$global模組裡已經存在了f1,f2,所以這裡起了別名
    bar:{
      f1:'bf1',
      f2:'bf2',
    }
  }
}
複製程式碼
  • 以上對startup的解釋相信不少讀者一定還有疑問,因為提前提到了一些後面還會進一步詳細解釋的名詞概念,
  • 比如配合講解reducer時提到了cc例項的$$dispatch,
  • 配合講解sharedToGlobalMapping時,提到了觀察多個模組狀態變化,cc除了使用sharedToGlobalMapping達到觀察多個模組狀態變化的目的,還提供更強大的方式,註冊為cc類時候宣告stateToPropMapping,可以不用把目標觀察模組的key對映到$$global就能夠觀察其他模組的狀態變化,後面會做詳解
  • 提到了一個模組可以註冊多個cc類,整個cc世界裡,cc類react類、和模組的關係會如下圖,大家可以先做簡單瞭解,後面再回顧此圖會理解更深

[react-control-center tutorial 1] 啟動cc

C_C welcom to cc world

quick-start demo: github.com/fantasticso…

相關文章