自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一款Facebook出品的JS框架--使用React.js和應(yīng)用緩存構(gòu)建快速同步應(yīng)用程序

開發(fā) 前端
JavaScript框架層出不窮,在很多程序員看來,React.js是創(chuàng)建大型、快速的Web應(yīng)用的最好方式。這一款由Facebook出品的JS框架,無論是在Facebook還是在Instagram中,它的表現(xiàn)都非常出色。如何權(quán)衡取決于特定的應(yīng)用系統(tǒng)和業(yè)務(wù)要求,本文就是我們的團(tuán)隊使用React.js和應(yīng)用緩存來解決這一問題的一個實(shí)例。

對大部分應(yīng)用系統(tǒng)來說,在某種程度上,應(yīng)用程序的快速加載和及時取得***數(shù)據(jù)兩個方面同樣重要。傾向于積極使用緩存數(shù)據(jù),可能會導(dǎo)致提供的數(shù)據(jù)陳舊;而傾向于及時獲取***數(shù)據(jù),可能會犧牲加載時間。當(dāng)然,也可以魚與熊掌兼得,但是可能會需要更多的硬件,更復(fù)雜的軟件,或兩者都需要(意味著一個字:錢)。

如何權(quán)衡取決于特定的應(yīng)用系統(tǒng)和業(yè)務(wù)要求,本文就是我們的團(tuán)隊使用React.js和應(yīng)用緩存來解決這一問題的一個實(shí)例。

我們從哪里開始

標(biāo)簽是每當(dāng)你在瀏覽器上打開一個標(biāo)簽去送出一份慈善捐助的好理由。這是一件很偉大的事——但事實(shí)上,我們僅僅點(diǎn)擊了一個價值 100,000美元的里程碑來完成慈善捐助——但是,我們有一個疑問。

我們的應(yīng)用也太慢了。大家都明白這點(diǎn)。當(dāng)用戶更換新的標(biāo)簽頁時,他們需要得是速度與連貫性。而且,我們也沒有宣布:載入頁面的延遲成為了人們關(guān)閉標(biāo)簽的***理由。

我們想讓我們的頁面除了更有用,還要更好地被接受。但隨著我們向頁面中加了些附加功能后, 我們的頁面載入問題也越來越突出了。因為人們需要我們的 APP 能快速地提供內(nèi)容信息。

我們正在用 Django 的模板系統(tǒng)做一個交互式服務(wù)器來召喚或服務(wù)一個頁面。當(dāng)使用者是在快速的網(wǎng)絡(luò)環(huán)境中,而且我們的服務(wù)狀態(tài)是健康的情況下,服務(wù)器響應(yīng)時間是 ~65毫秒,還不是比較慘。然而,如果在你父母的房子*里打開一個標(biāo)簽,或者我們的數(shù)據(jù)庫產(chǎn)生了一個短暫的停頓時,這可能會給你在對其的信任上,潑了一盆冷水。

比較讓人煩惱,我應(yīng)該承認(rèn)我們所建立的 APP 并沒有采用標(biāo)準(zhǔn)的前端框架,除了僅僅是使用了 JQuery。 考慮到我們的 APP 有太多的互動,而且太混亂了。在各種各樣的代碼類型上,我要怎么才能喜歡它。

我們需要去修改它。

* 我愛你們,老媽、老爸!時代華納有線電視, 沒有太多什么了。

明確我們的需求

