APP下载

基于React技术的单页APP的设计与实现

2019-10-23张志鹏黄素娟周永圣王楠

微型电脑应用 2019年10期
关键词:视图路由组件

张志鹏, 黄素娟, 周永圣, 王楠

(上海大学 通信与信息工程学院, 上海 200444)

0 引言

在互联网技术飞速发展的当下,应用开发也逐渐变得纷繁复杂起来,一方面是大数据下的需求整合,一方面也是因为开发技术的日新月异。为了满足多样的Web App 需求,越来越多的后端处理的业务逻辑开始转移到前端来处理[1]。应用开发可分为前后端两大块,而React是当下流行的一个前端框架,它让传统前端开发变得更加高效且便于维护。基于HTML的前端界面开发的核心理念就是将庞大的数据呈现到复杂的用户界面当中,而传统的MVC(即“模型—视图—控制器”框架)模式过渡依赖模型,过程复杂且性能不够优化[2]。

在MVC模式下的项目开发中, 应用的处理流程如图1所示。

图1 MVC模型

其缺点在于视图的呈现对模型的依赖太大,并且视图解析前服务器端已经完成大部分工作,前端处于一种被“阉割”的状态。结果导致在项目开发过程中,很多测试流程是冗余的,每一次需求的改动都要重新走一遍整个流程,交互成本太大,拖延进度[3]。

一个应用的发布往往伴随着多终端的要求,如果不做到完全的前后端分离开发,势必会增大后端代码的冗余[4]。最好是前端组装好视图内的各个组件,根据后端的数据来拿这些组件包装就行。这样核心在于组件匹配与数据交互,将前端独立出来进行开发调试,使分工更明确,也能够极大地提高开发效率及降低运维成本,故要实现前后端分离。

1 React技术栈

React提供了一种新的前端开发的思路,并且伴随有成熟的第三方技术库来辅助项目搭建。Web开发的过程就是一个将实时数据不断更新至用户界面的过程,这时就要对DOM进行操作,而复杂和频繁的DOM操作实际上又是性能瓶颈产生的重要原因[5]。为此React引入了一种虚拟DOM(Virtual DOM)的机制:在浏览器端用JavaScript实现了一套DOM API。基于React进行开发的所有的DOM构造都是通过虚拟DOM进行的。每当数据更新时,React都会重新构建一棵虚拟DOM树,然后将当前的DOM树与新构建的DOM树进行对比,得到差异节点,再进行浏览器端的局部真实DOM更新。因为虚拟DOM是内存数据,性能是极高的,而对真实DOM操作的那一部分,只是差异点所在的局部区域,因此能极大地提高性能。

虚拟DOM能够提供更为简明高效的UI开发逻辑,同时也带来了组件化的开发思想。React的思想就是将界面的构成拆分为组件,组件如何拆分取决于其功能上的差异,这需要开发人员自己去根据实际来设计,而完整的UI界面的形成就是通过这些小的组件的相互拼装与嵌套。每个组件只关心自己部分的逻辑,彼此独立,外层的界面的渲染只需要引入这个组件,这种方式下,每个组件的UI和逻辑都定义在组件内部,与外部完全通过API来交互,实现复杂的功能就通过自由组合,React当中,组件一般具有可组合、可重用、可维护、可测试四个特点。

除却核心的React核心框架,实际项目开发当中我们还需要依赖诸多开源的第三方库,比如Antd-mobile库、React-router-dom库、Redux库等,功能涵盖但不限于界面风格、路由技术、状态管理技术。

2 设计实现

本节首先介绍整个应用的项目工程的框架结构,再详细阐述项目设计当中的核心管理组件Redux的实现,并在组件化开发的核心思想下,设计页面组成的容器组件和UI组件,最后从App功能层面介绍应用的功能性设计。

2.1 前端项目结构

整个项目分为前后端两大工程,两个工程根据API接口进行交互,其中前端的项目工程的结构目录如图2所示。

图2 前端项目结构目录

前端整体项目借助React官方提供的基于React+Webpack+ES6的脚手架工具搭建,搭建完就已经能够实现热加载与打包发布的功能,剩下的工作就只剩下对于项目的业务设计。首先要做的就是为整个项目的前端呈现而设计一款简洁美观的风格样式,市面上已经有很多的开源模版,这里选取的是由蚂蚁金服自主开发的一款UI设计框架,也是与React搭配十分高效的一款,整个框架主题为淡蓝色,可自行进行设置。在项目当中只需在线安装并在组件设计当中引入对应模块即可。

