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

前端之家收集整理的这篇文章主要介绍了Egg + React 服务端渲染开发指南前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

## 1. 项目初始化

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

  1. 安装脚手架 npm install easywebpack-cli -g 命令行,然后就可以使用 easywebpackeasy 命令
  2. 命令行运行 easywebpack init
  3. 选择 egg + react server side render boilerplate 初始化骨架项目
  4. 安装依赖 npm install

1.2 通过骨架项目初始化

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

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

2. 项目运行

2.1 本地运行

  1. npm start

npm start 做了如下三件事情

  • 启动 egg 应用
  • 启动 Webpack 构建,文件不落地磁盘,构建的文件都在内存里面(只在本地启动,发布模式是提前构建好文件到磁盘)
  • 构建会同时启动两个 Webpack 构建服务,客户端js构建端口9000,服务端端口9001
  • 构建完成,Egg应用正式可用,自动打开浏览器

2.2 发布模式

  1. npm run build easywebpack build prod
  1. 启动 Webpack 构建,文件落地磁盘
  2. 服务端构建的文件放到 app/view 目录
  3. 客户端构建的文件放到 public 目录
  4. 生成buildConfig.jsonmanifest.json 放到 config 目录
  5. 构建的文件都是 gitignore 的,部署时请注意把这些文件打包进去
  • 运行

启动应用前, 请设置 EGG_SERVER_ENV 环境变量,测试环境设置 test, 正式环境设置 prod

  1. npm start

3. 项目构建

  • 通过 easywebpack-cli 统一构建,支持 dev,test,prod 模式构建
  • easywebpack-cli 通过项目根目录下的 webpack.config.js 配置文件构造出 Webpack 实际的配置文件,配置项请见 webpack.config.js
  • 获取 Webpack 实际的配置文件,egg-webpack 会使用到该功能。构建会根据 webpackConfigList.length 启动对应个数的 Webpack 编译实例,这里会同时启动两个 Webpack 构建服务,客户端jsbundle构建,端口9000,服务端jsbundle构建端口9001。默认端口为9000,端口依次递增。
  1. // config/config.local.js 本地 npm start 使用
  2. const EasyWebpack = require('easywebpack-react');
  3. exports.webpack = {
  4. webpackConfigList:EasyWebpack.getWebpackConfig()
  5. };
  • 该项目中,app/web/page 目录中所有 .jsx 文件当作 Webpack 构建入口是采用 app/web/framework/entry/loader.js 模板实现的,这个需要结合 webpack.config.js 下的 entry.loader 使用。
  1. entry: {
  2. include: ['app/web/page',{ layout: 'app/web/view/layout.jsx?loader=false' },{ 'spa/redux': 'app/web/page/spa/redux.jsx?loader=false' },{ 'spa/client': 'app/web/page/spa/client.jsx?loader=false' },{ 'spa/ssr': 'app/web/page/spa/ssr.jsx?loader=false' }
  3. ],exclude: ['app/web/page/test'],loader: {
  4. client: 'app/web/framework/entry/loader.js'
  5. }
  6. }

上面 { 'app/app': 'app/web/page/app/app.js?loader=false' } 这个 loader=false 的含义表示 app/web/page 目录下的 app/app.js 不使用 entry.loader 模板。因为这个app/app.js是一个SPA服务端渲染Example,实现逻辑与其他普通的页面不一样,不能用 entry.loader 模板, 这个功能自定义entry文件构建规范时使用。

4. 项目规范

  • 遵循 egg 开发规范
  • React 项目代码放到 app/web 目录,页面入口目录为 page,该目录的 所有 .jsx 文件默认会作为 Webpack 的 entry 构建入口。建议每个页面目录的只保留一个.jsx 文件,jsx关联的组件可以放到widget 或者 component 目录。如果非要放到当前目前,请配置 webpack.config.js entry.exclude 排除 .jsx 文件

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.  
  10. render() {
  11. return <div>
  12. <Header></Header>
  13. <div className="main">
  14. <div className="page-container page-component">
  15. <List list={this.props.list}></List>
  16. </div>
  17. </div>
  18. </div>;
  19. }