當(dāng)準(zhǔn)備去處理這個問題時,我們必須決定優(yōu)先處理哪些以及放棄哪此需求。在這里我們提出了一些建議:

  1. 頁面必須能快速載入。這是沒得討價還價的。

  2. 我們的頁面必須是非本地 URL。我們提高了 VIA 捐助廣告的價格,網(wǎng)絡(luò)在線廣告需要去核識真實(shí)性來確保這些廣告是夠安全的。由于瀏覽器端的用戶頁面插件總是將我們的廣告移除,以至于網(wǎng)絡(luò)廣告只能使用 http 或 https 協(xié)議。

  3. 我們希望頁面中的內(nèi)容是***的,但不必是實(shí)時性的。我們通過設(shè)備對用戶數(shù)據(jù)進(jìn)行同步, 并保持***的體驗。我們以分頁的形式顯示出用戶的反饋;例如,我們顯示出新用戶的統(tǒng)計數(shù)據(jù);我們有時也要運(yùn)行捐助設(shè)備來以滾動條的形式顯示出 "募集資金" 量。雖然我們愿意去接收一定程度上稍舊的數(shù)據(jù)(就像頁面展示后才提交數(shù)據(jù)),但理想得是在提交數(shù)據(jù)的瞬間發(fā)生。

  4. 我們要減少前端混亂的代碼。將非優(yōu)先權(quán)***的代碼肅清,這是一件讓人興奮的事。

讓我們動起手來實(shí)踐關(guān)于處理這些問題的思路。

一、采用主動的服務(wù)器端緩存
 

我們一度認(rèn)為應(yīng)該增加首先擴(kuò)展服務(wù)器端緩存結(jié)構(gòu),目前我們已經(jīng)十分依賴Django的低級緩存(low-level cache),它有助于達(dá)到我們的目標(biāo),但是我們不得不在每次都要寫語句來判斷是否存在緩存到期或失效情況,我想這張摘自一場精彩演講(an excellent presentation)的幻燈片能夠反映出Django在緩存問題上面臨的挑戰(zhàn):

Django low-level caching is quick but dirty.

此外,為了更好地從服務(wù)器端緩存中獲益,我們的緩存系統(tǒng)看起來是一個多層次的結(jié)構(gòu):(先是)每個用戶完整的頁面緩存,然后是用戶數(shù)據(jù)的模塊化的緩存,(同時)每當(dāng)數(shù)據(jù)變化時還要智能判斷數(shù)據(jù)是否失效。因為在實(shí)施過程中已經(jīng)遭遇到一些與緩存相關(guān)的(系統(tǒng))錯誤(bugs),所以我們并不希望繼續(xù)增加了緩存系統(tǒng)的復(fù)雜性。

更重要的是,還存在網(wǎng)絡(luò)傳遞差異的問題,例如對一個新的TAB頁面來說,在快速和慢速的因特網(wǎng)網(wǎng)絡(luò)上的表現(xiàn)有著顯著的差異,即使將我們的服務(wù)響應(yīng)時間降低到小于1毫秒,對大部分用戶而言,這個頁面顯示的還是不夠快的。

不,這樣可不行。

二: 在我們的頁面上使用應(yīng)用緩存
 

"應(yīng)用緩存? 他不是個douchebag么?"

不,別這么粗魯!

… 好吧, 也許他是有點(diǎn)兒. 在使用應(yīng)用緩存之前,充分了解它的怪癖和陷阱是明智的.我們主要關(guān)心的是應(yīng)用緩存會降低我們在調(diào)試時的透明度,因為我們服務(wù)器在輕便的請求上沒有日志  (接下來我們將解決這個問題). 在代碼變更后的另一個與之前不同的小問題是,在兩個視圖頁上應(yīng)用了這些變更: 它需要一個頁面去提示瀏覽器獲取資源, 另一個頁面則去使用新的資源.這不是很理想, 但是在我們的案例中是可以接受的. 在一般情況下, 我們的團(tuán)隊在應(yīng)用緩存的限制下相對沒有多少煩惱; 更多我們的app不適用的情況下解決起來會更輕松.

好吧, 也許我們可以與應(yīng)用緩存合作. 可能這是一個方法在不必通過大量的重構(gòu)去實(shí)現(xiàn)它?

我們快速而粗糙的主意就是,使用 Django 處理視圖模版并返回一個html頁面來保持我們當(dāng)前頁面的原狀.在任何用戶數(shù)據(jù)變更時,瀏覽器會從服務(wù)器和應(yīng)用緩存那里獲取一個重新渲染的頁面 .

