Skip to content
This repository was archived by the owner on May 8, 2025. It is now read-only.

zhangyuang/egg-react-ssr

 
 

Repository files navigation

简体中文 | English

Egg + React + SSR boilerplate

Build Status Coverage Status download standardjs License License Node

最小而美的服务端渲染应用模板,特点

  • 小:实现方式简洁,生产环境构建出来的bundle为同等复杂度的next.js项目的0.7倍,生成文件数量相比于next.js减少非常多
  • 全:支持HMR,支持本地开发以及生产环境CSR/SSR两种渲染模式无缝切换,支持定制组件的渲染模式,同时支持TypeScript版本
  • 美:基于ReactEggjs框架,拥有强大的插件生态,配置非黑盒,方便加入当前业务的个性化逻辑

正在使用这个项目的公司(应用), 如果您正在使用但名单中没有列出来的话请提issue,欢迎推广分享

优酷
优酷视频
vmate 积分商城
Vmate短视频
火炽星原CRM
火炽星原CRM
牛牛搭
牛牛搭
cvte
希沃帮助中心
腾讯微卡
腾讯微卡
微脉
微脉

快速入门

这里我们提供了一个脚手架,方便你创建快速项目。

$ npm install yk-cli -g $ ykcli init <Your Project Name> $ cd <Your Project Name> $ npm i $ npm start $ open http://localhost:7001 

在执行 ykcli init 的时候,可以选择javascript或typescript语言,非常方便。

npm scripts

1)启动服务

启动监听7001端口,此端口同时用于服务端渲染以及客户端渲染,通过query或者config来指定渲染模式

$ npm start # 建议以本方式启动应用,同时启动服务端渲染 + 客户端hydrate

2)只启动服务端渲染,此时仅服务端直出html,没有与客户端混合的步骤

$ npm run ssr 

3)启动客户端渲染

仅限于本地开发使用,启动监听8000端口,只启动客户端渲染,相当于传统的cra脚手架开发模式

$ npm run csr 

4)配套的脚本

$ npm run prod # 使用egg-scripts启动应用模拟SSR应用生产环境 $ npm run build # 打包服务端以及客户端资源文件 $ npm run analyze # 可视化分析客户端打包的资源详情

功能/特性

该模板特色为:写法简单、功能强大、一切都是组件、支持 SSR/CSR 两种渲染模式无缝切换。

更多功能/特性如下:

  • 基于cra脚手架开发,由cra开发的React App可无缝迁移,如果你熟悉cra的配置,上手成本几乎为0
  • 小而美,相比于beidou,next.js这样的高度封装方案,我们的实现原理和开发模式一目了然
  • 推荐使用egg作为Node.js框架但并不强制,事实上你可以发现几乎无需做任何修改即可迁移到koa,nest.js等框架
  • 同时支持SSR以及CSR两种开发模式,本地开发环境以及线上环境皆可无缝切换两种渲染模式
  • 统一前端路由与服务端路由,无需重复编写路由文件配置
  • 支持切换路由时自动获取数据
  • 支持本地开发HMR
  • 稳定性经过线上大规模应用验证,可提供性能优化方案
  • 支持tree shaking,优化构建bundle大小以及数量
  • 支持csr/ssr自定义layout,无需通过path来手动区分
  • 抛弃传统模版引擎,拥抱 React 组件,使用JSX来作为模版
  • 独创最佳发布实践,让你更新页面无需重启应用机器
  • 配套结合antd的example的实现
  • 配套结合react-loadable做路由分割的example的实现
  • 配套结合dva做数据管理的example的实现
  • 配套结合ssr-with-multipage多页面应用的example
  • 配套结合Rax版本的实现
  • 配套阿里云serverless FC版本的实现
  • 配套TypeScript版本的实现

写法

在写法上统一csr和ssr,采用next类似的静态的getInitialProps作为数据获取方法

function Page(props) { return <div> {props.name} </div> } Page.getInitialProps = async (ctx) => { return Promise.resolve({ name: 'Egg + React + SSR' }) } export default Page

具体说明如下。

  • render是React的视图渲染方法
  • getInitialProps是获取数据方法,将返回值赋值给组件状态
    • csr通过高阶组件实现
    • ssr通过Node执行

在运行时,通过npm run csrnpm run ssr来进行区分,是目前最简单的同构渲染方案。当页面初始化加载时,getInitialProps只会加载在服务端。只有当路由跳转(Link组件跳转或 API 方法跳转)时,客户端才会执行getInitialProps。

getInitialProps入参对象的属性如下:

  • ctx: Node应用请求的上下文(仅在SSR阶段可以获取)
  • Router Props: 包含路由对象属性,包括pathname以及Router params history 等对象,详细信息参考react-router文档

一切皆组件

我们的页面基础模版 html,meta 等标签皆使用JSX来生成,避免你去使用繁琐的模版引擎语法

