// @babel ignore import * as React from 'react' import * as fs from 'fs' import * as path from 'path' import {renderToString} from 'react-dom/server' import {match, RouterContext} from 'react-router' import {Provider} from 'react-redux' import ServerRequestHelper from '../server-request' import {setServerRender} from '../fetch' import configureStore from '../store' import service from '../service' // 渲染整个页面 const renderFullPage = (htmlText: string, element: string, initialState: any, initialTitle:string)=> { let content = htmlText // 将内容塞到 #react-dom content = content.replace(/
<\/div>/g, `
${element}
`) // 设置 Redux 初始状态 content = content.replace(/__serverData\((\'|\")__INITIAL_STATE__(\'|\")\)/g, JSON.stringify(initialState)) // 设置 title content = content.replace(/__serverData\((\'|\")__INITIAL_TITLE__(\'|\")\)/g, initialTitle) return content } export interface Option { req: any routes: ReactRouter.RouteConfig basename: string rootReducer: any htmlText: string enableServerRender?: boolean } /** * 后端渲染 promise */ const getMatch = (option: Option)=> { return new Promise((resolve, reject)=> { match({ routes: option.routes, location: option.req.url, basename: option.basename }, (error: Error, redirectLocation: any, renderProps: any) => { if (error) { resolve({ status: 500, result: error.message }) } else if (redirectLocation) { resolve({ status: 302, result: redirectLocation.pathname + redirectLocation.search }) } else if (renderProps) { const serverRequestHelper = new ServerRequestHelper(service, option.req) // 初始化 fetch setServerRender(serverRequestHelper.Request as Function) // 初始化 redux const store = configureStore({}, option.rootReducer) const InitialView = React.createElement(Provider, {store: store}, React.createElement(RouterContext, renderProps)) // 找到最深层组件的 title const title = renderProps.components[renderProps.components.length-1].title try { // 初次渲染触发所有需要的网络请求 renderToString(InitialView) // 拿到这些请求的action const actions = serverRequestHelper.getActions() Promise.all(actions.map((action: any)=> { return store.dispatch(action) })).then(()=> { const componentHTML = renderToString(InitialView) const initialState = store.getState() // 将初始状态输出到 html resolve({ status: 200, result: renderFullPage(option.htmlText, componentHTML, initialState, title) }) }) } catch (err) { resolve({ status: 200, result: renderFullPage(option.htmlText, `Server Render Error: ${err.toString()}`, {}, title) }) } } else { resolve({ status: 404, result: 'Not Found' }) } }) }) } // 后端渲染 export default async(option: Option)=> { // 如果不启动后端渲染,直接返回未加工的模板 if (!option.enableServerRender) { return { status: 200, result: renderFullPage(option.htmlText, '', {}, '') } } return await getMatch(option) }