基于 Egg + React + Webpack 服务端渲染开发指南

1. 项目初始化

1.1 通过 easywebpack-cli 脚手架初始化

  1. 安装脚手架 npm install easywebpack-cli -g 命令行,然后就可以使用 easy 命令

  2. 命令行运行 easy init

  3. 选择 egg + react server side render boilerplate 初始化骨架项目

  4. 安装依赖 npm install

1.2 通过骨架项目初始化

  1. git clone https://github.com/easy-team/egg-react-webpack-boilerplate.git
  2. npm install

初始化的项目提供多页面和SPA(react-router/react-redux)服务端渲染实例,可以直接运行。

快速开始 - 图1

1.3 vscode 插件初始化项目

https://marketplace.visualstudio.com/items?itemName=hubcarl.vscode-easy-plugin#overview

2. 项目运行

2.1 本地运行

  1. npm run dev

image.png

  • 本地开发启动 Webpack 构建, 默认配置文件为项目根目录 webpack.config.js 文件。 SSR 需要配置两份 Webpack 配置,所以构建会同时启动两个 Webpack 构建服务。web 表示构建 JSBundle 给前端用,构建后文件目录 public, 默认端口 9000; node 表示构建 JSBundle 给 Node 服务端渲染用,构建后文件目录 app/view 默认端口 9001.
  • 本地构建是 Webpack 内存构建,文件不落地磁盘,所以 app/viewpublic 在本地开发时,是看不到文件的。 只有发布模式(npm run build)才能在这两个目录中看到构建后的文件内容。

2.2 发布模式

  • Webpack 构建文件落地磁盘
  1. npm run build easy build
  1. 启动 Webpack 构建,文件落地磁盘

  2. 服务端构建的文件放到 app/view 目录

  3. 客户端构建的文件放到 public 目录

  4. 生成的 manifest.json 放到 config 目录

  5. 构建的文件都是 gitignore的,部署时请注意把这些文件打包进去

  • 启动应用

如果非 egg-scripts 方式启动应用, 请设置 EGG_SERVER_ENV 环境变量,测试环境设置 test, 正式环境设置 prod

  1. npm start
  • 本地模拟线上启动
  1. npm run build
  2. npm start

3. 项目构建

  • easywebpack 通过项目根目录下的 webpack.config.js 配置文件构造出 Webpack 实际的配置文件

  • 通过 egg-webpack 插件提供本地开发构建和热更新支持。SSR 模式时,egg-webpack 会启动两个 Webpack 构建服务, 客户端jsbundle构建,端口9000, 服务端jsbundle构建端口9001。

  1. // config/config.local.js 本地 npm start 使用
  2. const easywebpack = require('easywebpack-react');
  3. exports.webpack = {
  4. webpackConfigList:easywebpack.getWebpackConfig()
  5. };
  • webpack.config.js 配置

easywebpack 根据以下配置会自动生成两份 Webpack 配置,一份配置用于 SSR 渲染 Node 渲染使用,生成的文件放到 app/view 目录,也就是 Egg Controller 里的 render('home/home.js') ; 一份配置用于 SSR渲染的浏览器 hydrate 使用,放到 public 目录, 也就是渲染后 HTML 页面的 script <script src='/home/home.js'></script>

  1. // ${root}/webpack.config.js
  2. module.exports = {
  3. entry: {
  4. 'home/home': 'app/web/page/home/index.jsx'
  5. }
  6. };

4. 项目规范

  • 遵循 egg 开发规范

  • React 项目代码放到 app/web 目录,页面入口目录为 page,该目录的 所有 .jsx 文件默认会作为 Webpack 的 entry 构建入口。建议每个页面目录的只保留一个.jsx 文件,jsx关联的组件可以放到widget 或者 component 目录。具体 entry 入口配置与构建流程请阅读:https://www.yuque.com/easy-team/egg-react/config

5. 项目开发

支持多页面/单页面服务端渲染, 前端渲染, 静态页面三种方式.

5.1 多页面服务端渲染实现

5.1.1 多页面前端页面实现

