1. 程式人生 > >我是如何使用React+Redux構建大型應用的

我是如何使用React+Redux構建大型應用的

背景

我們團隊有個專案由於開發時間較長,且是前後端雜糅的開發方式,維護成本很高,在線上暴露的問題很多。而且因為對接了公司一百多條產品線,每天都會接到大量的客訴和產品線反饋的問題。2017年11月份以產品為主導,從產品層面對流程進行重新設計,對該專案進行了前後端的重構。作為前端的負責人我用這篇文章分享下,整個過程從技術選型,開發,上線的一些經驗。

技術選型的思考

首先我們先看下下面我們專案中的幾個頁面,來總結下一些他們的特點。

我們的頁面主要是需要使用者填寫的表單居多,在頁面載入的時候不需要去請求獲取和渲染大量的資料。而且一個頁面需要顯示的狀態較多(比如上面的3張圖,在專案中是一個元件)。還有一個最主要的業務需求,百度公司內部產品線較多,不同的業務都有其獨特的賬號標籤,這些賬號除了會走一些通用流程還要走一些對應產品線特色的流程。

結合這些業務特色和之前有Nodejs和React的開發經驗,我整體的一個技術選型是FIS3+Nodejs+React+Redux+React-Router。那麼這些技術選型能帶來什麼呢?

  1. 前端可以在瀏覽器端控制頁面跳轉的路由,增加了前端開發的靈活性;
  2. 頁面可以根據業務需求在服務選擇模板引擎渲染或者是同構渲染;
  3. 前端對錯誤碼文案和頁面文案做統一的管理,而且通過Nodejs來實現線下“熱更新”他們,線上實時生效;
  4. 有了Redux之後,做跨元件(多頁面)的資料共享更加方便。減少無意義的網路請求。提高專案執行的穩定性和可用性。

這裡簡單的聊下工程化工具的選擇。目前在業內最火的工程化工具就是Webpack了吧。除了看過文件之外,並沒有太多的實際應用經驗。我一直認為使用工具就是來幫助開發者解決一些開發過程中遇到的一些需要人為頻繁去操作的無異議的工作

。拋開Webpack我們依舊可以手動去編譯程式碼,手動部署,手動重新整理頁面來開發,使用工具只是讓這一系列的流程能夠連貫起來,降低開發成本。

在我的所有跟公司有關的專案中選擇的都是FIS3,我也認為他足夠的好用,能滿足我各色各樣的工程化需求。我並不是排斥Webpack。我只是還沒有找到一個理由,讓我選擇放棄現在使用的FIS3去使用Webpack。

新老框架機制的區別

這裡簡單介紹下,決定了技術選型之後,對於渲染頁面渲染機制的一些區別。

之前舊專案使用PHP+Smarty的渲染模式,將頁面在服務端渲染完成之後再統一吐給前端瀏覽器。而使用新的技術架構之後,我們渲染頁面的方式更加的靈活。可以選擇在服務端渲染,可以完全交給瀏覽器渲染,可以同構渲染。因為我們的頁面在首屏的時候不需要載入大量的資料,所以我還是讓大部分頁面在瀏覽器端進行渲染。

還有一種區別就是之前所有來自使用者的請求都會落到PHP的伺服器上去。而新框架的請求都會落到前端的Nodejs伺服器上去。所以前端工程師不僅僅是寫好頁面和做好相容性。對前端工程師的技術能力也會帶來考驗。

React帶給前端的便利

前端控制路由渲染頁面

前面談的技術選型已經提到了使用React-Router來做頁面路由控制。而且React-Router提供了非同步載入元件的功能,這為我們上線優化頁面的非同步載入提供了技術基礎。

<Route path="/v4/appeal/fillname" component={FillName} />
{* 這裡對某些元件做非同步載入 *}
<Route
    path="/v4/appeal/selectuser"
    getComponent={selectUser()}
/>

function selectUser() {
    return (location, cb) => {
            require(['../accountselect/container/AccountSelect'], function (component) {
                cb(null, component);
            });
        };
    }

通過React-Router來做路由控制除了前端程式碼之外,服務端也許呀做些配置。不然我們的頁面在回退的時候就會出現問題(頁面找不到路由)。其實就是在我們通常說的action成面做下路由控制,因為我使用的是Nodejs,所以我的配置下面這樣子的。

router.get('/fillname', router.action('index'));
router.get('/selectuser', router.action('index'));

事件

