Maison > Article > interface Web > Comment utiliser le rendu côté serveur React
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 :
Le front et le back-end peuvent partager du code
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 :
L'application entière uniquement L'arbre d'état (State) correspondant à un unique Store
Store est généré en appelant une fonction réductrice (root réducteur) sur l'état
tree Chaque champ peut être généré en outre par différentes fonctions de réduction
Store contient plusieurs méthodes telles que dispatch, getState pour traiter le flux de données
Store's L'arbre d'état ne peut être déclenché que par dispatch(action)
Flux de données Redux :
l'action est un objet { type , payload }
la fonction de réduction est déclenchée par store.dispatch(action)
la fonction de réduction accepte (état, action) deux paramètres, renvoie un nouvel état
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
接下来实现 ,
./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中文网其它相关文章!
推荐阅读:
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!