2.2 Redux组件设计

对于一个庞大的项目来说,势必有无数多的嵌套、平行级等各种关系的组件,组件之间的通信如果只借助原生的父级属性传值或者订阅发布机制的话,工作量会异常庞大,这时就需要Redux这样一个全局的管理者帮你在同一个地方查询状态、改变状态、传播状态的变化,对于各个组件之间的通信与数据交互进行一个系统地管理,使用Redux的场景如图3所示。

A、某个组件的状态,需要共享

B、某个状态需要在任何地方都可以拿到

C、一个组件需要改变全局状态

图3 Redux工作流程图

D、一个组件需要改变另一个组件的状态

Redux有三个核心的概念,即action、store、reducer。store是一个全局的状态包含容器,某个时间点的状态记为某一个state,这个状态对应着用户界面的一个视图,当前时间点的state在store当中是唯一的,action的作用就发出指令来给store通知state的变化,用户通过界面的某一个操作与action进行绑定,一旦视图层有变化操作,其对应的变化就会通过action传给store仓库。store再将当前的状态state与action交给reducer,reducer根据传入的两个参数,创造一个新的状态newState并返回给store,如果newState在仓库当中被监听到,就完成视图的重新渲染,这样就完成了一次交互流程。该项目当中的获取用户聊天信息列表功能action部分代码如下:

import …

function initIO(dispatch,userid) {…}

//异步获取消息列表数据

async function getMsgList(dispatch,userid) {…

//分发同步action

dispatch(receiveMsgList({users, chatMsgs,userid}))}

const receiveMsgList = ({users, chatMsgs,userid}) => ({

type: RECEIVE_MSG_LIST, data:{users, chatMsgs,userid}

})

import关键字里面包含的是该actions下所需要的所有的api接口,通过初始化函数获取前端与后台交互所需的参数。然后是异步的获取用户聊天信息列表的功能函数,先规定一个store所需的dispatch属性与用户的id属性,前者负责将该指令action传给store,后者则是查询后台数据的关键值,也就是只找本id下的所有聊天信息。与后台交互信息如果成功的话就通过dispatch分发一个action给store,也就是这里的receiveMsgList方法,这里将查询出来的数据全打包到此次的action里并传到store当中。其中store部分的实现如下:

import {…}

//向外暴露store对象

export default createStore(Reducer,composeWithDevTools(applyMiddleware(thunk)))

store部分旨在将当前state、action给到reducers模块,createStore当中传入的第二个属性函数composeWithDevTools是一款便于浏览器当中监控state的插件,applyMiddleware函数则是向Redux库提供异步方法。

获取用户消息部分的reducers设计部分如下:

import {…}

const initUserList = []

function chat(state=initChat,action) {… }

export default combineReducers({

user,userList,chat

});

import导入reducers模块需要的依赖环境,再进而处理store传来的指令数据,即上述的chat函数,由于聊天部分有多个功能点,所以在处理用户聊天信息函数chat当中利用了case语句来处理多个action,区分点在于action的type属性,当chat函数被调用并扫描到前面的所分发的action类型,便执行其对应的逻辑流程,从而创造一个新的状态newState返回给store模块以进行视图层的渲染刷新。这里检测到为RECEIVE_MSG_LIST时,便拿到action传递过来的参数,进行新的状态的数据构建。而combineReducers函数则是用于打包reducers模块下的多个reducer函数一并给store模块,因为一个项目往往伴随有许多的reducer函数,这样更便于管理。用户在视图层监听到一个新的状态newState之后便进行页面刷新,这便是前端对于某个功能点实现的设计,其他的功能点大致相同,区别于业务逻辑的实现。

2.3 容器组件搭建

组件的设计搭建是项目最核心的地方,React的使用就是基于这样一个组件化的开发模式,所以这部分的内容也最复杂,这里只介绍项目中main主界面的实现,其他的后续页面实现思路一致。其组件代码如下:

import {…}