我們的游戲計劃:

  • 我們將在當(dāng)前頁面上激活應(yīng)用緩存, 所以它將會繞過服務(wù)器去加載.

  • 當(dāng)一個用戶制造了一些數(shù)據(jù)改動而我們又想保留時, 我們的頁面將會使用一個ajax請求去保存數(shù)據(jù)到數(shù)據(jù)庫里,通常我們就是這么做的.

  • 我們將會從應(yīng)用緩存清單引入一個對用戶特殊的版本號,所以對于每個用戶來說這份清單都是***的. 當(dāng)用戶更新任意數(shù)據(jù)時, 我們將會對這個用戶的應(yīng)用緩存清單的內(nèi)容創(chuàng)建一個新的版本,而且瀏覽器會知道并獲取頁面資源來更新. 

  • 在客戶端方面, 我們將會在用戶修改任意數(shù)據(jù)時檢查應(yīng)用緩存并更新.瀏覽器將會獲取用戶的緩存清單, 查看已經(jīng)被處理成一個新版本號的被更改的內(nèi)容,并且重新獲取頁面的內(nèi)容.

  • 理論上, 當(dāng)用戶下次瀏覽這個頁面時, 應(yīng)用緩存會提供一個在服務(wù)端重新渲染過的***的頁面.

從好的方面講,這些選擇將會引入極小的工程投資.

一個小缺點(diǎn):這個選項沒起作用。

瀏覽器獲取資源的速度不夠快是主要的問題。 如果你在新的標(biāo)簽頁修改了數(shù)據(jù)(例如,在你的便簽里添加了一條筆記),然后在幾秒內(nèi)打開了一個新的標(biāo)簽,應(yīng)用緩存可能還沒有獲取到你修改的新的頁面,顯示的依舊是你沒添加筆記的舊頁面。從用戶體驗的角度看,這就像是數(shù)據(jù)丟失 — 即使是技術(shù)上的數(shù)據(jù)延遲 ,也是我們無法接受的。

當(dāng)多個設(shè)備參與時這個問題會變得更嚴(yán)重。如果你在設(shè)備A上對你的新標(biāo)簽頁有修改,接著在設(shè)備B打開一個標(biāo)簽頁,保證你得到的是舊的數(shù)據(jù)。在隨后的頁面加載之前你都看不到新的數(shù)據(jù)。

這不是很好。 抱歉,這是個快速而粗糙的選擇。

三:面向模板的本地存儲和應(yīng)用程序存儲
 

更簡潔地做到這一點(diǎn),我們可以結(jié)合客戶端模板使用應(yīng)用緩存,在本地存儲數(shù)據(jù)。這看起來是個很好的選擇,除了應(yīng)用緩存的“第二頁加載”那個問題所出現(xiàn)的糟糕情況,它是非??斓?,并且它還可以清理掉我們的前端(重構(gòu)...哇?)。作為獎勵,我們的新標(biāo)簽頁在在線的時候?qū)⒈辉L問。

我們選擇使用 React.js 作為模板是有一些原因的。最主要的一個就是我們有一些在其他領(lǐng)域使用應(yīng)用的經(jīng)驗。我們也覺得學(xué)習(xí)曲線比Angular更淺顯些,我們也是嚴(yán)肅地考慮過其他方案的。說來奇怪,長久以來建立一個前端框架都是在我們已有的jQuery上努力,我們的數(shù)據(jù)被改變更像是React中的“狀態(tài)”,這會讓我們轉(zhuǎn)換到React更容易些。

我們還選用了 Facebook 的 Flux 構(gòu)型, 因為我們認(rèn)可單向的數(shù)據(jù)流可以讓我們的代碼更順理成章. Flux 的調(diào)度器也能讓我們更加容易的進(jìn)行數(shù)據(jù)同步,下面我會對此進(jìn)行描述.

#p#

它是如何運(yùn)作的
 

