React Native項目框架搭建的一些心得體會
React Native 是Facebook于2015年4月開源的跨平臺移動應(yīng)用開發(fā)框架, 短短的一兩年的發(fā)展就已經(jīng)有很多家公司支持并采用此框架來搭建公司的移動端的應(yīng)用,
React Native使你能夠在Javascript和React的基礎(chǔ)上獲得完全一致的開發(fā)體驗,構(gòu)建世界一流的原生APP。
項目框架與項目結(jié)構(gòu)
1. 項目中使用的技術(shù)棧
react native、react hook、typescript、immer、tslint、jest等.
都是比較常見的,就不多做介紹了
2. 數(shù)據(jù)處理使用的是react hook中的useContext+useReducer
思想與redux是一致的,用起來相對比較簡單,適合不太復(fù)雜的業(yè)務(wù)場景.
const HomeContext = createContext<IContext>({
state: defaultState,
dispatch: () => {}
});
const ContextProvider = ({ urlQuery, pageCode }: IProps) => {
const initState = getInitState(urlQuery, pageCode);
const [state, dispatch]: [IHomeState, IDispatch] = useReducer(homeReducer, initState);
return (
<HomeContext.Provider value={{ state, dispatch }}>
<HomeContainer />
</HomeContext.Provider>
);
};
const HomeContainer = () => {
const { dispatch, state } = useContext(HomeContext);
...
3. 項目的結(jié)構(gòu)如下
|-page1
|-handler // 處理邏輯的純函數(shù),需進(jìn)行UT覆蓋
|-container // 整合數(shù)據(jù)、行為與組件
|-component // 純UI組件,展示內(nèi)容與用戶交互,不處理業(yè)務(wù)邏輯
|-store // 數(shù)據(jù)結(jié)構(gòu)不能超過3層,可使用外部引用、冗余字段的方式降低層級
|-reducer // 使用immer返回新的數(shù)據(jù)(immutable data)
|-...
|-page2
|-...
項目中的規(guī)范
1. Page
整個項目做為一個多頁應(yīng)用,最基本的拆分單元是page.
每一個page有相應(yīng)的store,并非整個項目使用一個store,這樣做的原因如下:
- 各個頁面的邏輯相對獨立
- 各個頁面都可作為項目入口
- 結(jié)合RN頁面生命周期進(jìn)行數(shù)據(jù)處理(避免數(shù)據(jù)初始化、緩存等一系列問題)
各個頁面中與外部相關(guān)的操作,都在Page組件中定義
- 頁面跳轉(zhuǎn)邏輯
- 回退之后要處理的事件
- 需要操作哪些storage中的數(shù)據(jù)
- 需要請求哪些服務(wù)等等
Page組件的主要作用
以其自身業(yè)務(wù)模塊為基礎(chǔ),把可以抽象出來的外部依賴、外部交互都集中到此組件的代碼中.
方便開發(fā)人員在進(jìn)行各個頁面間邏輯編寫、問題排查時,可根據(jù)具體頁面+數(shù)據(jù)源,準(zhǔn)確定位到具體的代碼.
2. reducer
在以往的項目中,reducer中可能會涉及部分?jǐn)?shù)據(jù)處理、用戶行為、日志埋點、頁面跳轉(zhuǎn)等等代碼邏輯.
因為在開發(fā)人員寫代碼的過程中,發(fā)現(xiàn)reducer作為某個處理邏輯的終點(更新了state之后,此次事件即為結(jié)束),很適合去做這些事情.
隨著項目的維護(hù),需求的迭代,reducer的體積不斷的增大.
因為缺乏條理,代碼量又龐大,再想去對代碼進(jìn)行調(diào)整,只會困難重重.
讓你去維護(hù)這樣的一個項目,可想而知,將會是多么的痛苦.
為此,對reducer中的代碼進(jìn)行了一些減法:
- reducer中只對state的數(shù)據(jù)進(jìn)行修改
- 使用immer的produce產(chǎn)生immutable data
- 冗余單獨字段的修改,進(jìn)行整合,枚舉出頁面行為對應(yīng)的action
reducer的主要作用
以可枚舉的形式,匯總出頁面中所有操作數(shù)據(jù)的場景.
在其本身適用于react框架的特性之外,賦予一定的業(yè)務(wù)邏輯閱讀屬性,在不依賴UI組件的情況下,可大致閱讀出頁面中的所有數(shù)據(jù)處理邏輯.
// 避免dispatch時進(jìn)行兩次,且定義過多單字段的更新case
// 整合此邏輯后,與頁面上的行為相關(guān)聯(lián),利于理解、閱讀
case EFHListAction.updateSpecifyQueryMessage:
return produce(state, (draft: IFHListState) => {
draft.specifyQueryMessage = payload as string;
draft.showSpecifyQueryMessage = true;
});
case EFHListAction.updateShowSpecifyQueryMessage:
return produce(state, (draft: IFHListState) => {
draft.showSpecifyQueryMessage = payload as boolean;
});
3. handler
這里先引入一個純函數(shù)的概念:
一個函數(shù)的返回結(jié)果只依賴于它的參數(shù),并且在執(zhí)行過程里面沒有副作用,我們就把這個函數(shù)叫做純函數(shù).
把盡可能多的邏輯抽象為純函數(shù),然后放入handler中:
- 涵蓋較多的業(yè)務(wù)邏輯
- 只能是純函數(shù)
- 必須進(jìn)行UT覆蓋
handler的主要作用
負(fù)責(zé)數(shù)據(jù)源到store、container到component、dispatch到reducer等等場景下的邏輯處理.
作為各類場景下,邏輯處理函數(shù)的存放地,整個文件不涉及頁面流程上的關(guān)聯(lián)關(guān)系,每個函數(shù)只要滿足其輸入與輸出的使用場景,即可復(fù)用,多用于container文件中.
export function getFilterAndSortResult(
flightList: IFlightInfo[],
filterList: IFilterItem[],
filterShare: boolean,
filterOnlyDirect: boolean,
sortType: EFlightSortType
) {
if (!isValidArray(flightList)) {
return [];
}
const sortFn = getSortFn(sortType);
const result = flightList.filter(v => doFilter(v, filterList, filterShare, 1, filterOnlyDirect)).sort(sortFn);
return result;
}
describe(getFilterAndSortResult.name, () => {
test('getFilterAndSortResult', () => {
expect(getFilterAndSortResult(flightList, filterList, false, EFlightSortType.PriceAsc)).toEqual(filterSortResult);
});
});
4. Container
由上面的項目結(jié)構(gòu)圖可以看出,每個Page都有base Container,作為數(shù)據(jù)處理的中心.
在此base Container之下,會根據(jù)不同模塊,定義出各個子Container:
- 生命周期處理(初始化時要進(jìn)行的一些異步操作)
- 為渲染組件Components提供數(shù)據(jù)源
- 定義頁面中的行為函數(shù)
Container的主要作用
整個項目中,各種數(shù)據(jù)、UI、用戶行為的匯合點,要盡可能的把相關(guān)的模塊抽離出來,避免造成代碼量過大,難以維護(hù)的情況.
Container的定義應(yīng)以頁面展示的模塊進(jìn)行抽象.如Head Contianer、Content Container、Footer Container等較為常見的劃分方式.
一些頁面中相對獨立的模塊,也應(yīng)該產(chǎn)出其對應(yīng)的Container,來內(nèi)聚相關(guān)邏輯,如贈送優(yōu)惠券模塊、用戶反饋模塊等.
特別注意的是行為函數(shù)
- 多個Container中公用的行為,可直接放入base Container中
- 在上文架構(gòu)圖中的action事例(setAction)為另外一種行為復(fù)用,根據(jù)具體的場景進(jìn)行應(yīng)用
利于代碼閱讀,A模塊的浮層展示邏輯,B模塊使用時
模塊產(chǎn)生的先后順序,先有A模塊再有B模塊需要使用A的方法
- 定義數(shù)據(jù)埋點、用戶行為埋點
- 頁面跳轉(zhuǎn)方法的調(diào)用(Page-->base Container-->子Container)
- 其他副作用的行為
const OWFlightListContainer = () => {
// 通過Context獲取數(shù)據(jù)
const { state, dispatch } = useContext(OWFlightListContext);
...
// 初次加載時進(jìn)行超時的倒計時
useOnce(overTimeCountDown);
...
// 用戶點擊排序
const onPressSort = (lastSortType: EFlightSortType, isTimeSort: boolean) => {
// 引用了handler中的getNextSortType函數(shù)
const sortType = getNextSortType(lastSortType, isTimeSort);
dispatch({ type: EOWFlightListAction.updateSortType, payload: sortType });
// 埋點操作
logSort(state, sortType);
};
// 渲染展示組件
return <.../>;
}
小結(jié)
由easy to code到easy to read
在整個項目中,定義了很多規(guī)范,是想在功能的實現(xiàn)之上,更利于項目人員的維護(hù).
- Page組件中包含頁面相關(guān)的外部依賴
- reducer枚舉出所有對頁面數(shù)據(jù)操作的事件
- handler中集合了業(yè)務(wù)邏輯的處理,以純函數(shù)的實現(xiàn)及UT的覆蓋,確保項目質(zhì)量
- Container中的行為函數(shù),定義出所有與用戶操作相關(guān)的事件,并記錄埋點數(shù)據(jù)
- Componet中避免出現(xiàn)業(yè)務(wù)邏輯的處理,只進(jìn)行UI展示,減少UI自動化case,增加UT的case
規(guī)范的定義是比較容易的,想要維護(hù)好一個項目,更多的是依靠團(tuán)隊的成員,在達(dá)成共識的前提下,持之以恒的堅持了
分享幾個實用的函數(shù)
根據(jù)對象路徑取值
/**
* 根據(jù)對象路徑取值
* @param target {a: { b: { c: [1] } } }
* @param path 'a.b.c.0'
*/
export function getVal(target: any, path: string, defaultValue: any = undefined) {
let ret = target;
let key: string | undefined = '';
const pathList = path.split('.');
do {
key = pathList.shift();
if (ret && key !== undefined && typeof ret === 'object' && key in ret) {
ret = ret[key];
} else {
ret = undefined;
}
} while (pathList.length && ret !== undefined);
return ret === undefined || ret === null ? defaultValue : ret;
}
// DEMO
const errorCode = getVal(result, 'rstlist.0.type', 0);
讀取根據(jù)配置信息
// 在與外部對接時,經(jīng)常會定義一些固定結(jié)構(gòu),可擴(kuò)展性的數(shù)據(jù)列表
// 為了適應(yīng)此類契約,便于更好的閱讀與維護(hù),總結(jié)出了以下函數(shù)
export const GLOBAL_NOTE_CONFIG = {
2: 'refund',
3: 'sortType',
4: 'featureSwitch'
};
/**
* 根據(jù)配置,獲取attrList中的值,返回json對象類型的數(shù)據(jù)
* @private
* @memberof DetailService
*/
export function getNoteValue<T>(
noteList: Array<T> | undefined | null,
config: { [_: string]: string },
keyName: string = 'type'
) {
const ret: { [_: string]: T | Array<T> } = {};
if (!isValidArray(noteList!)) {
return ret;
}
//@ts-ignore
noteList.forEach((note: any) => {
const typeStr: string = (('' + note[keyName]) as unknown) as string;
if (!(typeStr in config)) {
return;
}
if (note === undefined || note === null) {
return;
}
const key = config[typeStr];
// 有多個值時,改為數(shù)組類型
if (ret[key] === undefined) {
ret[key] = note;
} else if (Array.isArray(ret[key])) {
(ret[key] as T[]).push(note);
} else {
const first = ret[key];
ret[key] = [first, note];
}
});
return ret;
}
// DEMO
// 適用于外部定義的一些可擴(kuò)展note節(jié)點列表的取值邏輯
const { sortType, featureSwitch } = getNoteValue(list, GLOBAL_NOTE_CONFIG, 'ntype');
多條件數(shù)組排序
/**
* 獲取用于排序的sort函數(shù)
* @param fn 同類型元素比較函數(shù),true為排序優(yōu)先
*/
export function getSort<T>(fn: (a: T, b: T) => boolean): (a: T, b: T) => 1 | -1 | 0 {
return (a: T, b: T): 1 | -1 | 0 => {
let ret = 0;
if (fn.call(null, a, b)) {
ret = -1;
} else if (fn.call(null, b, a)) {
ret = 1;
}
return ret as 0;
};
}
/**
* 多重排序
*/
export function getMultipleSort<T>(arr: Array<(a: T, b: T) => 1 | -1 | 0>) {
return (a: T, b: T) => {
let tmp;
let i = 0;
do {
tmp = arr[i++](a, b);
} while (tmp === 0 && i < arr.length);
return tmp;
};
}
// DEMO
const ageSort = getSort(function(a, b) {
return a.age < b.age;
});
const nameSort = getSort(function(a, b) {
return a.name < b.name;
});
const sexSort = getSort(function(a, b) {
return a.sex && !b.sex;
});
//判斷條件先后順序可調(diào)整
const arr = [nameSort, ageSort, sexSort];
const ret = data.sort(getMultipleSort(arr));
以上就是React Native項目框架搭建的一些心得體會的詳細(xì)內(nèi)容,更多關(guān)于React Native項目框架搭建的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解React開發(fā)中使用require.ensure()按需加載ES6組件
本篇文章主要介紹了詳解React開發(fā)中使用require.ensure()按需加載ES6組件,非常具有實用價值,需要的朋友可以參考下2017-05-05
React?Hook?Form?優(yōu)雅處理表單使用指南
這篇文章主要為大家介紹了React?Hook?Form?優(yōu)雅處理表單使用指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
React為什么需要Scheduler調(diào)度器原理詳解
這篇文章主要為大家介紹了React為什么需要Scheduler調(diào)度器原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10