class Main extends Component{

navList = […]

componentDidMount () {…}

render(){…

return(

{currentNav ? : null}

{navList.map(nav => )}

{currentNav?:null}

)}

export default connect(state =>({user:state.user,unReadCount:state.chat.unReadCount}),

{getUser})(Main)

main组件的实质是一个类,它能够被其他组件引用也可以嵌套所需的其他组件,通过import导入即可,在界面渲染之前,组件类要对state数据进行解析处理,此处只介绍界面渲染成分,即return内部的虚拟dom对象。上述设计代码当中,整个界面被div标签包裹,内部分作三个部分,即NavBar、Switch、NavFooter三个标签,显然这并非原生标签,而是经过React封装设计的UI组件类,其功能是给界面提供一个顶部与底部的导航栏,且风格样式与使用方法都在其自身组件设计类当中设计好,main组件只要进行调用和传参。而Switch标签则取自于React的依赖库——React-router-dom,该库给页面提供多个组件以进行单页应用的前端路由技术的实现,该标签下分示main组件类所能到达的子路由,每个子路由对应一个视图层的一个子界面,虽然在视图层其本质仍是一个页面,但是由于其路由url是不同的,故浏览器端能够使用前进后退按钮回滚,提高了用户体验。switch标签下,分发子路由,进入到哪个子路由界面,取决于用户的操作,数据的来源取决于视图层用户创建的指令action,然后只要解析子界面的组件即可,如果子界面仍嵌套子组件,实现过程与上述一致。

2.4 App功能设计实现

App项目在功能上有用户登录注册模块与实时聊天模块两个核心模块。这两个部分都需频繁地同服务器端进行数据交互,因此要独立地构建一个新的后端项目。基于nodejs的express框架能够搭建一个完整的后端项目,express是一个快速、开放、极简的 web 开发框架[6]。在这个基础上要设计好数据库存储集合模型,这里用到的数据库是非关联性的MongoDB,相较于mysql而言MongoDB更加灵活更适合一个轻量型的应用项目。然后要调校好后端路由请求的接受和发送,数据处理业务直接让前台来做。

整个功能流程大致如图4所示。

图4 APP功能流程图

登录注册功能模块的设计与大多数的应用相同,界面提供3个输入项:账号、密码与身份。根据登录还是注册将数据传给后端,后端拿到数据来操作数据库,如果是首次注册或登录,还需要完善用户信息,包括用户的基本情况(不同身份需要完善的信息不同),如果已经完善则跳转至对应用户的主界面。

注册界面实现结果如图5、图6所示。

在实时通讯模块当中,不同身份的用户之间在用户列表当中是可以查看到对方的名片,只要在屏幕上点击对应的名片即可进入到对话界面,也可以由用户消息列表进入,消息列表显示了当前用户收到的未读消息条目,点击某一个用户的未读消息可与其进行聊天,聊天界面即可正常编辑聊天内容包括emoji表情。

实时通讯界面实现效果如图7所示。

图7 实时通讯页面实现

3 打包部署

对于前端项目而言,Webpack技术能够满足项目打包需求,作为一种js的模块打包器,它能够对项目的结构进行分析,并且找到一些浏览器不能直接解析的文件格式而进一步转化或打包而做到适配,而基于React的脚手架开发已经给项目配置好了Webpack,在配置文档package.jsaon当中封装了基本的项目操作,包括项目启动、打包、暴露内置配置等等,执行build 指令后,工程文件下会生成一个build文件夹,需要部署到云服务器上的所有内容就是这个文件夹。

而后端的项目则没有这样一个系统的打包过程,直接将工程文件放入到云服务上即可,由于后端项目是基于node.js搭建,这里可以在系统内全局安装pm2工具,以检测项目进程并守护。

4 总结

本文基于React框架单页App开发过程,从需求场景与特点、项目核心组件的设计分析、项目的打包部署等三个层面来全面分析了React技术栈应用,React实际开发当中的优势在于组件化、模块化、工程化的开发模式,能够极大地简化开发流程以及代码的精简,通过前后端分离的独立地开发,又使得开发人员分工更趋于细致,从而大大提高了开发效率并降低了运维成本。

猜你喜欢

视图路由组件
无人机智能巡检在光伏电站组件诊断中的应用
Kistler全新的Kitimer2.0系统组件:使安全气囊和安全带测试更加可靠和高效
3月光伏组件出口量增长164.6%至7.49GW!单价创新低
一种嵌入式软件组件更新方法的研究与实现
数据通信中路由策略的匹配模式
OSPF外部路由引起的环路问题
路由重分发时需要考虑的问题
视图
Y—20重型运输机多视图
SA2型76毫米车载高炮多视图