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

挨踢部落直播課堂第七期:如何使用React構建同構(isomorphic)應用

原創(chuàng)
移動開發(fā)
隨著前端的發(fā)展,為了用戶體驗,H5越來越多的使用SPA架構,導致JS代碼越來越多,體積也變的龐大,這時傳統(tǒng)的ajax方式在首屏訪問時就變得慢了,而且ajax在seo方面有天然的弱勢,這時服務端渲染又回來了。我們使用React搭配React Router等類庫來實現(xiàn)服務端渲染,讓首屏更快,seo更好。那么,如何使用React構建同構(isomorphic)應用呢,我們特此邀請到百安居前端架構師陳國興做直播分享。

【51CTO.com原創(chuàng)稿件】隨著前端的發(fā)展,為了用戶體驗,H5越來越多的使用SPA架構,導致JS代碼越來越多,體積也變的龐大,這時傳統(tǒng)的ajax方式在首屏訪問時就變得慢了,而且ajax在seo方面有天然的弱勢,這時服務端渲染又回來了。我們使用React搭配React Router等類庫來實現(xiàn)服務端渲染,讓首屏更快,seo更好。那么,如何使用React構建同構(isomorphic)應用呢,我們特此邀請到百安居前端架構師陳國興做直播分享。

隨著前端的發(fā)展,為了用戶體驗,H5越來越多的使用SPA架構,導致JS代碼越來越多,體積也變的龐大,這時傳統(tǒng)的ajax方式在首屏訪問時就變得慢了,而且ajax在seo方面有天然的弱勢,這時服務端渲染又回來了。我們使用React搭配React Router等類庫來實現(xiàn)服務端渲染,讓首屏更快,seo更好。那么,如何使用React構建同構(isomorphic)應用呢,我們特此邀請到百安居前端架構師陳國興做直播分享。

內容簡介

1. 移動端為什么要用SPA

2. 傳統(tǒng)ajax方式和服務端渲染加載速度比較

3. 服務端渲染技術詳解

4. 同構方式的react代碼編寫一些需要注意的地方

我們會用到的react、react-router、redux這些庫,,代碼示例是之前的項目,react-router是2的版本,和最新的API可能會有一些差異。

一、移動端為什么要用SPA

我們先從為什么用SPA說起。這是因為移動互聯(lián)網的發(fā)展。頁面的跳轉如果使用傳統(tǒng)鏈接跳轉的方式,尤其是在2.5G、3G時代,網速慢,不穩(wěn)定,很容易點擊鏈接后,然后就看到一片白茫茫的頁面,運氣好,等一會到新的頁面,運氣不好,那就一直在白頁面上。所以需要SPA,至少在網絡不好的時候,還可以看到頁面,這樣用戶的體驗會比較好。

因為使用SPA的方式開發(fā),必然導致客戶端JS是富客戶端的JS,那么就帶來一個問題,代碼量多了如何管理,以及如何可維護。這就有了早期的BackBone,SpineJs等MVC框架,以及之后的MVP,MVVM等框架,把原來服務端的架構思想逐漸帶到前端。目前,以angular、vue、react最為流行。

有人會問,為什么不選擇angular或者vue?用一句流行的話來說就是:angular(vue)你是個好人,但我們不適合。當然,真正的原因是React的組件化思想剛好和自己想要的匹配,是技術思想上的認同,而react出來時,vue那時還沒出來,angular真是又重又復雜。

二、傳統(tǒng)ajax問題和服務端渲染加載速度比較

我們今天是講同構,同構首先是服務端渲染(SSR),一般也稱為首屏優(yōu)化。我盜一張圖,來看傳統(tǒng)的頁面渲染流程。

1

最早的Web開發(fā)方式其實是服務端渲染,但是后來大家覺得體驗不好,每一次都是要重新刷新頁面,這就有了ajax。最初,ajax并沒有問題。但是,移動時代來了,JS框架來了。JS變的越來越大了。

