环境准备

编写前端代码

页面渲染数据 Node 端直接请求获取,然后在 Node 端通过 ctx.render 的第二个参数传递给 React 组件后通过 props 获取组件数据

添加 ${root}/app/web/page/example/node.js 前端代码

  1. import React, { Component } from 'react';
  2. import Layout from 'component/layout/default';
  3. class NodeDataMode extends Component {
  4. render() {
  5. const { title, article } = this.props;
  6. return <Layout>
  7. <h1 className="easy-article-detail-title">{title}</h1>
  8. <h2 className="easy-article-detail-title">{article.title}</h2>
  9. <div className="easy-article-info">
  10. <iframe src={article.url} frameBorder="0" width="100%" style={{minHeight: '800px'}}></iframe>
  11. </div>
  12. </Layout>;
  13. }
  14. }
  15. export default NodeDataMode;

页面渲染数据直接在 React 组件中通过 asyncData 请求,然后 React 通过 props 获取数据

添加 ${root}/app/web/page/example/data.js 前端代码

  1. import React, { Component } from 'react';
  2. import request from '../../framework/request';
  3. import Layout from 'component/layout/default';
  4. class AsyncDataMode extends Component {
  5. /**
  6. * locals {Object} Node render 传递过来的数据,也就是 render 方法的第二个参数
  7. **/
  8. static async asyncData(locals) {
  9. const res = await request.get('/example/data/api/article', locals);
  10. return res.data;
  11. }
  12. render() {
  13. const { title, article } = this.props;
  14. return <Layout>
  15. <h1 className="easy-article-detail-title">{title}</h1>
  16. <h2 className="easy-article-detail-title">{article.title}</h2>
  17. <div className="easy-article-info">
  18. <iframe src={article.url} frameBorder="0" width="100%" style={{minHeight: '800px'}}></iframe>
  19. </div>
  20. </Layout>;
  21. }
  22. }
  23. export default AsyncDataMode;

编写 Node 代码

添加 ${root}/app/controller/test/test.js Node 代码

  1. 'use strict';
  2. module.exports = app => {
  3. return class DataController extends app.Controller {
  4. // 1. ctx.render 方法是 egg-view-react-ssr 插件提供实现
  5. // 2. ctx.render 第一个参数 example/node.js 是 webpack.config.js example/node 配置构建后 JSBundle 文件
  6. // 3. ctx.render 第二个参数 { title, article } 是渲染数据,会与公共 ctx.locals 合并
  7. async nodeDataRender(ctx) {
  8. const title = 'Node 直接获取渲染数据';
  9. const article = await ctx.service.article.getArticle(1);
  10. await ctx.render('example/node.js', { title, article });
  11. }
  12. async asyncDataRender(ctx) {
  13. const title = '前端 React 代码 asyncData 获取渲染数据';
  14. await ctx.render('example/data.js', { title });
  15. }
  16. async article(ctx) {
  17. const article = await ctx.service.article.getArticle(1);
  18. ctx.body = { article };
  19. }
  20. };
  21. };

Egg 路由配置

添加 ${root}/app/router.js Egg 路由配置

  1. module.exports = app => {
  2. const { router, controller } = app;
  3. router.get('/example/data/node', controller.example.data.nodeDataRender);
  4. router.get('/example/data/async', controller.example.data.asyncDataRender);
  5. router.get('/example/data/api/article', controller.example.data.article);
  6. };

Webpack 构建配置

添加 ${root}/webpack.config.js 新增页面 entry 配置

  1. module.exports = {
  2. entry: {
  3. 'example/node': 'app/web/page/example/node.jsx',
  4. 'example/data': 'app/web/page/example/data.jsx'
  5. },
  6. }

辅助代码

  • ${root}/app/web/framework/request.js
  1. 'use strict';
  2. import axios from 'axios';
  3. // axios.defaults.baseURL = 'http://127.0.0.1:7001';
  4. axios.defaults.timeout = 15000;
  5. axios.defaults.xsrfHeaderName = 'x-csrf-token';
  6. axios.defaults.xsrfCookieName = 'csrfToken';
  7. export default {
  8. post(url, json, state = {}) {
  9. const headers = {};
  10. if (EASY_ENV_IS_NODE) {
  11. headers['x-csrf-token'] = state.csrf;
  12. headers.Cookie = `csrfToken=${state.csrf}`;
  13. }
  14. return axios.post(`${state.origin}${url}`, json, { headers });
  15. },
  16. get(url, state = {}) {
  17. return axios.get(`${state.origin}${url}`);
  18. }
  19. };
  • ${root}/app/web/component/layout/default.jsx
  1. import React, { Component } from 'react';
  2. export default class Layout extends Component {
  3. render() {
  4. if(EASY_ENV_IS_NODE) {
  5. return <html>
  6. <head>
  7. <title>{this.props.title}</title>
  8. <meta charSet="utf-8"></meta>
  9. <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"></meta>
  10. <meta name="keywords" content={this.props.keywords}></meta>
  11. <meta name="description" content={this.props.description}></meta>
  12. <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"></link>
  13. </head>
  14. <body><div id="app">{this.props.children}</div></body>
  15. </html>;
  16. }
  17. return this.props.children;
  18. }
  19. }