欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談React 服務(wù)器端渲染的使用

 更新時(shí)間:2018年05月08日 10:36:03   作者:劉輝  
本篇文章主要介紹了淺談React 服務(wù)器端渲染的使用,React是最受歡迎的客戶端 JavaScript 框架,在本教程中,我們將逐步向您介紹服務(wù)器端的渲染示例

React 提供了兩個(gè)方法 renderToString 和 renderToStaticMarkup 用來將組件(Virtual DOM)輸出成 HTML 字符串,這是 React 服務(wù)器端渲染的基礎(chǔ),它移除了服務(wù)器端對(duì)于瀏覽器環(huán)境的依賴,所以讓服務(wù)器端渲染變成了一件有吸引力的事情。

服務(wù)器端渲染除了要解決對(duì)瀏覽器環(huán)境的依賴,還要解決兩個(gè)問題:

  1. 前后端可以共享代碼
  2. 前后端路由可以統(tǒng)一處理

React 生態(tài)提供了很多選擇方案,這里我們選用 Redux 和 react-router 來做說明。

Redux

Redux 提供了一套類似 Flux 的單向數(shù)據(jù)流,整個(gè)應(yīng)用只維護(hù)一個(gè) Store,以及面向函數(shù)式的特性讓它對(duì)服務(wù)器端渲染支持很友好。

2 分鐘了解 Redux 是如何運(yùn)作的

關(guān)于 Store:

  1. 整個(gè)應(yīng)用只有一個(gè)唯一的 Store
  2. Store 對(duì)應(yīng)的狀態(tài)樹(State),由調(diào)用一個(gè) reducer 函數(shù)(root reducer)生成
  3. 狀態(tài)樹上的每個(gè)字段都可以進(jìn)一步由不同的 reducer 函數(shù)生成
  4. Store 包含了幾個(gè)方法比如 dispatch, getState 來處理數(shù)據(jù)流
  5. Store 的狀態(tài)樹只能由 dispatch(action) 來觸發(fā)更改

Redux 的數(shù)據(jù)流:

  1. action 是一個(gè)包含 { type, payload } 的對(duì)象
  2. reducer 函數(shù)通過 store.dispatch(action) 觸發(fā)
  3. reducer 函數(shù)接受 (state, action) 兩個(gè)參數(shù),返回一個(gè)新的 state
  4. reducer 函數(shù)判斷 action.type 然后處理對(duì)應(yīng)的 action.payload 數(shù)據(jù)來更新狀態(tài)樹

所以對(duì)于整個(gè)應(yīng)用來說,一個(gè) Store 就對(duì)應(yīng)一個(gè) UI 快照,服務(wù)器端渲染就簡化成了在服務(wù)器端初始化 Store,將 Store 傳入應(yīng)用的根組件,針對(duì)根組件調(diào)用 renderToString 就將整個(gè)應(yīng)用輸出成包含了初始化數(shù)據(jù)的 HTML。

react-router

react-router 通過一種聲明式的方式匹配不同路由決定在頁面上展示不同的組件,并且通過 props 將路由信息傳遞給組件使用,所以只要路由變更,props 就會(huì)變化,觸發(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 會(huì)根據(jù) route
// 規(guī)則匹配到的組件作為 `props.children` 傳入
const Container = (props) => {
 return (
  <div>{props.children}</div>
 );
};

// route 規(guī)則:
// - `/list` 顯示 `List` 組件
// - `/item/:id` 顯示 `Item` 組件
const routes = (
 <Route path="/" component={Container} >
  <Route path="list" component={List} />
  <Route path="item/:id" component={Item} />
 </Route>
);

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è)字段對(duì)應(yīng)也可以有自己的reducer,所以這里引入了 listReducer 和 itemReducer,可以看到這兩個(gè) reducer的 state 參數(shù)就只是整個(gè)狀態(tài)樹上對(duì)應(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

對(duì)應(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)樯婕暗疆惒秸?qǐng)求,這里的 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) <List>,<Item> 組件,然后把 redux 和 react 組件關(guān)聯(lián)起來,具體細(xì)節(jié)參見 react-redux

./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 (
  <div>
   <Provider store={store}>
    <Router history={createBrowserHistory()}>
     {routes}
    </Router>
   </Provider>
  </div>
 );
}