從上面的圖可以看出,我們要訪問一個頁面,首先是渲染一個沒有數據的空白頁面,然后加載資源,比如CSS,JS,一個打包壓縮好的JS文件甚至有好幾百K。等JS加載完了,這時才發(fā)起API請求,用戶還得繼續(xù)等,等到請求回來才能看到一個真正的頁面。所以這個時候,反而慢了,這時服務端渲染的方式又回來了。

我發(fā)個圖,極端情況在慢速3G下的訪問情況。

2

慢速的3G,沒有調用接口的情況,到可正常訪問時,總時間在22.94s(不計圖片加載)。可以看到,login頁面和main.css加載完是在3.96s。

如果是使用服務端渲染,是不需要js即可看到頁面的,也就是時間是這里的login頁面和css加載完就可以看到真正的頁面。而如果是傳統(tǒng)ajax方式,則是在22s多,兩者有6倍左右的差距,如果再加上接口調用,我們之前測試過,用戶看到首屏的的時間,有8-10倍左右的差距。

服務端渲染的首屏時間是:page+api request+css,page已經包含數據了。
客戶端的首屏時間是:page+css+js+api request。

除了客戶端需要加載一個很大的js文件外,API請求在服務端進行一般也是更快的。這里簡單解釋一下首屏的概念:非首頁。從任何地方進來的那個頁面都是首屏。也就是說,做isomorphic,首先要保證沒有js的情況,可以直接從瀏覽器輸入任何一個地址進行訪問也是可訪問的。和早期的服務端渲染是一樣的。所以,這也是為什么SEO能更友好的原因。

三、服務端渲染技術詳解

為什么要使用客戶端與服務端復用代碼的同構方式?維護性問題??蛻舳耸遣话踩?,所以服務端不能信任客戶端,需要做各種校驗,包括拉取數據后的ui渲染,這樣就需要前后端都要寫一次一樣邏輯的代碼。為了開發(fā)效率、維護性等,所以需要復用。

這點上,nodejs有天然的優(yōu)勢。如果不考慮同構的話,光服務端渲染,其實很簡單,react提供了一個方法:renderToString()。只要把它取得的數據塞到模版文件里就可以了,比如nodejs的ejs文件。為了代碼復用,我們會考慮ui放服務端渲染,邏輯放服務端,API請求的代碼也共用一套,路由最好也是只寫一次。react router它就支持服務端路由,并且它也為服務端渲染提供了一些友好的API,比如Link。

接下來,我們就把具體的代碼大概講一下。首先是路由定義。

3

這里,history屬性在瀏覽器端與服務端是不一樣的,所以需要傳進來。瀏覽器端使用browserHistory:

  1. import { browserHistory } from 'react-router' 

4

