Maison  >  Article  >  interface Web  >  Comment utiliser le rendu côté serveur React

Comment utiliser le rendu côté serveur React

php中世界最好的语言
php中世界最好的语言original
2018-05-31 09:48:331277parcourir

Cette fois, je vais vous montrer comment utiliser le rendu côté serveur React et quelles sont les précautions d'utilisation du rendu côté serveur React. Voici des cas pratiques, jetons un oeil.

React fournit deux méthodes renderToString et renderToStaticMarkup pour afficher le composant (Virtual DOM) en HTML String, qui est la base du rendu côté serveur React, qui supprime la dépendance côté serveur sur l'environnement du navigateur rend le rendu côté serveur attrayant.

En plus de résoudre la dépendance à l'égard de l'environnement du navigateur, le rendu côté serveur doit également résoudre deux problèmes :

  1. Le front et le back-end peuvent partager du code

  2. Le routage backend et le routage peuvent être gérés de manière uniforme

L'écosystème React offre de nombreuses options. Ici, nous utilisons Redux et React-Router à titre d'illustration.

Redux

Redux fournit un ensemble de flux de données unidirectionnels similaires à Flux. L'application entière ne gère qu'un seul Store et est fonctionnelle. Les fonctionnalités le rendent convivial pour la prise en charge du rendu côté serveur.

2 minutes pour apprendre comment fonctionne Redux

À propos du Store :

  1. L'application entière uniquement L'arbre d'état (State) correspondant à un unique Store

  2. Store est généré en appelant une fonction réductrice (root réducteur) sur l'état

  3. tree Chaque champ peut être généré en outre par différentes fonctions de réduction

  4. Store contient plusieurs méthodes telles que dispatch, getState pour traiter le flux de données

  5. Store's L'arbre d'état ne peut être déclenché que par dispatch(action)

Flux de données Redux :

  1. l'action est un objet { type , payload }

  2. la fonction de réduction est déclenchée par store.dispatch(action)

  3. la fonction de réduction accepte (état, action) deux paramètres, renvoie un nouvel état

  4. La fonction de réduction détermine action.type puis traite les données action.payload correspondantes pour mettre à jourl'arbre d'état

Donc pour pour l'ensemble de l'application, un magasin correspond à un instantané de l'interface utilisateur. Le rendu côté serveur est simplifié en initialisant le magasin côté serveur, en transmettant le magasin au composant racine de l'application et en appelant renderToString sur le composant racine pour restituer l'intégralité de l'application. . Sortie au format HTML contenant les données d'initialisation.

react-router

react-router affiche différents composants sur la page en faisant correspondre différentes décisions de routage de manière déclarative, et les informations de routage est transmis au composant via les accessoires, donc tant que le routage change, les accessoires changeront, déclenchant le nouveau rendu du composant.

Supposons qu'il existe une application très simple avec seulement deux pages, une page de liste /list et une page de détails /item/:id. Cliquez sur un élément de la liste pour accéder à la page de détails.

Vous pouvez définir des routes comme celle-ci, ./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;

À partir de là, nous expliquons la mise en œuvre du front-end de rendu côté serveur et back-end via cette application très simple Quelques détails impliqués.

Reducer

Store est généré par le réducteur, donc le réducteur reflète en fait la structure arborescente d'état du Store

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

Le paramètre d'état de rootReducer est l'arborescence d'état de l'ensemble du magasin. Chaque champ sous l'arborescence d'état peut également avoir son propre réducteur, donc listReducer et itemReducer sont introduits ici. les paramètres d'état de ces deux réducteurs ne sont que la liste et les champs d'éléments correspondants sur l'ensemble de l'arborescence d'état.

Plus précisément, ./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 est un simple tableau contenant des éléments, qui peuvent être similaires à cette structure : [{ id : 0, name : 'first item '}, {id : 1, name : 'second item'}], obtenu à partir de action.payload de 'FETCH_LIST_SUCCESS'.

Puis ./reducers/item.js, traitement des données d'article obtenues

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中文网其它相关文章!

推荐阅读:

怎样使用JS实现计算圆周率到小数点后100位

怎样使用vue axios 给生产与发布环境配置接口地址

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn