怎样高效的使用React服务器端渲染

这次给大家带来怎样高效的使用React服务器端渲染,使用React服务器端渲染的注意事项有哪些,下面就是实战案例,一起来看一下。

React 提供了两个方法 renderToString 和 renderToStaticMarkup 用来将组件(Virtual DOM)输出成 HTML 字符串,这是 React 服务器端渲染的基础,它移除了服务器端对于浏览器环境的依赖,所以让服务器端渲染变成了一件有吸引力的事情。

服务器端渲染除了要解决对浏览器环境的依赖,还要解决两个问题:

前后端可以共享代码

前后端路由可以统一处理

React 生态提供了很多选择方案,这里我们选用 Redux 和 react-router 来做说明。

Redux

Redux 提供了一套类似 Flux 的单向数据流,整个应用只维护一个 Store,以及面向函数式的特性让它对服务器端渲染支持很友好。

2 分钟了解 Redux 是如何运作的

关于 Store:

整个应用只有一个唯一的 Store

Store 对应的状态树(State),由调用一个 reducer 函数(root reducer)生成

状态树上的每个字段都可以进一步由不同的 reducer 函数生成

Store 包含了几个方法比如 dispatch, getState 来处理数据流

Store 的状态树只能由 dispatch(action) 来触发更改

Redux 的数据流:

action 是一个包含 { type, payload } 的对象

reducer 函数通过 store.dispatch(action) 触发

reducer 函数接受 (state, action) 两个参数,返回一个新的 state

reducer 函数判断 action.type 然后处理对应的 action.payload 数据来更新状态树

所以对于整个应用来说,一个 Store 就对应一个 UI 快照,服务器端渲染就简化成了在服务器端初始化 Store,将 Store 传入应用的根组件,针对根组件调用 renderToString 就将整个应用输出成包含了初始化数据的 HTML。

react-router

react-router 通过一种声明式的方式匹配不同路由决定在页面上展示不同的组件,并且通过 props 将路由信息传递给组件使用,所以只要路由变更,props 就会变化,触发组件 re-render。

假设有一个很简单的应用,只有两个页面,一个列表页 /list 和一个详情页 /item/:id,点击列表上的条目进入详情页。

可以这样定义路由,./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 (  

{props.children}

);};// route 规则:// - `/list` 显示 `List` 组件// - `/item/:id` 显示 `Item` 组件const routes = ( );export default routes;

登录后复制

从这里开始,我们通过这个非常简单的应用来解释实现服务器端渲染前后端涉及的一些细节问题。

Reducer

Store 是由 reducer 产生的,所以 reducer 实际上反映了 Store 的状态树结构

./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 参数就是整个 Store 的状态树,状态树下的每个字段对应也可以有自己的reducer,所以这里引入了 listReducer 和 itemReducer,可以看到这两个 reducer的 state 参数就只是整个状态树上对应的 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 的简单数组,可能类似这种结构:[{ id: 0, name: ‘first item’}, {id: 1, name: ‘second item’}],从 ‘FETCH_LIST_SUCCESS’ 的 action.payload 获得。

然后是 ./reducers/item.js,处理获取到的 item 数据

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 (  

{routes}

);}render(, 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 `                

${html}

window.INITIAL_STATE = ${JSON.stringify(initialState)}; `;}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( ); 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)) 获取数据,显示详情页内容。

相信看了本文案例你已经掌握了方法,更多精彩请关注【创想鸟】其它相关文章!

推荐阅读:

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

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

以上就是怎样高效的使用React服务器端渲染的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2759158.html

(0)
上一篇 2025年3月8日 08:23:33
下一篇 2025年2月23日 11:31:29

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • vue项目打包传送至服务器

    这次给大家带来vue项目打包传送至服务器,vue项目打包传送至服务器的注意事项有哪些,下面就是实战案例,一起来看一下。 当我们将 vue 项目完成后,面临的就是如何将项目进行打包上线,放到服务器中。我使用的是 vue-cli(simple)…

    2025年3月8日 编程技术
    200
  • VUE中地区选择器组件使用详解

    这次给大家带来VUE中地区选择器组件使用详解,VUE中地区选择器组件使用的注意事项有哪些,下面就是实战案例,一起来看一下。 废话不多说,直接进入正题。 安装,引用,这些直接从官网拷贝来的,就不多说了。 1、安装 使用 npm 安装: npm…

    编程技术 2025年3月8日
    200
  • ES6中Promise使用步骤详解

    这次给大家带来ES6中Promise使用步骤详解,ES6中Promise使用的注意事项有哪些,下面就是实战案例,一起来看一下。 当然,这并不代表迸发成了全栈。全栈的技能很集中,绝不仅仅是前端会写一些HTML和一些交互,后台熟悉数据库的增删查…

    编程技术 2025年3月8日
    200
  • 怎样用React Form完成组件封装

    这次给大家带来怎样用React Form完成组件封装,用React Form完成组件封装的注意事项有哪些,下面就是实战案例,一起来看一下。 前言 对于网页系统来说,表单提交是一种很常见的与用户交互的方式,比如提交订单的时候,需要输入收件人、…

    2025年3月8日
    200
  • 如何使用Vue.js计算属性与侦听器

    这次给大家带来如何使用Vue.js计算属性与侦听器,使用Vue.js计算属性与侦听器的注意事项有哪些,下面就是实战案例,一起来看一下。 一、 概述  计算属性   模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多…

    2025年3月8日
    200
  • 如何使用Node.js中Koa实现用户认证

    这次给大家带来如何使用Node.js中Koa实现用户认证,使用Node.js中Koa实现用户认证的注意事项有哪些,下面就是实战案例,一起来看一下。 一、前置知识 基于Token的身份验证 Koajs 中文文档 Koa 框架教程 二、环境 M…

    2025年3月8日 编程技术
    200
  • 如何使用node.js启动后台运行forever

    这次给大家带来如何使用node.js启动后台运行forever,使用node.js启动后台运行forever的注意事项有哪些,下面就是实战案例,一起来看一下。 我们知道想要项目部署后运行 需要使用命令行 cd 到项目目录然后执行 npm i…

    编程技术 2025年3月8日
    200
  • React-router v4使用步骤详解

    这次给大家带来React-router v4使用步骤详解,React-router v4使用的注意事项有哪些,下面就是实战案例,一起来看一下。 也许学习react-router最好的办法就是用react-router-dom v4来写一个多…

    编程技术 2025年3月8日
    200
  • React结合TypeScript和Mobx步骤详解

    这次给大家带来React结合TypeScript和Mobx步骤详解,React结合TypeScript和Mobx的注意事项有哪些,下面就是实战案例,一起来看一下。 为什么要使用TypeScript 侦测错误 通过静态类型检测可以尽早检测出程…

    2025年3月8日
    200
  • js原型使用详解

    这次给大家带来js原型使用详解,js原型使用的注意事项有哪些,下面就是实战案例,一起来看一下。 什么是原型? 在 javascript 中原形是一个对象,通过原形可以实现属性的继承。 let personBase = new Object(…

    2025年3月8日
    200

发表回复

登录后才能评论