ReactNative进阶(三十五):应用脚手架 Yo 构建 RN 页面
文章目录
- 一、前言
- 二、Bloc 数据流讲解
- 三、利用代码自动生成功能创建新页面
- 四、Bloc数据流使用说明
- 五、拓展阅读
一、前言
前期将脚手架yo
安装成功,本篇博文主要讲解如何利用yo
提供的代码自动生成功能生成项目代码。
二、Bloc 数据流讲解
Bloc 数据流工具安装:
sudo npm install -g yo
sudo npm install -g generator-bloc
安装完成后通过执行 npm ls generator-bloc -g
命令,查看bloc
模板生成位置。执行结果如下:
进入node_modules
目录,可看到生成的generator-bloc
文件夹,进入该文件夹,查看README.md
文件。
文件内容如下:
> # generator-bloc [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency
> Status][daviddm-image]][daviddm-url] [![Coverage
> percentage][coveralls-image]][coveralls-url]
> > react-bloc cli
>
> ## Installation
>
> First, install [Yeoman](http://yeoman.io) and generator-bloc using
> [npm](https://www.npmjs.com/) (we assume you have pre-installed
> [node.js](https://nodejs.org/)).
>
> ```bash npm install -g yo npm install -g generator-bloc ```
>
> Then generate your new project:
>
> ```bash yo bloc ```
>
> ## Getting To Know Yeoman
>
> * Yeoman has a heart of gold.
> * Yeoman is a person with feelings and opinions, but is very easy to work with.
> * Yeoman can be too opinionated at times but is easily convinced not to be.
> * Feel free to [learn more about Yeoman](http://yeoman.io/).
>
> ## License
>
> MIT © [MeePwn](https://github.com/maybewaityou)
>
>
> [npm-image]: https://badge.fury.io/js/generator-bloc.svg [npm-url]:
> https://npmjs.org/package/generator-bloc [travis-image]:
> https://travis-ci.org/maybewaityou/generator-bloc.svg?branch=master
> [travis-url]: https://travis-ci.org/maybewaityou/generator-bloc
> [daviddm-image]:
> https://david-dm.org/maybewaityou/generator-bloc.svg?theme=shields.io
> [daviddm-url]: https://david-dm.org/maybewaityou/generator-bloc
> [coveralls-image]:
> https://coveralls.io/repos/maybewaityou/generator-bloc/badge.svg
> [coveralls-url]: https://coveralls.io/r/maybewaityou/generator-bloc
三、利用代码自动生成功能创建新页面
此处以新建test界面为例介绍使用bloc创建界面的方法步骤,页面新建在项目中page-new目录下。
- 进入项目根目录输入命令:
yo bloc
回车; - 弹出选择界面后使用上下移动键选择:
create a new page
. 回车; - 输入页面名字:test 回车;
- 选择语言:
js
回车; - 输入模块路径:
src/page-new
回车; - 选择新建页面所在的模块:
home
回车; - 选择平台:
react-native
回车; - 待页面创建完成,未报错则说明页面创建成功;
通过以上步骤信息结合项目结构可知,
脚手架yo为我们新生成了
home/view/TestView.js、 home/bloc/TestBloc.js、home/bloc/interactor/TestUserCase.js、home/bloc/data/source/TestRepository.js、home/bloc/data/source/local/TestLocalDataSource.js、home/bloc/data/source/remote/TestRemoteDataSource.js
等文件。在
home/bloc/ioc/types.js
文件中新增bloc标识:
export const DATA_SOURCE_TYPES = {HomeRemoteDataSource: Symbol.for('HomeRemoteDataSource'),HomeLocalDataSource: Symbol.for('HomeLocalDataSource'),TestRemoteDataSource: Symbol.for('TestRemoteDataSource'),TestLocalDataSource: Symbol.for('TestLocalDataSource'),
};
export const USE_CASE_TYPES = {HomeUseCase: Symbol.for('HomeUseCase'),TestUseCase: Symbol.for('TestUseCase'),
};
export const REPOSITORY_TYPES = {HomeRepository: Symbol.for('HomeRepository'),TestRepository: Symbol.for('TestRepository'),
};
export const BLOC_TYPES = {HomeBloc: Symbol.for('HomeBloc'),TestBloc: Symbol.for('TestBloc'),
};
- 在
bloc/ioc/register.js
文件中新增容器配置:
import TestBloc from '../TestBloc';
import TestUseCase from '../interactor/TestUseCase';
import TestRepository from '../data/source/TestRepository';
import { TestRemoteDataSource } from '../data/source/remote/TestRemoteDataSource';
import { TestLocalDataSource } from '../data/source/local/TestLocalDataSource';container.bind(DATA_SOURCE_TYPES.TestRemoteDataSource).to(TestRemoteDataSource);
container.bind(DATA_SOURCE_TYPES.TestLocalDataSource).to(TestLocalDataSource);
container.bind(REPOSITORY_TYPES.TestRepository).to(TestRepository);
container.bind(USE_CASE_TYPES.TestUseCase).to(TestUseCase);
container.bind(BLOC_TYPES.TestBloc).to(TestBloc);
- 在
home/route-config/HomeRouteConfig.js
将新建页面添加到路由中;
将TestView添加到export
中即可使用router.navigate(“TestView”)
跳转到该页面。
注:本项目路由使用router
:import { router } from 'mario-navigation-extension';
四、Bloc数据流使用说明
bloc
数据流采用stream-builder
的形式绑定数据,当stream
绑定的数据有改变时,builder
便会重新渲染界面,类似于RN
的setState
,下面以刚创建的TestView为例介绍使用步骤:
- 首先,该页面的入口文件为
src/page-new/view/TestView.js
, 该界面只写view
渲染相关代码,export default ( ) ...
方法即为页面入口,该方法可接收路由传递的参数,可在此方法完成参数初始化,如果该页面需要在未被销毁的情形下多次加载,可以使用try catch
初始化,container.get(BLOC_TYPES.TestBloc)
方法会完成bloc
的唯一注册,只会在第一次初始化时渲染界面,重新渲染需要使用BlocProvider.of(BLOC_TYPES.TestBloc)
,因此多次加载同一界面可以使用如下代码:
export default (props) => {let _bloc = null;try {_bloc = BlocProvider.of(BLOC_TYPES.TestBloc);} catch (error) {_bloc = container.get(BLOC_TYPES.TestBloc);}const params = props.navigation.state.params;_bloc.init(params);return <BlocProvider bloc={_bloc} child={_viewBuilder} />;
};
其中init方法在TestBloc中定义。
- 接下来便是
_viewBuilder
中的界面UI:
function _viewBuilder() {const _bloc = BlocProvider.of(BLOC_TYPES.TestBloc);return (<View style={style.container}><StreamBuilder stream={_bloc.viewState$} builder={_testBuilder} /><StreamBuilder stream={_bloc.listData$} builder={_listBuilder} /></View>);
}
其中stream
为TestBloc中定义的数据,builder
为界面UI
,builder
会获取绑定数据的快照snapshot
,snapshot.data
即为绑定的数据。
function _testBuilder(snapshot){if(snapshot.hasData){return (<Text>{JSON.stringify(snapshot.data)}</Text>)} else return (<View></View>)
}
stream
可以绑定多个数据流:
<StreamBuilder stream={_bloc.listData$.pipe(combineLatest(_bloc.searchObj$))} builder={_userListBuilder} />
- 页面数据定义在
src/page-new/home/bloc/TestBloc.js
中,
listData = {viewState: {isRefresh: false,isLoading: true,hasMore: true,hint: '加载中...',pageNo: '1',pageSize: '15',},list: [],data: {},};listData$ = new EnhanceSubject(this.listData);viewState = {open: false,pickerItems: [{ text: '待发货', handleFlag: '1' }, { text: '已发货', handleFlag: '2' }],pickedItem: {text: '待发货',handleFlag: '1',},totalSize: 0,};viewState$ = new EnhanceSubject(this.viewState);
listData$
即为TestView中绑定的数据,当listData$
改变时界面就会重新渲染,例如:
init = (params) => {this.listData= processModify(this.listData, {viewState: {isRefresh: params.isRefresh,isLoading: params.isLoading,hint: '加载中...'}, });this.listData$.add(this.listData); // 相当于RN的setState};
!注:定义在TestBloc中的方法建议使用箭头函数,防止
this
指向跑偏。
- 网络请求使用封装好的WebService:
src/main/service/WebService.js
,WebService的BaseUrl为:src/main/constant/Constant.js
:BASE_URL
,请求参数有个meta
数据,该meta
数据会在真正请求前删除,使用meta: { silence: true }
可以在请求时不展示Loading
框,默认会加载Loading
框。
在bloc
中定义异步接口方法,
queryList = async (refresh, handleFlag) => {let index = refresh ? '1' : this.listData.viewState.pageNo;this.listData = processModify(this.listData, {viewState: {...this.listData.viewState,isRefresh: refresh,isLoading: !refresh,hint: '加载中...'},});this.listData$.add(this.listData); // 发起请求时刷新界面状态// 使用userCase调用接口,返回的是两个对象,第一个为接口报错(非业务逻辑错)时的对象,第二个为接口正常返回的对象const [error, data] = await this.useCase.execute({bizline: '4',tasktyp: '25',handleFlag: handleFlag || this.viewState.pickedItem.handleFlag,pageNo: index,pageSize: this.listData.viewState.pageSize,meta: { silence: true },});if (error) {...this.listData$.add(this.listData); // 接口报错时更新界面UIreturn;}let hasList = this.listData.list || [];if (refresh) {hasList = [];}let list = data.resultList;let isMore = list.length >= parseInt(this.listData.viewState.pageSize);let hint = '';if (hasList.length < 1 && list.length < 1) {hint = '抱歉~没有相关信息';}this.listData = processModify(this.listData, {list: hasList.concat(list),data,viewState: {...this.listData.viewState,isRefresh: false,isLoading: false,hasMore: isMore,pageNo: `${parseInt(index) + 1}`,hint: hint || (isMore ? '加载更多' : '没有更多了'),}});this.listData$.add(this.listData); // 请求得到后台数据后刷新界面数据this.viewState = processModify(this.viewState, {totalSize: data.totalSize,});this.viewState$.add(this.viewState); // 刷新界面状态};
添加:
buildUseCasePromise(params) {return this.repository.queryList(params);
}
添加接口参数,也就是this.useCase.execute
传过来的参数,userCase的excute
方法执行的是buildUseCasePromise
方法,如果是其它方法需要自定义:
handleCheck(params) {return to(this.repository.handleCheck(params));}
此时需要将结果用to
包裹,以规范返回数据的格式:
import { to } from '../../../../main/utilities';
调用自定义方法时,需要将bloc里的调用方法由this.useCase.execute({})
改为this.useCase.handleCheck({})
;
然后定义数据流策略,使用远程服务器数据还是本地数据库数据(定义在src/page-new/home/bloc/data/source/TestRepository.js
:):
queryList(params) {return this.remoteDataSource.queryList(params);
}
在该处可以进行数据的并行或者串行请求或者数据的其它一些处理:
async queryList(params) {const data = await this.remoteDataSource.queryList({ meta: { silence: true } });if (data.list.length > 0) {let customeridStr = '';data.list.forEach((item) => {customeridStr += item.customerid + ',';});// 串行接口const signalData = await this.remoteDataSource.queryNewsTheme({ customeridStr, flag: 'index', ...params, meta: { silence: true } });let listIndex = []for (let i = 0; i < signalData.listIndex.length; i++) {...}return { totalSize: signalData.totalSize, listIndex };} else {return { 'listIndex': [] }}}
最后就是调用webService请求后台接口:
queryList(params) {return this.webService.request(queryTaskPageList, params);}
其中params是传过来的,queryTaskPageList为接口名称,需要自定义:
const queryTaskPageList = 'queryTaskPageList';
数据流如下图所示:
五、拓展阅读
- 《ReactNative进阶(三十二):前端构建工具–Yeoman》
- 《ReactNative进阶(三十一): IoC 框架 InversifyJS解读》
- 《ReactNative进阶(二十九):BloC模式》
ReactNative进阶(三十五):应用脚手架 Yo 构建 RN 页面相关推荐
- 跨平台应用开发进阶(三十五) :Android权限列表permission说明
文章目录 一.前言 二.权限信息汇总 一.前言 uni-app开发完APP后,上架到应用市场,审核时会对APP内部设置的权限进行核准,并给出相应的理由. 如项目中有以下权限设置: "andr ...
- NeHe OpenGL第三十五课:播放AVI
NeHe OpenGL第三十五课:播放AVI 在OpenGL中播放AVI: 在OpenGL中如何播放AVI呢?利用Windows的API把每一帧作为纹理绑定到OpenGL中,虽然很慢,但它的效果不错. ...
- 微信小程序把玩(三十五)Video API
原文:微信小程序把玩(三十五)Video API 电脑端不能测试拍摄功能只能测试选择视频功能,好像只支持mp4格式,值得注意的是成功之后返回的临时文件路径是个列表tempFilePaths而不是tem ...
- Gradle 1.12用户指南翻译——第三十五章. Sonar 插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- Python编程基础:第三十五节 文件删除Delete a File
第三十五节 文件删除Delete a File 前言 实践 前言 我们这一节来介绍如何删除一个文件,这里需要用到函数os.remove(path)用于删除指定路径下的文件,os.rmdir(path) ...
- 东枝戛古舍利佛塔群(缅三十五)
东枝戛古舍利佛塔群(缅三十五) 撰文.摄影/张源 http://szju2000.blog.163.com/blog/static/16995144420115137370192/ 有2000 ...
- 三十五、深入Java中的泛型(下篇)
@Author:Runsen @Date:2019年10月26日 17:10:34 作者介绍:Runsen目前大三下学期,专业化学工程与工艺,大学沉迷日语,Python, Java和一系列数据分析软件 ...
- [Python从零到壹] 三十五.图像处理基础篇之OpenCV绘制各类几何图形
欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...
- 鲁迅散文——随感录三十五
随感录三十五 从清朝末年,直到现在,常常听人说"保存国粹"这回一句话. 前清末年说这话的人,大约有两种:一是爱国志士,一是出洋游历的大官.他们在这题目的背后,各各藏着别的意思.志士 ...
最新文章
- 敏捷方法适合什么样的团队?
- 王木匠,我服了你一半
- PHP 之 函数 sprintf() 的学习
- ubuntu etc目录下配置文件profile和profile.d的区别?(文件解析)
- xbox虚拟服务器,Xbox One平台真相:原生Win8/虚拟化运行
- Debit and Credit Memo
- phpstorm 2019.1 mac
- 五子棋的禁手c++语言实现,C++实现简单五子棋游戏
- Word 2003中打开最近操作过的文档的两种推荐的方法
- PrinterLogic 厂商修复影响所有联网端点的3个RCE漏洞
- 一步到位,用SQL检索ElasticSearch
- python中∧_python中xor是什么意思
- 极光IM使用教程-极光推送
- AX2012 常用表关系(客户地址,联系信息)
- Stack Frame JAVA运行时数据区域之栈帧
- java 填充图片_java图片缩放实现图片填充整个屏幕
- iPhone如何快速设置自定义铃声?苹果手机铃声设置教程
- ARPG游戏的战斗系统设计
- Dissect Eclipse Plugin Framework
- 电子科技大学计算机科学与技术考研复试,电子科技大学计算机科学与工程学院2021考研招生复试工作安排...
热门文章
- Unix C语言编写基于IO多路复用的小型并发服务器
- springmvc入门:web.xml编写
- Ubuntu WPS系统缺失字体symbol、wingdings、wingdings wingdings webding
- 前端工程师的摸鱼日常(12)
- K-means (代碼)
- ssm 基于微信小程序美容理发店预约系统app
- Wiz写Blog? 不会再爱了,全面拥抱Markdown+Pandoc
- Android系统添加USB共享网络
- 【翻译】HyNet: Learning Local Descriptor with Hybrid Similarity Measure and Triplet Loss
- python初学的小坑