亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

詳解基于React.js和Node.js的SSR實現方案

2024-05-06 16:49:18
字體:
來源:轉載
供稿:網友

基礎概念

SSR:即服務端渲染(Server Side Render) 傳統的服務端渲染可以使用Java,php 等開發語言來實現,隨著 Node.js 和相關前端領域技術的不斷進步,前端同學也可以基于此完成獨立的服務端渲染。

過程:瀏覽器發送請求 -> 服務器運行 react代碼生成頁面 -> 服務器返回頁面 -> 瀏覽器下載HTML文檔 -> 頁面準備就緒 即:當前頁面的內容是服務器生成好給到瀏覽器的。

React.js,Node.js,SSR

對應CSR:即客戶端渲染(Client Side Render) 過程:瀏覽器發送請求 -> 服務器返回空白 HTML(HTML里包含一個root節點和js文件) -> 瀏覽器下載js文件 -> 瀏覽器運行react代碼 -> 頁面準備就緒 即:當前頁面的內容是js渲染出來

 

 

React.js,Node.js,SSR

如何區分頁面是否服務端渲染: 右鍵點擊 -> 顯示網頁源代碼,如果頁面上的內容在HTML文檔里,是服務端渲染,否則就是客戶端渲染。

對比

  • CSR:首屏渲染時間長,react代碼運行在瀏覽器,消耗的是瀏覽器的性能
  • SSR:首屏渲染時間短,react代碼運行在服務器,消耗的是服務器的性能

為什么要用服務端渲染

首屏加載時間優化,由于SSR是直接返回生成好內容的HTML,而普通的CSR是先返回空白的HTML,再由瀏覽器動態加載JavaScript腳本并渲染好后頁面才有內容;所以SSR首屏加載更快、減少白屏的時間、用戶體驗更好。

SEO (搜索引擎優化),搜索關鍵詞的時候排名,對大多數搜索引擎,不識別JavaScript 內容,只識別 HTML 內容。 (注:原則上可以不用服務端渲染時最好不用,所以如果只有 SEO 要求,可以用預渲染等技術去替代)

構建一個服務端渲染的項目

(1) 使用 Node.js 作為服務端和客戶端的中間層,承擔 proxy代理,處理cookie等操作。

(2) hydrate 的使用:在有服務端渲染情況下,使用hydrate代替render,它的作用主要是將相關的事件注水進HTML頁面中(即:讓React組件的數據隨著HTML文檔一起傳遞給瀏覽器網頁),這樣可以保持服務端數據和瀏覽器端一致,避免閃屏,使第一次加載體驗更高效流暢。

 ReactDom.hydrate(<App />, document.getElementById('root'));

(3) 服務端代碼webpack編譯:通常會建一個webpack.server.js文件,除了常規的參數配置外,還需要設置target參數為'node'。

