umajs-react-ssr

Umajs-react-ssr是由@umajs/plugin-react-ssr在新窗口中打开 搭配Srejs在新窗口中打开构建的轻量级,使用简单,灵活的React服务端渲染解决方案;可以在controllermiddleware中灵活使用,通过模板引擎式的语法对react页面组件进行服务端渲染。

1、插件介绍

plugin-react-ssr插件扩展了Umajs中提供的统一返回处理Result对象,新增了reactView页面组件渲染方法,可在controller自由调用,使用类似传统模板引擎;也同时将方法挂载到了 koa 中间件中的ctx对象上;当一些公关的页面组件,比如 404、异常提示页面、登录或者需要在中间件中拦截跳转时可以在middleware中调用。

2、特性

  • 不默认路由,不需区分前端路由和后端路由概念,且支持页面级组件 AB 测;灵活
  • 页面组件中没有__isBrowser__之类变量对ssrcsr模式进行特殊区分处理;统一
  • 自定义HTML采用htmlWebpackPlugin,没有runtime,页面响应速度更高;高性能
  • 支持html中使用nunjucks类模板引擎语法实现SEO易上手
  • 页面开发不依赖框架包装的任何模块,保持原生的React开发体验;友好,易升级
  • 数据获取由服务端统一处理加工,页面视图开发和数据加工分开处理;逻辑更清晰
  • 支持SSRCSR动态调整,支持SSR缓存,降级。高可用
  • 支持其他koa开发框架使用。可扩展
  • 支持 MPA,各页面组件可单独构建,可页面级更新

3、插件安装

yarn add @umajs/plugin-react-ssr --save
1

4、插件配置

// plugin.config.ts
export default <{ [key: string]: TPluginConfig }>{
  'react-ssr': {
    enable: true,
    options: {
      rootDir: 'web', // 客户端页面组件根文件夹
      rootNode: 'app', // 客户端页面挂载根元素ID
      ssr: true, // 全局开启服务端渲染
      cache: false, // 全局使用服务端渲染缓存 开发环境设置true无效
      prefixCDN: '/', // 客户端代码部署CDN前缀
    },
  },
}
1
2
3
4
5
6
7
8
9
10
11
12
13

5、web 目录结构

   - web # rootDir配置可修改
        - pages # 固定目录
            - home #页面名称
                - index.tsx
                - index.scss
1
2
3
4
5

6、创建 react 页面组件

页面组件开发模式支持 js ,tsx。

import './home.scss'
import React from 'react'
type typeProps = {
  say: string
}
export default function (props: typeProps) {
  const { say } = props
  return <div className="ts-demo">{say}</div>
}
1
2
3
4
5
6
7
8
9

7、脚手架初始化模板工程【推荐】

在 cli 中支持快速创建umajs-react-ssr模板工程。

npm i @umajs/cli -g  // 安装cli工具
uma project umajs-react-demo  //通过uma初始化工程,选择react模板工程
1
2

image

cd umajs-react-demo
yarn install
yarn start

1
2
3
4

8、API

插件扩展了Umajs中提供的统一返回处理Result方法,新增了reactView页面组件可在controller自由调用,方式类似传统模板引擎使用方法;也同时将方法挂载到了 koa 中间件中的ctx对象上;当一些公关的页面组件,比如 404、异常提示页面、登录或者需要在中间件中拦截跳转时可以在middleware中调用。

interface TviewOptions{
    ssr?: boolean, // 全局开启服务端渲染
    cache?: boolean, // 全局使用服务端渲染缓存
    useEngine?: boolean, // 渲染自定义html的页面组件时,选择性开启使用模板引擎
    baseName?: string, //客户端根路由 仅使用react-router时有效
}
Result.reactView(viewName:string,initProps?:object,options?:TviewOptions);
ctx.reactView(viewName:string,initProps?:object,options?:TviewOptions);
1
2
3
4
5
6
7
8

如果 options 参数传递为空 则默认会使用全局配置属性,全局配置采用插件集成时传递的 options 参数

注意 cache只在生产环境开启有效。

9、controller中使用