新的tab一打開,瀏覽器就會從應(yīng)用緩存中獲取我們的頁面。我們的React應(yīng)用或從Flux存儲中獲取數(shù)據(jù) (1), 后者會去本地存儲里抽取數(shù)據(jù) (2). React 應(yīng)用一安裝,頁面就會被加載 (3, 4). 然后,我們的頁面會向應(yīng)用服務(wù)器進(jìn)行一次Ajax調(diào)用 (5), 發(fā)送應(yīng)用的所有數(shù)據(jù)—其實(shí)就是一個帶有所有Flux存儲數(shù)據(jù)的對象. 服務(wù)器接收到用戶所有的本地數(shù)據(jù),并使用用戶在數(shù)據(jù)庫中的數(shù)據(jù)對其進(jìn)行調(diào)和 (這會在下面的 "數(shù)據(jù)同步" 中有更詳細(xì)的描述), 然后向應(yīng)用返回***的數(shù)據(jù) (6). 應(yīng)用會從服務(wù)器接收到***的數(shù)據(jù),并且更新每一個Flux存儲 (7, 8).  Flux 存儲一更新,存儲會觸發(fā)一個變化事件 (3), 而 React 組件就會更新他們的狀態(tài) (4). 當(dāng)用戶改變了什么東西的時候,就會發(fā)起一個動作 (11), 更新存儲的數(shù)據(jù) (7, 8); 當(dāng)存儲更新并觸發(fā)變化時間的時候,我們會將數(shù)據(jù)持久化到本地存儲中 (9) 而如果用戶在線的話,就將數(shù)據(jù)持久化到數(shù)據(jù)庫中 (10).

如果你了解過有關(guān) Flux 的東西, 下面這幅圖看起來應(yīng)該會很熟悉:

Our Flux data flow

(抱歉,這圖看起來有點(diǎn)亂.)

這幅數(shù)據(jù)流圖的意思是,我們會向你快速的顯示新的tab頁,然后在一秒鐘左右之內(nèi),我們將會用來自服務(wù)器的新數(shù)據(jù)更新你的頁面. 這份新數(shù)據(jù)可能包含你在另外一個設(shè)備上對一個窗口小組件做出的變化 (像便條里的內(nèi)容) , 或者也可能是一個慈善活動中“籌集資金" 實(shí)時統(tǒng)計.

有關(guān)使用 Flux 構(gòu)型最令人驚奇的一件事情就是通過調(diào)度器的遠(yuǎn)程數(shù)據(jù)流同步時間完全同用戶的操作保持一致,使得調(diào)試異常的簡單. 因為存儲是我們應(yīng)用狀態(tài)的真實(shí)來源, 所以我們可以放心的讓應(yīng)用在存儲的數(shù)據(jù)被遠(yuǎn)程的數(shù)據(jù)同步或者用戶的輸入改變時,仍然可以始終如一地響應(yīng). 我們?nèi)匀豢梢栽僬麄€應(yīng)用中保持單項的數(shù)據(jù)流, 這使得代碼理所當(dāng)然的變得簡單很多.

在應(yīng)用速度和數(shù)據(jù)實(shí)時性兩者之間,我們已經(jīng)找到了理想的平衡.

數(shù)據(jù)同步

我提到過我們會在每次頁面加載的時候向服務(wù)器同步你的數(shù)據(jù)。那我們是如何去實(shí)現(xiàn)這個東西的呢?

我們通過為數(shù)據(jù)“塊”的***一次更新打上時間戳,然后在客戶端(的Flux存儲)上,以及遠(yuǎn)程的數(shù)據(jù)庫中保存數(shù)據(jù)發(fā)生變化的時間戳(modified_at),這樣的方式來處理同步. 例如,如果在你的一個便條窗口中進(jìn)行了輸入,就會把窗口的modified_at時間戳設(shè)置成現(xiàn)在,然后把你的便條內(nèi)容保存到本地存儲中,并入如果條件可能的話,也會保存到遠(yuǎn)程數(shù)據(jù)庫中. 而后,下次你打開一個tab的時候,我們將會把有關(guān)窗口的數(shù)據(jù)發(fā)送到應(yīng)用服務(wù)器,在那里會對跟該窗口相關(guān)的客戶端時間戳跟數(shù)據(jù)庫中保存的時間戳進(jìn)行比對,并返回***的數(shù)據(jù).

