你的star將是我最大的精神鼓勵,歡迎star🥺🥺🥺javascript
concent是一個專爲react
提供狀態管理服務的框架,提煉現有各大框架的精華,以及社區公認的最佳實踐,經過良好的模塊設計,既保證react的最佳性能又容許用戶很是靈活的解耦UI邏輯與業務邏輯的關係,從總體上提升代碼的可讀性、可維護性和可擴展性。css
concent攜帶如下特性vue
爲用戶提供更溫馨和簡單的react編碼體驗java
concent對模塊的定義是通過對實際業務場景反覆思考和推敲,最終得出的答案,首先,數據是模塊的靈魂,承載着對你的功能模塊的最基礎的字符描述,離開數據,一切上層業務功能都是空談,因此state
是模塊裏的必包含的定義。react
修改數據的方式靈活度是concent提供給用戶驚喜之一,由於concent的核心是經過接管setState
作狀態管理,因此用戶接入concent那一刻能夠無需當即改造現有的代碼就可以享受到狀態管理的好處,一樣的,concent也支持用戶定義reducer函數修改狀態,這也是推薦的最佳實踐方式,能夠完全解耦UI渲染與業務邏輯,由於reducer
函數本質上只是setState
的變種寫法,因此強調的是老是返回須要更新的片斷狀態,並且因爲concent支持reducer
函數之間相互調用,任意組合,因此能夠容許用戶按需任意切割reducer
函數對狀態的更新粒度,而後造成鏈式調用關係,而後經過dispatch
句柄來觸發reducer
函數git
若是鏈式調用層級過深,會形成不少次渲染,從上圖中能夠看出有3個函數返回新的片斷狀態,形成3次渲染,因此concent
也一樣提供lazyDispatch
句柄來讓用戶能夠有多一種選擇來觸發對reducer
函數的調用,concent
會在調動過程當中自動緩存當前調用鏈上全部屬於同一個模塊的狀態並作合併,直到調用鏈結束,才作一次性的提交github
computed
提供一個入口定義須要對發生變化的key作計算的函數,一般來講,大部分state的數據並不是是UI渲染直接須要的數據,咱們一般須要對其作一些格式化或者轉換操做,可是這些操做其實沒有必要再每次渲染前都作一遍,computed
將只對發生了變化的key
計算並將其結果緩存起來。web
watch
和computed
最大的不一樣是,不須要返回一個具體的結果,一般用於在關心某些key
變化時,作一些異步操做,就能夠對這些key
定義watch
函數vuex
咱們知道state的定義是同步的,init
容許用戶有一次對state
作異步獲取並改寫的機會,注意,若是此時存在着該模塊的實例,改寫了模塊的狀態後,concent
會自動將這些狀態廣播到對應的實例上,一樣的,若是不存在,在有些的該模塊的實例生成時,這些實例將同步到模塊最新的狀態,因此當咱們有一些狀態不是須要依賴實例掛載上且觸發componentDidMount
來獲取的時候,就能夠將狀態的初始化提高到模塊的init
裏typescript
模塊是先於組件存在的概念,當咱們有了模塊的定義後,即可以對組件提供強有力的支持,concent
裏經過register
函數將react組件註冊爲concent組件(也稱之爲concent類)
註冊的時候,能夠指定專屬的模塊,理論來講,咱們應該保持組件與模塊乾淨的對應關係,即一個組件專屬於某個模塊,消費的是該模塊的數據,操做的所屬模塊的reducer
函數,可是實際場景可能有很多組件都是跨多個模塊消費和修改數據的,因此concent
也容許用戶經過connect
定義來指定組件鏈接的其餘模塊,惟一不一樣的是調用句柄默認帶的上下文是指向本身專屬模塊的,若是須要調用其餘模塊的方法,則須要顯示指定模塊名
@register('Foo', {module:'foo', connect:{bar:'*'}})
class Foo extends Component(){
onNameChange = (name)=>{
this.$$dispatch('changeName', name);//默認調用的foo模塊reducer裏的changeName方法
this.$$dispatch('bar/changeName', name);//指定bar模塊, 調用bar模塊的reducer裏的changeName方法修改bar模塊的數據
}
}
複製代碼
對於
CcClass
來講,由於調用
setState
就可以修改store,因此數據是直接注入到
state
裏的,對於其餘模塊的數據,是注入到
connectedState
,這樣既保持了所屬模塊和其餘模塊的數據隔離,又可以讓用戶很是方便消費多個模塊的數據。
因此總體來講,組件與store
之間將構成一張關係明確和清晰的結構網,有利於用戶爲大型的react
工程初期整齊的劃分業務模塊,中期靈活的調整模塊定義
在hook
提案落地後,現有的react社區,已經從class component慢慢轉向function component寫法,可是正如Vue Function-based API RFC所說,hook
顯而易見的要建立不少臨時函數和產生大量閉包的問題,以及經過提供輔助函數useMemo/useCallback
等來解決過分更新或者捕獲了過時值等問題,提出了setup
方案,每一個組件實例只會在初始化時調用一次 ,狀態經過引用儲存在 setup() 的閉包內。
綜合上述的setup
思路和好處,concent
針對react
的函數組件引入setup
機制並對其作了更優的改進,一樣在在組件實例化時只調用一次,能夠定義各類方法並返回,這些方法將收集在上下文的settings
對象裏,還額外的容許setup
裏定義effect
、computed
、watch
函數(固然,這些是實例級別的computed
和watch
了)
const AwardPanelUI = (props) => {
return (
<div style={stBox}>
{/** 其餘略 */}
<div>displayBonus: {props.displayBonus}</div>
</div>
);
};
複製代碼
const setup = ctx => {
//定義反作用,第二位參數寫空數組,表示只在組件初次掛載完畢後執行一次
ctx.defineEffect(ctx => {
ctx.dispatch('init');
//返回清理函數,組件卸載時將觸發此函數
return () => ctx.dispatch('track', 'user close award panel')
}, []);
/** 也支持函數式寫法
ctx.defineWatch(ctx=>{
return {...}
});
*/
ctx.defineWatch({
//key inputCode的值發生變化時,觸發此觀察函數
'inputCode':(nevVal)=> ctx.setState({msg:'inputCode 變爲 '+nevVal })
});
ctx.defineComputed({
//key inputCode的值發生變化時,觸發此計算函數
'inputCode':(newVal)=>`${newVal}_${Date.now()}`
});
//定義handleStrChange方法
const handleStrChange = (e) => {
const inputCode = e.currentTarget.value;
//兩種寫法等效
ctx.dispatch('handleInputCodeChange', inputCode);
// ctx.reducer.award.handleInputCodeChange(inputCode);
}
//定義init函數
const init = ctx.reducer.award.init;
//const init = ()=> ctx.dispatch('init');
//setup會將返回結果放置到settings
return { handleStrChange, init };
}
複製代碼
//函數組件每次渲染前,mapProps都會被調用,幫助用戶組裝想要的props數據
const mapProps = ctx => {
//將bonus的計算結果取出
const displayBonus = ctx.moduleComputed.bonus;
//將settings裏的 handleStrChange方法、init方法 取出
const { handleStrChange, init } = ctx.settings;
//將inputCode取出
const { inputCode, awardList, mask, msg } = ctx.moduleState;
//從refConnectedComputed獲取實例對模塊key的計算值
const { inputCode:cuInputCode } = ctx.refComputed.award;
//該返回結果會映射到組件的props上
return { msg, cuInputCode, init, mask, inputCode, awardList, displayBonus, handleStrChange }
}
複製代碼
const AwardPanel = connectDumb({setup, mapProps, module:'award'})(AwardPanelUI);
複製代碼
有了setup
的支持,能夠將這些要用到方法提高爲靜態的上下文api,而不須要反覆重定義,也不存在大量的臨時閉包問題,同時基於函數式的寫法,能夠更靈活的拆分和組合你的U代碼與業務代碼,同時這些setup函數,通過進一步抽象,還能夠被其餘地方複用。
同時函數式編程也更利於typescript
作類型推導,concent
對函數組件友好支持,讓用戶能夠在class
和function
之間按需選擇,concent
還容許定義state
來作局部狀態管理,因此通過connectDumb
包裹的function
組件,既可以讀寫本地狀態,又可以讀寫store狀態,還有什麼更好的理由非要使用hook
不可呢?
const AwardPanel = connectDumb({
//推薦寫爲函數式寫法,由於直接聲明對象的話,concent也會對其作深克隆操做
//state:()=>({localName:1});
state:{localName:1},
setup,
mapProps,
connect:{award:'*'}
})(AwardPanelUI);
//code in setup
const setup = ctx =>{
const changeLocalName = name => ctx.setState({localName});
return {changeLocalName};
}
//code in mapProps
const mapProps = ctx =>{
const localName = ctx.state.localName;
return {localName};
}
複製代碼
將concent
接入react
應用是很是輕鬆和容易的,對於已存在的react應用,不須要你修改現有的react應用任何代碼,只須要先將concent
啓動起來,就可使用了,不須要在頂層包裹Provider
之類的組件來提供全局上下文,由於啓動concent
以後,concent
自動就維護着一個本身的全局上下文,因此你能夠理解concent
和react
應用是一個平行的關係,而非嵌套或者包裹的關係,惟一注意的是在渲染react
應用以前,優先將concent
啓動就能夠了。
concent
並不是要求用戶在啓動時就配置好各個模塊的定義,容許用戶定義某些組件時,調用configure
函數配置模塊,這將極大提升定義page model
或者component model
的編碼體驗。
.
|____page
| |____Group
| | |____index.js
| | |____model//定義page model
| | |____reducer.js //可選
| | |____index.js
| | |____computed.js //可選
| | |____state.js //必包含
| | |____watch.js //可選
| | |____init.js //可選
| |
| |____...//各類page組件定義
|
|____App.css
|____index.js
|____utils
| |____...
|____index.css
|____models// 各類業務model的定義
| |____home
| | |____reducer.js
| | |____index.js
| | |____computed.js
| | |____state.js
|
|____components
| |____Nav.js
|
|____router.js
|____logo.png
|____assets
| |____...
|____run-cc.js //啓動concent,在入口index.js裏第一行就調用
|____App.js
|____index.js //項目入口文件
|____services
| |____...
複製代碼
以上圖代碼文件組織結構爲例,page組件Group
包含了一個本身的model
,在model/index.js
裏完成定義模塊到concent
的動做,
// code in page/Group/model/index.js
import state form './state';
import * as reducer form './reducer';
import * as computed form './computed';
import * as watch form './watch';
import init form './init';
import {configure} from 'concent';
//配置模塊到`concent`裏,命名爲'group'
configure('group', {state, reducer, computed, watch, init});
複製代碼
在Group組件對外暴露前,引入一下model
就能夠了
import './model';
@register('GroupUI', {module:'group'})
export default class extends Component {
}
複製代碼
這種代碼組織方式爲用戶發佈攜帶完整model
定義的concent
組件到npm成爲了可能,其餘用戶只需安裝它的concent
應用裏,安裝了該組件就能直接使用該組件,甚至不使用組件的UI邏輯,只是註冊他新寫的組件到該組件攜帶的模塊裏,完徹底全複用模塊的除了ui的其餘全部定義。
對於已有的模塊,有的時候咱們想徹底的複用裏面的全部定義可是運行時是完全隔離的,若是用最笨的方法,就是徹底copy目標模塊下的全部代碼,而後起一個新的名字,配置到concent
就行了,但是若是有10個、20個甚至更多的組件想複用邏輯可是保持運行時隔離怎麼辦呢?顯然複製多份代碼是行不通的,concent
提供cloneModule
函數幫助你完成此目的,實際上cloneModule
函數只是對state
作了一個深拷貝,其餘的由於都是函數定義,因此只是讓新模塊指向那些函數的引用。
基於cloneModule
能夠在運行時任意時間調用的特性,你甚至能夠寫一個工廠函數,動態創解綁定了新模塊的組件!
//makeComp.js
import existingModule from './demoModel';
import { register, cloneModule } from 'concent';
const module_comp_= {};//記錄某個模塊有沒有對應的組件
class Comp extends Component(){
//......
}
export makeComp(module, CompCcClassName){
let TargetComp = module_comp_[module];
if(TargetComp) return TargetComp;
//先基於已有模塊克隆新模塊
cloneModule(module, existingModule);
//由於module是不能重複的,ccClassName也是不能重複的,
//全部用戶若是沒有顯式指定ccClassName值的話,能夠默認ccClassName等於module值
const ccClassName = CompCcClassName || module;
//註冊Comp到新模塊裏
TargetComp = register(ccClassName, {module})(Comp);
//緩存起來
module_comp_[module] = TargetComp;
return TargetComp;
}
複製代碼
concent組件並不是魔改了react組件,只是在react組件上作了一層語法糖封裝,整個react組件的生命週期依然須要被你瞭解,而concentDumb
將原生命週期作了巧妙的抽象,才得以使用defineEffect
、defineWatch
、defineComputed
等有趣的功能而無需在關注類組件的生命週期,無需再和this
打交道,讓函數組件和類組件擁有徹底對等的功能。
咱們知道,現有的狀態框架,主要有兩大類型,一個是redux
爲表明的基於對數據訂閱的模式來作狀態全局管理,一種是以mobx
爲表明的將數據轉變爲可觀察對象來作主動式的變動攔截以及狀態同步。
咱們先聊一聊redux
,這個當前react
界狀態管理的一哥。
寫過redux
的用戶,或者redux wrapper
(如dva
、rematch
等)的用戶,都應該很清楚 redux
的一個約定:reducer
必需是純函數,若是狀態改變了,必需解構原state
返回一個新的state
// fooReducer.js
export default (state, action)=>{
switch(action.type){
case 'FETCH_BOOKS':
return {...state, ...action.payload};
default:
return state;
}
}
複製代碼
純函數沒有反作用,容易被測試的特色被提起過不少次,咱們寫着寫着,對於actionCreator
和reducer
,有了兩種流派的寫法,
actionCreator
寫完了,而後將數據封裝爲payload
,調用dispatch
, 講數據派發給對應的reducer
。此種流派代碼,慢慢變成
reducer
裏全是解構payload
而後合成新的state
並返回的操做,業務邏輯所有在actionCreator
裏了,此種有一個有一個嚴重的弊端,由於業務邏輯所有在actionCreator
裏,reducer
函數裏的type
值所有變成了一堆相似CURD
的命名方式,saveXXModel
、updateXXModelXXField
、setXXX
、deleteXXX
等看起來已經和業務邏輯全然沒有任何關係的命名,大量的充斥在reducer
函數裏,而咱們的狀態調試工具記錄的type
值偏偏全是這些命名方式,你在調試工具裏看到變遷過程對應的type
列表,只是獲取到了哪些數據被改變了的信息,但全然不知這些狀態是從哪些地方派發了payload
致使了變化,甚至想知道是那些UI視圖的什麼交互動做致使了狀態的改變,也只能從代碼的reducer
的type
關鍵字做爲搜索條件開始,反向查閱其餘代碼文件。
actionCreator
儘量的薄,派發同步的action就直接return,異步的action使用thunk函數或者redux-saga
等第三方庫作處理,拿到數據都儘量早的作成action
對象,派發到reducer
函數裏,此種模式下,咱們的
actionCreator
薄了,作的事情如其名字同樣,只是負責產生action
對象,同時由於咱們對數據處理邏輯在reducer
裏了,咱們的type
值能夠根據調用方的動機或者場景來命名了,如formatTimestamp
、handleNameChanged
、handelFetchedBasicData
等,可是因爲redux
的架構致使,你的ui觸發的動做避免不了的要要通過兩個步驟,一步通過actionCreator
生成action
,第2步進過通過reducer
處理payload
合成新的state
,因此actionCreator
的命名和reducerType
的命名一般爲了方便之後閱讀時可以帶入上下文信息頗有可能會變成同樣的命名,如fetchProductList
,在actionCreator
裏出現一遍,而後在reducerType
又出現一遍
concent裏reducer
擔任的角色就是負責返回一個新的片斷視圖,因此你能夠認爲它就是一個partialStateGenerator
函數,你能夠聲明其爲普通函數
//code in fooReducer.js
function fetchProductList(){
}
複製代碼
也能夠是async函數或者generator函數
async function fetchProductList(){
}
複製代碼
若是,你的函數須要幾步請求才能完成所有的渲染,可是每一步都須要及時觸發視圖更新,concent容許你自由組合函數,若是同屬於一個模塊裏的reducer
函數,相互之間還能夠直接基於函數簽名來調用
function _setProductList(dataList){
return {dataList};
}
//獲取產品列表計基礎數據
async function fetchProductBasicData(payload, moduleState, ctx){
const dataList = await api.fetchProductBasicData();
return {dataList};//返回數據,觸發渲染
// or ctx.dispatch(_setProductList, dataList);
}
//獲取產品列表計統計數據,統計數據較慢,分批拉取 (僞代碼)
async function fetchProductStatData(payload, moduleState, ctx){
const dataList = moduleState.dataList;
//作分批拉取統計數據的ids,邏輯略......
const batchIdsList = [];
const len = batchIds.length;
for(let i=0; i<len; i++){
const ids = batchIdsList[i];
const statDataList = await api.fetchProductBasicData(ids);
//邏輯略...... 遊標開始和結束,改變對應的data的統計數據
let len = statDataList.length;
for(let j=0; j<len; j++){
dataList[j+cursor].stat = statDataList[j];//賦值統計數據
}
await ctx.dispatch(_setProductList, dataList);//修改dataList數據,觸發渲染
}
}
//一個完整的產品列表既包含基礎數據、也包含統計數據,分兩次拉取,其中統計數據須要屢次分批拉取
async function fetchProductList(payload, moduleState, ctx){
await ctx.dispatch(fetchProductBasicData);
await ctx.dispatch(fetchProductStatData);
}
複製代碼
如今你只須要視圖實例裏調用$$dispatch
觸發更新便可
//屬於product模塊的實例調用
this.$$dispatch('fetchProductList');
//屬於其餘模塊的實例調用
this.$$dispatch('product/fetchProductList');
複製代碼
能夠看到,這樣的代碼組織方式,更符合調用者的使用直覺,沒有多餘的操做,相互調用或者多級調用,能夠按照開發者最直觀的思路組織代碼,而且很方便後期不停的調整後者重構模塊裏的reducer。
concent強調返回欲更新的片斷狀態,而不是合成新的狀態返回,從工做原理來講,由於concent類裏標記了觀察key信息,reducer提交的狀態越小、越精準,就越有利於加速查找到關心這些key值變化的實例,還有就是concent是容許對key定義watch
和computed
函數的,保持提交最小化的狀態不會觸發一些冗餘的watch
和computed
函數邏輯;從業務層面上來講,你返回的新狀態是須要符合函數名描述的,咱們直觀的解讀一段函數時,大致知道作了什麼處理,最終返回一個什麼新的片斷狀態給concent,是符合線性思惟的^_^,剩下的更新UI的邏輯就交給concent吧。
可能讀者留意到了,redux
所提倡的純函數容易測試、無反作用的優點呢?在concent裏可以體現嗎,其實這一點擔憂徹底沒有必要,由於你觀察上面的reducer示例代碼能夠發現,函數有無反作用,徹底取決於你聲明函數的方式,async(or generator)就是反作用函數,不然就是純函數,你的ui裏能夠直接調用純函數,也能夠調用反作用函數,根據你的使用場景具體決定,函數名就是type
,沒有了actionCreator
是否是世界清靜了不少?
進一步挖掘reducer
本質,上面提到過,對於concent來講,reducer
就是partialStateGenerator
函數,因此若是討厭走dispatch流派的你,能夠直接定義一個函數,而後調用它,而非必須要放置在模塊的reducer
定義下。
function inc(payload, moduleState, ctx){
ctx.dispatch('bar/recordLog');//這裏不使用await,表示異步的去觸發bar模塊reducer裏的recordLog方法
return {count: moduleState.count +1 };
}
@register('Counter', 'counter')(Counter)
class Counter extends Component{
render(){
return <div onClick={()=> this.$$invoke(inc}>count: {this.state.count}</div>
}
}
複製代碼
concent
不只書寫體驗友好,由於concent
是以引用收集爲基礎來作狀態管理,因此在concent提供的狀態調試工具裏能夠精確的定位到每一次狀態變動提交了什麼狀態,調用了哪些方法,以及由哪些實例觸發。
儘管redux
核心代碼很簡單,提供composeReducers
、bindActionCreators
等輔助函數,做爲橋接react
的react-redux
提供connect
函數,須要各類手寫mapStateToProps
和mapDispatchToProps
等操做,整個流程下來,其實已經讓代碼顯得很臃腫了,因此有了dva
、rematch
等redux wrapper
作了此方面的改進,化繁爲簡,可是不管怎麼包裝,從底層上看,對於redux
的更新流程來講,任何一個action
派發都要通過全部的reducer
,reducer
返回的狀態都要通過全部connect
到此reducer
對應狀態上的全部組件,通過一輪淺比較(這也是爲何redux必定要藉助解構語法,返回一個新的狀態的緣由),決定要不要更新其包裹的子組件。
const increaseAction = {
type: 'increase'
};
const mapStateToProps = state => {
return {value: state.count}
};
const mapDispatchToProps = dispatch => {
return {
onIncreaseClick: () => dispatch(increaseAction);
}
};
const App = connect(
mapStateToProps,
mapDispatchToProps
)(Counter);
複製代碼
註冊成爲concent類的組件,天生就有操做store的能力,並且數據將直接注入state
//Counter裏直接可使用this.$$dispatch('increase')
class Counter extends Component{
render(){
return <div onClick={()=> this.$$dispatch('increase')}>count: {this.state.count}</div>
}
}
const App = register('Counter', 'counter')(Counter);
複製代碼
你能夠注意到,concent直接將$$dispatch
方法,綁定在this上,由於concent默認採用的是反向繼承策略來包裹你的組件,這樣產生更少的組件嵌套關係從而使得Dom層級更少。
store的state
也直接注入了this上,這是由於從setState
調用開始,就具有了將轉態同步到store
的能力,因此注入到state
也是順其天然的事情。
固然concent也容許用戶在實例的state
上聲明其餘非store
的key,這樣他們的值就是私有狀態了,若是用戶不喜歡state
被污染,不喜歡反向繼承策略,一樣的也能夠寫爲
class Counter extends Component{
constructor(props, context){
super(props, context);
this.props.$$attach(this);
}
render(){
return(
<div onClick={()=> this.props.$$dispatch('increase')}>
count: {this.props.$$connectedState.counter.count}
</div>
)
}
}
const App = register('Counter', {connect:{counter:'*'}, isPropsProxy:true} )(Counter);
複製代碼
mobx
是一個函數響應式編程的庫,提供的橋接庫mobx-react
將react
變成完全的響應式編程模式,由於mobx
將定義的狀態的轉變爲可觀察對象,因此 用戶只須要修改數據,mobx
會自動將對應的視圖更新,因此有人戲稱mobx
將react
變成相似vue
的編寫體驗,數據自動映射視圖,無需顯示的調用setState
了。
本質上來講,全部的mvvm
框架都在圍繞着數據和視圖作文章,react把單項數據流作到了極致,mobx
爲react
打上數據自動映射視圖的補丁,提到自動映射,自動是關鍵,框架怎麼感知到數據變了呢?mobx
採用和vue
同樣的思路,採用push
模式來作變化偵測,即對數據getter
和setter
作攔截,當用戶修改數據那一刻,框架就知道數據變了,而react
和咱們當下火熱的小程序等採用的pull
模式來作變化偵測,暴露setState
和setData
接口給用戶,讓用戶主動提交變動數據,才知道數據發生了變化。
concent
本質上來講沒有改變react
的工做模式,依然採用的是pull
模式來作變化偵測,惟一不一樣的是,讓pull
的流程更加智能,當用戶的組件實例建立好的那一刻,concent
已知道以下信息:
同時實例的引用將被收集到並保管起來,直到卸載纔會被釋放。
因此能夠用0改形成本的方式,直接將你的react代碼接入到concent
,而後支持用戶能夠漸進式的分離你的ui和業務邏輯。
這裏咱們先把問題先提出來,咱們真的須要自動映射嗎?
當應用愈來愈大,模塊愈來愈多的時候,直接修改數據致使不少不肯定的額外因素產生而沒法追查,因此vue
提供了vuex
來引導和規範用戶在大型應用的修改狀態的方式,而mobx
也提供了mobx-state-tree
來約束用戶的數據修改行爲,經過統一走action
的方式來讓整個修改流程可追查,可調試。
因此在大型的應用裏,咱們都但願規範用戶修改數據的方式,那麼concent
從骨子裏爲react
而打造的優點將體現出來了,能夠從setState
開始享受到狀態管理帶來的好處,無需用戶接入更多的輔助函數和大量的裝飾器函數(針對字段級別的定義),以及完美的支持用戶漸進式的重構,優雅的解耦和分離業務邏輯與ui視圖,寫出的代碼始終仍是react
味道的代碼。
concent
圍繞react
提供一種了更溫馨、更符合閱讀直覺的編碼體驗,同時新增了更多的特性,爲書寫react
組件帶來更多的趣味性和實用性,無論是傳統的class
流派,仍是新興的function
流派,都可以在concent
裏享受到統一的編碼體驗。
依託於concent的如下3點核心工做原理:
基於引用收集和觀察key標記,就能夠作到熱點更新路徑緩存,理論上來講,某一個reducer若是返回的待更新片斷對象形狀是不變的,初次觸發渲染的時候還有一個查找的過程(儘管已經很是快),後面的話相同的reducer調用均可以直接命中並更新,有點相似v8裏的熱點代碼緩存,不過concent緩存的reducer返回數據形狀和引用之間的關係,因此應用能夠越運行越快,尤爲是那種一個界面上百個組件,n個模塊的應用,將體現出更大的優點,這是下一個版本concent正在作的優化項,爲用戶帶來更快的性能表現和更好的編寫體驗是concent
始終追求的目標。
儘管concent
有一套本身的標準的開發方式,可是其靈活的架構設計很是的容易與現有的項目集成,此案例將concent
接入到antd-pro
(js版本的最後一個版本2.2.0),源代碼業務邏輯沒有作任何改動,只是作了以下修改,lint-staged驗收經過:
concent
替換了dva
,並作了少量語法的修改concent-plugin-loading
插件,用於自動設置reducer
函數的開始和結束狀態react-router-concent
,用於鏈接react-router
和concent
concent-middleware-web-devtool
(第一個可用版本,比較簡陋^_^),用於查看狀態concent
狀態變遷過程注意,運行期項目後,能夠打開console,輸入
sss
,查看store,輸入cc.dispatch
或cc.reducer.**
直接觸發調用,更多api請移步concent官網文檔查看,更多antd-pro知識瞭解請移步antd-pro官網
git clone git@github.com:concentjs/antd-pro-concent.git
複製代碼
npm i
複製代碼
npm start
複製代碼
默認src目錄下放置的是
concent
版本的源代碼,如需運行dva
版本,執行npm run start:old
,切換爲concent
,執行npm run start:cc
happy coding, enjoy concent ^_^
歡迎star
An out-of-box UI solution for enterprise applications as a React boilerplate.