最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

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

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

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

Redux

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

2 分鐘了解 Redux 是如何運作的

關(guān)于 Store:

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

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

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

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

react-router

react-router 通過一種聲明式的方式匹配不同路由決定在頁面上展示不同的組件,并且通過 props 將路由信息傳遞給組件使用,所以只要路由變更,props 就會變化,觸發(fā)組件 re-render。

假設(shè)有一個很簡單的應(yīng)用,只有兩個頁面,一個列表頁 /list 和一個詳情頁 /item/:id,點擊列表上的條目進(jìn)入詳情頁。

可以這樣定義路由,./routes.js

import React from 'react';
import { Route } from 'react-router';
import { List, Item } from './components';

// 無狀態(tài)(stateless)組件,一個簡單的容器,react-router 會根據(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;

從這里開始,我們通過這個非常簡單的應(yīng)用來解釋實現(xiàn)服務(wù)器端渲染前后端涉及的一些細(xì)節(jié)問題。

Reducer

Store 是由 reducer 產(chǎn)生的,所以 reducer 實際上反映了 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ù)就是整個 Store 的狀態(tài)樹,狀態(tài)樹下的每個字段對應(yīng)也可以有自己的reducer,所以這里引入了 listReducer 和 itemReducer,可以看到這兩個 reducer的 state 參數(shù)就只是整個狀態(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 就是一個包含 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)該要有兩個 action 來獲取 list 和 item,觸發(fā) reducer 更改 Store,這里我們定義 fetchList 和 fetchItem 兩個 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 是一個前后端通用的 Ajax 實現(xiàn),前后端要共享代碼這點很重要。

另外因為涉及到異步請求,這里的 action 用到了 thunk,也就是函數(shù),redux 通過 thunk-middleware 來處理這類 action,把函數(shù)當(dāng)作普通的 action dispatch 就好了,比如 dispatch(fetchList())

Store

我們用一個獨立的 ./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

接下來實現(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ù)器端用一個 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 `
  <!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)樹寫入一個全局變量(__INITIAL_STATE__),這樣客戶端初始化 render 的時候能夠校驗服務(wù)器生成的 HTML 結(jié)構(gòu),并且同步到初始化狀態(tài),然后整個頁面被客戶端接管。

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

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

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

更多參考

Universal (Isomorphic)
isomorphic-redux-app

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

相關(guān)文章

  • 新建的React Native就遇到vscode報警解除方法

    新建的React Native就遇到vscode報警解除方法

    這篇文章主要為大家介紹了新建的React Native就遇到vscode報警解除方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • React數(shù)據(jù)傳遞之組件內(nèi)部通信的方法

    React數(shù)據(jù)傳遞之組件內(nèi)部通信的方法

    這篇文章主要介紹了React數(shù)據(jù)傳遞之組件內(nèi)部通信的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • React+Vite從零搭建項目及配置的實現(xiàn)

    React+Vite從零搭建項目及配置的實現(xiàn)

    本文主要介紹了React+Vite從零搭建項目及配置的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-04-04
  • React項目中不需要jQuery原因分析

    React項目中不需要jQuery原因分析

    在Web開發(fā)的早期,jQuery是一個革命性的庫,它極大地簡化了DOM操作、事件處理、動畫制作以及Ajax請求等任務(wù),React的出現(xiàn),jQuery在新項目中的必要性開始受到質(zhì)疑,本文將探討為什么在React應(yīng)用中不需要jQuery,感興趣的朋友可以參考下
    2024-02-02
  • React+umi+typeScript創(chuàng)建項目的過程

    React+umi+typeScript創(chuàng)建項目的過程

    這篇文章主要介紹了React+umi+typeScript創(chuàng)建項目的過程,結(jié)合代碼介紹了項目框架搭建的方式,本文給大家講解的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-02-02
  • 一起來學(xué)習(xí)React元素的創(chuàng)建和渲染

    一起來學(xué)習(xí)React元素的創(chuàng)建和渲染

    這篇文章主要為大家詳細(xì)介紹了React元素的創(chuàng)建和渲染,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • React onBlur回調(diào)中使用document.activeElement返回body完美解決方案

    React onBlur回調(diào)中使用document.activeElement返回body完美解決方案

    這篇文章主要介紹了React onBlur回調(diào)中使用document.activeElement返回body完美解決方案,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-04-04
  • react自適應(yīng)布局px轉(zhuǎn)rem實現(xiàn)示例詳解

    react自適應(yīng)布局px轉(zhuǎn)rem實現(xiàn)示例詳解

    這篇文章主要為大家介紹了react自適應(yīng)布局px轉(zhuǎn)rem實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • React生命周期方法之componentDidMount的使用

    React生命周期方法之componentDidMount的使用

    這篇文章主要介紹了React生命周期方法之componentDidMount的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • react中的watch監(jiān)視屬性-useEffect介紹

    react中的watch監(jiān)視屬性-useEffect介紹

    這篇文章主要介紹了react中的watch監(jiān)視屬性-useEffect使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09

最新評論

通山县| 阜宁县| 和政县| 潮州市| 图木舒克市| 永宁县| 大关县| 纳雍县| 华池县| 道真| 汉源县| 新邵县| 盱眙县| 昌乐县| 肃南| 崇州市| 黄龙县| 克山县| 新密市| 西贡区| 宿松县| 嘉峪关市| 三原县| 安乡县| 阜宁县| 沁水县| 天等县| 手游| 新化县| 宜章县| 建宁县| 邢台市| 根河市| 乌兰县| 盐源县| 六枝特区| 鄂托克旗| 余干县| 汉阴县| 宣汉县| 内江市|