环境准备
- git clone https://github.com/easy-team/egg-react-webpack-boilerplate.git
yarn installornpm 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.jsNode 代码
'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.jsEgg 路由配置
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;}}
