耗时 1 年的前端技术框架切换之旅
摘要:一个电话,我便开启了为期 1 年的前端技术框架切换之旅。
本文分享自华为云社区《记一次难忘的前端技术框架切换之旅【WEB前端大作战】》,原文作者:一颗白菜 。
一、旅行之始
2020 年初,某个普通的工作日,正在聚精会神“搞事情”的我,接到 MAE-Access 前端技术专家的 espace 语音,被告知 MAE-Access 域使用的前端技术框架需要从 AngularJS1.x 切换到 React,要求 2020 年底完成。接到消息的我,忧喜交加,机会与挑战并存,这次前端技术框架切换之旅在所难免,但该如何开始,又该如何结束。
问:MAE-Access 切换前端技术框架,基站产品三部的 FMA LTE,为何也“在所难免”?
原因大体可以总结为以下三点,图示如图 1-1:
1)FMA LTE 以 FMA LTE Website 和 FMA LTE Service 两个微服务,集成在 MAE-Access 上,与整个 MAE-Access 域统一构建。
2)MAE-Access 域统一为各 Website 微服务提供前端工程化解决方案,各 Website 微服务统一使用 Cloudsop 平台自研的前端 UI 组件---eview 。一方面统一网管各 UI 界面风格;另一方面方便统一管理前端相关的开源及三方件,同时也便于统一构建。
3))Cloudsop 提供的 eview 组件,有基于 angularJs 前端开源框架和 react 前端开源框架两个版本的。angularJs 版的 eview 因使用 angularJs1.X,21B 后便不再满足开源三方件生命周期管理要求,需要统一切换为 react 版的 eview 。
图 1-1 前端技术框架切换原因
二、旅行攻略
2.1 目的地—React 技术框架及前端工程化
2.1.1Web 前端发展简史
正式介绍 React 和前端工程化之前,先简单了解下 Web 前端发展史。如图 2-1 所示,Web 前端发展主要经历 5 个关键时代。
图 2-1 Web 前端发展简史
① 简单明快的早期时代:适合小项目,不分前后端,页面由 JSP、PHP 等在服务端生成,浏览器负责展现。
② 后端为主的 MVC 时代:为了降低复杂度,以后端为出发点,有了 Web Server 层的架构升级,比如 Structs、Spring MVC 等。
③ Ajax 带来的 SPA 时代:2005 年 Ajax 正式提出,前端开发进入 SPA(Single Page Application 单页面应用)时代。
④ 前端为主的 MVC、MV* 时代:为了降低前端开发复杂度,Backbone、EmberJS、KnockoutJS、AngularJS、React、Vue 等大量前端框架涌现。
⑤ Node 带来的全栈时代:随着 Node.js 的兴起,为前端开发带来一种新的开发模式。
纵观 5 个时代的变迁,每个后时代都在尝试解决前时代的痛点。
1)①、②时代,前端开发重度依赖开发环境;前后端职责依旧纠缠不清,可维护性越来越差。
2)③时代,SPA 应用大多以功能交互型为主,存在大量 JS 代码的组织,与 View 层的绑定等,都不是容易的事情,需要进行前端负责度控制。
3)④、⑤时代,前后端职责清晰;前端开发复杂度可控,通过合理的分层,让项目更可维护;部署相对独立,产品体验可以快速改进。
2.1.2React 技术框架
从 Web 前端简史来看,React 其实是前端为主的 MVC、MV* 时代的产物,为降低前端开发复杂度而生。
React 官方解释 React 是一个用于构建用户界面的 JavaScript 库,可以使创建交互式 UI 变的轻而易举。通过使用 React,可以创建拥有各种状态的组件,再由这些组件构成更加复杂的 UI,组件逻辑使用 javascript 编写而非模板(此处不同于 JSP、PHP),可以轻松地在应用中传递数据,使得状态与 DOM 分离。
FMA 废除原本 jQuery+AngularJs1.x 混搭的多页面 iframe 嵌套实现,进行 React 技术框架的切换,重新划分并组织各个 UI 组件为 SAP,需要对整个前端进行“换血”式重写。
2.1.3 前端工程化
为了高效高质量完成 Web 应用的迭代上线,出现了前端工程化解决方案及相关架构如图 2-2 所示。
图 2-2 前端工程化架构
工程化解决的问题是,如何提高编码、测试、维护阶段的生产效率。前端工程化要解决的问题包括:
1)制定各项规范,让工作有章可循:编码规范统一、开发流程规范、前后端接口规范等。
2)使用合适的前端技术和框架,提高生产效率:采用模块化的方式组织代码(ES6 Module);采用组件化的编程思想,处理 UI 层(React);将数据层分离管理(Redux);使用面向对象或者函数编程的方式组织架构。
3)提高代码的可测试性,引入单元测试,提高代码质量。
4)通过使用各种自动化的工程工具(Gulp/Webpack),提升整个开发、部署效率。
FMA 进行 React 技术框架切换的同时,引入业界流行的前端工程化解决方案,以组件化、模块化、自动化、规范化等手段,提升开发及维护效率。
综上所述,分析此次前端技术框架切换将发生的变化,从③+④混搭到④+⑤相结合,再加上集成组件/模块的编译构建、规范检查、自动化持续集成、部署为一体的前端工程,实则是整个产品软件工程技术的转变与提升。
2.2 游玩路线—技术框架切换关键步骤
游玩路线—技术框架切换关键步骤
2.2.1React 项目工程搭建
1)React 项目工程搭建:React 官网提供了一套创建 React 项目的脚手架工程 Create React App,可以快速创建出一个新的单页面的、且已经集成好标准前端构建流水线的 React 项目工程(可通过修改 webpack 等构建工具的参数配置,自定义打包、构建、调试工程)。
(1)先要安装 Nodejs(一个 javascript 运行环境),上官网下载不同操作系统的版本,一键式安装即可。
(2)再通过 Nodejs 的包管理器工具 npm,安装 create-react-app 脚手架工具(npm install -g create-react-app)
(3)在需要创建项目的位置打开命令行,输入 create-react-app + 项目名称的命令(create-react-app myProject),进行项目创建。
(4)至此,项目已经创建成功,可以进入项目(cd myproject),直接启动(npm start)。 如果需要构建出包,则执行(npm build)。需要注意的是,npm 脚本在创建好的项目的 packge.json 文件 script 中可以自行进行修改或扩展。
2.2.2 开发视图设计及组件目录规划
业界比较主流的相对通用的目录结构如下表所示。具体业务开发时,需按照下述结构进行业务本身的目录及文件划分,基本上自定义 components 及 contaniners 以下的目录,进行组件划分即可。
2.2.3 前端组件梳理划分
1)组件划分原则
(1)标准性:任何一个组件都应该遵守一套标准,可以使得不同区域的开发人员据此标准开发出一套标准统一的组件
(2)独立性:描述了组件的细粒度,遵循单一职责原则,保持组件的纯粹性,属性配置等 API 对外开放,组件内部状态对外封闭,尽可能的少与业务耦合。
(3)复用与易用:UI 差异,消化在组件内部(注意并不是写一堆 if/else),输入输出友好,易用。避免暴露组件内部实现,避免直接操作 DOM,避免使用 ref。
2)组件分类及层次关系
(1)基础组件:为了更关注业务逻辑的实现,可以结合自身业务,选择适合的成熟的 UI 组件库,作为整个项目的基础组件库。如,FMA 选择了平台提供的 eview UI 组件。
(2)容器型组件(Container):一个容器性质组件,一般作为一个业务子模块的入口,如 FMA 的故障总览组件;容器组件内的子组件通常具有业务或数据依赖关系;集中/统一进行状态管理,向其他展示型/容器型组件提供数据(充当数据源)和行为逻辑处理(接收回调);如果使用了全局状态管理,那么容器内部的业务组件可以自行调用全局状态处理业务;充当子级组件通信的状态中转站,进行业务模块内子组件的通信统筹,如故障总览组件,保存总览分析的接口响应数据向子组件传递,同时也会保存子组件当前的交互状态,已协调与其它子组件之间的交互联动;模版基本都是子级组件的集合,很少包含 DOM 标签。
(3)展示型组件(stateless):主要表现为组件是怎样渲染的,就像一个简单的模版渲染过程;只通过 props 接受数据和回调函数,不充当数据源;可能包含展示和容器组件 并且一般会有 Dom 标签和 css 样式;通常用 props.children(react) 或者 slot(vue)来包含其他组件;可以有状态,只在其生命周期内操纵并改变其内部状态,职责单一,将不属于自己的行为通过回调传递出去,让父级组件去处理。
(4)业务组件:通常是根据最小业务状态抽象而出,有些业务组件也具有一定的复用性,但大多数是一次性组件。
(5)通用组件:可以在一个或多个 APP 内通用的组件。
(6)逻辑组件:不包含 UI 层的某个功能的逻辑集合,比如 FMA 中的时间处理组件、字符串处理组件等。
(7)高阶组件(HOC):类比函数式编程中的组合,可以看做一个接收其它组件作为参数,并返回一个功能增强的组件的函数。如 FMA 中的 ErrorBoundry 组件。
(8)多数 Web 应用的组件层次关系,如下图所示的树状关系。
3)FMA 组件划分
通常可根据业务进行划分,或根据技术进行划分。FMA 根据业务设计并开发应用中的组件树。
(1)切割模版(页面结构模块化):主界面为入口容器组件;其次,分左右两个面板容器组件;左面板根据业务功能,分为主 topo 业务组件和自定义 topo 业务组件;右面板根据业务功能,分为故障总览、快速故障匹配等业务组件。以此类推,从外到内、从大到小、分层进行组件划分,如下图所示。
(2)设计并开发通用业务组件,或基础组件,使得组件尽可能复用,如 FMA 特有的表格组件、画图组件等。
(3)明确各个组件的边界,内部 state 的设计,props 的设计以及与其他组件的关系
(4)明确各个组件的定位与职能划分,设计好父子组件、兄弟组件的通信机制
(5)搭架子,并开始填充
三、不一样的风景
了解了前端发展史、搭建好了 React 项目工程、划分好了组件,那么如何写一个 React 的组件?
3.1 单个组件目录
首先,对于单个组件来说,标准的组件目录要有,但可通过组件分类进行目录裁剪。创建一个组件,需要建一个单独的文件夹。文件夹通常包含主文件入口 index.js(视图层的逻辑);样式采用的是 scss 或 less css 预编译语言,写在 module.scss/module.less 中,webpack 会自动把 scss 或 less 编译成 css 文件,并且会解决掉浏览器兼用的差异;常量的定义为 type.js;逻辑处理调用接口函数写在 actions.js 中;如果需要使用 redux,定义在 reducers.js 文件中;如果该组件包含其它业务组件,可直接嵌套一个新的组件文件夹。如,下面的辅助恢复业务组件目录。
3.2 组件主文件 index.js 的基本结构
1)line01-05 引入 react 库:import React, {Component} from 'react';包括引入的需要的第三方的组件,自己定义的组件、函数、常量、css 文件、图片等静态资源文件等。
2)line06-41 进行组件类声明实现:javascript 其实是没有类的概念的,es6 的 class 其实是一种语法糖,本质是构造函数 Function。Constructor 可以省略,不写也会默认存在,建议在有状态组件下中添加,然后在 Constructor 做初始化的功能。
3)line25-40 render 函数相当于我们 angularjs 中的 template,用来渲染到浏览器上面的视图。需要注意的是,这里使用的是 React jsx 语法,样式定义使用 className 属性而非 class,style 的定义格式为 style={{marginLeft:’2rem’}},最终 return 的元素有且仅有一个 Element。
4)view 层变量的定义与更新是固定的。分为自动触发视图层更新和不触发视图层更新两种。自动触发视图层更新相关的 state 变量,初始化定义如 line09-13,state 变量重新赋值如 line17,必须使用 setState 函数。其他不触发视图层更新的变量直接定义即可。
5)react 提供了组件在进行初始加载,参数变更,注销等动作时的钩子函数(亦称生命周期函数),类似 angularjs 中的 $onInit、$onChange、$postLink。其中,componentWillMount 方法在 mounting 和 render()之前调用,因此在此方法中 setState 不会触发重新渲染,所以可以在这个周期使用 setState 来更改 state 值;componentWillReceiveProps 方法在一个 mounted 的组件接收到并赋值新 props 前被调用,如果我们需要通过 prop 来更新 state,可以在此方法中比较 this.props 和 nextProps 不相等时,再使用 this.setState 来更改 state,以此减少组件的不必要渲染次数,达到性能优化的目的。
6)图片等静态资源的引用和组件的引用是一样的,通过 import 关键字进行导入,通过属性变量进行引用。如 Import iconImg from ‘图片路径’; <img src={iconImg} alt=”” />。图片资源建议直接存放到当前的组件目录下面,避免引用目录太深。
3.3 组件国际化
1)使用第三方插件 react-intl
2)资源配置:创建 i18n 目录,配置国际化资源文件。
3)资源初始化与应用:在项目入口的 index.js 文件中引入 import { injectIntl } from 'react-intl'; 在 render 中添加<IntlProvider locale={ lang.locale} messages={ lang.messages}> </IntlProvider>。Locale 采用的是语言,messages,需要国际化的语言配置。
3.4 后台数据请求
后台数据请求使用第三方组件 axios(一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中)。
1)axios 特性:从浏览器中创建 XMLHttpRequests;从 node.js 创建 http 请求;支持 Promise API;拦截请求和响应;转换请求数据和响应数据;取消请求;自动转换 JSON 数据;客户端支持防御 XSRF。
2)axios 请求实例:
(1)get
(2)Post
(3)执行多个并发请求
3.5 Redux 使用
3.5.1 什么时候 Redux
Redux 的作用就是为了解决平行组件,或者没有父子关系组件的之间的通信。因此,当两个组件无法通过状态提升,将通信消息通过父组件进行中转时,就需要使用 Redux 技术进行消息通信。
3.5.2 Redux 配置使用
1)定义 store 文件并进行 store 树挂接。
2)应用入口 index.js 全局 store 上下文配置,<Provider store={store}></Provider>
3)leftPanelReducer.js 定义:types.js+reducers.js+actions.js
(1)types.js
(2)Reducers.js
(3)actions.js
4)使用 redux 传递全局数据,通知所有接收方全局数据的更新
(1)首先在传递数据组件中引入 redux 相关组件,及修改全局数据的函数 setCatName
(2)导出组件时,使用 connect 中间件进行组件属性和全局 store 关联,此时 setCatName 函数相当于挂在 this.props 上,使用时直接调用 this.props.setCatName (name),进行全局数据的修改更新,通知动作则由整个 Redux 机制执行。
5)使用 redux 监听全局数据的更新,接受最新值,类似于数据传递。
(1)首先在组件中引入 redux 相关组件
(2)导出组件时,使用 connect 中间件进行组件属性和全局 store 关联,此时全局数据 catName 挂在了 this.props 上,使用时直接调用 this.props.catName,数据的及时性由整个 Redux 机制保障。
四、到达终点后的意外收获
4.1 历史债务
1)AngularJs(不满足生命周期管理要求)/ jQuery 框架混搭;
2)在线分析模式和导出报告离线分析模式源码分居两个代码仓;
3)多个功能模块 400+函数小函数堆积成“上帝类”,代码重复率 44%,相同业务逻辑的增加、删除、修改等扩展维护工作,存在重复劳动、修改遗漏引入缺陷等问题。
4.2 无债一身轻
在切换前端技术框架(React、单页面、UI 组件化)的背景下,进行以下几点重构,在线导出源码共仓、相同业务功能共用业务组件,代码重复率从 44%降低到 4.8%,减少重复代码 1W+。
1) 应用“MVC 分层原则”,将数据封装保存(model)、业务逻辑(controller)、界面显示(controller)进行开发视图分层归类,如图 2-1 所示;
2) 应用“单一职责原则”以及“最少知道”原则,对“上帝类”进行梳理拆分,将平铺堆积的功能函数,按功能职责,抽取封装成一个个高内聚低耦合的可插拔的组件类。同时按照组件功能,进一步分组归类为偏底层的基础组件、偏上层的业务组件、以及用来进行数据处理的工具组件。上层业务组件可按需“组合”使用其他业务组件或基础组件。在线分析和导出报告组件,同理按需组合使用各业务组件或基础组件,如图 2-1、2-2 所示。
图 2-1 开发视图分层
图 2-2 组件划分
3)为使导出和在线最大限度地通用业务组件,对来源不同、数据结构不同的数据,传入业务组件前进行数据标准化、归一化。
图 2-3 组件数据标准化、归一化
五、后记
本次前端技术框架切换,事务本身比较被动,好在能够主动识别交付难点。提前梳理工作量,主动管理切换过程。最终,及时、有效、高质量完成交付,确保 FMA 前端开源组件满足生命周期管理要求的同时,提升 FMA 组前端软件技术,从无到有建立 FMA 前端工程化能力。
1)结合交互界面框图,将功能模块的业务逻辑及交互界面,进行组件化封装后,在线和导出分析模式可高度通用业务组件,不再需要同时对两套代码,进行相同或相似功能点的开发维护,避免重复“造轮子”,提高开发效率,提升可维护性、易维护性,同时,避免因代码修改漏合,引入功能缺陷。
2)业务组件的设计开发,可高度内聚,使其功能单一,易维护。且多人协同开发同一功能模块时,可按小粒度的 UI 组件进行任务划分,并行开发,源码上库也不易造成冲突,提高开发质量及效率。
参考链接
版权声明: 本文为 InfoQ 作者【华为云开发者社区】的原创文章。
原文链接:【http://xie.infoq.cn/article/631b9d40abb5bfc4f8c2364b5】。文章转载请联系作者。
评论