vue和react的差異

jsdt發表於2017-11-26

引言

平時開發單頁專案應用基於vue,目前另外兩個比較熱的庫還有angular和react,angular 1系列用過,進入公司後由於基於vue技術棧就沒在關注了。
一直在關注react,目的不是學習用法,只是為了擴充自己的視野和思維,通過了解一些使用上的差異性,來進一步的思考其底層設計的思想。

環境搭建

在具體業務邏輯開發前,我們首先要做的是搭建專案骨架,vue的話可以使用vue-cli,通過腳手架產生的配置完全暴露出來,我們可以靈活的修改配置來定製化需求。
我常用幾個配置如下

    build時assetsPublicPath會修改成相對引入,或者配置成公共字首,方便測試
    dev時proxyTable代理一些介面,聯呼叫,自己測試話就直接用mock資料服務
    關閉devtool,去除contenthash,chunkhash,html的minify,新增externals,定製化一些eslint……等等
    其它也有一些不通用改動,上面是每個專案都通用的配置

至於react可以使用create-react-app架手架,然後就直接可以使用create-react-app建立專案了,預設隱藏配置,定製化配置可以直接npm run eject。需要注意點就是使用create-react-app建立比較慢,需要做如下設定

    npm config set registry https://registry.npm.taobao.org 

其實自定義配置直接在配置依賴包裡也可以改動,不過這樣不太好。其次腳手架建立專案最好不要在已有git目錄下載建立,不然使用npm run eject會報錯,此時用空目錄下建立即可。
無論是哪個腳手架常用的配置,都是基於webpack核心生態圈構建,所以核心重點就是webpack用的熟練的話,無論使用哪個自定義化配置都不成問題。

使用

vue稍微複雜些單頁會基於vue+vuex+vue-router,react棧是react+redux+react-router。
不同點是vue元件是(html+css+js),對開發友好,上手容易。react一切都是js,特別的靈活,通過在render中利用純js邏輯控制渲染輸出模板。下面是基於react的一個示例。

render() {
    let name = this.state.flag ? `true`:`false`
    return (
        <ul>
            {this.props.items.map(item => (
                <li key={item.id}>{item.text}</li>
            ))}
            {name}
        </ul>
    );
}

vue的語法糖可以讓我們在表單上輕鬆的實現雙向繫結,而react是純粹的基於UI=render(data)的理念。vue利用es5的set,get機制收集依賴,能詳細的定位修改元素,react每次setState對元件建構函式中私有屬性進行修改時,元件都會更新,除非你在shouldComponentUpdate加入一些邏輯處理。另外vue提供的api多,有很多好用又方便的指令,但是具有兩面性,而react核心概念少,js用的溜,上手挺容易的。

資料流

無論是vue還是react都支援元件私有屬性;元件之間prop,元件之間簡單關資料系修改的話,可以使用事件的方式,vue此時又提供了個語法糖sync,吼吼,其它的話沒啥差異。

比較麻煩的一點就是複雜網狀元件之間資料流動時處理,此時就需要合理組織資料了,不然維護,除錯就是一個大坑,事件方式就不適合了,此時就要說到vuex和redux了,vuex的getter獲取state中資料,對映到元件data屬性上,Mutation同步commit修改資料,Action中dispatch可同步or非同步修改資料,核心是單一狀態樹,通過設計層層方法最後達到修改資料的目的就是為了更好的管理,檢測資料的流動。redux因為平時用的少,所以此刻我要描述的詳細些,以做備忘。

import {createStore, applyMiddleware, compose} from `redux`
import thunk from `redux-thunk`
import {Provider} from `react-redux`
const store = createStore(counter,
    compose(
        applyMiddleware(thunk),
        window.devToolsExtension?window.devToolsExtension(): f => f
    )) 
ReactDOM.render(
    <Provider store={store}>
        <JSDT/>
    </Provider>,
    document.getElementById(`root`)
);

瞭解背後原理,可以讓我們更靈活的控制程式碼,下面開始詳細的分析。
執行createStore,提供一個狀態樹的初始化環境,返回一個物件,其中包含一些閉包函式,引用createStore初始化環境中中的各種函式和變數,例如dispatch(派發action),getState(獲取只讀狀態樹上的值),subscribe(訂閱資料改變)等等,其中

dispatch({ type: ActionTypes.INIT })(原始碼:248)

此程式碼的目的是建立store的時候,給reduce一個預設值,初始化currentState值,方便初次getState呼叫。

applyMiddleware中介軟體機制,可以在處理store前後加一些通用處理,其它例如express,koa,springMVC這些框架中都有這種思想,實現方式不同,目的都一樣,解耦,可插拔效果,便於維護。
而rudux中applyMiddleware實現原理是利用高階函式compose,通過reduce將多個函式組合成一個可執行執行函式,關鍵步驟程式碼如下所示。

//applyMiddleware.js
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch) 
//compose.js
funcs.reduce((a, b) => (...args) => a(b(...args)))

thunk 至於這個就是用來方便做非同步處理的,是一個高階函式中介軟體,如下所示,一般action返回的都是一個行為描述物件,但是這個在你對store進行處理前加了一層邏輯判斷,以便我們在元件上統一的方式寫dispatch相關的程式碼。

 if (typeof action === `function`) {
      return action(dispatch, getState, extraArgument);
    }

devToolsExtension //開啟除錯工具,沒有vue方便,不能自動檢測

Provider提供一個上下文環境,讓一個樹上的所有元件都能訪問同一個物件。必要前提條件是新增加childContextTypes和getChildContext。

優化

為了更好的體驗,一般我們會採取一些措施,下面總結一下針對vue和react的優化措施。
重單頁應用,在路由中我們可以非同步載入元件,雖然兩者都支援,原理類似,但是vue使用極其方便,如下所示

const My = () => import(`../components/My.vue`)

有一點需要注意的就是需要配置下assetsPublicPath,這樣打包後的url中是全路徑,否則按照相對路徑處理容易出問題;相對而言react還要用模板程式碼包裝下元件,這一點不好,不過未來react未來會開啟非同步渲染元件的支援,這一點很贊。

vue和react都是基於dom diff更新差異元素。vue中,我們操作的data資料是vue封裝處理過,修改資料時,vue會將資料初始化時收集的相關依賴元素進行更新,而react每次setState更新資料針對的是元件,為了優化,元件設計的時候儘量細粒度,尤其是react當中的展示類元件。

因為兩者使用的是針對web頁面情況優化過的dom diff演算法,以使複雜度降低為O(n),所以有一些預設前提,理解並按照預設diff規則才能使程式碼達到最優,比如說,保持根節點一致;一個dom父節點下有多個子節點並列時,給子節點新增key,防止對父節點使用insertBefore插入子節點這種情況等等。

總結

陸陸續續的學習過程中,將自己的想法自己總結下來。

說明,文中的總結基於下面這些版本,
vue ^2.4.2
react 16.0.0-rc.2
redux 3.7.2
react-router 4.2

相關文章