react 服务端渲染教程(基于DVA)

react 服务端渲染教程(基于DVA)

Github Repo 地址

dva-ssr

使用

  • npm install
  • npm run buildClient
  • npm run buildServer
  • npm run ssr
  • view localhost:3000

功能

  • 基于 Dva 的 SSR 解决方案
  • 支持 Code Splitting (不再使用Dva自带的 dva/dynamic加载组件)
  • 支持 CSS Modules

SSR实现逻辑

概览

在这里插入图片描述

上图是SSR的运行时流程图(暂时不考虑构建的问题)

图中左侧是浏览器端看到的页面源码。其中红色框标识的3个部分,是SSR需要关注的重点内容。

  • 最简单的是中间一个框,它是服务端渲染的App的内容部分。

  • 第一个是分片(splitting)代码文件。即SSR Server必须要知道,浏览器要正确展示这个页面,需要包含哪些分片的js代码。
    如果不计算并返回这个script标签,那么浏览器render这个list 组建时,会发现这个组件不存在,还需要异步加载并re-render 页面。

  • 最后一个框,是服务端返回的 window._preloadedState 即 全局状态对象。浏览器端要使用这个对象对redux的store进行初始化。

收到客户端的SSR请求后,SSR Server将依次执行如下五部操作:

  1. 对请求的路径,进行路由匹配;并 “获取/加载”(获取对应同步组件,加载对应异步组件) 所涉及的组件
  
  // 初始化
  const history = createMemoryHistory();
  history.push(req.path);
  const initialState = {
    };

  const app = dva({
    history, initialState});
  app.router(router);
  const App = app.start();
  let routes = getRoutes(app);

  // 匹配路由,获取需要加载的Route组件(包含Loadable组件)
  const matchedComponents = matchRoutes(routes, req.path).map(({
    route}) => {
    
    if (!route.component.preload) {
    
      // 同步组件
      return route.component;
    } else {
    
      // 异步组件
      return route.component.preload().then(res => res.default)
    }
  });
  const loadedComponents = await Promise.all(matchedComponents);
  1. 对1中组件进行初始化(如需),进行接口请求,并等待请求返回。

注: 需要进行数据初始化的组件,需要定义 static fetching 方法

const actionList = loadedComponents.map(component => {
    
    if (component.fetching) {
    
      return component.fetching({
    
        ...app._store,
        ...component.props,
        path: req.path
      });
    } else {
    
      return null;
    }
  });
await Promise.all(actionList);
  1. 调用 ReactDOMServer.renderString 渲染数据

//  Render Dva App。同时使用Loadable.Capture 捕捉本次渲染包含的Loadable组件集合Array<String>。
const modules = [];
const markup = renderToString(
    <Loadable.Capture report={
    module => modules.push(module)}>
      <App location={
    req.path} context={
    {
    }}/>
    </Loadable.Capture>
);

//  构造需要render的 script标签。其中利用了react-loadable的webpack插件在构建过程中生成的module字典
let bundles = getBundles(moduleDict, modules);
let scripts = bundles.filter(bundle => bundle.file.endsWith('.js'));
let scriptMarkups = scripts.map(bundle => {
    
  return `<script src="/public/${
      bundle.file}"></script>`
}).join('\n');

Loadable 的相关概念和用法,请参考 github: react-loadable

Code Splitting
  1. 获取preloadedState
const preloadedState = app._store.getState();
  1. 拼装Html,并返回
res.send(`
<!DOCTYPE html>
<html>
  &
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值