1. 程式人生 > >react+redux+webpack移動端專案總結

react+redux+webpack移動端專案總結

前言

距離我進新公司也有一個多月,這一個月的事件使用react寫了一個專案,期間斷斷續續重構了兩三次,目前已經完成第一階段測試,也總結分享一些使用react的一些坑。

react

先囉嗦幾句講一下react原理,新人可以認真看下,老鳥可跳過。

react並沒有像其他如vue,ng一樣採用MVVM模式,所謂MVVM模式,狹義來說就是將模板與資料繫結在一起,當資料發生改變時,模板自動更新,這是中間的VM,最左邊的M可以理解為我們看到的頁面,而最右邊的M可以理解為原始資料(例如資料庫資料)。
其實要知道,這些框架模式歸根結底的目的希望使程式碼更容易開發和維護。當你寫一個小頁面你不覺得什麼,但是當你的頁面越來越龐大,越來越複雜,開發人員走了一批又一批的時候,你就會明白了。而如何解決,現在比較公認的理念一個是元件化,將頁面拆分成一個個元件,其實拆分成元件的目的並不全是為了複用,我覺得更多是為了維護;一個就是希望讓應用層的程式設計能更專注於業務邏輯,那麼這些框架都去做了處理,減少了大量DOM操作,讓業務開發更加專注。

那麼我們看看react採用的是什麼原理。其實react採用的方法簡單粗暴:它並沒有對模板做資料繫結,而是每當資料變化時,就重新渲染模板。這有一個很大的好處就是每當資料變化時,對頁面來說只有一次IO操作,而單純的雙向繫結更新DOM會有很多次;但有一個問題,如果只改變了一個dom的資料,整個模板都會重新渲染。那react解決的方法是每當資料改變時,就進行對比。

那麼該如何對比呢,最簡單的方法是每當資料改變,就去頁面獲取相應的DOM資訊,然後與現在的DOM資訊做比較。這個方法有個致命的缺點就是每次有DOM改變,就會有許多讀取操作,IO操作太多,很影響效能。那麼可以通過空間換時間的方法,將DOM資訊儲存起來,就避免了去頁面獲取資訊的IO操作。那麼我們看看一個真實DOM有多少資料

domproperty.png

虛擬DOM

如果我們把所有原生DOM快取起來進行比較顯然記憶體會爆炸,而我們所需要的僅僅是幾個為數不多的狀態資訊(例如style啊這些),這時虛擬DOM應運而生,如果說原生DOM是一塊豬肉,那麼虛擬DOM就是這塊豬肉中多精肉,他剔除了那些對我們來說沒有太多意義相反還佔記憶體的狀態資訊,而只將我們所需要的內容留了下來。那虛擬DOM該如何比較呢,就涉及到了虛擬DOM的diff演算法。

diff演算法

簡單來說兩個模板就像兩棵樹一樣,傳統的樹對比的時間複雜度是O^3,也就是說要整棵樹遍歷三次,那react根據DOM的特點:跨層級的操作較少。什麼叫跨層級,舉個例子一個元件有三個層級關係(巢狀三層),把第二層的div放到第三層就屬於跨層級操作。這種操作其實還是比較少的,react官方也不推薦這麼寫。那麼利用這個特點,react只diff同層級的DOM。這裡涉及以下幾種情況
1. tag變化(標籤變化)
2. 屬性變化

第二種沒什麼好說,就是進行同層級對比。這裡詳細說一下第一種變化,react的方法是當前層級往下全部刪除替換,簡單暴力。在業務開發來看,你同層級型別都不同了,比如div變成了input,那麼你的子元件相同的機率也較小,因此不如整個替換簡單暴力。同時這也說明為什麼列表需要key屬性,因為列表很多的刪除操作對於react來說是不知道的,它需要一個key來了解到底誰是誰。

踩過的一些坑

state

作為前端,拿到原型第一件事就是要與你的產品充分溝通,評估該專案是否需要引入redux,那麼如何確定需要引入redux呢,這邊有幾點:
1. 頁面之間元件之間通訊較多,且是跨層級的;
2. 頁面的互動邏輯較複雜,且經常引起多處元件變化。