在前端事件因為開源協議的問題曾經短暫使用過Preact。React和Preact最大的區別就是對於一些事件的封裝。這些造成了Preact相對於React體積小很多。
做移動端開發,前端經常會面臨的一個問題就是click事件 300ms 延時的問題。在React中提供的onClick事件同樣也會出現這樣的問題。如果如果我們想要在點選一個按鈕之後,在其它地方立即出現反饋,最好就是使用onTouchEnd事件,或者就是使用開源的Npm包react-fastclick能很好的解決click事件 300ms延時的問題。

使用的方法就是在我們程式碼的入口地方,宣告以下語句,他預設會改變react的onClick事件的行為

import initReactFastclick from 'react-fastclick';

initReactFastclick();

元件的設計

在使用React的時候可能都會面臨的問題,我的元件應該是無狀態的還是有狀態的。我的元件狀態怎麼共享。什麼時候我應該使用Redux來管理元件的狀態。剛開始接觸react都會有這樣的疑問吧。

一種比較極端的做法就是,不管狀態需不需要共享,元件的所有狀態都試用Redux來管理。這樣的做法就是我們需要寫大量的Action。如果是一兩個頁面還好,如果是十幾個頁面,真的寫action是能把人寫崩潰的。

那麼最佳實踐是什麼呢?看下圖

當我們要寫一個元件的時候,首先想下這個元件是不是需要與其它元件共享它本身的狀態。如果需要我們應該把它當做有狀態的元件來設計,而且共享的狀態使用Redux來管理。如果簡單的就是無狀態元件或者是這個元件本身的狀態改變不會影響其它的元件,就可以將元件設計為無狀態元件(雖然叫無狀態元件,其實元件本身的狀態也是可以使用this.state來管理的)。

元件的複用關係

React的一大熱點就是元件化的開發思想。小到頁面上的一個按鈕都是可以設計成一個元件。既然是元件我們首先就應該考慮這個元件怎麼被其它元件複用。

舉個簡單的例子,在整個專案中都會用到的彈窗元件:


class AlertForm extends Component {
    constructor(props) {
        super(props);

        this.state = {
            showlayout: false,  // false 以tip的方式提示錯誤, true以彈層的方式提示錯誤
            btnlist: false,
            formbtn: false
        };
    }

    componentWillReceiveProps(nextProps) {

    }

    handleHideLayout = () => {
    }

    handleMobile = () => {
    }

    handleChangeCheck = () => {
        history.go(-1);
    }

    render() {

        return (
            <div className="component-alertform" style={this.state.showlayout ? {display: 'block'} : {display: 'none'}}>

            </div>
        );
    }
}

export default AlertForm;

我們將這種可能在其他頁面都用的元件單獨抽象成出來,在需要用的地方import

import AlertForm from '../../components/AlertForm';

<AlertForm
    errno={errno}
    stateObj={fillAppealName}
    actions={actions}
/>

開發環境和生產環境打包優化

完成專案之後肯定要做的一項工作就是上下前的優化,上線前我做的工作主要如下:

前面已經談到錯對於大多數使用者來說都只是會走一些普通流程。有些具有產品線特色的使用者會走一些特殊流程。所以在上線前肯定要拆包,和做元件的非同步載入。具體的前面已經提到過了。在打包的時候對這些頁面的js需要使用打包工具做單獨的處理。

其實除了這些需要非同步載入的頁面之外還會存在一些其他自己編寫的lib庫(自己編寫的小函式)。還有比如全國省市地區對應關係,電話區號對應關係。因為這些函式或者是地區關係對映圖在上線以後基本上都是不會再變化的,所以與業務的js分開打包。

我們的打包的配置檔案如下:

運維

前面已經談到使用Nodejs做中間層,做路由控制和服務端渲染。下面的這張圖是我寫這篇文章的時候擷取的額以上服務實時狀態圖。可以發現,整個應用對於記憶體、磁碟IO利用率還是很正常的,對於CPU的利用率有點兒高,這也是後續需要優化的地方。

這裡想要說的是,如果使用了Nodejs,使用了服務端渲染,對於前端工程師的個人素質要求會比較高,因為需要處理很多服務端的問題。前面也分享過一篇處理安全工單的問題,不僅僅要面對服務端的問題,還有面對來自網際網路安全的問題。

其它能力補充

使用Nodejs除了來做服務端渲染。我還在使用Nodejs做了一些其它的工作。

比如我在服務端使用Nodejs管理了這樣一個JSON檔案。PHP端不在維護錯誤碼和錯誤碼顯示的文案。所有前端需要顯示文案放在Nodejs端做統一的管理。而且,我線下也可以同通過系統對這些錯誤文案進行動態的更新。提高系統的可用性。