前言

本节介绍基于该架构的一个 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 模块,所以执行顺序为,依此类推其他模块

  1. Core.preInitialize
  2. Test.preInitialize
  3. Core.initialize
  4. Test.initialize
  5. Core.postInitialize
  6. 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 三端框架)相关推荐

  1. 基于React搭建个人空间(更新版)

    基于React搭建个人空间 开始 注意 原理 涉及知识点 项目 项目结构 描述 源码 Login Home Header Asider Message Photo 其它文件 运行项目 效果图 开始 注 ...

  2. OpenStack环境搭建(四:web控制端各节点的部署及配置)

    实验要求: 完成Virtual box平台安装,会应用相关操作: 在virtual box虚拟平台上部署Fuel Master节点: 在virtual box虚拟平台上部署计算节点Computer: ...

  3. Thinkphp5新版聚合VIP影视APP源码 安卓/IOS苹果双端

    Thinkphp5新版聚合VIP影视APP源码 安卓/IOS苹果双端,非常棒的一款在线视频VIP解析APP,代理裂变版 下载地址:http://www.sucaihuo.com/source/1251 ...

  4. 基于React搭建一个美团WebApp

    一:React基础准备 1.1React是一个专注于View层的组件库,React提供设计的时候目标是尽可能的是接近原生,没有设计出很多高级的功能,这也为后来开发者自定义功能模块提供了很大的空间 1. ...

  5. React Native适配安卓IOS刘海屏、异形屏方案

    首先顶部引入这几个模块 import {Platform,SafeAreaView,NativeModules,StatusBar } from "react-native"; c ...

  6. 短信平台系统搭建 后台功能管理 web源码架构 多线路由通道详解介绍

    一:短信通道路由模块 系统根据需要开发了专业版本的功能,主要特色功能是: 1:通道路由模块 2:客户模板审核和管理模块 3:客户账号审核和管理模块 4:客户签名审核和管理哦快 二:后台账号管理 1:权 ...

  7. 基于React的仿QQ音乐(移动端)

    前言 由于这段时间工作上也是挺忙的,就没有时间去写这个项目,中间一直都是写写停停,进度也是非常慢的.正好前几天都还比较空,就赶紧抓着空闲时间去写这个项目,最后紧赶慢赶地完成了.本项目采用了React的 ...

  8. Web全栈架构师(三)——NodeJS+持久化学习笔记(2)

    NodeJS+持久化学习笔记 持久化 nodejs中实现持久化的方法 文件系统数据库 MySQL 资源 安装配置 node.js原生驱动 Sequelize 基本使用: Getters & S ...

  9. Mdebug:基于React开发的移动web调试工具

    作者:thinkchen,腾讯 PCG 高级前端开发工程师 mdebug是腾讯新闻 TNTWEB 团队推出的基于React开发的新的web调试工具, 沉淀自腾讯新闻微信手 q 双插件多年的移动 web ...

最新文章

  1. Verilog中`define和parameter有什么区别
  2. python中栈_Python中的栈
  3. python扩展库安装
  4. 需要额外端口信息_二端口网络及算例
  5. IIS优化-解决IIS访问速度慢问题
  6. JDBC:深入理解PreparedStatement和Statement
  7. 分享一个关于跨境电商的竞品分析报告
  8. 中兴b860刷机运行Linux,全国各地中兴B860A刷机越狱全贴(2016年2月26日更新)
  9. Ubuntu18.04安装后检测不到集成声卡问题
  10. Hadoop解除安全模式
  11. github 访问慢,一键加速,完美访问
  12. 初次使用tshark小结
  13. 计算机视觉--实时车速检测
  14. 电子货架标签----智能仓库管理
  15. poj解题报告——2325
  16. 解决安装 fireworks、photoshop 时卡在输入账号、手机号处等问题
  17. 妄想山海采矿/自动精灵
  18. linux snmp 客户端,从Linux SNMP说起到community strings
  19. YouTube图片幻灯片分享技巧
  20. 『JavaScript』JS简介

热门文章

  1. iOS上架app store详细教材
  2. 谷歌图标html5代码,HTML5 canvas绘制chrome浏览器 logo图标_网页代码站(www.webdm.cn)
  3. 关于为什么出现粘包问题及如何解决!
  4. 计算机视觉投稿期刊整理
  5. 为战疫助力,半导体功不可没
  6. Python3.9安装教程
  7. ethos1.3.0最新版免费放出,彻底解决claymore算力变0问题
  8. 进程CPU使用率计算
  9. Android计步器悦步——百度地图
  10. 为什么很多电商都不把合规当回事?