环境准备
- git clone https://github.com/easy-team/egg-react-webpack-boilerplate.git
yarn install
ornpm install
编写前端代码
页面渲染数据 Node 端直接请求获取,然后在 Node 端通过 ctx.render 的第二个参数传递给 React 组件后通过 props 获取组件数据
添加
${root}/app/web/page/example/node.js
前端代码
import React, { Component } from 'react';
import Layout from 'component/layout/default';
class NodeDataMode extends Component {
render() {
const { title, article } = this.props;
return <Layout>
<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>
</Layout>;
}
}
export default NodeDataMode;
页面渲染数据直接在 React 组件中通过 asyncData 请求,然后 React 通过 props 获取数据
添加
${root}/app/web/page/example/data.js
前端代码
import React, { Component } from 'react';
import request from '../../framework/request';
import Layout from 'component/layout/default';
class AsyncDataMode extends Component {
/**
* locals {Object} Node render 传递过来的数据,也就是 render 方法的第二个参数
**/
static async asyncData(locals) {
const res = await request.get('/example/data/api/article', locals);
return res.data;
}
render() {
const { title, article } = this.props;
return <Layout>
<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>
</Layout>;
}
}
export default AsyncDataMode;
编写 Node 代码
添加
${root}/app/controller/test/test.js
Node 代码
'use strict';
module.exports = app => {
return class DataController extends app.Controller {
// 1. ctx.render 方法是 egg-view-react-ssr 插件提供实现
// 2. ctx.render 第一个参数 example/node.js 是 webpack.config.js example/node 配置构建后 JSBundle 文件
// 3. ctx.render 第二个参数 { title, article } 是渲染数据,会与公共 ctx.locals 合并
async nodeDataRender(ctx) {
const title = 'Node 直接获取渲染数据';
const article = await ctx.service.article.getArticle(1);
await ctx.render('example/node.js', { title, article });
}
async asyncDataRender(ctx) {
const title = '前端 React 代码 asyncData 获取渲染数据';
await ctx.render('example/data.js', { title });
}
async article(ctx) {
const article = await ctx.service.article.getArticle(1);
ctx.body = { article };
}
};
};
Egg 路由配置
添加
${root}/app/router.js
Egg 路由配置
module.exports = app => {
const { router, controller } = app;
router.get('/example/data/node', controller.example.data.nodeDataRender);
router.get('/example/data/async', controller.example.data.asyncDataRender);
router.get('/example/data/api/article', controller.example.data.article);
};
Webpack 构建配置
添加
${root}/webpack.config.js
新增页面 entry 配置
module.exports = {
entry: {
'example/node': 'app/web/page/example/node.jsx',
'example/data': 'app/web/page/example/data.jsx'
},
}
辅助代码
${root}/app/web/framework/request.js
'use strict';
import axios from 'axios';
// axios.defaults.baseURL = 'http://127.0.0.1:7001';
axios.defaults.timeout = 15000;
axios.defaults.xsrfHeaderName = 'x-csrf-token';
axios.defaults.xsrfCookieName = 'csrfToken';
export default {
post(url, json, state = {}) {
const headers = {};
if (EASY_ENV_IS_NODE) {
headers['x-csrf-token'] = state.csrf;
headers.Cookie = `csrfToken=${state.csrf}`;
}
return axios.post(`${state.origin}${url}`, json, { headers });
},
get(url, state = {}) {
return axios.get(`${state.origin}${url}`);
}
};
${root}/app/web/component/layout/default.jsx
import React, { Component } from 'react';
export default class Layout extends Component {
render() {
if(EASY_ENV_IS_NODE) {
return <html>
<head>
<title>{this.props.title}</title>
<meta charSet="utf-8"></meta>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"></meta>
<meta name="keywords" content={this.props.keywords}></meta>
<meta name="description" content={this.props.description}></meta>
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"></link>
</head>
<body><div id="app">{this.props.children}</div></body>
</html>;
}
return this.props.children;
}
}