import { BaseController, Path } from '@umajs/core'
import { Result } from '@umajs/plugin-react-ssr'

export default class Index extends BaseController {
  @Path('/')
  index() {
    return Result.reactView(
      'home',
      { say: 'hi,I am a ReactView' },
      { cache: true }
    )
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

10、middleware中使用

对于中间件的使用,引入顺序需要在插件之后。

;async (ctx, next) => {
  try {
    await next()
  } catch (e) {
    return ctx.reactView('error', { msg: e.stack }, { cache: false })
  }
}
1
2
3
4
5
6
7

11、客户端嵌套路由(react-router)

在页面组件中使用 react-router 时,只能在 controller 中使用,切需要服务端对路由做支持。框架默认集成了 BrowserRouter,无需开发者在页面组件中引入

// 页面组件 web/home/index.js
export default class APP extends Component {
    render() {
        return (
            <Switch>
                <Route exact path="/" component={Home} />
                <Route exact path="/about" component={About} />
                <Route exact path="/about/:msg" component={About} />
                <Route component={Home} />
            </Switch>
        );
    }
}

// 服务端路由 前后端路由规则必须保持一致。
@Path("/home","/home/:path")
browserRouter() {
    return Result.reactView('home',{say:"hi,I am a ReactView"},{cache:true,baseName:'home'});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

注:baseName默认为页面组件标识名称。和 pages 下的页面文件名称保持一致,当服务端根路由和文件名称不一致时,需要给插件传递baseName属性,以确保服务端和客户端根路由一致。

12、SEO 和自定义 HTML

在 SEO 场景时,需要动态修改页面的标题和关键字等信息时,我们可以在自定义 html 中使用模板引擎语法,使用模板引擎时需要先开启使用@umajs/plugin-views插件并设置useEngine:true;建议和nunjucks搭配使用。参考 demo在新窗口中打开

  • 插件配置
// plugin.config.ts
views: {
        enable: true,
        name: 'views',
        options: {
            root: `${process.cwd()}/views`,
            autoRender:true,
            opts: {
                map: { html: 'nunjucks' },
            },
        },
    },
1
2
3
4
5
6
7
8
9
10
11
12
  • 设置 useEngine
// src/index.controller
//调用时开启使用模板引擎标识,为提高性能,对未动态修改模板数据的页面组件不要设置此属性
Result.reactView(
  'index',
  { msg: 'This is the template text!', title: 'hi,umajs-react-ssr' },
  { cache: false, useEngine: true }
)
1
2
3
4
5
6
7
  • 模板

框架内置 HTMLWebpackPlugin 插件,开发者在页面组件同级目录下可以覆盖默认 html 模板自定义引入第三方资源和脚本。 更多规则使用请看自定义 HTML在新窗口中打开

<!-- web/pages/index/index.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width"
    />
    <meta name="format-detection" content="telephone=no" />
    <meta name="format-detection" content="email=no" />
    <meta name="format-detection" content="address=no;" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="default" />
    <meta name="keywords" content="{{msg}}" />
    <title>{{title}}</title>
    <!-- 引入第三方组件库样式 -->
  </head>
  <body>
    <div id="app"></div>
    <!-- 引入第三方sdk脚本 -->
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

13、生产部署

在部署生产环境之前,客户端代码需要提前编译。否则线上首次访问时会耗时比较长,影响用户体验。编译脚本命令为npx srejs build

"scripts": {
    "dev": "ts-node-dev --respawn src/app.ts",
    "build": "tsc && npx srejs build",
    "prepublish": "npm run build",
    "prod": "node app/app.js --production"
  },

1
2
3
4
5
6
7

源码请查看@umajs/plugin-react-ssr在新窗口中打开Srejs在新窗口中打开 欢迎 Star 和提供使用反馈。

14、案例

15、FAQ

  • 引入插件后启动项目报错TypeError:Cannot read property 'ROOT' of undefinedimage

此问题为项目@umajs/core版本冲突导致,解决方案为升级项目所依赖的包版本号,确保项目依赖的包版本号大于或者等于@umajs/plugin-react-ssr所依赖的@umajs/core版本号