基于 React 搭建的 安卓,Ios,Web 三端架构(附上 IceE 三端框架)
前言
本节介绍基于该架构的一个 CMS 项目,如果你没有兴趣了解,可以直接跳到下一节
本栏目的文章基于 IceEmblem 架构设计
Github:https://github.com/IceEmblem
演示地址:http://www.iceemblem.cn/
演示账号/密码:admini/123456 (是admini,不是admin)
IceEmblem 是跨越了三端的 CMS 产品,可视化编辑,通过组件堆积搭建页面,组件数据配置极为灵活,可发布各种类型的文章,如下一些示例图片
如下是通过 CMS 生成的 Web 端和 App 端页面
如果你觉得这个页面有点丑,那么你可以随意编辑这个页面
当然,你也可以编辑 App 页面
IceEmblem 的灵活程度超乎你以为的,看到这里有的同学就有问了,这么 perfect 的项目到底在哪里,好吧,项目在这里,各位看官行行好,我已经好多天没吃到 Star 了,有没有人给我个 Star >_<|||
Github:https://github.com/IceEmblem
IceEmblem 的架构图
好了介绍完毕,如下开始介绍 IceE 框架
三端框架 IceE
github 地址:https://github.com/IceEmblem/IceE
github 上有开始使用教程,这里就不介绍了如果安装了
框架架构(目前阶段,随时更新)
可能 IceEmblem 的架构图比较复杂,反正你们也不看,所以这里还是介绍 IceE框架 架构吧
如上架构分为 3 个区域,WebApp,RNApp,Common,顾名思义
WebApp:Web 端的代码
RNApp:ReactNative 端的代码
Common:公共代码
这里的区域分别对应项目的三个文件夹
每个区域下面都有很多模块(如 RNApp 包含 3 个模块 RNStart,RNCommon …),所以我们的代码目录如下:
注:RNTest 是一个示例模块,我没有再架构图中画出来
区域依赖关系
WebApp 和 RNApp 均依赖于 Common,而 Common 不能有依赖于这两个区域,可以说,删除 WebApp 和 RNApp,Common 也可以正常通过编译
Common 区域下的代码,Web 端,RN 端均可使用
模块系统
上面所提供的模块不同于我们 ES6 中所说的模块,IceEmblem 所说的模块相当于一个 npm 包(C# 的一个项目或者说一个 dll)
IceE 的启动依赖于模块系统,模型系统的设计很简单,如下
如下,是一个 Test 模块的声明
import {BaseModule, PageProvider, ModuleFactory, Page} from 'ice-common'
import CoreModule from 'Core/Module';
import Test from './Test'export default class Module extends BaseModule
{initialize(){PageProvider.register(new Page("Test", "/Test", Test));}
}// 向模块工厂组成本模块,本模块依赖于 CoreModule 模块
new ModuleFactory().register(Module, [CoreModule
]);
Test 模块的目录
如何声明一个模块
要声明一个模块,你需要再区域下面新建一个文件夹,再其下并新建一个 package.json 包声明,并运行 yarn install 安装该包,然后新建一个 Module.js 文件,Module.js 的内容参考 Test 模块,如此,便完成一个模块的声明,IceE 会自动运行该模块
模块的运行顺序
每个模块都有三个方法
class BaseModule
{constructor(){}preInitialize(){}initialize(){}postInitialize(){}
}export default BaseModule;
分别对应 初始化前 preInitialize,初始化 initialize,初始化后 postInitialize
以 Test 模块示例
Test 模块依赖于 Core 模块,所以执行顺序为,依此类推其他模块
- Core.preInitialize
- Test.preInitialize
- Core.initialize
- Test.initialize
- Core.postInitialize
- Test.postInitialize
异步初始化:preInitialize,initialize,postInitialize 允许返回一个 Promise,如果返回 Promise,则会等待该 Promise 执行完成后才进入下一步
关于模块依赖关系
Core模块是整个项目的核心,所有模块都直接或间接依赖于该模块
IceE 启动介绍
IceE 的启动很简单,如下代码
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import { Provider } from 'react-redux'// 导入当前模块
import './Module'import {ModuleFactory} from 'ice-common'
import {IEStore} from 'ice-common'
import {PageProvider} from 'ice-common'// 部件
import Error from './Parts/Error';
import Loading from './Parts/Loading'import 'css/bootstrap.min.css'
import 'css/open-iconic-bootstrap.min.css';
import 'css/common.css';import './index.css';import {Spin} from 'antd'let moduleFactory = new ModuleFactory();
// 运行所有模块
moduleFactory.init().then(()=>{// 运行完成后// 获取 redux storelet store = IEStore.ieStore;// 向页面渲染ReactDOM.render(<Provider store={store}><BrowserRouter><Suspense fallback={<Spin className="w-100 h-100" size="large"></Spin>}><Switch>{PageProvider.pages.map(item => (<Route key={item.url} path={item.url} component={item.component} />))}</Switch></Suspense><Error /><Loading /></BrowserRouter></Provider>,document.getElementById('root'));});
你可能会区域内的模块为什么会自动被 import 到系统中,那么,请你网下看
导入模块
上面代码中有一段代码如下
// 导入当前模块
import './Module'
Module.js 代码
import {BaseModule} from 'ice-common'
import {ModuleFactory} from 'ice-common'
import CoreModule from 'Core/Module';
import ModuleList from '../../ModuleList';
import {MiddlewareFactory} from 'ice-common'
import ReduxErrorMiddleware from './Middlewares/ReduxErrorMiddleware'export default class Module extends BaseModule
{initialize(){MiddlewareFactory.register(ReduxErrorMiddleware);}
}// ModuleList 为当前区域的所有模块,ModuleList 在 js 编译阶段生成
new ModuleFactory().register(Module, [...ModuleList, CoreModule]);
原因就是:Start 模块自动导入了当前区域的所有模块
ice-common 包提供的其他功能
ice-common 包位于 Common 区域下,它是构建整个框架的核心,
AntIcons
AntIcons 是一个图标的封装,如下:
class AntIcons {icons : Map<string, React.Component>;getIcon : (name: IconNameType, style: any) => React.Component;
}
icons 是 AntIcons 的所有图标
getIcon 获取图标
如果要扩展图标,你需要扩展 IconNameType 类型,然后修改 RNApp 区域的 RNCommon 的 AntIcons,然后在修改 WebApp 区域的 Common 的 AntIcons,修改很简单,看源码即可
IocContainer
IocContainer 并不能算做依赖注入容器,更像是一个键值对注册工具,其使用如下:
import React from 'react'
import {IocContainer} from 'ice-common';// 要注册的类型
export class ICommonStyleConfigComponent extends BaseConfig{}
// iocKey 是必须的
ICommonStyleConfigComponent.iocKey = Symbol();export default class CommonStyleConfigComponent extends ICommonStyleConfigComponent {render() {return <></>}
}// 向 IocContainer 注册 ICommonStyleConfigComponent 单例实例,其实例为 CommonStyleConfigComponent
IocContainer.registerSingleIntances(ICommonStyleConfigComponent, CommonStyleConfigComponent);// 获取 ICommonStyleConfigComponent 类型的实例
IocContainer.getService(ICommonStyleConfigComponent);
PageProvider
页面提供者提供了页面的注册,如下,我们一般在模块初始化时向其注册页面
import {BaseModule, PageProvider, ModuleFactory, Page} from 'ice-common'
import CoreModule from 'Core/Module';
import Test from './Test'export default class Module extends BaseModule
{initialize(){// 注册一个页面名为 Test,其 url 为 /Test,其使用的组件为 Test// 当访问 /Test 时,会显示 Test 组件PageProvider.register(new Page("Test", "/Test", Test));}
}// 向模块工厂组成本模块,本模块依赖于 CoreModule 模块
new ModuleFactory().register(Module, [CoreModule
]);
Theme 主题
IceE 提供了动态修改主题,并提供了当前主题的 10 中颜色,默认主题为 明青色,如果想修改默认主题,修改 Theme 即可
主题特定平台的代码在 WebApp 区域的 Common 模块,RNApp 区域的 RNCommon 模块
IETool
IETool 是一个工具类,其封装了一些方法
BaseIERedux
BaseIERedux 是对 Redux 的一个封装,如下是 BaseSetting 模块的示例:
BaseSetting 的 IERedux.js
import {BaseIERedux} from 'ice-common'class Redux extends BaseIERedux
{getStateType(): string {return "Setting";}
}export default new Redux()
BaseSetting 的 Module.js
import {BaseModule} from 'ice-common'
import {ModuleFactory} from 'ice-common'
import {IEStore} from 'ice-common'
import {getSiteSettingsFetch} from './IEReduxs/Actions'
import Redux from './IEReduxs/IERedux'
import {reducer} from './IEReduxs/Reducer'
import RootRedux from 'Core/IEReduxs/RootRedux'
import CoreModule from 'Core/Module'export default class Module extends BaseModule
{initialize(){// 设置当前模块的 Redux 所使用的 reducerRedux.setReducer(reducer);// 将当前 Redux 注册到 RootRedux 下RootRedux.register(Redux);}postInitialize(){IEStore.ieStore.dispatch(getSiteSettingsFetch());}
}new ModuleFactory().register(Module, [CoreModule
]);
将当前 Redux 注册到 RootRedux 下,实际会生成这样一个 state 结构
state = {// 这里是根 stateSetting: {// 这里是 BaseSetting 模块的 state// 这里的 siteSettings 只是一个假设数据siteSettings: {...}...}
}
使用 Redux
import React from 'react';
import IERedux from 'BaseSetting/IEReduxs/IERedux';import './index.css'class SiteSetting extends React.Component {render() {return (<></>);}
}const mapStateToProps = (state, ownProps) => { // ownProps为当前组件的propsreturn {siteSettings: state.siteSettings,}
}const mapDispatchToProps = (dispatch, ownProps) => {return {}
}// 使用 BaseSetting 的 IERedux
const Container = IERedux.connect(mapStateToProps, // 关于statemapDispatchToProps
)(SiteSetting)export default Container;
封装的好处就是我们不用从根 state 下面获取,而直接从当前模块获取,还有一个看不见的好处,我们不用关心其他模块的 state
IEStore
class IEStore {ieStore = undefined;createIEStore(reducer) {...}
}export default new IEStore();
IEStore 是对 redux store 的封装,我们不用关心其 createIEStore 方法,框架初始化时会调用,我们可以通过 IEStore.ieStore 获取当前的 store
MiddlewareFactory
MiddlewareFactory 是对 Redux 中间件的封装,在模块 initialize 方法中,我们可以向 MiddlewareFactory 注册中间件
Core 模块提供的功能
ApiScopeAuthorityManager
ApiScopeAuthorityManager(Api域权限管理器),这是针对 Api 访问的权限检查,其设计如下:
UserScopeAccessAuthority 是当前用户可以访问的 Api 域
AccessScope 是 Api 域
其源码很简单
import AccessScope, { ApiScopeNodeType } from "./AccessScope";
import UserScopeAccessAuthority from "./UserScopeAccessAuthority";export default class ApiScopeAuthorityManager
{userScopeAccessAuthorities: Array<UserScopeAccessAuthority>;// 实例化 ApiScopeAuthorityManager// userScopeAccessAuthorities 是用户可以访问的 Api 域集合constructor(userScopeAccessAuthorities:Array<UserScopeAccessAuthority>) {this.userScopeAccessAuthorities = userScopeAccessAuthorities;}// 检查当前用户是否可以访问 accessScope 这个 Api域isAllowAccessScope(accessScope: AccessScope){for(let item in this.userScopeAccessAuthorities){if(accessScope.isAllowAccess(this.userScopeAccessAuthorities[item])){return true;}}return false;}
}
IEToken
IEToken 封装了 IEToken 的存取,在 Web 会存在 Cookie 中,RN 则会存在 缓存中
Core 模块 > IEReduxs > Actions > createIEThunkAction
createIEThunkAction 是对 redux ThunkAction 的封装,由于每个系统的后端返回的数据格式均不一致,所以这里不进行封装,在使用前 createIEThunkAction,你需要对其进行修改,其源码并不难
如果不使用 createIEThunkAction ,你将无法使用框架提供的错误处理方式
// 生成 ieThunkAcion,如果请求成功
// url: 请求的 url
// postData: 发送的数据
// actionType: 数据接收成功后要分发的 action 类型
// method: fetch 方法类型,默认 post
// isPackage: 结果是否有包装有,一般后端都会包装 post 请求结果(如后端返回{ success: true, message: "", result: "正真的结果数据" })
export function createIEThunkAction(url:string, postData:any, actionType:string, method: string = 'post', isPackage: boolean = true) {return async function (dispatch:any) {let curFecthSign = fecthSign++;let requestAction = request(postData);requestAction.fecthSign = curFecthSign;dispatch(requestAction);// 生成 fetch 请求数据结果let token = await IEToken.getToken();let headers : any = {'Content-Type': 'application/json'}if(token && token != ""){headers.Authorization = "Bearer " + token;}return await fetch(Weburl.handleWeburl(url), {method: method,headers: headers,body: postData && JSON.stringify(postData)}).then(response => {// 再这里处理 html 异步请求结果,如 404 等问题if (response.status >= 200 && response.status < 300) {return response.json();}if(response.status == 400 || response.status == 401 || response.status == 403 || response.status == 500){return response.json();}const error = new Error(response.statusText);throw error;}).then(data => {// 在这里处理后端的结果,如 后端返回 { success: false, message: "文章发表以达到上限", result: null }// 如果后端处理通过,你应该将数据返回,如 return data.result;// 否则,你应该抛出异常 throw new Error(data.message);// 后续的处理流程由框架处理if(!isPackage){return data;}if (data.success == true){return data.result;}if(data && data.error.validationErrors && data.error.validationErrors.length > 0){throw new Error(data.error.validationErrors[0].message);}throw new Error(data.error.message);}).catch(errorData => {let errorAction = error(errorData.message);errorAction.fecthSign = curFecthSign;dispatch(errorAction);return Promise.reject(errorData.message);}).then(value => {let receiveAction = receivePack(actionType, value);receiveAction.fecthSign = curFecthSign;dispatch(receiveAction);return value;})}
}
IEReduxFetch
IEReduxFetch 封装了 fetch,其每次请求时会触发 redux 的动作,使用 IEReduxFetch 之前,你需要更改 Core 模块 > IEReduxs > Actions > createIEThunkAction 方法
RNInfrastructure 模块提供的功能
RNInfrastructure 的目的是为了封装对设备的调用,模块提供了几个封装
Camera:摄像头调用
IEWebView:WebView 高度自适应封装
Device:当前设备的信息
好吧,这篇文章就到这里,其他模块没什么好介绍的,后续如果有更新请留意如下教程地址,bye star
本系列教程文章地址https://blog.csdn.net/dabusidede/category_10348509.html
基于 React 搭建的 安卓,Ios,Web 三端架构(附上 IceE 三端框架)相关推荐
- 基于React搭建个人空间(更新版)
基于React搭建个人空间 开始 注意 原理 涉及知识点 项目 项目结构 描述 源码 Login Home Header Asider Message Photo 其它文件 运行项目 效果图 开始 注 ...
- OpenStack环境搭建(四:web控制端各节点的部署及配置)
实验要求: 完成Virtual box平台安装,会应用相关操作: 在virtual box虚拟平台上部署Fuel Master节点: 在virtual box虚拟平台上部署计算节点Computer: ...
- Thinkphp5新版聚合VIP影视APP源码 安卓/IOS苹果双端
Thinkphp5新版聚合VIP影视APP源码 安卓/IOS苹果双端,非常棒的一款在线视频VIP解析APP,代理裂变版 下载地址:http://www.sucaihuo.com/source/1251 ...
- 基于React搭建一个美团WebApp
一:React基础准备 1.1React是一个专注于View层的组件库,React提供设计的时候目标是尽可能的是接近原生,没有设计出很多高级的功能,这也为后来开发者自定义功能模块提供了很大的空间 1. ...
- React Native适配安卓IOS刘海屏、异形屏方案
首先顶部引入这几个模块 import {Platform,SafeAreaView,NativeModules,StatusBar } from "react-native"; c ...
- 短信平台系统搭建 后台功能管理 web源码架构 多线路由通道详解介绍
一:短信通道路由模块 系统根据需要开发了专业版本的功能,主要特色功能是: 1:通道路由模块 2:客户模板审核和管理模块 3:客户账号审核和管理模块 4:客户签名审核和管理哦快 二:后台账号管理 1:权 ...
- 基于React的仿QQ音乐(移动端)
前言 由于这段时间工作上也是挺忙的,就没有时间去写这个项目,中间一直都是写写停停,进度也是非常慢的.正好前几天都还比较空,就赶紧抓着空闲时间去写这个项目,最后紧赶慢赶地完成了.本项目采用了React的 ...
- Web全栈架构师(三)——NodeJS+持久化学习笔记(2)
NodeJS+持久化学习笔记 持久化 nodejs中实现持久化的方法 文件系统数据库 MySQL 资源 安装配置 node.js原生驱动 Sequelize 基本使用: Getters & S ...
- Mdebug:基于React开发的移动web调试工具
作者:thinkchen,腾讯 PCG 高级前端开发工程师 mdebug是腾讯新闻 TNTWEB 团队推出的基于React开发的新的web调试工具, 沉淀自腾讯新闻微信手 q 双插件多年的移动 web ...
最新文章
- Verilog中`define和parameter有什么区别
- python中栈_Python中的栈
- python扩展库安装
- 需要额外端口信息_二端口网络及算例
- IIS优化-解决IIS访问速度慢问题
- JDBC:深入理解PreparedStatement和Statement
- 分享一个关于跨境电商的竞品分析报告
- 中兴b860刷机运行Linux,全国各地中兴B860A刷机越狱全贴(2016年2月26日更新)
- Ubuntu18.04安装后检测不到集成声卡问题
- Hadoop解除安全模式
- github 访问慢,一键加速,完美访问
- 初次使用tshark小结
- 计算机视觉--实时车速检测
- 电子货架标签----智能仓库管理
- poj解题报告——2325
- 解决安装 fireworks、photoshop 时卡在输入账号、手机号处等问题
- 妄想山海采矿/自动精灵
- linux snmp 客户端,从Linux SNMP说起到community strings
- YouTube图片幻灯片分享技巧
- 『JavaScript』JS简介