reactnative示例代码(React Native项目框架搭建的一些心得体会)
reactnative示例代码
React Native项目框架搭建的一些心得体会React Native 是Facebook于2015年4月开源的跨平台移动应用开发框架, 短短的一两年的发展就已经有很多家公司支持并采用此框架来搭建公司的移动端的应用,
React Native使你能够在Javascript和React的基础上获得完全一致的开发体验,构建世界一流的原生APP。
1. 项目中使用的技术栈
react native、react hook、typescript、immer、tslint、jest等.
都是比较常见的,就不多做介绍了
2. 数据处理使用的是react hook中的useContext+useReducer
思想与redux是一致的,用起来相对比较简单,适合不太复杂的业务场景.
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. 项目的结构如下
|-page1 |-handler // 处理逻辑的纯函数,需进行UT覆盖 |-container // 整合数据、行为与组件 |-component // 纯UI组件,展示内容与用户交互,不处理业务逻辑 |-store // 数据结构不能超过3层,可使用外部引用、冗余字段的方式降低层级 |-reducer // 使用immer返回新的数据(immutable data) |-... |-page2 |-...
1. Page
整个项目做为一个多页应用,最基本的拆分单元是page.
每一个page有相应的store,并非整个项目使用一个store,这样做的原因如下:
- 各个页面的逻辑相对独立
- 各个页面都可作为项目入口
- 结合RN页面生命周期进行数据处理(避免数据初始化、缓存等一系列问题)
各个页面中与外部相关的操作,都在Page组件中定义
- 页面跳转逻辑
- 回退之后要处理的事件
- 需要操作哪些storage中的数据
- 需要请求哪些服务等等
以其自身业务模块为基础,把可以抽象出来的外部依赖、外部交互都集中到此组件的代码中.
方便开发人员在进行各个页面间逻辑编写、问题排查时,可根据具体页面+数据源,准确定位到具体的代码.
2. reducer
在以往的项目中,reducer中可能会涉及部分数据处理、用户行为、日志埋点、页面跳转等等代码逻辑.
因为在开发人员写代码的过程中,发现reducer作为某个处理逻辑的终点(更新了state之后,此次事件即为结束),很适合去做这些事情.
随着项目的维护,需求的迭代,reducer的体积不断的增大.
因为缺乏条理,代码量又庞大,再想去对代码进行调整,只会困难重重.
让你去维护这样的一个项目,可想而知,将会是多么的痛苦.
为此,对reducer中的代码进行了一些减法:
- reducer中只对state的数据进行修改
- 使用immer的produce产生immutable data
- 冗余单独字段的修改,进行整合,枚举出页面行为对应的action
以可枚举的形式,汇总出页面中所有操作数据的场景.
在其本身适用于react框架的特性之外,赋予一定的业务逻辑阅读属性,在不依赖UI组件的情况下,可大致阅读出页面中的所有数据处理逻辑.
// 避免dispatch时进行两次,且定义过多单字段的更新case // 整合此逻辑后,与页面上的行为相关联,利于理解、阅读 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
这里先引入一个纯函数的概念:
一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,我们就把这个函数叫做纯函数.
把尽可能多的逻辑抽象为纯函数,然后放入handler中:
- 涵盖较多的业务逻辑
- 只能是纯函数
- 必须进行UT覆盖
负责数据源到store、container到component、dispatch到reducer等等场景下的逻辑处理.
作为各类场景下,逻辑处理函数的存放地,整个文件不涉及页面流程上的关联关系,每个函数只要满足其输入与输出的使用场景,即可复用,多用于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
由上面的项目结构图可以看出,每个Page都有base Container,作为数据处理的中心.
在此base Container之下,会根据不同模块,定义出各个子Container:
- 生命周期处理(初始化时要进行的一些异步操作)
- 为渲染组件Components提供数据源
- 定义页面中的行为函数
整个项目中,各种数据、UI、用户行为的汇合点,要尽可能的把相关的模块抽离出来,避免造成代码量过大,难以维护的情况.
Container的定义应以页面展示的模块进行抽象.如Head Contianer、Content Container、Footer Container等较为常见的划分方式.
一些页面中相对独立的模块,也应该产出其对应的Container,来内聚相关逻辑,如赠送优惠券模块、用户反馈模块等.
特别注意的是行为函数- 多个Container中公用的行为,可直接放入base Container中
- 在上文架构图中的action事例(setAction)为另外一种行为复用,根据具体的场景进行应用
利于代码阅读,A模块的浮层展示逻辑,B模块使用时
模块产生的先后顺序,先有A模块再有B模块需要使用A的方法
- 定义数据埋点、用户行为埋点
- 页面跳转方法的调用(Page-->base Container-->子Container)
- 其他副作用的行为
const OWFlightListContainer = () => { // 通过Context获取数据 const { state, dispatch } = useContext(OWFlightListContext); ... // 初次加载时进行超时的倒计时 useOnce(overTimeCountDown); ... // 用户点击排序 const onPressSort = (lastSortType: EFlightSortType, isTimeSort: boolean) => { // 引用了handler中的getNextSortType函数 const sortType = getNextSortType(lastSortType, isTimeSort); dispatch({ type: EOWFlightListAction.updateSortType, payload: sortType }); // 埋点操作 logSort(state, sortType); }; // 渲染展示组件 return <.../>; }
由easy to code到easy to read
在整个项目中,定义了很多规范,是想在功能的实现之上,更利于项目人员的维护.
- Page组件中包含页面相关的外部依赖
- reducer枚举出所有对页面数据操作的事件
- handler中集合了业务逻辑的处理,以纯函数的实现及UT的覆盖,确保项目质量
- Container中的行为函数,定义出所有与用户操作相关的事件,并记录埋点数据
- Componet中避免出现业务逻辑的处理,只进行UI展示,减少UI自动化case,增加UT的case
规范的定义是比较容易的,想要维护好一个项目,更多的是依靠团队的成员,在达成共识的前提下,持之以恒的坚持了
分享几个实用的函数根据对象路径取值
/** * 根据对象路径取值 * @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);
读取根据配置信息
// 在与外部对接时,经常会定义一些固定结构,可扩展性的数据列表 // 为了适应此类契约,便于更好的阅读与维护,总结出了以下函数 export const GLOBAL_NOTE_CONFIG = { 2: 'refund', 3: 'sortType', 4: 'featureSwitch' }; /** * 根据配置,获取attrList中的值,返回json对象类型的数据 * @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]; // 有多个值时,改为数组类型 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 // 适用于外部定义的一些可扩展note节点列表的取值逻辑 const { sortType, featureSwitch } = getNoteValue(list, GLOBAL_NOTE_CONFIG, 'ntype');
多条件数组排序
/** * 获取用于排序的sort函数 * @param fn 同类型元素比较函数,true为排序优先 */ 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; }); //判断条件先后顺序可调整 const arr = [nameSort, ageSort, sexSort]; const ret = data.sort(getMultipleSort(arr));
以上就是React Native项目框架搭建的一些心得体会的详细内容,更多关于React Native项目框架搭建的资料请关注开心学习网其它相关文章!
- react循环有几种方法(React forwardRef的使用方法及注意点)
- reactnative示例代码(React Native项目框架搭建的一些心得体会)
- react hooks详解(React Hooks使用常见的坑)
- react代码展示(教你如何从 html 实现一个 react)
- react怎么使用父组件(关于antd tree和父子组件之间的传值问题react 总结)
- react新手指引页面编写(React+TypeScript进行项目构建案例讲解)
- react动态创建菜单并实现局部刷新(使用react-virtualized实现图片动态高度长列表的问题)
- react初学难点(使用react的7个避坑案例小结)
- react执行流程(React开启代理的2种实用方式)
- react组件封装成函数方法(React虚拟列表的实现)
- yii2对比springboot(yii2.0框架使用 beforeAction 防非法登陆的方法分析)
- vscode react插件(基于visual studio code + react 开发环境搭建过程)
- react源码教程(详解React 代码共享最佳实践方式)
- react怎么绑定state(react纯函数组件setState更新页面不刷新的解决)
- linux虚拟内存实现需要哪六种机制(解析Linux高性能网络IO和Reactor模型)
- react的动画实现(React实现动效弹窗组件)
- 文明6金币太少怎么办 文明6无限刷钱教程(文明6金币太少怎么办)
- 开国中将,王牌军63军首任政委,两个连襟一个上将一个少将传为佳话(王牌军63军首任政委)
- 臭名昭著的731部队最高负责人 石井四郎(臭名昭著的731部队最高负责人)
- 王牌部队,你看的剧情我看的时尚(你看的剧情我看的时尚)
- 被鉴定的古董价值300万 当心,你可能遇到诈骗了(被鉴定的古董价值300万)
- 英语难学吗(初中英语难学吗)
热门推荐
- python获取字符串类型(python3利用ctypes传入一个字符串类型的列表方法)
- vue定时器中间变颜色(Vue如何优雅的清除定时器)
- iis服务器ftp设置(在云服务器上使用iis搭建一个ftp站点的方法图解)
- css3伸缩布局教学(css3 利用transform-origin 实现圆点分布在大圆上布局及旋转特效)
- django参数配置(详解重置Django migration的常见方式)
- mysql 操作日志(MySQL中的undo日志)
- phpstudy如何部署在linux上(phpstudy linux面板CC防护功能使用教程)
- 程序员面试需要注意的地方
- vmware虚拟机搭建服务器(VMware虚拟机建立HTTP服务步骤解析)
- 如何用iis7.5设置网站(IIS .7z文件支持下载的添加方法)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9