summary: a phone call, I started a one-year front-end technology framework switching journey.
This article is shared from the HUAWEI CLOUD community " remember an unforgettable front-end technology framework switching journey [WEB front-end battle] ", the original author: a cabbage.
1. The beginning of the journey
At the beginning of 2020, on an ordinary working day, I, who was concentrating on "doing things", received an espace voice from MAE-Access front-end technical experts, and was told that the front-end technology framework used by the MAE-Access domain needed to be switched from AngularJS1.x to React. , It is required to be completed by the end of 2020. When I received the news, I was full of sorrows and joys. Opportunities and challenges coexist. This front-end technology framework switching journey is inevitable, but how to start and how to end.
Question: Why is it inevitable for MAE-Access to switch the front-end technology framework and the FMA LTE of the three base station products?
The reasons can be summarized as the following three points, as shown in Figure 1-1:
1) FMA LTE integrates two microservices, FMA LTE Website and FMA LTE Service, on MAE-Access, and is constructed uniformly with the entire MAE-Access domain.
2) The MAE-Access domain uniformly provides front-end engineering solutions for each website microservice, and each website microservice uses the front-end UI component self-developed by the Cloudsop platform ---eview. On the one hand, the UI interface style of the network management is unified; on the other hand, it is convenient for the unified management of front-end related open source and tripartite components, and it is also convenient for unified construction.
3)) The eview component provided by Cloudsop has two versions based on the angularJs front-end open source framework and the react front-end open source framework. Since angularJs version of eview uses angularJs1.X, it no longer meets the requirements of open source tripartite lifecycle management after 21B, so it needs to be unified switch to react version of eview.
Figure 1-1 Reasons for switching the front-end technology framework
Two, travel strategy
2.1 Destination—React technical framework and front-end engineering
2.1.1 A brief history of web front-end development
Before officially introducing React and front-end engineering, let's briefly understand the history of web front-end development. As shown in Figure 2-1, the development of the Web front-end has mainly experienced five key eras.
Figure 2-1 A brief history of web front-end development
① Simple and lively early era: suitable for small projects, regardless of front and back ends, the page is generated on the server side by JSP, PHP, etc., and the browser is responsible for displaying.
② Back-end-based MVC era: In order to reduce complexity, starting from the back-end, there is an architecture upgrade of the Web Server layer, such as Structs, Spring MVC, etc.
③ SPA era brought by Ajax: Ajax was formally proposed in 2005, and front-end development entered the SPA (Single Page Application) era.
④ Front-end MVC and MV* era: In order to reduce the complexity of front-end development, a large number of front-end frameworks such as Backbone, EmberJS, KnockoutJS, AngularJS, React, and Vue have emerged.
⑤ The full stack era brought by Node: With the rise of Node.js, a new development model is brought to front-end development.
Looking at the changes in the five eras, each post-era is trying to solve the pain points of the previous era.
1) In the era of ① and ②, front-end development relies heavily on the development environment; front-end and back-end responsibilities are still entangled, and maintainability is getting worse.
2) In the era, SPA applications are mostly functionally interactive. There is a large amount of JS code organization, binding with the View layer, etc., which are not easy tasks, and front-end responsibility control is required.
3) In the era of ④, ⑤, the front-end and back-end responsibilities are clear; the front-end development complexity is controllable, and the project is more maintainable through reasonable stratification; the deployment is relatively independent, and the product experience can be quickly improved.
2.1.2 React technical framework
From a brief history of the Web front-end, React is actually a product of the front-end-based MVC and MV* era, and was born to reduce the complexity of front-end development.
React officially explained that React is a JavaScript library for building user interfaces, which makes it easy to create interactive UIs. By using React, you can create components with various states, and these components form a more complex UI. The component logic is written in javascript instead of templates (different from JSP and PHP here), and data can be easily transferred in the application. Separate the state from the DOM.
FMA abolished the original multi-page iframe nesting implementation of the jQuery+AngularJs1.x mashup, switched the React technology framework, re-divided and organized each UI component into SAP, and required a "replacement" rewrite of the entire front end.
2.1.3 Front-end engineering
In order to complete the iterative launch of Web applications with high efficiency and high quality, front-end engineering solutions and related architectures have emerged as shown in Figure 2-1.
Figure 2-2 Front-end engineering architecture
The problem solved by engineering is how to improve the production efficiency in the coding, testing, and maintenance phases. The problems to be solved by front-end engineering include:
1) Formulate various specifications so that the work has rules to follow: unified coding specifications, development process specifications, front-end and back-end interface specifications, etc.
2) Use appropriate front-end technologies and frameworks to improve production efficiency: organize code in a modular way (ES6 Module); use componentized programming ideas to process the UI layer (React); separate the data layer for management (Redux); use Organize the structure in an object-oriented or functional programming manner.
3) Improve the testability of the code, introduce unit testing, and improve the quality of the code.
4) Improve the overall development and deployment efficiency by using various automated engineering tools (Gulp/Webpack).
While switching the React technology framework, FMA introduced popular front-end engineering solutions in the industry to improve development and maintenance efficiency by means of componentization, modularization, automation, and standardization.
To sum up, analyze the changes that will occur in this front-end technology framework switch, from ③+④ mashup to ④+⑤ combination, plus the compilation and construction of integrated components/modules, specification checking, automated continuous integration, and deployment. The front-end engineering is actually the transformation and improvement of the software engineering technology of the entire product.
2.2 Play Route-Key Steps in Switching the Technical Framework
Play route-key steps for technical framework switching
2.2.1React project construction
1) React project construction: React official website provides a set of scaffolding projects Create React App for creating React projects, which can quickly create a new single-page React project project that has integrated the standard front-end construction pipeline (can be modified by Parameter configuration of construction tools such as webpack, custom packaging, construction, and debugging projects).
(1) Install Nodejs (a javascript runtime environment) first, download the versions of different operating systems on the official website, and install it with one click.
(2) Install the create-react-app scaffolding tool through the Nodejs package manager tool npm (npm install -g create-react-app)
(3) Open the command line at the location where the project needs to be created, and enter the command (create-react-app myProject) of create-react-app + project name to create the project.
(4) At this point, the project has been created successfully, you can enter the project (cd myproject) and start it directly (npm start). If you need to build a package, execute (npm build). It should be noted that the npm script can be modified or extended by itself in the packge.json file script of the created project.
2.2.2 Development view design and component catalog planning
The relatively mainstream and relatively common directory structure in the industry is shown in the following table. When developing a specific business, you need to divide the catalog and files of the business itself according to the following structure. Basically, you can customize the catalogs under components and contaniners to divide the components.
| 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 Sorting out front-end components
1) The principle of component division
(1) Standardization: Any component should abide by a set of standards, so that developers in different regions can develop a set of standardized components based on this standard
(2) Independence: Describes the fine-grained components, follows the principle of single responsibility, maintains the purity of the components, and opens up APIs such as attribute configuration to the outside world. The internal state of the components is closed to the outside world, and there is as little business coupling as possible.
(3) Reuse and ease of use: UI differences are digested inside the component (note that it is not written a bunch of if/else), input and output are friendly, and easy to use. Avoid exposing the internal implementation of the component, avoid directly manipulating the DOM, and avoid using ref.
2) Component classification and hierarchical relationship
(1) Basic components: In order to pay more attention to the realization of business logic, you can combine your own business and choose a suitable mature UI component library as the basic component library of the entire project. For example, FMA chose the eview UI component provided by the platform.
(2) Container component (Container): a container-like component, generally used as the entrance of a business sub-module, such as the fault overview component of FMA; the sub-components in the container component usually have business or data dependencies; centralized/unified status Management, providing data (acting as a data source) and behavioral logic processing (receiving callbacks) to other display/container-type components; if global state management is used, the business components inside the container can call the global state processing business by themselves; act as a child The state transfer station of the component communication, for the communication coordination of the sub-components in the business module, such as the fault overview component, saves the interface response data of the overview analysis and transfers to the sub-components, and also saves the current interaction status of the sub-components, and has coordinated with other sub-components The interaction between the template; the template is basically a collection of child components, rarely contains DOM tags.
(3) Stateless: Mainly shows how the component is rendered, just like a simple template rendering process; it only accepts data and callback functions through props, and does not act as a data source; it may contain display and container components and is general There will be Dom tags and css styles; usually props.children (react) or slot (vue) are used to include other components; they can have state, and they can only manipulate and change their internal state during their life cycle. The responsibility is single and will not belong to oneself The behavior is passed through the callback and let the parent component handle it.
(4) Business components: usually abstracted based on the minimum business state, some business components also have a certain degree of reusability, but most of them are disposable components.
(5) Common components: components that can be used in one or more APPs.
(6) Logic components: a logical collection that does not include a certain function of the UI layer, such as time processing components and string processing components in FMA.
(7) Higher-order components (HOC): The combination in analog functional programming can be seen as a function that receives other components as parameters and returns a component with enhanced functions. Such as the ErrorBoundry component in FMA.
(8) The hierarchical relationship of most web applications is the tree-like relationship shown in the figure below.
3) FMA component division
Usually can be divided according to business, or according to technology. FMA designs and develops the component tree in the application according to the business.
(1) Cutting template (modular page structure): the main interface is the entrance container component; secondly, it is divided into two panel container components; the left panel is divided into main topo business components and custom topo business components according to business functions; right panel According to business functions, it is divided into business components such as fault overview and fast fault matching. By analogy, components are divided from outside to inside, from large to small, and hierarchically, as shown in the figure below.
(2) Design and develop general business components, or basic components, so that components can be reused as much as possible, such as FMA-specific table components and drawing components.
(3) Clarify the boundaries of each component, the design of the internal state, the design of props and the relationship with other components
(4) Clarify the positioning and function division of each component, and design the communication mechanism of parent-child components and brother components
(5) Set up a shelf and start filling
Three, different scenery
After understanding the front-end development history, building a React project, and dividing the components, how to write a React component?
3.1 Single component catalog
First of all, for a single component, there must be a standard component catalog, but the catalog can be tailored through component classification. To create a component, you need to create a separate folder. The folder usually contains the main file entry index.js (the logic of the view layer); the style uses scss or less css pre-compiled language, written in module.scss/module.less, webpack will automatically compile scss or less into css File, and will solve the difference between browsers; the constant is defined as type.js; the logic processing call interface function is written in actions.js; if you need to use redux, it is defined in the reducers.js file; if the component contains other Business components can be directly nested into a new component folder. For example, the following auxiliary recovery business component catalog.
3.2 The basic structure of the component main file 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: <p>{this.state.message}</p>
Line 29: <p>苹果的单价:{this.applePrice}¥</p>
Line 30: <p>购买的苹果的数量:<Spinner
Line 31: value={this.state.appleNumber}
Line 32: min={0}
Line 33: max={100}
Line 34: step={1}
Line 35: onChange={this.onBuyApple}/></p>
Line 36: <p>共花去:{this.state.totalPrice}¥
Line 37: </p>
Line 38: </div>
Line 39: )
Line 40: }
Line 41:}
1) Line01-05 introduces the react library: import React, {Component} from'react'; including imported third-party components, self-defined components, functions, constants, css files, pictures and other static resource files.
2) Line06-41 implements the component class declaration: JavaScript actually has no concept of a class. The class of es6 is actually a kind of syntactic sugar, and the essence is the constructor Function. Constructor can be omitted. If it is not written, it will exist by default. It is recommended to add it under stateful components, and then do the initialization function in Constructor.
3) The line25-40 render function is equivalent to the template in our angularjs, which is used to render to the view above the browser. It should be noted that the React jsx syntax is used here. The style definition uses the className attribute instead of class. The definition format of style is style={{marginLeft:'2rem'}}. The final return element has one and only one Element.
4) The definition and update of view layer variables are fixed. It is divided into two types: automatically triggering the view layer update and not triggering the view layer update. The view layer is automatically triggered to update the relevant state variables. Initialization definitions such as line09-13 and state variables re-assignment such as line17 must use the setState function. Other variables that do not trigger the update of the view layer can be defined directly.
5) React provides hook functions (also known as life cycle functions) for components during initial loading, parameter changes, logout and other actions, similar to $onInit, $onChange, and $postLink in angularjs. Among them, the componentWillMount method is called before mounting and render(), so setState will not trigger re-rendering in this method, so you can use setState to change the state value in this cycle; the componentWillReceiveProps method receives and assigns new props to a mounted component Before being called, if we need to update the state through props, we can compare this.props and nextProps in this method when they are not equal, and then use this.setState to change the state, so as to reduce the number of unnecessary renderings of the component and achieve performance optimization the goal of.
6) The references of static resources such as pictures are the same as the references of components, which are imported through the import keyword and referenced through attribute variables. Such as Import iconImg from'picture path'; <img src={iconImg} alt=”” />. It is recommended to store image resources directly under the current component catalog to avoid deep reference to the catalog.
3.3 component internationalization
1) Use the third-party plug-in react-intl
2) Resource configuration: create i18n directory and configure internationalized resource files.
3) Resource initialization and application: Import {injectIntl} from'react-intl'; in the index.js file of the project entry; add <IntlProvider locale={ lang.locale} messages={ lang.messages}> < /IntlProvider>. Locale uses language, messages, and requires internationalized language configuration.
Line 01: import { lang, messages } 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%', width: '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 background data request
The background data request uses the third-party component axios (a promise-based HTTP library that can be used in browsers and node.js).
1) Axios features: create XMLHttpRequests from the browser; create http requests from node.js; support Promise API; intercept requests and responses; convert request data and response data; cancel requests; automatically convert JSON data; the client supports XSRF defense.
2) Axios request example:
(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) Execute multiple concurrent requests
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 use
3.5.1 When is Redux
The role of Redux is to solve the communication between parallel components or components without parent-child relationship. Therefore, when two components cannot pass the state promotion and transfer the communication message through the parent component, it is necessary to use Redux technology for message communication.
3.5.2 Redux configuration and use
1) Define the store file and mount the store tree.
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) Application entry index.js global store context configuration, <Provider store={store}></Provider>
3) definition of 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) Use redux to pass global data and notify all receivers of the update of global data
(1) First introduce redux related components in the data transfer component, and modify the global data function setCatName
import {combineReducers} from 'redux';
import { connect } from 'react-redux ';
import { setCatName } from './actions;
(2) When exporting components, use the connect middleware to associate component properties with the global store. At this time, the setCatName function is equivalent to hanging on this.props, and this.props.setCatName (name) is directly called when in use to modify global data Update and notification actions are performed by the entire Redux mechanism.
const mapDispatchToProps = dispatch => bindActionCreators({
setCatName
}, dispatch);
export default connect(null, mapDispatchToProps)(injectIntl(LeftPanel))
5) Use redux to monitor global data updates and accept the latest values, similar to data transfer.
(1) First introduce redux related components in the components,
import {combineReducers} from 'redux';
import { connect } from 'react-redux ';
(2) When exporting components, use the connect middleware to associate component properties with the global store. At this time, the global data catName is hung on this.props, and this.props.catName is directly called when in use. The timeliness of the data is determined by the entire Redux mechanism. Guaranteed.
const mapStateToProps = state => ({catName: state.leftPanel.catName});
export default connect(mapStateToProps)(injectIntl(LeftPanel))
Fourth, the unexpected gain after reaching the end
4.1 Historical debt
1) AngularJs (does not meet life cycle management requirements) / jQuery framework mix and match;
2) Online analysis mode and export report offline analysis mode The source code is separated into two code warehouses;
3) Multiple function modules with 400+ small functions are piled up into "God", the code duplication rate is 44%, the same business logic is added, deleted, modified and other extended maintenance work, there are problems such as duplication of labor, missing modifications and introducing defects.
4.2 No debt and light weight
Under the background of switching the front-end technology framework (React, single page, UI componentization), the following refactorings were carried out, the source code was exported online, the same business function shared business components, and the code duplication rate was reduced from 44% to 4.8%. Reduce repetitive code 1W+.
1) Apply the "MVC layering principle" to encapsulate and store data (model), business logic (controller), and interface display (controller) for hierarchical classification of development views, as shown in Figure 2-1;
2) Apply the "single responsibility principle" and the "least know" principle to sort out the "God class", and extract and encapsulate the stacked functional functions into high cohesion and low coupling pluggable functions according to functional responsibilities. The component class of the pull. At the same time, according to the function of the component, it is further grouped and classified into the basic components of the lower layer, the business components of the upper layer, and the tool components for data processing. The upper-level business components can be "combined" to use other business components or basic components as needed. Online analysis and export of report components, similarly combine business components or basic components as needed, as shown in Figures 2-1 and 2-2.
Figure 2-1 Development view hierarchy
Figure 2-2 Component division
3) In order to maximize common business components for export and online, data with different sources and different data structures is standardized and normalized before being transferred to business components.
Figure 2-3 Standardization and normalization of component data
Five, postscript
This time the front-end technology framework switch, the transaction itself is relatively passive, fortunately, it can actively identify the difficulties of delivery. Sort out the workload in advance and actively manage the handover process. In the end, the delivery is completed in a timely, effective, and high-quality manner to ensure that the FMA front-end open source components meet the life cycle management requirements, while improving the FMA group's front-end software technology, and establishing FMA front-end engineering capabilities from scratch.
1) Combined with the interactive interface block diagram, the business logic and interactive interface of the functional modules are packaged into components. The online and export analysis modes can be highly versatile business components. It is no longer necessary to perform the same or similar function points on two sets of codes at the same time. Development and maintenance, avoid repeated "making wheels", improve development efficiency, improve maintainability and ease of maintenance, and at the same time, avoid the introduction of functional defects due to code modification.
2) The design and development of business components can be highly cohesive, making it single-function and easy to maintain. And when multiple people develop the same functional module collaboratively, tasks can be divided according to small-grained UI components, parallel development, source code library is not easy to cause conflicts, improve development quality and efficiency.
Reference link
- https://juejin.cn/post/6844903588553048077
- https://zhuanlan.zhihu.com/p/78472109
- https://segmentfault.com/a/1190000019759949#3
Click to follow and learn about Huawei Cloud's fresh technology for the first time~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。