umijs 服务端渲染(SSR) 指南

umijs 服务端渲染(SSR) 指南

Umi 是什么?

Umi,中文可发音为乌米,是可扩展的企业级前端应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。

Umi 是蚂蚁集团的底层前端框架,已直接或间接地服务了 3000+ 应用,包括 java、node、H5 无线、离线(Hybrid)应用、纯前端 assets 应用、CMS 应用等。他已经很好地服务了我们的内部用户,同时希望他也能服务好外部用户。

选择umijs v3版本

umijs v4版本已经发布,但是ssr功能还不完善,所以建议使用v3版本。

https://v3.umijs.org/zh-CN/docs/ssr

创建项目

mkdir umi-ssr-demo
cd umi-ssr-demo
npx @umijs/create-umi-app
npm i
npm run start

配置

    1. 配置 .umirc.js
import { defineConfig } from 'umi';

export default defineConfig({
  nodeModulesTransform: {
    type: 'none',
  },
  routes: [
    { path: '/', component: '@/pages/index' },
  ],
  fastRefresh: {},
  /** 开启ssr */
  ssr: {}
});

    1. 查看是否开启成功

20240606112924

如上图所示,直接返回了dom,不止一个根节点,这就算成功了

页面title、meta

每个页面都可以配置title和meta,通过配置<Helmet>组件即可

import React from 'react';
import { Helmet } from 'umi';

export default () => {
  return (
    <>
      {/* 可自定义需不需要编码 */}
      <Helmet>
        <title>Hello Umi Bar Title</title>
        <meta
          name="keywords"
          content="Hello Umi Bar Title"
        />
        <meta
          name="description"
          content="Hello Umi Bar Title"
        />
      </Helmet>
    </>
  );
};

获取接口数据

可以直接使用 umi-request 像spa应用一样获取接口数据,但是服务端渲染的,我们需要再服务器返回前就把数据获取好

每个页面有getInitialProps方法,在这个方法里可以获取数据,只有页面才有,组件是没有的

import React from 'react';

const Home = (props) => {
  const { data } = props;
  return (
    {/* <div>Hello World</div> */}
    <div>{data.title}</div>
  )
}

Home.getInitialProps = (async (ctx) => {
  return Promise.resolve({
    data: {
      title: 'Hello World',
    }
  })
})

当一个页面请求多个接口时,可以使用Promise.all()合并数据,能使请求速度变快,多个请求同时发出,而不是链式获取

import React from 'react';

const Home = (props) => {
  const { data } = props;
  return (
    {/* <div>Hello World</div> */}
    <div>{data.title}</div>
  )
}

Home.getInitialProps = (async (ctx) => {
  const [res1, res2] = await Promise.all([
    /** */
  ])
  return {
    res1,
    res2
  }
})

当数据量大了之后,会导致直接卡住,比如分页接口,调整了pageSize参数过大,可能导致页面卡死,可以将接口分片成多个请求,然后合并数据

export const useFiberRequest = async (fn, parames) => {
  const { page = 1, page_size = 10 } = parames?.pagination || {};
  const fiberSize = 10;
  const promiseList: any[] = [];

  for (let p = 1; p <= page_size / fiberSize; p++) {
    const pagination = {
      page: (page - 1) * (page_size / fiberSize) + p,
      page_size: fiberSize,
    };
    promiseList.push(
      fn({
        ...parames,
        pagination,
      }),
    );
  }

  const resList: any[] = await Promise.all(promiseList);

  const list = resList?.map((item) => item?.data?.list);
  const total = resList?.[0]?.data?.total || 0;
  return {
    data: {
      list: list?.flat(),
      total,
    },
  };
};

富文本

只有 div 标签 dangerouslySetInnerHTML 属性才能被 SSR 渲染,正常的写法应该是:

- <p dangerouslySetInnerHTML={{ __html: '<p>Hello</p>' }} />
+ <div dangerouslySetInnerHTML={{ __html: '<p>Hello</p>' }} />

与 dva 结合使用

已内置 dva,通过以下步骤使用:

    1. 配置.umirc.ts开启
export default {
  dva: {}
}

使用antd v5

由于antd v5是css in js的形式,所以样式是异步导入,导致ssr渲染时会先渲染出接口,然后闪一下,才加载出样式

我们可以把antd v5的样式提前加载,在ssr渲染时,先渲染出样式,然后再渲染出结构

  • 安装包
npm i -D @ant-design/static-style-extract ts-node cross-env
  • 生成antd v5 样式脚本
// src/scripts/genAntdCss.tsx
import { extractStyle } from '@ant-design/static-style-extract';
import fs from 'fs';

const outputPath = './public/css/antd.min.css';

// 1. default theme

const css = extractStyle();

// 2. With custom theme

// const css = extractStyle(withTheme);

fs.writeFileSync(outputPath, css);

console.log(`🎉 Antd CSS generated at ${outputPath}`);
  • 在package.json中添加脚本
{
  "scripts": {
    "predev": "ts-node --project ./tsconfig.node.json ./scripts/genAntdCss.tsx",
    "prebuild": "cross-env NODE_ENV=production ts-node --project ./tsconfig.node.json ./scripts/genAntdCss.tsx",
  }
}
  • 在app.tsx中引入
