簡體中文| English
注: 出於功能豐富度以及可維護性考慮,不再建議使用
egg-react-ssr。現推薦使用最新的ssr框架能夠同時支持在React, Vue2/3 場景下使用且支持使用Vite。我們強烈建議你使用新的升級版,無論是渲染機制還是功能豐富度以及可維護性上都有極大提升並經過多個大型線上項目驗證。如果你更喜歡開箱即用的體驗且希望藉助Serverless 能力一鍵部署應用或使用Vue2/Vue3 來做服務端渲染。項目鏈接https://github.com/zhangyuang/ssr 官方文檔http://doc.ssr-fc.com/ 我們將會持續更新維護它
最小而美的服務端渲染應用模板,特點
正在使用這個項目的公司(應用)名單,按新增時間排序, 如果您正在使用但名單中沒有列出來的話請提issue,歡迎推廣分享。
優酷視頻 | Vmate短視頻 | 火熾星原CRM | 牛牛搭 | 希沃幫助中心 | 騰訊微卡 | 微脈 |
![]() 騰訊手游助手 | 國家現代農業科技創新中心 | 國盛證券 | 極速二維碼 | 100教育 |
我們使用create-ssr-app 來快速的創建項目, 支持創建集成了(js|ts|antd|dva)等多種功能的應用模版
$ npm init ssr-app my-ssr-project --template=ssr-with-js
$ cd my-ssr-project
$ npm install
$ npm start注:當Node.js version >=15 時,應使用npm init ssr-app my-ssr-project -- --template=ssr-with-js來傳遞參數
1)啟動服務
啟動監聽7001 端口,此端口同時用於服務端渲染以及客戶端渲染,通過query 或者config 來指定渲染模式
$ npm start # 建议以本方式启动应用,同时启动服务端渲染 + 客户端hydrate2)只啟動服務端渲染,此時僅服務端直出html,沒有與客戶端混合的步驟
$ npm run ssr3)啟動客戶端靜態資源編譯
僅限於本地開發使用,啟動監聽8000 端口託管前端靜態資源,相當於傳統的cra 腳手架開發模式
$ npm run csr4)配套的腳本
$ npm run prod # 使用egg-scripts模拟SSR应用生产环境,如无特殊定制要求生产环境可以用该方式启动
$ npm run build # 打包服务端以及客户端资源文件
$ npm run analyze # 可视化分析客户端打包的资源详情該模板特色為:寫法簡單、功能強大、一切都是組件、支持SSR/CSR 兩種渲染模式無縫切換。
更多功能/特性如下:
我們在Serverless 場景下的SSR 框架已經正式發布,如果你更喜歡平滑開箱即用體驗的更高層次解決方案並且希望能夠快速部署。推薦使用該框架進行開發。與本項目不衝突,互相補位
在寫法上統一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具體說明如下。
在運行時,通過config.type來進行區分,是目前最簡單的同構渲染方案。當頁面初始化加載時,getInitialProps 只會加載在服務端。只有當路由跳轉(Link 組件跳轉或API 方法跳轉)時,客戶端才會執行getInitialProps。
getInitialProps 入參對象的屬性如下:
我們的頁面基礎模版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来设置渲染模式為了足夠靈活使用,這裡我們將一些關鍵項提供可配置的選項,可根據實際需要來配置,如無特殊必要,使用默認配置即可。服務端渲染相關配置信息我們放在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
}修改默認webpack-dev-server的配置
// build/webpack.config.client.js
module . epxorts = {
devServer : {
// custom webpack-dev-server config
}
} 目錄結構保持了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每一個版本的詳細改動請查看release notes
請查看該wiki
請查看該wiki
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!
MIT
如果你想了解本應用的設計思路,歡迎下載查看本人在2020.1.11 日在北京NodeParty 上所做的分享PPT,其中討論了需要關注的一些問題的設計思路和解決方案的選取
雖然我們已經盡力檢查了一遍應用,但仍有可能有疏漏的地方,如果你在使用過程中發現任何問題或者建議,歡迎提issue或者PR 歡迎直接掃碼加入釘釘群