為了簡單起見,我們用Flux存儲對象來進(jìn)行數(shù)據(jù)的發(fā)送和接收. 這讓我們可以無痛的用發(fā)送自應(yīng)用的數(shù)據(jù)更新Flux存儲, 因為我們明白它將會被保持***,并且同我們的存儲一樣具有相同的數(shù)據(jù)結(jié)構(gòu).

我們當(dāng)前的同步過程肯定是不***的: 在發(fā)生同步?jīng)_突的情況下,我們會簡單地去獲取***的數(shù)據(jù). 對我們而言,這只是一個可以接受的細(xì)節(jié)狀況; 畢竟,我們可不是 Evernote. 即使這會變得不可接受,也可以在以后用更智能的數(shù)據(jù)合并和用戶消息進(jìn)行解決.

讓我們運(yùn)行得更快一些 ! (或者說與呈現(xiàn))

加載應(yīng)用緩存的頁面很不錯,但在我們向用戶展示之前,我們?nèi)匀灰\(yùn)行React應(yīng)用代碼,并對所有的組件進(jìn)行渲染. 對于一個相當(dāng)大的應(yīng)用而言,這可能要花上幾百毫秒甚至超過1秒.

為了能有一個快速的***次加載體驗, React 提供了一個方便的 renderToString 方法,讓你可以先向瀏覽器發(fā)送DOM(讓頁面先出現(xiàn)) ,然后再連接上所有的偵聽器 (讓頁面可交互). 這樣就適應(yīng)服務(wù)器端的預(yù)呈現(xiàn)了. 在我們的案例中,我們想是否可以用把它用在客戶端上 — 而我們做到了.

每次我們將數(shù)據(jù)持久化到本地存儲時,我們也會將我們的React應(yīng)用做成字符串,并將這個字符串保存到本地存儲中. 然后,頁面一加載,在我們做任何事情之前,我們會從本地存儲中加載渲染好的應(yīng)用并將它放到一個HTML元素中. 換言之,頁面只用了3行JavaScript就加載了DOM! 對于我們的應(yīng)用而言,預(yù)呈現(xiàn)減少了大概400毫秒的預(yù)加載時間。

"見鬼":挑戰(zhàn)和缺陷

沒有什么東西是***的。重構(gòu)的時候還是有些事情不那么有趣的。

再見吧, JQuery UI

在轉(zhuǎn)換到React過程中的一個速度損失讓我們放棄了幾乎所有的JQuery UI組件,比如 draggable. 這稍微煩人地讓我們花了點(diǎn)時間來重新做之前已經(jīng)做過的事情. 不過,事實(shí)證明我們還是可以依靠不斷增長的實(shí)用的 開源React組件 來構(gòu)建我們自身想要的東西.

"為什么, renderToString, 為什么?"

另外一個小的實(shí)現(xiàn)上的挑戰(zhàn): 如果你用過React的 renderToString 方法, 你可能已經(jīng)看到過這個錯誤:

  1. React attempted to use reuse markup in a container but the checksum was invalid.  

當(dāng)React在已經(jīng)有預(yù)渲染DOM存在之后渲染它的應(yīng)用時,它就要預(yù)計預(yù)渲染好的DOM應(yīng)該同將要被渲染的DOM相同. 那就意味著你不能讓像 Date.now() 和 Math.random() 這樣的東西影響到你的DOM. 為了解決這個問題,你將可能要花點(diǎn)時間在你的差異編輯器上面,來比對這兩個DOM字符串.

不夠靈活的存儲數(shù)據(jù)結(jié)構(gòu)

我們設(shè)計為應(yīng)用同服務(wù)器返回的應(yīng)用數(shù)據(jù)結(jié)構(gòu)之間的不匹配敞開了大門. 在我們想生產(chǎn)環(huán)境推送新的代碼之后,你***次加載的頁面視圖會包含從應(yīng)用緩存加載的老應(yīng)用代碼. 不過,從應(yīng)用服務(wù)器返回的同步數(shù)據(jù)將會是結(jié)構(gòu)化的,而我們的新版本會對其進(jìn)行構(gòu)造.