import '../public/css/antd.min.css';

部署

需要再起一个node服务,然后将ssr生成的dist目录作为静态资源目录

var Koa = require('koa'),
  logger = require('koa-logger'),
  json = require('koa-json'),
  views = require('koa-views'),
  onerror = require('koa-onerror');
const { extname } = require('path');

const app = new Koa();

// error handler
onerror(app);

let render;
app.use(async (ctx, next) => {
  const req = ctx.req;
  const res = ctx.res;
  const ext = extname(ctx.request.path);

  if (ext) {
    await next();
    return;
  }
  // 或者从 CDN 上下载到 server 端
  // const serverPath = await downloadServerBundle('http://cdn.com/bar/umi.server.js');
  if (!render) {
    render = require('./dist/umi.server');
  }
  res.setHeader('Content-Type', 'text/html');

  const context = {};
  const { html, error, rootContainer } = await render({
    // 有需要可带上 query
    path: req.url,
    context,

    // 可自定义 html 模板
    // htmlTemplate: defaultHtml,

    // 启用流式渲染
    // mode: 'stream',

    // html 片段静态标记(适用于静态站点生成)
    // staticMarkup: false,

    // 扩展 getInitialProps 在服务端渲染中的参数
    // getInitialPropsCtx: {},

    // manifest,正常情况下不需要
  });

  if (error) {
    console.log('----------------服务端报错-------------------', error);
    ctx.throw(500, error);
  }

  ctx.body = html;
});

// global middlewares
app.use(
  views('views', {
    root: __dirname + '/views',
    default: 'jade',
  }),
);
app.use(require('koa-bodyparser')());
app.use(json());
app.use(logger());

app.use(function* (next) {
  var start = new Date();
  yield next;
  var ms = new Date() - start;
  console.log('%s %s - %s', this.method, this.url, ms);
});

app.use(require('koa-static')(__dirname + '/dist'));

// error-handling
app.on('error', (err, ctx) => {
  console.error('server error', err, ctx);
});

module.exports = app;

运行node服务,将ssr生成的dist目录替换服务中的dist目录即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/683969.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

枚举(enum)+联合体(union)

枚举联合 一.枚举类型1.枚举类型的声明2.枚举类型的优点3.枚举类型的使用 二.联合体1.联合体类型的声明2.联合体的特点3.相同成员的结构体和联合体对比4.联合体大小的计算5.联合体的练习&#xff08;判断大小端&#xff09;6.联合体节省空间例题 一.枚举类型 1.枚举类型的声明…

安全U盘和普通U盘有什么区别?

安全U盘&#xff08;也称为加密U盘或安全闪存驱动器&#xff09;与普通U盘肯定是有一些区别的&#xff0c;从字面意思上来看&#xff0c;就能看出&#xff0c;安全U盘是能够保护文件数据安全性的&#xff0c;普通U盘没这一些功能的&#xff0c;可随意拷贝文件&#xff0c;不防盗…

Hadoop3:MapReduce源码解读之Mapper阶段的TextInputFormat切片机制(3)

Job那块的断点代码截图省略&#xff0c;直接进入切片逻辑 参考&#xff1a;Hadoop3&#xff1a;MapReduce源码解读之Mapper阶段的Job任务提交流程&#xff08;1&#xff09; 5、TextInputFormat源码解析 类的继承关系 它的内容比较少 重写了两个父类的方法 这里关心一下泛型…

《开源模型食用指南》基于Linux环境快速部署开源模型,更适合中国宝宝的部署教程

今天给大家推荐一个非常适合中国宝宝学习的专属大模型教程&#xff0c;也就是它《开源模型食用指南》&#xff01; 当前百模大战正值火热&#xff0c;开源LLM层出不穷。 如今国内外已经涌现了众多优秀开源LLM&#xff0c;国外如LLaMA、Alpaca&#xff0c;国内如ChatGLM、BaiCh…

【Unity Shader入门精要 第13章】使用深度和法线纹理(一)

1. 原理 深度纹理的本质是一张RenderTexture&#xff0c;只不过其中记录的不是颜色值&#xff0c;而是一个深度值 这些深度值来自于顶点在空间变换后得到的归一化设备坐标&#xff08;NDC&#xff09;的Z值 由于NDC坐标的分量取值范围在[-1, 1]之间&#xff0c;要使颜色值能…

欧盟EDPS发布首份生成式人工智能与数据安全指南解读

6月3日&#xff0c;欧洲数据保护监督机构&#xff08;EDPS&#xff09;在其官网上发布了题为《生成式人工智能与EUDPR》的指南&#xff08;注&#xff1a;EUDPR指的是《欧盟2018/1725号条例》&#xff09;&#xff0c;这是首份适用于欧盟机构的人工智能与数据安全指南。 01 指南…

STM32 SPI驱动读取LSM6DSRTR

提示&#xff1a;通过SPI驱动读取传感器数据 文章目录 前言一、LSM6DSRTR二、配置步骤1.配置SPI2.引入 LSM驱动库3.结果 总结 前言 制作一个倾角传感器&#xff0c;通过SPI读取LSM6DSRTR的加速度数据转换为角度&#xff0c;不用IIC的原因是考虑IIC通讯的协议过于繁琐&#xff…

c# iText使用

引入包 用nuget安装itext和itext.bouncy-castle-adapter包&#xff1a; 创建pdf string path "a.pdf"; PdfWriter writer new PdfWriter(path); PdfDocument pdfDoc new PdfDocument(writer); var docnew Document(pdfDoc); Paragraph p new Paragraph(&quo…

Java装饰器模式,装饰器模式通常通过创建一个接口和一个或多个实现了该接口的类来开始,然后创建装饰器类,这些类也实现了相同的接口

1、定义一个接口Component public interface Component { void operation(); }2、创建一个实现了Component接口的简单类SimpleComponent public class SimpleComponent implements Component { Override public void operation() { System.out.println("SimpleCom…

正大国际期货:什么是主力合约?

一个期货品种&#xff0c;在同一时间段&#xff0c;会上市多个月份的合约&#xff0c; 由于主力合约交易量大&#xff0c;流动性高&#xff0c;一般建议新手交易主力合约。 主力合约通常指交易集中&#xff0c;流动性好的合约 &#xff0c;即在一段时间内交易量和持仓量最大的…

java框架树结构实现(带层级、编码、排序)

1、需求 实现一个影像资料库的功能&#xff0c;用树结构对资料进行分类 2、数据结构 通过id、pid表示父子关系 通过code表示层级关系 通过layer表示层级 通过sort进行排序 3、实体类 package org.jeecg.modules.image.entity;import com.baomidou.mybatisplus.annotation…

交叉编译freetype

目录 一、前言 二、交叉编译 freetype 1.交叉编译安装工具链 zlib 2.交叉编译安装工具链 libpng 3.交叉编译安装工具链 freetype 4.编译测试发现错误并解决 5.上机测试 一、前言 交叉编译常见错误解决方法可看&#xff1a;交叉编译中常见错误解决方法_交叉编译后fail t…

DevExpress Installed

一、What’s Installed 统一安装程序将DevExpress控件和库注册到Visual Studio中&#xff0c;并安装DevExpress实用工具、演示应用程序和IDE插件。 Visual Studio工具箱中的DevExpress控件 Visual Studio中的DevExpress菜单 Demo Applications 演示应用程序 Launch the Demo…

基于细节增强卷积和内容引导注意的单图像去雾

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 摘要Abstract文献阅读&#xff1a;DEA-Net&#xff1a;基于细节增强卷积和内容引导注意的单图像去雾1、研究背景2、方法提出3、相关知识3.1、DEConv3.3、多重卷积的…

Springboot+druid+多数据源

背景&#xff1a;早期项目是springboot2.x druid 的单数据源工程&#xff0c;其中使用了dblink的方式进行跨数据库访问。现在客户的机房搬迁&#xff0c;记账的下游数据库说是搬到不同区域&#xff0c;dblink的方式需要长期占用资源&#xff0c;需要修改成直连方式。 按照AI的…

AttenFace一个基于人脸识别的实时考勤验证系统算法研究

0 、引言 论文提出了一个使用面部识别、允许实时监控考勤的考勤系统&#xff0c; 可以检查由于欺骗和遗漏造成的欺诈。 论文地址&#xff1a;https://arxiv.org/abs/2211.07582v1 1. 概述 在大学和其他机构的课堂上&#xff0c;通常会进行考勤。然而&#xff0c;这种方式往往…

工业互联网基本概念及关键技术(295页PPT)

资料介绍&#xff1a; 工业互联网的核心是通过工业互联网平台把设备、生产线、工厂、供应商、产品和客户紧密地连接融合起来。这种连接能够形成跨设备、跨系统、跨厂区、跨地区的互联互通&#xff0c;从而提高效率&#xff0c;推动整个制造服务体系智能化。同时&#xff0c;工…

2024最新华为OD机试-C/D卷 - 在线OJ使用说明

文章目录 &#x1fa90;在线 OJ 入口&#x1f3a7;申请OD使用权限&#x1f353;在线 OJ 的使用说明OJ主界面专题系列语言支持评测结果 &#x1fa90;在线 OJ 入口 &#x1f517; 2024最新华为OD机试 - 在线OJ入 &#x1f3a7;申请OD使用权限 本专栏配套 OJ 的为了配合考友更高…

git: 批量删除分支

环境&#xff1a; window11git version 2.42.0git-bash.exe window环境下&#xff1a; 1. 批量删除本地 git branch |grep xxx |xargs git branch -D比如&#xff1a; 想批量删除本地含有 release 关键字的分支&#xff1a; 2. 批量删除远程 git branch -r | grep xxxx | …

Qt for Android 申请摄像头权限

步骤 1. 添加用户权限 AndroidManifest.xml 中新增&#xff08;不添加后面申请选项时不弹窗&#xff09; 或者再Qt Creator中直接添加 2. Qt代码申请权限 Qt自己封装好了一些常用的权限申请&#xff0c; 详情Qt Assistant文档搜索 QPermission查看 #include <QPermi…