render(<Root />, 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 對(duì)象傳遞給組件。

./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 `
  <!DOCTYPE html>
  <html lang="en">
  <head>
   <meta charset="UTF-8">
  </head>
  <body>
   <div id="root">
    <div>
     ${html}
    </div>
   </div>
   <script>
    window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};
   </script>
   <script src="/static/bundle.js"></script>
  </body>
  </html>
 `;
}

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(
     <Provider store={store}>
      <RoutingContext {...renderProps} />
     </Provider>
    );
    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 的部分(<div id="root">),前后端的 HTML 結(jié)構(gòu)應(yīng)該是一致的。然后要把 store 的狀態(tài)樹寫入一個(gè)全局變量(__INITIAL_STATE__),這樣客戶端初始化 render 的時(shí)候能夠校驗(yàn)服務(wù)器生成的 HTML 結(jié)構(gòu),并且同步到初始化狀態(tài),然后整個(gè)頁面被客戶端接管。

最后關(guān)于頁面內(nèi)鏈接跳轉(zhuǎn)如何處理?

react-router 提供了一個(gè) <Link> 組件用來替代 <a> 標(biāo)簽,它負(fù)責(zé)管理瀏覽器 history,從而不是每次點(diǎn)擊鏈接都去請(qǐng)求服務(wù)器,然后可以通過綁定 onClick 事件來作其他處理。

比如在 /list 頁面,對(duì)于每一個(gè) item 都會(huì)用 <Link> 綁定一個(gè) route url:/item/:id,并且綁定 onClick 去觸發(fā) dispatch(fetchItem(id)) 獲取數(shù)據(jù),顯示詳情頁內(nèi)容。

更多參考

Universal (Isomorphic)
isomorphic-redux-app

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • React+ts實(shí)現(xiàn)二級(jí)聯(lián)動(dòng)效果

    React+ts實(shí)現(xiàn)二級(jí)聯(lián)動(dòng)效果

    這篇文章主要為大家詳細(xì)介紹了React+ts實(shí)現(xiàn)二級(jí)聯(lián)動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • react實(shí)現(xiàn)頁面水印效果的全過程

    react實(shí)現(xiàn)頁面水印效果的全過程

    大家常常關(guān)注的是網(wǎng)站圖片增加水印,而很少關(guān)注頁面水印,其實(shí)這個(gè)需求也是比較常見的,比如公文系統(tǒng)、合同系統(tǒng)等,這篇文章主要給大家介紹了關(guān)于react實(shí)現(xiàn)頁面水印效果的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • react路由配置方式詳解

    react路由配置方式詳解

    本篇文章主要介紹了react路由配置方式詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • React 中的 useContext使用方法

    React 中的 useContext使用方法

    這篇文章主要介紹了React中的useContext使用,使用useContext在改變一個(gè)數(shù)據(jù)時(shí),是通過自己逐級(jí)查找對(duì)比改變的數(shù)據(jù)然后渲染,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • React改變?cè)貥邮降牟僮鞔a

    React改變?cè)貥邮降牟僮鞔a

    這篇文章主要介紹了React技巧之改變?cè)貥邮?本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • React從Class方式轉(zhuǎn)Hooks詳解

    React從Class方式轉(zhuǎn)Hooks詳解

    這篇文章主要介紹了React從Class方式轉(zhuǎn)Hooks詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-09-09
  • 詳解React如何優(yōu)雅地根據(jù)prop更新state值

    詳解React如何優(yōu)雅地根據(jù)prop更新state值

    這篇文章主要為大家詳細(xì)介紹了React如何優(yōu)雅地實(shí)現(xiàn)根據(jù)prop更新state值,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以了解下
    2023-11-11
  • React 自動(dòng)聚焦字段使用詳解

    React 自動(dòng)聚焦字段使用詳解

    這篇文章主要為大家介紹了React 自動(dòng)聚焦字段使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • React中style的使用及注意事項(xiàng)(推薦)

    React中style的使用及注意事項(xiàng)(推薦)

    React中style的使用和直接在HTML中使用有些不同,第一,React中必須是style="opacity:{this.state.opacity};"這種寫法,第二如果設(shè)置多個(gè)style格式如下,多個(gè)style中間使用逗號(hào)分割,這篇文章主要介紹了React中style的使用注意事項(xiàng),需要的朋友可以參考下
    2023-02-02
  • react 移動(dòng)端實(shí)現(xiàn)列表左滑刪除的示例代碼

    react 移動(dòng)端實(shí)現(xiàn)列表左滑刪除的示例代碼

    這篇文章主要介紹了react 移動(dòng)端實(shí)現(xiàn)列表左滑刪除的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07

最新評(píng)論