所以,如果在新版本中我們決定對存儲中的一塊數(shù)據(jù)進(jìn)行重命名或者移除,你的頁面就會在新的tab***次打開時被打斷; 老的應(yīng)用代碼不會知道如何去處理它. 在打開下一個tab之前,你的瀏覽器可能已經(jīng)獲取到了***的應(yīng)用代碼,并將其放到了應(yīng)用緩存中,因此頁面會運(yùn)作得很好.

為了防止新tab的打斷, 我們需要為我們的存儲數(shù)據(jù)維護(hù)一套可靠的內(nèi)部API. 那樣會有點(diǎn)兒痛苦.

說到代碼的推送...

如果我們搞砸了,弄壞了應(yīng)用,每個看到一個破頁面的用戶都會在我們修復(fù)它之前看到一個額外的破頁面. 應(yīng)用程序緩存就會進(jìn)行惱人的二次重新加載更新。

大家好,結(jié)局才會好

切換到React和Flux是一件令人很愉快的事情. 我們的團(tuán)隊發(fā)現(xiàn)我們自己重新愛上了前端開發(fā), 而我們做出的變化讓新進(jìn)工程師接觸代碼庫容易了許多。

在用戶體驗方面,我們的新tab一直在快速推進(jìn). 對于擁有優(yōu)良網(wǎng)絡(luò)條件的用戶而言,這次的版本不會有太多的變化;但是對于其他人,他們是能在發(fā)現(xiàn)我們的應(yīng)用不可用和喜歡上使用它之間發(fā)現(xiàn)不同的。

因為Tab需要從橫幅廣告展示為慈善機(jī)構(gòu)籌集基金的原因,更快的頁面加載能增加在用戶離開我們的頁面之前看到的廣告的數(shù)量. 這次的版本增加了大約12%的廣告展示 (還有對應(yīng)的籌資收入).

當(dāng)然,一個快速的應(yīng)用并不會是一個好的應(yīng)用; 它只是好的應(yīng)用不會是一個馬上就會讓人討厭的應(yīng)用. 對于我們而言,它提供了未來更多有趣動人的事情的基礎(chǔ).

-----

這是不是很有趣 ? 你想要通過一個有趣,充滿活力的團(tuán)隊工作不 ? 我們在招人哦 ! 還有,如果你本周就在 San Francisco 附近, 我就會在周五的 React.js 推介見面會 上 — 如果你想要一次會談的話就讓我知道吧.

感謝 Ti Zhao 和 Josiah Gaskin 對這篇文章的評論。

英文原文:Using React.js and Application Cache for a fast, synced app

責(zé)任編輯:林師授 來源: 開源中國社區(qū)編譯
相關(guān)推薦

2009-01-03 14:25:10

ibmdwWeb

2024-03-22 11:40:40

Node.jsNodeCRUD

2022-05-09 17:33:23

PWA漸進(jìn)式Web應(yīng)用程序離線優(yōu)先

2021-07-14 17:39:46

ReactRails API前端組件

2015-12-31 10:14:54

React.js開發(fā)Web應(yīng)用

2010-11-09 10:37:21

2021-04-25 05:31:33

React.js項目FastReactAp

2016-11-14 15:51:42

JavaScriptAngular.jsReact.js

2022-11-14 00:14:49

2014-08-13 16:36:13

2021-11-26 21:38:44

JavaScript框架開發(fā)

2024-03-27 11:18:02

2013-02-18 09:48:25

JS框架JavaScriptjQuery

2014-03-27 10:28:31

移動Web開發(fā)框架

2020-09-22 07:35:42

Node.jsVue.js文件壓縮

2011-09-16 17:00:19

iOS應(yīng)用Camera Geni

2025-01-17 09:29:42

2024-12-24 08:12:59

2013-03-28 14:54:36

2012-05-10 09:28:04

云計算應(yīng)用程序
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號