Heim  >  Artikel  >  Web-Frontend  >  So nutzen Sie das serverseitige Rendering von React effizient

So nutzen Sie das serverseitige Rendering von React effizient

php中世界最好的语言
php中世界最好的语言Original
2018-05-23 10:58:471750Durchsuche

Dieses Mal zeige ich Ihnen, wie Sie das serverseitige Rendering von React effizient nutzen und welche Vorsichtsmaßnahmen bei der Verwendung des serverseitigen Renderings von React gelten. Hier sind praktische Fälle, werfen wir einen Blick darauf.

React bietet zwei Methoden renderToString und renderToStaticMarkup, um die Komponente (Virtual DOM) in HTML String auszugeben. Dies ist die Grundlage für das serverseitige Rendern von React, wodurch die serverseitige Abhängigkeit entfernt wird in der Browserumgebung macht das serverseitige Rendering zu einer attraktiven Sache.

Zusätzlich zur Lösung der Abhängigkeit von der Browserumgebung muss das serverseitige Rendering auch zwei Probleme lösen:

  1. Das vordere und hintere Ende können Code gemeinsam nutzen

  2. Sowohl als auch Back-End-Routing können einheitlich gehandhabt werden

Das React-Ökosystem bietet viele Optionen zur Veranschaulichung.

Redux

Redux bietet eine Reihe von unidirektionalen Datenflüssen ähnlich wie Flux. Die gesamte Anwendung verwaltet nur einen Store und ist funktionsfähig. Die Funktionen erleichtern die serverseitige Rendering-Unterstützung.

2 Minuten, um zu erfahren, wie Redux funktioniert

Über Store:

  1. Nur ​​die gesamte Anwendung Der Zustandsbaum (State), der einem eindeutigen Store

  2. Store entspricht, wird durch Aufrufen einer Reduzierfunktion (Root Reducer) für den

  3. -Status generiert Baum Jedes Feld kann durch verschiedene Reduzierfunktionen weiter generiert werden

  4. Store enthält mehrere Methoden wie „dispatch“ und „getState“, um den Datenfluss zu verarbeiten

  5. Store's Zustandsbaum kann nur durch Dispatch(Aktion) ausgelöst werden

Redux-Datenfluss:

  1. Aktion ist ein { Typ, Nutzlast }-Objekt

  2. Reduzierfunktion wird durch store.dispatch(action) ausgelöst

  3. Reduzierfunktion akzeptiert (Zustand, Aktion) zwei Parameter und gibt einen neuen Zustand zurück

  4. Die Reduzierfunktion bestimmt action.type und verarbeitet dann die entsprechenden action.payload-Daten, um den Zustandsbaum zu aktualisieren

So für Für die gesamte Anwendung entspricht ein Store einem UI-Snapshot. Das serverseitige Rendern wird vereinfacht, indem der Store auf der Serverseite initialisiert, der Store an die Root-Komponente der Anwendung übergeben und renderToString für die Root-Komponente aufgerufen wird, um die gesamte Anwendung zu rendern . Ausgabe als HTML mit Initialisierungsdaten.

react-router

react-router gleicht verschiedene Routing-Entscheidungen ab, um verschiedene Komponenten deklarativ auf der Seite anzuzeigen, und die Routing-Informationen wird über Requisiten an die Komponente übergeben. Solange sich also das Routing ändert, ändern sich auch die Requisiten, was ein erneutes Rendern der Komponente auslöst.

Angenommen, es gibt eine sehr einfache Anwendung mit nur zwei Seiten, einer Listenseite /list und einer Detailseite /item/:id. Klicken Sie auf ein Element in der Liste, um die Detailseite aufzurufen.

Sie können Routen wie folgt definieren, ./routes.js

import React from 'react';
import { Route } from 'react-router';
import { List, Item } from './components';
// 无状态(stateless)组件,一个简单的容器,react-router 会根据 route
// 规则匹配到的组件作为 `props.children` 传入
const Container = (props) => {
 return (
  <p>{props.children}</p>
 );
};
// route 规则:
// - `/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;

Von hier aus verwenden wir diese sehr einfache Anwendung, um das Front-End und Back- zu erklären. Endaspekte bei der Implementierung des serverseitigen Renderings. Einige Details.

Reducer

Store wird vom Reducer generiert, daher spiegelt der Reducer tatsächlich die Zustandsbaumstruktur von Store

wider /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)
 };
}

Der Statusparameter von rootReducer ist der Statusbaum des gesamten Stores. Jedes Feld unter dem Statusbaum kann auch einen eigenen Reduzierer haben, daher werden hier listReducer und itemReducer eingeführt, wie Sie sehen können Die Statusparameter dieser beiden Reduzierer sind lediglich die entsprechenden Listen- und Elementfelder im gesamten Statusbaum.

Konkret ist ./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 ein einfaches Array mit Elementen, das dieser Struktur ähneln kann: [{ id: 0, name: 'first item' } , {id: 1, name: 'second item'}], abgerufen aus action.payload von 'FETCH_LIST_SUCCESS'.

Dann ./reducers/item.js, um die erhaltenen Artikeldaten zu verarbeiten

const initialState = {};
export default function listReducer(state = initialState, action) {
 switch(action.type) {
 case 'FETCH_ITEM_SUCCESS': return [...action.payload];
 default: return state;
 }
}

Action

对应的应该要有两个 action 来获取 list 和 item,触发 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 实现,前后端要共享代码这点很重要。

另外因为涉及到异步请求,这里的 action 用到了 thunk,也就是函数,redux 通过 thunk-middleware 来处理这类 action,把函数当作普通的 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

接下来实现 组件,然后把 redux 和 react 组件关联起来,具体细节参见 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` 来自服务器端渲染,下一部分细说
const initialState = window.INITIAL_STATE;
const store = configureStore(initialState);
const Root = (props) => {
 return (
  <p>
   <Provider store={store}>
    <Router history={createBrowserHistory()}>
     {routes}
    </Router>
   </Provider>
  </p>
 );
}
render(<Root />, document.getElementById('root'));

至此,客户端部分结束。

Server Rendering

接下来的服务器端就比较简单了,获取数据可以调用 action,routes 在服务器端的处理参考 react-router server rendering,在服务器端用一个 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>
   <p id="root">
    <p>
     ${html}
    </p>
   </p>
   <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');
  }
 });
});

服务器端渲染部分可以直接通过共用客户端 store.dispatch(action) 来统一获取 Store 数据。另外注意 renderFullPage 生成的页面 HTML 在 React 组件 mount 的部分(

),前后端的 HTML 结构应该是一致的。然后要把 store 的状态树写入一个全局变量(INITIAL_STATE),这样客户端初始化 render 的时候能够校验服务器生成的 HTML 结构,并且同步到初始化状态,然后整个页面被客户端接管。

最后关于页面内链接跳转如何处理?

react-router 提供了一个 组件用来替代 标签,它负责管理浏览器 history,从而不是每次点击链接都去请求服务器,然后可以通过绑定 onClick 事件来作其他处理。

比如在 /list 页面,对于每一个 item 都会用 绑定一个 route url:/item/:id,并且绑定 onClick 去触发 dispatch(fetchItem(id)) 获取数据,显示详情页内容。

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

vue axios生产环境与发布环境配置不同接口地址步骤详解

JS计算圆周率到小数点后100位实现步骤详解

Das obige ist der detaillierte Inhalt vonSo nutzen Sie das serverseitige Rendering von React effizient. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn