本文主要是介绍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
配置
-
- 配置
.umirc.js
- 配置
import { defineConfig } from 'umi';export default defineConfig({nodeModulesTransform: {type: 'none',},routes: [{ path: '/', component: '@/pages/index' },],fastRefresh: {},/** 开启ssr */ssr: {}
});
-
- 查看是否开启成功
如上图所示,直接返回了dom,不止一个根节点,这就算成功了
页面title、meta
每个页面都可以配置title和meta,通过配置<Helmet>
组件即可
import React from 'react';
import { Helmet } from 'umi';export default () => {return (<>{/* 可自定义需不需要编码 */}<Helmet><title>Hello Umi Bar Title</title><metaname="keywords"content="Hello Umi Bar Title"/><metaname="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,通过以下步骤使用:
-
- 配置
.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 themeconst 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({// 有需要可带上 querypath: 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目录即可
这篇关于umijs 服务端渲染(SSR) 指南的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!