const serverConfig = { target: 'node', entry: './src/server/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, '../dist') }, externals: [nodeExternals()], module: { rules: [{  test: //.js?$/,  loader: 'babel-loader',  exclude: [  path.join(__dirname, './node_modules')  ] } ... ] } (此處省略樣式打包,代碼壓縮,運行壞境配置等等...) ...};

(4) 使用react-dom/server下的 renderToString方法在服務器上把各種復雜的組件和代碼轉化成 HTML 字符串返回到瀏覽器,并在初始請求時發送標記以加快頁面加載速度,并允許搜索引擎抓取頁面以實現SEO目的。

const render = (store, routes, req, context) => { const content = renderToString(( <Provider store={store}>  <StaticRouter location={req.path} context={context}>  <div>   {renderRoutes(routes)}  </div>  </StaticRouter> </Provider> )); return ` <html>  <head>  <title>ssr</title>  </head>  <body>  <div id='root'>${content}</div>  <script src='/index.js'></script>  </body> </html> `;}app.get('*', function (req, res) { ... const html = render(store, routes, req, context); res.send(html);});

與renderToString類似功能的還有: i. renderToStaticMarkup:區別在于renderToStaticMarkup 渲染出的是不帶data-reactid的純HTML,在JavaScript加載完成后因為不認識之前服務端渲染的內容導致重新渲染(可能頁面會閃一下)。

ii. renderToNodeStream:將React元素渲染為其初始HTML,返回一個輸出HTML字符串的可讀流。

iii. renderToStaticNodeStream:與renderToNodeStream此類似,除了這不會創建React在內部使用的額外DOM屬性,例如data-reactroot。

(5) 使用redux 承擔數據準備,狀態維護的職責,通常搭配react-redux, redux-thunk(中間件:發異步請求用到action)使用。(本猿目前使用比較多是就是Redux和Mobx,這里以Redux為例)。 A. 創建store(服務器每次請求都要創建一次,客戶端只創建一次):

const reducer = combineReducers({ home: homeReducer, page1: page1Reducer, page2: page2Reducer});export const getStore = (req) => { return createStore(reducer, applyMiddleware(thunk.withExtraArgument(serverAxios(req))));}export const getClientStore = () => { return createStore(reducer, window.STATE_FROM_SERVER, applyMiddleware(thunk.withExtraArgument(clientAxios)));}

B. action: 負責把數據從應用傳到store,是store數據的唯一來源

export const getData = () => { return (dispatch, getState, axiosInstance) => { return axiosInstance.get('interfaceUrl/xxx')  .then((res) => {  dispatch({   type: 'HOME_LIST',   list: res.list  })  }); }}

C. reducer:接收舊的state和action,返回新的state,響應actions并發送到store。

export default (state = { list: [] }, action) => { switch(action.type) { case 'HOME_LIST':  return {  ...state,  list: action.list  } default:  return state; }}export default (state = { list: [] }, action) => { switch(action.type) { case 'HOME_LIST':  return {  ...state,  list: action.list  } default:  return state; }} 

D. 使用react-redux的connect,Provider把組件和store連接起來

Provider 將之前創建的store作為prop傳給Provider

const content = renderToString(( <Provider store={store}> <StaticRouter location={req.path} context={context}>  <div>  {renderRoutes(routes)}  </div> </StaticRouter> </Provider>)); 

connect([mapStateToProps],[mapDispatchToProps],[mergeProps], [options])接收四個參數 常用的是前兩個屬性 mapStateToProps函數允許我們將store中的數據作為props綁定到組件上mapDispatchToProps將action作為props綁定到組件上

 connect(mapStateToProps(),mapDispatchToProps())(MyComponent)

(6) 使用react-router承擔路由職責 服務端路由不同于客戶端,它是無狀態的。React 提供了一個無狀態的組件StaticRouter,向StaticRouter傳遞當前URL,調用ReactDOMServer.renderToString() 就能匹配到路由視圖。

服務端

import { StaticRouter } from 'react-router-dom';import { renderRoutes } from 'react-router-config'import routes from './router.js'<StaticRouter location={req.path} context={{context}}>{renderRoutes(routes)}</StaticRouter> 

瀏覽器端

import { BrowserRouter } from 'react-router-dom';import { renderRoutes } from 'react-router-config'import routes from './router.js'<BrowserRouter> {renderRoutes(routes)}</BrowserRouter>

當瀏覽器的地址欄發生變化的時候,前端會去匹配路由視圖,同時由于req.path發生變化,服務端匹配到路由視圖,這樣保持了前后端路由視圖的一致,在頁面刷新時,仍然可以正常顯示當前視圖。如果只有瀏覽器端路由,而且是采用BrowserRouter,當頁面地址發生變化后去刷新頁面時,由于沒有對應的HTML,會導致頁面找不到,但是加了服務端路由后,刷新發生時服務端會返回一個完整的html給客戶端,頁面仍然正常顯示。 推薦使用 react-router-config插件,然后如上代碼在StaticRouter和BrowserRouter標簽的子元素里加renderRoutes(routes):建一個router.js文件

const routes = [{ component: Root, routes: [ { path: '/',  exact: true,  component: Home,  loadData: Home.loadData }, { path: '/child/:id',  component: Child,  loadData: Child.loadData  routes: [  path: '/child/:id/grand-child',  component: GrandChild,  loadData: GrandChild.loadData  ] } ]}];

在瀏覽器端請求一個地址的時候,server.js 里在實際渲染前可以通過matchRouters 這種方式確定要渲染的內容,調用loaderData函數進行action派發,返回promise->promiseAll->renderToString,最終生成HTML文檔返回。

import { matchRoutes } from 'react-router-config' const loadBranchData = (location) => { const branch = matchRoutes(routes, location.pathname) const promises = branch.map(({ route, match }) => {  return route.loadData  ? route.loadData(match)  : Promise.resolve(null) }) return Promise.all(promises)}

(7) 寫組件注意代碼同構(即:一套React代碼在服務端執行一次,在客戶端再執行一次) 由于服務器端綁定事件是無效的,所以服務器返回的只有頁面樣式(&注水的數據),同時返回JavaScript文件,在瀏覽器上下載并執行JavaScript時才能把事件綁上,而我們希望這個過程只需編寫一次代碼,這個時候就會用到同構,服務端渲染出樣式,在客戶端執行時綁上事件。

優點: 共用前端代碼,節省開發時間 弊端: 由于服務器端和瀏覽器環境差異,會帶來一些問題,如document等對象找不到,DOM計算報錯,前端渲染和服務端渲染內容不一致等;前端可以做非常復雜的請求合并和延遲處理,但為了同構,所有這些請求都在預先拿到結果才會渲染。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
色婷婷av一区二区三区在线观看| 在线日韩中文字幕| 亚洲www视频| 欧美中文字幕在线观看| 亚洲男人天堂久| 91在线高清免费观看| 中文欧美在线视频| 九色精品美女在线| 亚洲男人7777| 亚洲成人av片在线观看| 国产精品福利观看| 欧美伦理91i| 操91在线视频| 日韩中文在线观看| 国产精品男人爽免费视频1| 日韩av男人的天堂| 亚洲国模精品私拍| 福利视频第一区| 日韩av免费在线播放| 午夜精品久久久久久99热软件| 欧美综合在线第二页| 久久精品99久久久久久久久| 亚洲欧美日韩精品久久亚洲区| 亚洲综合自拍一区| 91亚洲精品一区| 精品视频偷偷看在线观看| 久久国产视频网站| 疯狂做受xxxx高潮欧美日本| 欧美一区二粉嫩精品国产一线天| 黑人巨大精品欧美一区二区三区| 亚洲欧美激情精品一区二区| 亚洲人成毛片在线播放| 亚洲高清色综合| 日韩福利视频在线观看| 中文字幕国产亚洲2019| 欧美主播福利视频| 粉嫩av一区二区三区免费野| 青青久久av北条麻妃海外网| 久久99久国产精品黄毛片入口| 国产欧美精品一区二区三区介绍| 欧美夫妻性视频| 国产成人福利夜色影视| 国产亚洲精品美女久久久| 51色欧美片视频在线观看| 美女视频黄免费的亚洲男人天堂| 91亚洲精品一区二区| 欧美电影免费观看网站| 91在线中文字幕| 精品偷拍各种wc美女嘘嘘| 国产亚洲美女久久| www.久久色.com| 欧美大片va欧美在线播放| 中文字幕日韩精品在线| 久久综合网hezyo| 欧美高清视频在线播放| 久热精品视频在线观看一区| 日韩欧亚中文在线| 精品露脸国产偷人在视频| 欧美成人精品一区二区| 精品少妇v888av| 91免费综合在线| 久久精品视频在线| 久久久久99精品久久久久| 国模吧一区二区三区| 久久久在线视频| 欧美日本在线视频中文字字幕| 久久精品国产亚洲精品2020| 国产不卡在线观看| 日韩欧美高清视频| 亚洲欧美日韩第一区| 欧美激情精品久久久久久变态| 欧美一级在线播放| 亚洲欧美激情四射在线日| 日韩国产在线看| 亚洲春色另类小说| 国产日韩视频在线观看| 97国产一区二区精品久久呦| 欧美精品久久久久久久免费观看| 久久69精品久久久久久国产越南| 国产免费一区二区三区在线观看| 国产精品美女网站| 疯狂做受xxxx欧美肥白少妇| 亚洲国产成人精品久久久国产成人一区| 97久久精品视频| 国产日韩av在线播放| 亚洲欧美在线免费观看| 亚洲欧美日韩一区二区在线| 久久高清视频免费| 日韩成人小视频| 亚洲欧洲在线观看| 成人在线播放av| 国产精品尤物福利片在线观看| www.久久色.com| 精品久久久国产精品999| 日本aⅴ大伊香蕉精品视频| 亚洲国产精品电影| 精品高清美女精品国产区| 麻豆国产精品va在线观看不卡| 亚洲网站在线播放| 欧美成人精品在线视频| 日韩中文理论片| 91精品国产91久久| 97视频在线观看免费| 亚洲区免费影片| 欧美电影免费在线观看| 日韩欧美精品网站| 欧美成人激情视频免费观看| 青青草一区二区| 欧美日韩国内自拍| 久久久久久亚洲| 91精品视频网站| 一区二区三区视频观看| 成人黄色av网站| 69久久夜色精品国产7777| 欧美与黑人午夜性猛交久久久| 精品美女国产在线| 亚洲综合精品一区二区| 96sao精品视频在线观看| 91系列在线观看| 精品久久久999| 九色成人免费视频| 日本一本a高清免费不卡| 国产精品免费久久久久久| 91色p视频在线| 最好看的2019的中文字幕视频| 欧美国产日韩一区二区在线观看| 欧美视频免费在线观看| 精品女厕一区二区三区| 日韩精品欧美激情| 色噜噜亚洲精品中文字幕| 成人精品一区二区三区| 红桃视频成人在线观看| 亚洲影视中文字幕| 欧美高跟鞋交xxxxhd| 成人国内精品久久久久一区| 精品国产精品自拍| 欧美日韩国产综合视频在线观看中文| 亚洲色在线视频| 亚洲国产欧美久久| 日韩在线观看免费全| 欧美高清视频在线| 国产精品男人的天堂| 美女国内精品自产拍在线播放| 国产亚洲美女久久| 久久中文字幕在线视频| 国产久一一精品| 欧美日韩另类字幕中文| 亚洲美女av电影| 精品国产自在精品国产浪潮| 日韩经典中文字幕| 成人在线中文字幕| 亚州精品天堂中文字幕| 国产成人+综合亚洲+天堂| 91高清视频免费观看| 国产精品一区二区三区毛片淫片| 国产专区精品视频| 福利微拍一区二区| 国产精品狠色婷| 日韩视频免费中文字幕| 中文字幕自拍vr一区二区三区| 亚洲精品福利资源站| 91国产美女在线观看| 久久电影一区二区| 日本一区二区不卡|