[react-control-center tutorial 1] cc.startup
注:本教程針對的有一定react基礎知識的使用者,如無任何react只是瞭解或者開發經驗,可以先通過
create-react-app
快速跑起來一個應用並結合官網的知識介紹,再來閱讀此文, 對於react開發者可以執行起來quick-start專案做更深的瞭解quick-start demo: github.com/fantasticso…
startup,讓我們把cc啟動起來
- cc的啟動非常容易,且對第三方包依賴極少,目前僅僅依賴了
co
和uuid
,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
一個模組一定包含state
,reducer
、init
、computed
是可選項,根據使用者的實際情況考慮是否配置
- 非模組化模式啟動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
模組配置reducer
、init
、computed
/** 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類
、和模組
的關係會如下圖,大家可以先做簡單瞭解,後面再回顧此圖會理解更深
C_C welcom to cc world
quick-start demo: github.com/fantasticso…