5.1.2 多页面后端渲染实现,通过 egg-view-react-ssr 插件 render 方法实现

  • 创建controller文件home.js
  1. exports.index = function* (ctx) {
  2. yield ctx.render('home/home.js',Model.getPage(1,10));
  3. };
  1. app.get('/home',app.controller.home.home.index);

5.1.3 多页面走前端渲染(后端路由)实现,通过 egg-view-react-ssr 插件 renderClient 方法实现

  • 创建controller文件home.js
  1. exports.client = function* (ctx) {
  2. yield ctx.renderClient('home/home.js',10));
  3. };
  1. app.get('/client',app.controller.home.home.client);

5.2 HTML静态页面前端渲染

  • 直接有easywebpack构建出静态HTML文件,请见 webpack.config.js 配置和 app/web/page/html代码实现
  • 通过 egg-static 静态文件访问HTML文件

5.3 单页面服务器渲染同构实现

5.3.1 单页面前端实现

在app/web/page 目录下面创建app目录,spa/ssr.jsx 文件.

  1. import React,{ Component } from 'react'
  2. import ReactDOM from 'react-dom'
  3. import { Provider } from 'react-redux'
  4. import {match,RouterContext} from 'react-router'
  5. import { BrowserRouter,StaticRouter } from 'react-router-dom';
  6. import { matchRoutes,renderRoutes } from 'react-router-config'
  7. import Header from 'component/layout/standard/header/header';
  8. import SSR from 'component/spa/ssr/ssr';
  9. import { create } from 'component/spa/ssr/store';
  10. import routes from 'component/spa/ssr/routes'
  11. import './spa.css';
  12.  
  13.  
  14. if (typeof window === 'object') { // 前端渲染构建
  15. const store = create(window.__INITIAL_STATE__);
  16. const url = store.getState().url;
  17. ReactDOM.render(
  18. <div>
  19. <Header></Header>
  20. <Provider store={ store }>
  21. <BrowserRouter>
  22. <SSR url={ url }/>
  23. </BrowserRouter>
  24. </Provider>
  25. </div>,document.getElementById('app')
  26. );
  27. } else { // 服务端渲染构建和render入口,这里 export 函数,服务端会负责处理
  28. module.exports = (context,options) => {
  29. const url = context.state.url;
  30. const branch = matchRoutes(routes,url);
  31. // 获取组件数据
  32. const promises = branch.map(({route}) => {
  33. const fetch = route.component.fetch;
  34. return fetch instanceof Function ? fetch() : Promise.resolve(null)
  35. });
  36. return Promise.all(promises).then(data => {
  37. // 初始化store数据
  38. const initState = context.state;
  39. data.forEach(item => {
  40. Object.assign(initState,item);
  41. });
  42. context.state = Object.assign({},context.state,initState);
  43. const store = create(initState);
  44. return () =>(
  45. <div>
  46. <Header></Header>
  47. <Provider store={store}>
  48. <StaticRouter location={url} context={{}}>
  49. <SSR url={url}/>
  50. </StaticRouter>
  51. </Provider>
  52. </div>
  53. )
  54. });
  55. };
  56. }

5.3.2 单页面后端实现

  • 创建controller文件app.js
  1. exports.ssr = function* (ctx) {
  2. yield ctx.render('spa/ssr.js',{ url: this.url });
  3. };
  1. app.get('/spa/ssr',app.controller.spa.ssr);
  • 构建配置

spa 单页面实现复杂,不能使用 entry.loader,所以需要在 webpack.config.js 配置

  1. {
  2. entry: {
  3. include: ['app/web/page',loader: {
  4. client: 'app/web/framework/entry/loader.js'
  5. }
  6. },}

详细代码请参考骨架项目实现

6. 项目部署

  • 正式环境部署,请设置 EGG_SERVER_ENV=prod 环境变量,更多请见运行环境
  • 构建的 app/view 目录,public 目录以及 buildConfig.jsonmanifest.json文件,都是 gitignore 的,部署时请注意把这些文件打包进去。

7. 项目和插件

猜你在找的React相关文章