在app/web/page 目录下面创建 home 目录 和 home.jsx 文件, Webpack 自动根据 .jsx 文件创建 entry入口, 具体实现请见webpack.config.js

  • home.jsx 以组件的方式实现页面逻辑
  1. import React, { Component } from 'react';
  2. import Header from 'component/layout/standard/header/header.jsx';
  3. import List from 'component/home/list.jsx';
  4. import './home.css';
  5. export default class Home extends Component {
  6. componentDidMount() {
  7. console.log('----componentDidMount-----');
  8. }
  9. render() {
  10. return <div>
  11. <Header></Header>
  12. <div className="main">
  13. <div className="page-container page-component">
  14. <List list={this.props.list}></List>
  15. </div>
  16. </div>
  17. </div>;
  18. }

5.1.2 通过 egg-view-react-ssr 插件 render 方法实现 Server Side Render

  • 创建 controller 文件 home.js
  1. exports.index = function* (ctx) {
  2. // home/home.js 是 app/web/page/home/home.js 构建后的服务端页面渲染的 JSBundle 文件。
  3. yield ctx.render('home/home.js', Model.getPage(1, 10));
  4. };
  • 添加路由配置
  1. app.get('/home', app.controller.home.home.index);

5.1.3 通过 egg-view-react-ssr 插件 renderClient 方法实现 Client Side Render

  • 在 controller 文件home.js 添加 client 方法
  1. exports.client = function* (ctx) {
  2. yield ctx.renderClient('home/home.js', Model.getPage(1, 10));
  3. };
  • 添加路由配置
  1. app.get('/client', app.controller.home.home.client);

6. 项目部署

  • 正式环境部署,请设置 EGG_SERVER_ENV=prod 环境变量, 更多请见运行环境

  • 构建的 app/view 目录, public 目录以及 buildConfig.jsonmanifest.json等文件, 都是 gitignore 的,部署时请注意把这些文件打包进去。

一. Webpack构建目录

  • Webpack构建服务端(Node) JSBundle运行文件, 构建的服务端渲染模板文件位置 ${app_root}/app/view

  • Webpack构建浏览器JSBundle运行文件, 构建的前端资源(js/css/image)文件位置 ${app_root}/public

  • Webpack构建的 manifest.jsonbuildConfig.js 文件位置 ${app_root}/config 目录

  • easywebpack-cli 构建配置文件 webpack.config.js 放到项目根目录${app_root}/webpack.config.js

  • React代码文件${app_root}/app/web 下面, 主要包括 asset, component, framework, page, store, view 等目录

  1. ├── asset // 资源文件
  2. │ ├── css
  3. │ │ ├── global.css
  4. │ │ ├── normalize.css
  5. │ │ └── style.css
  6. │ ├── images
  7. │ │ ├── favicon.ico
  8. │ │ ├── loading.gif
  9. │ │ └── logo.png
  10. ├── component // jsx组件
  11. │ ├── home
  12. │ │ └── list.jsx
  13. │ └── layout
  14. │ └── standard
  15. │ └── header
  16. │ ├── header.css
  17. │ └── header.jsx
  18. ├── framework
  19. │ └── entry
  20. │ ├── app.js
  21. │ └── loader.js
  22. ├── page // 页面目录, jsx结尾的的文件默认作为entry入口
  23. │ ├── hello
  24. │ │ └── hello.jsx // 页面入口文件, 根据framework/entry/loader.js模板自动构建
  25. │ └── home
  26. │ ├── home.css
  27. │ └── home.jsx
  28. └── view
  29. └── layout.jsx // layout模板文件, 提供统一html, header, body结构, page下面的jsx文件无需关心

二. 项目结构和基本规范

  1. ├── app
  2. │ ├── controller
  3. │ │ ├── test
  4. │ │ │ └── test.js
  5. │ ├── extend
  6. │ ├── lib
  7. │ ├── middleware
  8. │ ├── mocks
  9. │ ├── proxy
  10. │ ├── router.js
  11. │ ├── view
  12. │ │ ├── home
  13. │ │ │ └── home.js // 服务器编译的jsbundle文件
  14. │ └── web // 前端工程目录
  15. │ ├── asset // 存放公共js,css资源
  16. │ ├── framework // 前端公共库和第三方库
  17. │ │ └── entry
  18. │ │ ├── loader.js // 根据jsx文件自动生成entry入口文件loader
  19. │ ├── page // 前端页面和webpack构建目录, 也就是webpack打包配置entryDir
  20. │ │ ├── home // 每个页面遵循目录名, js文件名, scss文件名, jsx文件名相同
  21. │ │ │ ├── home.scss
  22. │ │ │ ├── index.jsx
  23. │ └── component // 公共业务组件, 比如loading, toast等, 遵循目录名, js文件名, scss文件名, jsx文件名相同
  24. │ ├── loading
  25. │ │ ├── loading.scss
  26. │ │ └── loading.jsx
  27. │ ├── test
  28. │ │ ├── test.jsx
  29. │ │ └── test.scss
  30. │ └── toast
  31. │ ├── toast.scss
  32. │ └── toast.jsx
  33. ├── config
  34. │ ├── config.default.js
  35. │ ├── config.local.js
  36. │ ├── config.prod.js
  37. │ ├── config.test.js
  38. │ └── plugin.js
  39. ├── doc
  40. ├── index.js
  41. ├── webpack.config.js // easywebpack-cli 构建配置
  42. ├── public // webpack编译目录结构, render文件查找目录
  43. │ ├── static
  44. │ │ ├── css
  45. │ │ │ ├── home
  46. │ │ │ │ ├── home.07012d33.css
  47. │ │ │ └── test
  48. │ │ │ ├── test.4bbb32ce.css
  49. │ │ ├── img
  50. │ │ │ ├── change_top.4735c57.png
  51. │ │ │ └── intro.0e66266.png
  52. │ ├── test
  53. │ │ └── test.js
  54. │ └── vendor.js // 生成的公共打包库

8. 项目和插件