• Reactjs —— 前端框架切换


    经验贴:

    记一次难忘的前端技术框架切换之旅【WEB前端大作战】

     

    2.1.1   Web前端发展简史

         正式介绍React和前端工程化之前,先简单了解下Web前端发展史。如图2-1所示,Web前端发展主要经历5个关键时代。

    前端发展简史.png

    图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.2   React技术框架

         从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-1所示。

    前端工程化架构.PNG

    图2-2 前端工程化架构 

          工程化解决的问题是,如何提高编码、测试、维护阶段的生产效率。前端工程化要解决的问题包括:

          1)制定各项规范,让工作有章可循:编码规范统一、开发流程规范、前后端接口规范等。

          2)使用合适的前端技术和框架,提高生产效率:采用模块化的方式组织代码(ES6 Module);采用组件化的编程思想,处理UI层(React);将数据层分离管理(Redux);使用面向对象或者函数编程的方式组织架构。

          3)提高代码的可测试性,引入单元测试,提高代码质量。

          4)通过使用各种自动化的工程工具(Gulp/Webpack),提升整个开发、部署效率。

          FMA进行React技术框架切换的同时,引入业界流行的前端工程化解决方案,以组件化、模块化、自动化、规范化等手段,提升开发及维护效率。

          综上所述,分析此次前端技术框架切换将发生的变化,从③+④混搭到④+⑤相结合,再加上集成组件/模块的编译构建、规范检查、自动化持续集成、部署为一体的前端工程,实则是整个产品软件工程技术的转变与提升。

    云社区 博客 博客详情

    记一次难忘的前端技术框架切换之旅【WEB前端大作战】

     一颗白菜 发表于 2021-05-06 11:42:24
     
     2264 
     
     
     
     0
     
    【摘要】 记一次难忘的前端技术框架切换之旅1     旅行之始     2020年初,某个普通的工作日,正在聚精会神“搞事情”的我,接到MAE-Access前端技术专家的espace语音,被告知MAE-Access域使用的前端技术框架需要从AngularJS1.x切换到React,要求2020年底完成。接到消息的我,忧喜交加,机会与挑战并存,这次前端技术框架切换之旅在所难免,但该如何开始,又该如何结束...

    记一次难忘的前端技术框架切换之旅

    1     旅行之始

         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 。

    前端技术框架切换原因.png

    图1-1 前端技术框架切换原因

    2     旅行攻略

    2.1         目的地—React技术框架及前端工程化

    2.1.1   Web前端发展简史

         正式介绍React和前端工程化之前,先简单了解下Web前端发展史。如图2-1所示,Web前端发展主要经历5个关键时代。

    前端发展简史.png

    图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.2   React技术框架

         从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-1所示。

    前端工程化架构.PNG

    图2-2 前端工程化架构 

          工程化解决的问题是,如何提高编码、测试、维护阶段的生产效率。前端工程化要解决的问题包括:

          1)制定各项规范,让工作有章可循:编码规范统一、开发流程规范、前后端接口规范等。

          2)使用合适的前端技术和框架,提高生产效率:采用模块化的方式组织代码(ES6 Module);采用组件化的编程思想,处理UI层(React);将数据层分离管理(Redux);使用面向对象或者函数编程的方式组织架构。

          3)提高代码的可测试性,引入单元测试,提高代码质量。

          4)通过使用各种自动化的工程工具(Gulp/Webpack),提升整个开发、部署效率。

          FMA进行React技术框架切换的同时,引入业界流行的前端工程化解决方案,以组件化、模块化、自动化、规范化等手段,提升开发及维护效率。

          综上所述,分析此次前端技术框架切换将发生的变化,从③+④混搭到④+⑤相结合,再加上集成组件/模块的编译构建、规范检查、自动化持续集成、部署为一体的前端工程,实则是整个产品软件工程技术的转变与提升。

    2.2        游玩路线—技术框架切换关键步骤

    关键步骤.png

    2.2.1   React项目工程搭建

          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以下的目录,进行组件划分即可。

    |   index.js // 入口js

    |   router.js // 路由入口

    |   base.css // 全局样式文件

    +---store  //redux

    |   |    store.js // redux store 入口,此处可用以注册中间件

    |   |    reducers.js // reducers入口

    +---services  //数据访问 (通常为api) 各域按需使用,不做统一要求

    +---contexts    //contexts

    +---utils   //公⽤用⽅方法逻辑 

    +---assets  //资源文件

    |   +---i18n  //多语言

    |                images  //图片

    |                fonts   //字体资源

    |                media    //媒体资源

    +---constants  //公用常量 (通常为后端各种枚举)

    +---components // 通用展示组件目录

    |   +---Header

    |   |       index.js

    |   |       Header.less

    |   ---NotFound

    |           index.js

    ---containers // 容器组件目录

    |   +---Todo // 声明页面的目录

    |   |       |---index.js // 页面入口

    |   |       +---components // 页面通用组件

    |   |       |   +---Button

    |   |       |           index.js

    |   |       |           Button.jsx //推荐用法

    |   |       |           Button.less

    |   |       |           Button.stories.js

    |   |       |   +---Input

    |   |       |           index.js

    |   |       |           Input.jsx

    |   |       |           Input.less

    |   |       |           Input.stories.js

    |   |       +---containers

    |   |       |       Search.js

    |   |       |       Body.js

    |   |       +---store

    |   |              types.js

    |   |               action.js

    |   |              reducer.js

     ---test // 测试目录  和src目录的结果保持一致

         +---components // 通用展示组件目录

         |   +---Header

         |   |       index.spec.js //对index.js的测试文件

         ---containers

             +---Todo

             |       +---components

             |       |   +---Button

             |       |           Button.spec.js //对Button.jsx的测试文件

             |       |   +---Input

             |       |           Input.spec.js //对Input.jsx的测试文件

             |       +---store

             |               reducer.spec.js //对reducer.js的测试文件

    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应用的组件层次关系,如下图所示的树状关系。

    组件层次树.PNG

    3) FMA组件划分

          通常可根据业务进行划分,或根据技术进行划分。FMA根据业务设计并开发应用中的组件树。

          (1)切割模版(页面结构模块化):主界面为入口容器组件;其次,分左右两个面板容器组件;左面板根据业务功能,分为主topo业务组件和自定义topo业务组件;右面板根据业务功能,分为故障总览、快速故障匹配等业务组件。以此类推,从外到内、从大到小、分层进行组件划分,如下图所示。

    FMA组件划分.PNG

          (2)设计并开发通用业务组件,或基础组件,使得组件尽可能复用,如FMA特有的表格组件、画图组件等。

          (3)明确各个组件的边界,内部state的设计,props的设计以及与其他组件的关系

          (4)明确各个组件的定位与职能划分,设计好父子组件、兄弟组件的通信机制

          (5)搭架子,并开始填充

    3     不一样的风景

          了解了前端发展史、搭建好了React项目工程、划分好了组件,那么如何写一个React的组件?

    3.1         单个组件目录


          首先,对于单个组件来说,标准的组件目录要有,但可通过组件分类进行目录裁剪。创建一个组件,需要建一个单独的文件夹。文件夹通常包含主文件入口index.js(视图层的逻辑);样式采用的是scss或less css预编译语言,写在module.scss/module.less中,webpack会自动把scss或less编译成css文件,并且会解决掉浏览器兼用的差异;常量的定义为type.js;逻辑处理调用接口函数写在actions.js中;如果需要使用redux,定义在reducers.js文件中;如果该组件包含其它业务组件,可直接嵌套一个新的组件文件夹。如,下面的辅助恢复业务组件目录。

    辅助恢复目录.PNG

    3.2          组件主文件index.js的基本结构

    Line 01:import React, {Component} from 'react';

    Line 02:import Spinner from '@huawei/eview-react/Spinner';

    Line 03:import {injectIntl} from 'react-intl';

    Line 04:import './module.css';

    Line 05:import {getTotalPrice} from './actions'

    Line 06:class LeftPanel extends Component {

    Line 07:  constructor(props) {

    Line 08:    super(props);

    Line 09:    this.state = {

    Line 10:      message: '',

    Line 11:      totalPrice: 0,

    Line 12:      appleNumber: 0

    Line 13:    }

    Line 14:    this.applePrice = 2;

    Line 15:  }

    Line 16:  componentWillMount() {

    Line 17:    this.setState({message: '左边组件初始化完成!'});

    Line 18:  }

    Line 19:  getDom = (dom) => {

    Line 20:  }

    Line 21:  onBuyApple = (value) => {

    Line 22:    const totalPrice = getTotalPrice(value, this.applePrice);

    Line 23:    this.setState({appleNumber: value, totalPrice});

    Line 24:  }

    Line 25:  render() {

    Line 26:    return (

    Line 27:      <div className={"ev_layout_fix left-panel"} ref={this.getDom}>

    Line 28:      {this.state.message}

    Line 29:       苹果的单价:{this.applePrice}¥

    Line 30:       购买的苹果的数量:<Spinner

    Line 31:          value={this.state.appleNumber}

    Line 32:          min={0}

    Line 33:          max={100}

    Line 34:          step={1}

    Line 35:          onChange={this.onBuyApple}/>

    Line 36:       共花去:{this.state.totalPrice}¥

    Line 37:       

    Line 38:      </div>

    Line 39:    )

    Line 40:  }

    Line 41:}

          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目录,配置国际化资源文件。

    i18n.PNG

          3)资源初始化与应用:在项目入口的index.js文件中引入 import { injectIntl } from 'react-intl'; 在render中添加<IntlProvider locale={ lang.locale} messages={ lang.messages}> </IntlProvider>。Locale采用的是语言,messages,需要国际化的语言配置。

      Line 01: import { langmessages } from './asserts/i18n/index';

    Line 02: import App from './containers/MainContainer';

    Line 03: const rootNode = document.getElementById('root');

    Line 04:ReactDOM.render(

    Line 05:    <IntlProvider locale={ lang.locale} messages={ lang.messages}>

    Line 06:        <Provider store={store}>

    Line 07:            <div style={{ height: '100%', '100%'

    Line 08:                <App />

    Line 09:            </div>

    Line 10:        </Provider>

    Line 11:    </IntlProvider>,

    Line 12:    rootNode

    Line 13:);

          4)导出国际化组件export default injectIntl(组件名);

          5)在组件的具体函数中,使用国际化资源项如line01-02

    Line 01: const { intl } = this.props;

    Line 12: const loadingWaitLabel = intl.formatMessage({ id: 'loadingWait' })

    3.4          后台数据请求

          后台数据请求使用第三方组件axios(一个基于promise的HTTP库,可以用在浏览器和 node.js中)。

          1)axios特性:从浏览器中创建 XMLHttpRequests;从 node.js 创建 http 请求;支持Promise API;拦截请求和响应;转换请求数据和响应数据;取消请求;自动转换 JSON 数据;客户端支持防御 XSRF。

          2)axios请求实例:

          (1)get

    // 为给定 ID 的 user 创建请求

    axios.get('/user?ID=12345')

      .then(function (response) {

        console.log(response);

      })

      .catch(function (error) {

        console.log(error);

      });

          (2)Post

    axios.post('/user', {

        firstName: 'Fred',

        lastName: 'Flintstone'

      })

      .then(function (response) {

        console.log(response);

      })

      .catch(function (error) {

        console.log(error);

      });

          (3)执行多个并发请求

    function getUserAccount() {

      return axios.get('/user/12345');

    }

    function getUserPermissions() {

      return axios.get('/user/12345/permissions');

    }

    axios.all([getUserAccount(), getUserPermissions()])

      .then(axios.spread(function (acct, perms) {

        // 两个请求现在都执行完成

      }));

    3.5          Redux使用

    3.5.1   什么时候Redux

          Redux的作用就是为了解决平行组件,或者没有父子关系组件的之间的通信。因此,当两个组件无法通过状态提升,将通信消息通过父组件进行中转时,就需要使用Redux技术进行消息通信。

    3.5.2   Redux配置使用

          1)定义store文件并进行store树挂接。

    import {combineReducers} from 'redux';

    import {routerReducer} from 'react-router-redux';

    import leftPanelReducer from './containers/Home/LeftPanelContainer/reducers';

    export default combineReducers({router: routerReducer, leftPanel: leftPanelReducer});

          2)应用入口index.js全局store上下文配置,<Provider store={store}></Provider>

          3)leftPanelReducer.js定义:types.js+reducers.js+actions.js

          (1)types.js

    const ACTION_TYPE = {

        SET_CAT_NAME: 'SET_CAT_NAME '

    };

    export { ACTION_TYPE };

          (2)Reducers.js

    import { ACTION_TYPE } from './types';

    const initState = {

        catName: “ketty”

        }

    };

    export default (state = initState, action) => {

        switch (action.type) {

            case ACTION_TYPE.SET_CAT_NAME: {

                return {

                    ...state,

                    catName: action.data

                };

            }

            default: {

                return state;

            }

        }

    };

          (3)actions.js

    import { ACTION_TYPE } from './types';

    export const setCatName= catName => dispatch => {

        dispatch({

            type: ACTION_TYPE.SET_CAT_NAME,

            data: catName

        });

    };

          4)使用redux传递全局数据,通知所有接收方全局数据的更新

          (1)首先在传递数据组件中引入redux相关组件,及修改全局数据的函数setCatName

    import {combineReducers} from 'redux';

    import { connect } from 'react-redux ';

    import { setCatName } from './actions;

          (2)导出组件时,使用connect中间件进行组件属性和全局store关联,此时setCatName函数相当于挂在this.props上,使用时直接调用this.props.setCatName (name),进行全局数据的修改更新,通知动作则由整个Redux机制执行。

    const mapDispatchToProps = dispatch => bindActionCreators({

      setCatName

    }, dispatch);

    export default connect(null, mapDispatchToProps)(injectIntl(LeftPanel))

          5)使用redux监听全局数据的更新,接受最新值,类似于数据传递。

          (1)首先在组件中引入redux相关组件,

    import {combineReducers} from 'redux';

    import { connect } from 'react-redux ';

          (2)导出组件时,使用connect中间件进行组件属性和全局store关联,此时全局数据catName挂在了this.props上,使用时直接调用this.props.catName,数据的及时性由整个Redux机制保障。

    const mapStateToProps = state => ({catName: state.leftPanel.catName});

    export default connect(mapStateToProps)(injectIntl(LeftPanel))

    4     到达终点后的意外收获

    4.1          历史债务

          1)AngularJs(不满足生命周期管理要求)/ jQuery框架混搭;

          2)在线分析模式和导出报告离线分析模式源码分居两个代码仓;

          3)多个功能模块400+函数小函数堆积成“上帝类”,代码重复率44%,相同业务逻辑的增加、删除、修改等扩展维护工作,存在重复劳动、修改遗漏引入缺陷等问题。 

    历史债务.PNG

    4.2          无债一身轻

          在切换前端技术框架(React、单页面、UI组件化)的背景下,进行以下几点重构,在线导出源码共仓、相同业务功能共用业务组件,代码重复率从44%降低到4.8%,减少重复代码1W+。

          1) 应用“MVC分层原则”,将数据封装保存(model)、业务逻辑(controller)、界面显示(controller)进行开发视图分层归类,如图2-1所示;

          2) 应用“单一职责原则”以及“最少知道”原则,对“上帝类”进行梳理拆分,将平铺堆积的功能函数,按功能职责,抽取封装成一个个高内聚低耦合的可插拔的组件类。同时按照组件功能,进一步分组归类为偏底层的基础组件、偏上层的业务组件、以及用来进行数据处理的工具组件。上层业务组件可按需“组合”使用其他业务组件或基础组件。在线分析
      和导出报告组件,同理按需组合使用各业务组件或基础组件,如图2-1、2-2所示。

    开发视图.PNG

    图2-1 开发视图分层

    组件划分2.PNG

    图2-2 组件拆分

          3)为使导出和在线最大限度地通用业务组件,对来源不同、数据结构不同的数据,传入业务组件前进行数据标准化、归一化。

    数据清理归一.PNG

    图2-3 组件数据标准化、归一化

    5     后记

          本次前端技术框架切换,事务本身比较被动,好在能够主动识别交付难点。提前梳理工作量,主动管理切换过程。最终,及时、有效、高质量完成交付,确保FMA前端开源组件满足生命周期管理要求的同时,提升FMA组前端软件技术,从无到有建立FMA前端工程化能力。

         1)结合交互界面框图,将功能模块的业务逻辑及交互界面,进行组件化封装后,在线和导出分析模式可高度通用业务组件,不再需要同时对两套代码,进行相同或相似功能点的开发维护,避免重复“造轮子”,提高开发效率,提升可维护性、易维护性,同时,避免因代码修改漏合,引入功能缺陷。

          2)业务组件的设计开发,可高度内聚,使其功能单一,易维护。且多人协同开发同一功能模块时,可按小粒度的UI组件进行任务划分,并行开发,源码上库也不易造成冲突,提高开发质量及效率。

    参考链接

  • 相关阅读:
    jQuery_第一章_JavaScript基础
    呵呵双钻
    MINIDVD
    幸运抽奖
    三章
    复习
    三种循环
    百文买百鸡
    1~100的奇数和
    Python memcache和redis
  • 原文地址:https://www.cnblogs.com/panpanwelcome/p/14754777.html
Copyright © 2020-2023  润新知