forgeplus-react/server/render.js

139 lines
4.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from "react";
import {renderToString} from "react-dom/server";
import {StaticRouter, matchPath} from "react-router-dom";
import StyleContext from 'isomorphic-style-loader/StyleContext'
import {renderRoutes} from "react-router-config";
import Routes, { deepRoutes, specialRoute } from "../Routes";
import { Provider } from "react-redux";
import { serverStore } from "../src/redux/stores/configureStore";
import { setDefaultMeta } from '../src/common/UrlTool'
import Logger from "./log";
import { changeHead } from './setHead'
// 目前只用项目详情页的,不需要动态添加
const css = new Set() // 这个必须在路由函数里面在外面的话就会累加出现之前的css
const insertCss = (...styles) => styles.forEach(style => css.add(style._getCss()));
export const render = async (req,res)=>{
let _route = null,
_match = null;
Logger.info(`${req.url} UA:${req.headers['user-agent']}`)
let store = serverStore();
function matchSubRoutes(routes, url) {
for (const route of routes) {
// 检查当前路由是否匹配
let match = matchPath(url, { path: route.path, exact: true });
if (match) {
// 如果当前路由匹配,并且它有子路由,递归检查子路由
if (route.routes) {
// 递归调用匹配子路由
const subMatch = matchSubRoutes(route.routes, url);
if (subMatch) {
_route = route;
_match = subMatch;
return true;
}
}
// 如果没有子路由,或者子路由不匹配,但当前路由匹配,返回当前路由
_route = route;
_match = match;
return true;
}
}
// 如果没有找到匹配的路由返回false
return false;
}
matchSubRoutes(deepRoutes, req.url.split("?")[0])
let context = {
code: 200,
};
if (_route && _route.component && _route.component.preFetch) {
// 设置服务端请求cookie
if (req.headers.cookie) {
let cookies = req.headers.cookie.split(';')
for (let index = 0; index < cookies.length; index++) {
const element = cookies[index];
domObj.window.document.cookie = element
}
}
context = await _route.component.preFetch({
store,
match: _match,
query: req.path,
});
// 当前只做了项目详情页,没获取到数据使用原来的
if(!store.getState().projectReducer.projectBase.id) {
return false
}
}
let content = ''
// 匹配路由、并且不是特定一级路由、且不是只动态修改head的在服务端渲染页面
if (_route && !specialRoute.includes(_match.url && _match.url.replace('/', '')) && !_route.onlyHead) {
content = renderToString((
<Provider store={store}>
<StaticRouter location={req.path} context={context}>
<StyleContext.Provider value={{insertCss}}>
{renderRoutes(Routes)}
{/* <Route path='/:owner/:projectsId' exact component={App}></Route> */}
</StyleContext.Provider>
</StaticRouter>
</Provider>
))
}
if (!content) {
// 未匹配到详情页恢复默认header
setDefaultMeta()
} else {
// 匹配到服务端渲染页面注入state
let script = domObj.window.document.getElementById('initState')
if (!script) {
script = domObj.window.document.createElement('script')
script.setAttribute('id', 'initState')
}
script.textContent = `window.__initState__ = ${ JSON.stringify(store.getState()) }`
// 目前直接把详情页涉及到的css抽出来塞到返回的html里没有使用insertCss
const styleNode = domObj.window.document.createElement('style')
styleNode.type = 'text/css'
// styleNode.innerHTML = `${ [...css].join('') }`
styleNode.innerHTML = detailCss
domObj.window.document.head.appendChild(script);
domObj.window.document.head.appendChild(styleNode);
}
if (_route && !specialRoute.includes(_match.url && _match.url.replace('/', ''))) {
// 匹配上的并且只修改head的页面
if (_route.onlyHead) {
Logger.info(`matchRoot: ${ _match.url }`)
const result = await changeHead(_route, _match.params)
if (!result) return result
}
} else {
return false
}
let html = domObj.serialize()
const prepHTML=(data,rootString)=>{
data=data.replace('<div id="root" class="page -layout-v -fit widthunit"></div>',`<div id="root" class="page -layout-v -fit widthunit">${rootString}</div>`);
return data;
}
res.send(prepHTML(html, content))
store = null
}