服務器端使用createMemoryHistory:

  1. import { RouterContext, createMemoryHistory, match } from 'react-router' 

 我們把服務器端(nodejs)的路由配置全部貼出來,其實使用的是react-router提供的方法。

  1. server.get('*', (req, res, next) => { 
  2.   const history = createMemoryHistory() 
  3.   const routes = createRoutes(history) 
  4.   let store = configStore() 
  5.  
  6.   match({ routes, location: req.url }, (err, redirectLocation, renderProps) => { 
  7.     if (err) { 
  8.       res.status(500).send(err.message) 
  9.     } else if (!renderProps) { 
  10.       res.status(404).send('page not found') 
  11.     } else { 
  12.       getComponentFetch(renderProps, history, store).then(() => { 
  13.         let reduxState = escape(JSON.stringify(store.getState())) 
  14.         let html = ReactDOM.renderToString( 
  15.           <Provider store={store}> 
  16.             {<RouterContext {...renderProps} />} 
  17.           </Provider> 
  18.           ) 
  19.         res.render('home', { html, scriptSrcs, cssSrc, reduxState }) 
  20.       }) 
  21.       .catch((err) => { 
  22.         next(err) 
  23.       }) 
  24.     } 
  25.   }) 
  26. }) 
  27.  
  28. function getComponentFetch (renderProps, history, store) { 
  29.   let { query, params } = renderProps 
  30.   let component = renderProps.components[renderProps.components.length - 1].WrappedComponent 
  31.   let promise = component && component.fetchData ? component.fetchData({ query, params, store, history }) : Promise.resolve() 
  32.   return promise 

路由匹配所有請求,當訪問時,根據路由配置,取得對應的react組件,因為要在服務端馬上調用API接口獲取數據,我們會在容器組件放一個靜態(tài)方法:fetchData,調用這個方法來取得數據,然后放在一個變量傳給ejs模版文件。當然,我們這時頁面已經渲染出數據了。這個reduxState變量的數據是做為js加載完后 渲染時使用。 

我們看一下客戶端的代碼:

  1. let reduxState = {} 
  2. if (window.__STATE__) { 
  3.   try { 
  4.     reduxState = JSON.parse(unescape(__STATE__)) 
  5.   } catch (e) { 
  6.   } 
  7. const store = configStore(reduxState) 
  8.   ReactDOM.render(( 
  9.     <Provider store={store}> 
  10.       {createRoutes(browserHistory)} 
  11.     </Provider> 
  12.     ), document.getElementById('container-root')) 

window.__STATE__ 這個就是我從服務端傳過來的變量reduxState的值,用來初始化redux的store。

同時,如果為了避免首屏服務端請求一次數據,瀏覽器又再請求一次數據,我們可以把當前的container組件的displayName也從服務端傳回瀏覽器端,這樣在組件里判斷有值,則不發(fā)起fetch請求,而是直接使用的是redux store的值。

fetchData的大概代碼我也貼一下:

  1. static fetchData ({store}) { 
  2.     let cityId = global.currentCityId 
  3.     return store.dispatch(actions.getHomeData(cityId)) 
  4.   } 

寫這個方法的目的也是為了復用redux的邏輯,不管是action還是store。這樣,我們不需要掌握很多nodejs知識,只需要在server端配置一下路由,即可實現(xiàn)nodejs與瀏覽器端一套代碼復用。包括UI、邏輯、redux、路由。后續(xù)只需要正常寫組件,寫數據請求、邏輯等即可。

四、同構方式的react代碼編寫一些需要注意的地方

最后,講一下一些注意點。

1、在react的初次渲染的周期(constructor\componentWillMount\render),不要寫瀏覽器相關對象的代碼,比如window。另外:要注意componentDidMount是在瀏覽器端執(zhí)行,在node端并不會執(zhí)行。也不要在上面的幾個生命周期寫setState。

2、用戶首屏渲染后,在沒有加載js的情況下,有可能馬上進行操作,比如鏈接跳轉或者表單提交,所以要假設沒有JS的情況也可以正常訪問。比如,表單提交使用form,鏈接使用href(react router的link)而不是onClick。這里,react router的Link,當你js加載完后會自動把鏈接變成hash的形式。同時js加載完成后,就可以把表單事件或者鏈接轉給js來處理了,后續(xù)的頁面就全部走ajax的方式跳轉。補充一下同構方式的渲染流程:用戶發(fā)起請求—>服務端接收到請求—> 匹配路由—>拉數據—>渲染界面—>拉JS代碼—>匹配瀏覽器路由—>走路由對應的組件的生命周期—>拉數據——>更新組件。所以,當js都down下來后,這時你的onClick事件才是真正可以生效的。

3、瀏覽器要訪問API地址,這個涉及到多個環(huán)境,我這里為了方便,是在我的node做代理中轉API請求的,這樣,瀏覽器端的請求的API地址只要是http://localhost 就可以。nodejs端根據不同的環(huán)境取不同的API接口配置,而且這樣做有額外的好處,可以繞過跨域,API后端服務不需要去配跨域這么麻煩,瀏覽器的請求也可以少一個option去校驗是否允許跨域訪問。

react同構,差不多就這些東西了。

以下問題是來自51CTO開發(fā)者社群小伙伴們的提問和分享

Q:Java-workman-北京:如果只用react+ajax的情況效率會有變化嗎?不是一個新的應用,只是在原有基礎上使用react的dom去展示,和普通的ajax會有太大的出處嗎?

A:百安居-陳老師:這個效率就是之前說的,你要數據出來,必須得等你的JS文件下載完,然后發(fā)起請求,所以肯定會比較慢。


Q:前端-Jouryjc-深圳:老師麻煩貼一下項目github。

A:百安居-陳老師:我自己有弄了一個startkit,并沒傳到github。


Q:數據-unicorn-北京:ant.design是目前最好的react框架嗎?

A:百安居-陳老師: ant.design不是react框架。只是UI。


Q:前端-秋香姐-深圳:node做代理中轉API請求 這個是怎么做的啊?這個http-proxy是在服務端做的還是在客戶的做的啊?

A:百安居-陳老師:用http-proxy。在nodejs端。

  1. import httpProxy from 'http-proxy' 
  2. const proxy = httpProxy.createProxyServer({ 
  3.   target: `${targetUrl}/api` 
  4. }) 
  5. server.use('/api', (req, res) => { 
  6.   proxy.web(req, res) 
  7. }) 

Q:前端-秋香姐-深圳:static fetchData 方法是啥時候怎么調用的呀?

A:百安居-陳老師:

5

6


Q:數據-unicorn-北京:react UI框架您推薦那個呢?

A:百安居-陳老師: 這個要根據具體的場景,我們一般都不用UI框架,都是根據具體設計來做。后臺的話,可以考慮用Ant.Design,這個聽說比較大,不適合面向終端用戶。


Q:前端-秋香姐-深圳:陳老師,做這個服務端渲染我們是不是需要有一個node服務器呀?

A:百安居-陳老師:對的。


Q:前端-秋香姐-深圳:我們對這個node服務器怎么搭建配置呢?

A:百安居-陳老師:一般用node最好,因為語言一樣,復用性最高。我是用express,其實沒幾行代碼,基本都貼了。其實很簡單。就是配置一個路由,一個靜態(tài)的獲取數據方法供nodejs端調用。其他的注意一下一些細節(jié)就好了。


Q:前端-秋香姐-深圳:對了,我們做這個react同構,需要運維同學幫我們做些什么配置嗎?還是跟之前沒做react同構的服務器一樣嗎?

A:百安居-陳老師:需要跑一個nodejs服務??赡苣阒暗捻撁媸怯蒍ava之類的渲染,現(xiàn)在都交給nodejs就好了。Java之類的只需要提供API接口。


Q:呆丸-搬磚-烏龜:“Java之類的只需要提供API接口。” 這個意思是,前臺要自己搞個node服務器?

A:百安居-陳老師:nodejs服務器。同構,就是服務端、客戶端復用一套代碼。那么既然有服務端了。


Q:Java-workman-北京:陳老師能否簡單的描述一下React的精髓或最優(yōu)美的地方是什么?

A:百安居-陳老師:我最佩服的是React那么復雜的功能,它暴露出來的API卻非常簡潔,可以說,只要一個render方法,就入門了,懂props、state就能寫大部分功能了。化繁為簡的功力非常高深。

【51CTO原創(chuàng)稿件,合作站點轉載請注明原文作者和出處為51CTO.com】

責任編輯:何星 來源: 51CTO開發(fā)者交流群
相關推薦

2017-07-12 14:21:41

技術坐診

2017-01-17 14:01:18

大數據框架結構 數據源

2017-11-14 17:57:55

抓娃娃SDK

2017-12-13 17:34:06

人機交互

2017-07-11 15:47:14

PythonPHP FC

2018-03-07 16:52:50

編程函數

2017-08-25 09:52:47

遠控技術

2017-11-22 14:24:21

Reactjavaajax

2017-09-11 14:08:50

技術坐診

2017-03-15 15:11:52

開發(fā)者

2017-04-07 16:44:43

開發(fā)者

2017-05-10 15:43:53

開發(fā)者

2017-06-08 14:43:00

開發(fā)者

2017-08-11 16:20:36

技術坐診

2017-09-28 17:46:44

技術坐診

2018-04-18 10:46:29

AI

2018-01-23 17:51:24

2017-06-30 16:13:19

HTML5

2017-11-09 18:10:28

APPH5

2018-03-28 16:48:12

深度學習
點贊
收藏

51CTO技術棧公眾號