const commonNode = props => ( // 为了同时兼容ssr/csr请保留此判断,如果你的layout没有内容请使用 props.children ? { props.children } : '' props.children ? <div className='normal'><h1 className='title'><Link to='/'>Egg + React + SSR</Link><div className='author'>by ykfe</div></h1>{props.children}</div> : '' ) const Layout = (props) => { if (__isBrowser__) { return commonNode(props) } else { const { serverData } = props.layoutData const { injectCss, injectScript } = props.layoutData.app.config return ( <html lang='en'> <head> <meta charSet='utf-8' /> <meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no' /> <meta name='theme-color' content='#000000' /> <title>React App</title> { injectCss && injectCss.map(item => <link rel='stylesheet' href={item} key={item} />) } </head> <body> <div id='app'>{ commonNode(props) }</div> { serverData && <script dangerouslySetInnerHTML={{ __html: `window.__USE_SSR__=true; window.__INITIAL_DATA__ =${serialize(serverData)}` }} /> } <div dangerouslySetInnerHTML={{ __html: injectScript && injectScript.join('') }} /> </body> </html> ) } }

如何切换渲染模式

在本地开发时,你可以同时启动ssr/csr两种渲染模式查看区别,在生产环境时,你可以通过设置config中的type属性来切换不同的渲染模式或者通过query来切换,在流量较大时可以降级为csr渲染模式 参考文档如何切换渲染模式

$ open http://localhost:7001/ # 以SSR模式渲染应用 $ open http://localhost:7001/?csr=true # 切换为CSR模式渲染或者通过config.type来设置渲染模式

执行环境

  • 服务器Node.js >= 7.6, 为了原生的使用async/await语法
  • 浏览器版本大于等于IE9, React支持到IE9,但为了更好的在IE下使用,你可能需要引入Polyfill

执行流程

配置

为了足够灵活使用,这里我们将一些关键项提供可配置的选项,可根据实际需要来配置,如无特殊必要,使用默认配置即可。服务端渲染相关配置信息我们放在config.ssr.js,在这里我们建议不要将配置放在egg的配置文件当中,避免前端bundle中包含后端配置文件信息

// config/config.ssr const resolvePath = (path) => require('path').resolve(process.cwd(), path) module.exports = { type: 'ssr', // 指定运行类型可设置为csr切换为客户端渲染,此时服务端不会做获取数据生成字符串的操作以及不会使用hydrate API static: { // 设置Node应用的静态资源目录,为了生产环境读取静态资源文件 prefix: '/', dir: resolvePath('dist') }, routes: [ // 前后端统一使用的路由配置文件,防止重复编写相同的路由 { path: '/', // 请求的path exact: true, // 是否精确匹配 Component: () => (require('@/page/index').default), // 这里使用一个function包裹为了让它延迟require, 否则Node环境无法识别前端组件中用到的import关键字会报错 controller: 'page', // 需要调用的controller handler: 'index' // 需要调用的controller中具体的method }, { path: '/news/:id', exact: true, Component: () => (require('@/page/news').default), controller: 'page', handler: 'index' } ], injectCss: [ `/static/css/Page.chunk.css` ], // 客户端需要加载的静态样式表 injectScript: [ `<script src='/static/js/runtime~Page.js'></script>`, `<script src='/static/js/vendor.chunk.js'></script>`, `<script src='/static/js/Page.chunk.js'></script>` ], // 客户端需要加载的静态资源文件表 serverJs: resolvePath(`dist/Page.server.js`): string|function, // 打包后的server端的bundle文件路径支持传入CDN地址, 接受直接传入require后的function layout: resolvePath(`dist/Layout.server.js`): string|function // 打包后的server端的bundle文件路径支持传入CDN地址, 接受直接传入require后的function }

目录结构

目录结构保持了Egg的方式,以app和config目录为主。将前端React相关代码放到web目录下,webpack打包相关文件位于build目录。整体来看,目录不多,层级不深,属于刚刚好那种。

├── README.md ├── app # egg核心目录 │   ├── controller │   ├── extend │   ├── middleware │   └── router.js # egg路由文件,无特殊需求不需要修改内容 ├── app.js # egg 启动入口文件 ├── build # webpack配置目录 │   ├── paths.js │   ├── util.js │   ├── webpack.config.base.js # 通用的webpack配置 │   ├── webpack.config.client.js # webpack客户端打包配置 │   └── webpack.config.server.js # webpack服务端打包配置 ├── config # egg 配置文件目录 │   ├── config.daily.js │   ├── config.default.js │   ├── config.ssr.js │   ├── config.local.js │   ├── config.prod.js │   ├── plugin.js │   └── plugin.local.js ├── dist # build生成静态资源文件目录 │   ├── Page.server.js # 服务端打包后文件(即打包后的serverRender方法) │   └── static # 前端打包后静态资源目录 └── web # 前端文件目录 ├── assets │   └── common.less ├── entry.js # webpack打包入口文件,分环境导出不同配置 ├── layout │   ├── index.js # 页面布局 │   └── index.less └── page ├── index └── news

Changelog

每一个版本的详细改动请查看 release notes

与其他方案的对比

本地如何调试源码

请查看该wiki

如何向本项目贡献代码

请查看该wiki

Contributors

Thanks goes to these wonderful people (emoji key):


LeonCheung

💻

狼叔

💻

Xu Zhiyong

🐛

Menteceso

📖

jerryYu

💻

dydong

💻

snoy

📖

zhaoxingyue

📖

九牧

🐛

JohannLai

🐛

robert.xu

💻

zhushijie

💻

Cheng Zhongmin

🐛

This project follows the all-contributors specification. Contributions of any kind welcome!

License

MIT

NodeParty 分享

如果你想了解本应用的设计思路,欢迎下载查看本人在2020.1.11日在北京NodeParty上所做的分享PPT,其中讨论了需要关注的一些问题的设计思路和解决方案的选取

答疑群

虽然我们已经尽力检查了一遍应用,但仍有可能有疏漏的地方,如果你在使用过程中发现任何问题或者建议,欢迎提issue或者PR 欢迎直接扫码加入钉钉群

About

最小而美的Egg + React + SSR 服务端渲染应用骨架,同时支持JS和TS

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 22