typescript-koa-ssr-react-boilerplate
專案地址:https://github.com/m-Ryan/typ…
特色
- 前後端分離
- typeScript
- 服務端渲染
- react-router按需載入
- redux
- 使用最新的react v.16.4
服務端渲染實現
router.tsx 路由
...
function RouterConfig() {
return (
<Switch>
<Route path="/" exact={true} component={Home} />
<Route path="/about" component={About} />
<Route path="/hello" component={Hello} />
</Switch>
);
}
ssr.tsx 匹配路徑
...
export default (url: string | Object, context: any,store: any)=>{
return <Provider store={store}>
<StaticRouter location={url} context={context}>
<Routes/>
</StaticRouter>
</Provider>
}
renderFullPage.ts 渲染頁面
...
const renderFullPage = (ctx : IRouterContext, newState:Object ) => {
let context = {}
const myHtml = fs.readFileSync("app/web/assets/dist/templete.html", `utf-8`);
// 得到初始 state 建立新的 Redux store 例項
const store = createStore(reducers, newState);
// 從 Redux store 得到初始 state,注入到window
const finalState = store.getState()
let initState = `<script> window.__INITIAL_STATE__ = ${JSON.stringify(finalState)}</script>`;
//根據路由獲取html並注入到<div id="root"></div>,將 initState 插到該節點後面
const html = ReactDOMServer.renderToString(ssr(ctx.req.url, context, store));
let renderPage = myHtml.replace(/(<divs+id="root">)(.|
|
)*(</div>)/i, "$1" + html + "$3" + initState);
ctx.type = `html`;
ctx.body = renderPage;
}
export default renderFullPage;
index.tsx 前端的路由處理
...
const preloadedState = window.__INITIAL_STATE__ || {} //
// 使用初始 state 建立 Redux store
const store = createStore(reducers, preloadedState)
render(
<Provider store={store}>
<BrowserRouter>
<Routes/>
</BrowserRouter>
</Provider>,
document.getElementById(`root`) as HTMLElement
)
1..redux方面,先建立新的 Redux store 例項,將我們需要初始的state合併到 store
2.通過 store.getState() 獲取最終的finalState
3.通過 StaticRouter 可以獲取路徑匹配的頁面元件,並通過 ReactDOMServer.renderToString 轉化為HTML元素
ssr.tsx 主要做了兩件事:
1.將初始的 store 注入redux
2.返回帶有 store 資料的路徑匹配的頁面元件,也就是說這個頁面已經是有初始資料了4.將讀取的html模板注入資料,在這裡我們需要通過簡單的正則替換一下
在 <div id=”root”></div> 中插入我們的html元素
在 <div id=”root”></div> 後面插入<script> window.__INITIAL_STATE__ = ${JSON.stringify(finalState)}</script>
5.將這個頁面傳送出去
6.js載入的時候會讀取
window.__INITIAL_STATE__
的資料,合併到 store
注意:這裡 我們是用 fs模組 讀取 html模板,而不是直接使用 類似以下函式
export default (html, finalState)=>(
`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
<meta name="renderer" content="webkit">
<title>typeScript-koa-ssr-antd</title>
<link href="/dist/index.076a9ad74b9f609c4d81.css" rel="stylesheet">
</head>
<body>
<div id="root">${html}</div>
window.__INITIAL_STATE__ = ${JSON.stringify(finalState)}</script>
</body>
</html>
`
)
原因主要是因為打包得到的 js、css 需要有hash值來管理版本快取,所以是不能直接寫死的
怎麼使用
git clone git@github.com:m-Ryan/typescript-koa-ssr-react-boilerplate.git
cd typescript-koa-ssr-react-boilerplate
前端
- cd app/web 首次使用,先 npm install
- 開發環境 npm start
- 生產環境 npm run build
後臺(前後端是分離的,使用前,前端要先打包)
- 首次使用,先 npm install
- 開發環境 npm start
- 生產環境 npm run build