ReactNative进阶(三十五):应用脚手架Yo构建RN页面

举报
SHQ5785 发表于 2021/05/24 01:37:16 2021/05/24
【摘要】 文章目录 一、前言二、Bloc数据流讲解三、利用代码自动生成功能创建新页面四、Bloc数据流使用说明拓展阅读 一、前言 前期将脚手架yo安装成功,本篇博文主要讲解如何利用yo提供的代码自动生成功能生成项目代码,话不多说,开干! 二、Bloc数据流讲解 Bloc数据流工具安装: sudo npm install -g yo sudo npm ...


一、前言

前期将脚手架yo安装成功,本篇博文主要讲解如何利用yo提供的代码自动生成功能生成项目代码,话不多说,开干!

二、Bloc数据流讲解

Bloc数据流工具安装:

sudo npm install -g yo
sudo npm install -g generator-bloc

  
 
  • 1
  • 2

安装完成后通过执行 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


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

三、利用代码自动生成功能创建新页面

此处以新建test界面为例介绍使用bloc创建界面的方法步骤,页面新建在项目中page-new目录下。

  1. 进入项目根目录输入命令:yo bloc 回车;
  2. 弹出选择界面后使用上下移动键选择: create a new page. 回车;
  3. 输入页面名字:test 回车;
  4. 选择语言:js 回车;
  5. 输入模块路径:src/page-new 回车;
  6. 选择新建页面所在的模块:home 回车;
  7. 选择平台:react-native 回车;
  8. 待页面创建完成,未报错则说明页面创建成功;
    在这里插入图片描述

通过以上步骤信息结合项目结构可知,

  • 脚手架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'),
};

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
export const USE_CASE_TYPES = {
  HomeUseCase: Symbol.for('HomeUseCase'),
	TestUseCase: Symbol.for('TestUseCase'),
};

  
 
  • 1
  • 2
  • 3
  • 4
export const REPOSITORY_TYPES = {
  HomeRepository: Symbol.for('HomeRepository'),
	TestRepository: Symbol.for('TestRepository'),
};

  
 
  • 1
  • 2
  • 3
  • 4
export const BLOC_TYPES = {
  HomeBloc: Symbol.for('HomeBloc'),
	TestBloc: Symbol.for('TestBloc'),
};

  
 
  • 1
  • 2
  • 3
  • 4
  • 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);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • home/route-config/HomeRouteConfig.js将新建页面添加到路由中;

将TestView添加到export中即可使用router.navigate(“TestView”)跳转到该页面。

注:本项目路由使用router:import { router } from 'mario-navigation-extension';

四、Bloc数据流使用说明

在这里插入图片描述

bloc数据流采用stream-builder的形式绑定数据,当stream绑定的数据有改变时,builder便会重新渲染界面,类似于RNsetState,下面以刚创建的TestView为例介绍使用步骤:

  1. 首先,该页面的入口文件为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} />;
};

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

其中init方法在TestBloc中定义。

  1. 接下来便是_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>
  );
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

其中stream为TestBloc中定义的数据,builder为界面UI,builder会获取绑定数据的快照snapshotsnapshot.data即为绑定的数据。

function _testBuilder(snapshot){
  if(snapshot.hasData){ return ( <Text>{JSON.stringify(snapshot.data)}</Text> )
  } else return ( <View></View>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

stream可以绑定多个数据流:

<StreamBuilder stream={_bloc.listData$.pipe(combineLatest(_bloc.searchObj$))} builder={_userListBuilder} />

  
 
  • 1
  1. 页面数据定义在 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);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

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
  };

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

!注:定义在TestBloc中的方法建议使用箭头函数,防止this指向跑偏。

  1. 网络请求使用封装好的WebService: src/main/service/WebService.js,WebService的BaseUrl为:src/main/constant/Constant.jsBASE_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); // 接口报错时更新界面UI return; } 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); // 刷新界面状态
  };

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

添加:

buildUseCasePromise(params) { return this.repository.queryList(params);
}

  
 
  • 1
  • 2
  • 3

添加接口参数,也就是this.useCase.execute传过来的参数,userCase的excute方法执行的是buildUseCasePromise方法,如果是其它方法需要自定义:

  handleCheck(params) { return to(this.repository.handleCheck(params));
  }

  
 
  • 1
  • 2
  • 3

此时需要将结果用to包裹,以规范返回数据的格式:

import { to } from '../../../../main/utilities';

  
 
  • 1

调用自定义方法时,需要将bloc里的调用方法由this.useCase.execute({})改为this.useCase.handleCheck({});

然后定义数据流策略,使用远程服务器数据还是本地数据库数据(定义在src/page-new/home/bloc/data/source/TestRepository.js:):

queryList(params) { return this.remoteDataSource.queryList(params);
}

  
 
  • 1
  • 2
  • 3

在该处可以进行数据的并行或者串行请求或者数据的其它一些处理:

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': [] } }
  }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

最后就是调用webService请求后台接口:

queryList(params) { return this.webService.request(queryTaskPageList, params);
 }

  
 
  • 1
  • 2
  • 3

其中params是传过来的,queryTaskPageList为接口名称,需要自定义:

const queryTaskPageList = 'queryTaskPageList'; 

  
 
  • 1

数据流如下图所示:
在这里插入图片描述

拓展阅读

文章来源: shq5785.blog.csdn.net,作者:No Silver Bullet,版权归原作者所有,如需转载,请联系作者。

原文链接:shq5785.blog.csdn.net/article/details/117185409

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。