這篇文章給大家分享的是有關(guān)React服務(wù)器端渲染怎么用的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。
項(xiàng)城ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:13518219792(備注:SSL證書合作)期待與您的合作!React 提供了兩個(gè)方法 renderToString 和 renderToStaticMarkup 用來將組件(Virtual DOM)輸出成 HTML 字符串,這是 React 服務(wù)器端渲染的基礎(chǔ),它移除了服務(wù)器端對于瀏覽器環(huán)境的依賴,所以讓服務(wù)器端渲染變成了一件有吸引力的事情。
服務(wù)器端渲染除了要解決對瀏覽器環(huán)境的依賴,還要解決兩個(gè)問題:
前后端可以共享代碼
前后端路由可以統(tǒng)一處理
React 生態(tài)提供了很多選擇方案,這里我們選用 Redux 和 react-router 來做說明。
Redux
Redux 提供了一套類似 Flux 的單向數(shù)據(jù)流,整個(gè)應(yīng)用只維護(hù)一個(gè) Store,以及面向函數(shù)式的特性讓它對服務(wù)器端渲染支持很友好。
2 分鐘了解 Redux 是如何運(yùn)作的
關(guān)于 Store:
整個(gè)應(yīng)用只有一個(gè)唯一的 Store
Store 對應(yīng)的狀態(tài)樹(State),由調(diào)用一個(gè) reducer 函數(shù)(root reducer)生成
狀態(tài)樹上的每個(gè)字段都可以進(jìn)一步由不同的 reducer 函數(shù)生成
Store 包含了幾個(gè)方法比如 dispatch, getState 來處理數(shù)據(jù)流
Store 的狀態(tài)樹只能由 dispatch(action) 來觸發(fā)更改
Redux 的數(shù)據(jù)流:
action 是一個(gè)包含 { type, payload } 的對象
reducer 函數(shù)通過 store.dispatch(action) 觸發(fā)
reducer 函數(shù)接受 (state, action) 兩個(gè)參數(shù),返回一個(gè)新的 state
reducer 函數(shù)判斷 action.type 然后處理對應(yīng)的 action.payload 數(shù)據(jù)來更新狀態(tài)樹
所以對于整個(gè)應(yīng)用來說,一個(gè) Store 就對應(yīng)一個(gè) UI 快照,服務(wù)器端渲染就簡化成了在服務(wù)器端初始化 Store,將 Store 傳入應(yīng)用的根組件,針對根組件調(diào)用 renderToString 就將整個(gè)應(yīng)用輸出成包含了初始化數(shù)據(jù)的 HTML。
react-router
react-router 通過一種聲明式的方式匹配不同路由決定在頁面上展示不同的組件,并且通過 props 將路由信息傳遞給組件使用,所以只要路由變更,props 就會變化,觸發(fā)組件 re-render。
假設(shè)有一個(gè)很簡單的應(yīng)用,只有兩個(gè)頁面,一個(gè)列表頁 /list 和一個(gè)詳情頁 /item/:id,點(diǎn)擊列表上的條目進(jìn)入詳情頁。
可以這樣定義路由,./routes.js
import React from 'react'; import { Route } from 'react-router'; import { List, Item } from './components'; // 無狀態(tài)(stateless)組件,一個(gè)簡單的容器,react-router 會根據(jù) route // 規(guī)則匹配到的組件作為 `props.children` 傳入 const Container = (props) => { return ({props.children}); }; // route 規(guī)則: // - `/list` 顯示 `List` 組件 // - `/item/:id` 顯示 `Item` 組件 const routes = (); export default routes;
從這里開始,我們通過這個(gè)非常簡單的應(yīng)用來解釋實(shí)現(xiàn)服務(wù)器端渲染前后端涉及的一些細(xì)節(jié)問題。
Reducer
Store 是由 reducer 產(chǎn)生的,所以 reducer 實(shí)際上反映了 Store 的狀態(tài)樹結(jié)構(gòu)
./reducers/index.js
import listReducer from './list'; import itemReducer from './item'; export default function rootReducer(state = {}, action) { return { list: listReducer(state.list, action), item: itemReducer(state.item, action) }; }
rootReducer 的 state 參數(shù)就是整個(gè) Store 的狀態(tài)樹,狀態(tài)樹下的每個(gè)字段對應(yīng)也可以有自己的reducer,所以這里引入了 listReducer 和 itemReducer,可以看到這兩個(gè) reducer的 state 參數(shù)就只是整個(gè)狀態(tài)樹上對應(yīng)的 list 和 item 字段。
具體到 ./reducers/list.js
const initialState = []; export default function listReducer(state = initialState, action) { switch(action.type) { case 'FETCH_LIST_SUCCESS': return [...action.payload]; default: return state; } }
list 就是一個(gè)包含 items 的簡單數(shù)組,可能類似這種結(jié)構(gòu):[{ id: 0, name: 'first item'}, {id: 1, name: 'second item'}],從 'FETCH_LIST_SUCCESS' 的 action.payload 獲得。
然后是 ./reducers/item.js,處理獲取到的 item 數(shù)據(jù)
const initialState = {}; export default function listReducer(state = initialState, action) { switch(action.type) { case 'FETCH_ITEM_SUCCESS': return [...action.payload]; default: return state; } }
Action
對應(yīng)的應(yīng)該要有兩個(gè) action 來獲取 list 和 item,觸發(fā) reducer 更改 Store,這里我們定義 fetchList 和 fetchItem 兩個(gè) action。
./actions/index.js
import fetch from 'isomorphic-fetch'; export function fetchList() { return (dispatch) => { return fetch('/api/list') .then(res => res.json()) .then(json => dispatch({ type: 'FETCH_LIST_SUCCESS', payload: json })); } } export function fetchItem(id) { return (dispatch) => { if (!id) return Promise.resolve(); return fetch(`/api/item/${id}`) .then(res => res.json()) .then(json => dispatch({ type: 'FETCH_ITEM_SUCCESS', payload: json })); } }
isomorphic-fetch 是一個(gè)前后端通用的 Ajax 實(shí)現(xiàn),前后端要共享代碼這點(diǎn)很重要。
另外因?yàn)樯婕暗疆惒秸埱螅@里的 action 用到了 thunk,也就是函數(shù),redux 通過 thunk-middleware 來處理這類 action,把函數(shù)當(dāng)作普通的 action dispatch 就好了,比如 dispatch(fetchList())
Store
我們用一個(gè)獨(dú)立的 ./store.js,配置(比如 Apply Middleware)生成 Store
import { createStore } from 'redux'; import rootReducer from './reducers'; // Apply middleware here // ... export default function configureStore(initialState) { const store = createStore(rootReducer, initialState); return store; }
react-redux
接下來實(shí)現(xiàn) ,
./app.js
import React from 'react'; import { render } from 'react-dom'; import { Router } from 'react-router'; import createBrowserHistory from 'history/lib/createBrowserHistory'; import { Provider } from 'react-redux'; import routes from './routes'; import configureStore from './store'; // `__INITIAL_STATE__` 來自服務(wù)器端渲染,下一部分細(xì)說 const initialState = window.__INITIAL_STATE__; const store = configureStore(initialState); const Root = (props) => { return (); } render({routes} , document.getElementById('root'));
至此,客戶端部分結(jié)束。
Server Rendering
接下來的服務(wù)器端就比較簡單了,獲取數(shù)據(jù)可以調(diào)用 action,routes 在服務(wù)器端的處理參考 react-router server rendering,在服務(wù)器端用一個(gè) match 方法將拿到的 request url 匹配到我們之前定義的 routes,解析成和客戶端一致的 props 對象傳遞給組件。
./server.js
import express from 'express'; import React from 'react'; import { renderToString } from 'react-dom/server'; import { RoutingContext, match } from 'react-router'; import { Provider } from 'react-redux'; import routes from './routes'; import configureStore from './store'; const app = express(); function renderFullPage(html, initialState) { return ``; } app.use((req, res) => { match({ routes, location: req.url }, (err, redirectLocation, renderProps) => { if (err) { res.status(500).end(`Internal Server Error ${err}`); } else if (redirectLocation) { res.redirect(redirectLocation.pathname + redirectLocation.search); } else if (renderProps) { const store = configureStore(); const state = store.getState(); Promise.all([ store.dispatch(fetchList()), store.dispatch(fetchItem(renderProps.params.id)) ]) .then(() => { const html = renderToString(${html}); res.end(renderFullPage(html, store.getState())); }); } else { res.status(404).end('Not found'); } }); });
服務(wù)器端渲染部分可以直接通過共用客戶端 store.dispatch(action) 來統(tǒng)一獲取 Store 數(shù)據(jù)。另外注意 renderFullPage 生成的頁面 HTML 在 React 組件 mount 的部分(
最后關(guān)于頁面內(nèi)鏈接跳轉(zhuǎn)如何處理?
react-router 提供了一個(gè) 組件用來替代 標(biāo)簽,它負(fù)責(zé)管理瀏覽器 history,從而不是每次點(diǎn)擊鏈接都去請求服務(wù)器,然后可以通過綁定 onClick 事件來作其他處理。
比如在 /list 頁面,對于每一個(gè) item 都會用 綁定一個(gè) route url:/item/:id,并且綁定 onClick 去觸發(fā) dispatch(fetchItem(id)) 獲取數(shù)據(jù),顯示詳情頁內(nèi)容。
感謝各位的閱讀!關(guān)于“React服務(wù)器端渲染怎么用”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。