再然後就是對state的設計。對於後臺的返回的資料,你並不知道到底哪些是有用的,哪些可能現在沒用以後有用,因此我的建議是對於每個api都將它存在一個object裡。那麼元件該如何去獲取,首先不能全部通過頂部container獲取依次往下傳,也不能粒度很細的去一個個connect,我的建議是對於一個object,可以用一個container去connect,粒度把握主要看你的業務需求。

還有一個要不要全域性都使用redux。我的觀點是否定的,我認為對於區域性一個元件內的狀態完全可以通過setState來滿足。

圖片處理

首先將圖片做一些劃分。例如以500k作為分界,小於500k為小圖片,否則是大圖片。對於小圖片,我們需要做如下判斷:
1. 頁面是否重複使用?是就用雪碧圖,否則轉base64.
2. 對於大圖片,可以進行壓縮後使用。

base64可以用webpack的url-loader替換。
舉個例子

require('url?limit=250!./xxx.png')
//這裡就會使用url-loader,假如圖片小於250,就會轉為base64

移動端適配

對於適配,我所知比較好的方法是利用rem作為單位,將頁面寬度等分成10個rem,根據頁面動態的用js去改變font-size,達到適配不同瀏覽器的目的。例如愛瘋寬320px,那麼font-size設定為32px。那麼10rem就是等於整個螢幕的寬度。但是有一個特例就是高清屏,一般高清屏的物理畫素是實際畫素的2倍,那麼當你想顯示一個寬度為1的邊框時,在普通螢幕是1px,在高清屏可以有0.5px(問題是很多瀏覽器不支援,為將0.5px認為是0)。雖然都使用1px在兩者螢幕上實際上是一樣的,但是高清屏裡的1px在射雞師眼裡是無法達到他對於1的要求的。於是就有一些解決方案,比較簡單是是使用transform:scale(0.5)。那麼還有一種解決方案就是阿里的移動端解決方案,原理是將頁面整體scale縮小,然後放大font-size,來保證rem為單位的佈局不變,但是px為單位的會被縮小。

效能提升

首先先確定需求,確實有這個需求的時候再談。

懶載入

webpack其實會幫我們做第三方依賴的懶載入處理,那麼針對react,我們可以通過現成的庫來實現懶載入react-lazyload,或者使用webpack現成的

require.ensure([],()=>{
require('public.js')
})

來實現。

shouldComponentUpdate

其實這個鉤子可以極大的幫助我們去提升效能,由於它的存在,我們可以自己判斷哪些是我們元件需要的state,哪些是不需要的,那麼這就可以阻止react進行不必要diff。但是有一個問題就是對於物件我們很難去判斷他們是否相等,那麼可以通過immutableJs的fromJS和is方法來解決這個問題。其實immutableJs的好處遠不止於此,目前我也尚在填坑中。

使用不可變資料,可以更好的達到函數語言程式設計,不僅利於單元測試,也更有利於後期維護整個大的state。因為他的不可變特點,我們不會在不經意見不小心改變了state,而引起不必要的問題。

建立元件的痛點

為了使元件中的css作用域相互獨立,一般採用Css Module,那麼為了使元件看上去更像元件,並且易於後期維護,一般我們會這樣結構化元件檔案:

folder.png

那麼對於每一個初始的jsx,我們大多會這樣初始化

import s from './App.scss'
import{Component} from 'react';//得到元件方法

class App extends Component{

  render(){
    return (
      <div className={s.container}>
      </div>
    )
  } 
}
export default App;

不難發現,對於每一個元件,我都需要去手動的建立這些檔案和資料夾,而這些操作其實是重複且無意義的勞動,於是我造了一個小輪子,一個命令列小工具,來解決這個痛點。
react-component-maker,歡迎猛戳點star!

結語

困了,本寶寶要睡覺了,還有的內容下次再說吧,再見。