服务端渲染 Node 层直接获取数据
在 Egg 项目如果使用模板引擎规范时是通过 render 方法进行模板渲染,render 的第一个参数模板路径,第二个参数时模板渲染数据. 如如下调用方式:
async index(ctx) {
// 获取数据,可以是从数据库,后端 Http 接口 等形式
const list = ctx.service.article.getArtilceList();
// 对模板进行渲染,这里的 index.js 是 React jsx 文件通过 Webpack 构建的 JSBundle 文件
await ctx.render('index.js', { list });
}
从上面的例子可以看出,这种使用方式是非常典型的也容易理解的模板渲染方式。在实际业务开发时,对于常规的页面渲染也建议使用这种方式获取数据,然后进行页面渲染。Node 获取数据后,在 React 文件里面就可以通过 this.props.list
的方式拿到 Node 获取的数据,然后就可以进行 React 模板 jsx 文件数据绑定了。
在这里有个高阶用法,可以直接把 ctx 等 Node 对象传递到第二个参数里面, 这个时候你在模板里面就直接拿到 ctx 这些对象。 但这个时候就需要自己处理好 SSR 渲染时导致的 hydrate 问题,因为前端hydrate时并没有 ctx 对象。
async index(ctx) {
// 获取数据,可以是从数据库,后端 Http 接口 等形式
const list = ctx.service.article.getArtilceList();
// 对模板进行渲染,这里的 index.js 是 React jsx 文件通过 Webpack 构建的 JSBundle 文件
await ctx.render('index.js', { ctx, list });
}
如果把 ctx 对象传递到 React JSX 模板文件里面,你可以通过在 React jsx 模板文件进行 ctx.service.article.getArtilceList() 调用, 也就是在前端代码模板里面进行 Node 调用。
服务端渲染 asyncData 方式获取数据
在 React 进行 SSR 时涉及数据的请求方式,除了 Node 层直接获取数据方式可以继续使用,还可以在 React 组件里面编写 asyncData 方式获取数据。具体实现见 egg-view-react-ssr (^3.0.0) 插件 asyncData 逻辑处理。
egg-view-react-ssr 插件会判断 render 的组件是否包含静态的 asyncData 方法 (可以是 async 形式或者返回 Promise 方法),如果包括,则触发 asyncData 逻辑,然后获取的到数据与 Node 端的数据进行合并。
React 组件代码
${root}/app/web/page/test.jsx
'use strict';
import React, { Component } from 'react';
import request from '../../framework/request';
class AsyncDataMode extends Component {
static async asyncData(locals) {
return request.get(`/test/api/article`, locals);
}
render() {
const { title, article } = this.props;
return <div>
<h1 className="easy-article-detail-title">{title}</h1>
<h2 className="easy-article-detail-title">{article.title}</h2>
<div className="easy-article-info">
<iframe src={article.url} frameBorder="0" width="100%" style={{minHeight: '800px'}}></iframe>
</div>
</div>;
}
}
export default AsyncDataMode;
Node 渲染逻辑
添加
${root}/app/controller/test.js
Egg 路由配置
'use strict';
module.exports = app => {
return class TestController extends app.Controller {
async index(ctx) {
const title = 'Frontend asyncData 获取渲染数据';
await ctx.render('test.js', { title });
}
async article(ctx) {
const article = await ctx.service.article.query({ id: 1 });
ctx.body = { article };
}
};
};
Egg 路由配置
添加
${root}/app/router.js
Egg 路由配置
module.exports = app => {
const { router, controller } = app;
router.get('/test', controller.test.index);
router.get('/test/api/article', controller.test.article);
};
Webpack 构建配置
添加
${root}/webpack.config.js
新增页面 entry 配置
module.exports = {
entry: {
test: 'app/web/page/test.jsx',
}
}