react-drawing-board
Version:
Browser drawing board created with canvas and React.
216 lines (200 loc) • 6.06 kB
JavaScript
import './polyfills';
import history from './history';
import React from 'react';
import ReactDOM from 'react-dom';
import findRoute, {
getUrlQuery,
} from '/Users/xumm_12/Code/react-drawing-board/node_modules/umi-build-dev/lib/findRoute.js';
// runtime plugins
const plugins = require('umi/_runtimePlugin');
window.g_plugins = plugins;
plugins.init({
validKeys: [
'patchRoutes',
'render',
'rootContainer',
'modifyRouteProps',
'onRouteChange',
'modifyInitialProps',
'initialProps',
],
});
// render
let clientRender = async () => {
window.g_isBrowser = true;
let props = {};
// Both support SSR and CSR
if (window.g_useSSR) {
// 如果开启服务端渲染则客户端组件初始化 props 使用服务端注入的数据
props = window.g_initialData;
} else {
const pathname = location.pathname;
const activeRoute = findRoute(require('@@/router').routes, pathname);
// 在客户端渲染前,执行 getInitialProps 方法
// 拿到初始数据
if (
activeRoute &&
activeRoute.component &&
activeRoute.component.getInitialProps
) {
const initialProps = plugins.apply('modifyInitialProps', {
initialValue: {},
});
props = activeRoute.component.getInitialProps
? await activeRoute.component.getInitialProps({
route: activeRoute,
isServer: false,
location,
...initialProps,
})
: {};
}
}
const rootContainer = plugins.apply('rootContainer', {
initialValue: React.createElement(require('./router').default, props),
});
ReactDOM[window.g_useSSR ? 'hydrate' : 'render'](
rootContainer,
document.getElementById('root'),
);
};
const render = plugins.compose(
'render',
{ initialValue: clientRender },
);
const moduleBeforeRendererPromises = [];
// client render
if (__IS_BROWSER) {
Promise.all(moduleBeforeRendererPromises)
.then(() => {
render();
})
.catch(err => {
window.console && window.console.error(err);
});
}
// export server render
let serverRender, ReactDOMServer;
if (!__IS_BROWSER) {
const { matchRoutes } = require('react-router-config');
const { StaticRouter } = require('react-router');
// difference: umi-history has query object
const { createLocation } = require('umi-history');
// don't remove, use stringify html map
const stringify = require('serialize-javascript');
const router = require('./router');
/**
* 1. Load dynamicImport Component
* 2. Get Component initialProps function data
* return Component props
* @param pathname
* @param props
*/
const getInitialProps = async (pathname, props) => {
const { routes } = router;
const matchedComponents = matchRoutes(routes, pathname)
.map(({ route }) => {
if (route.component) {
return !route.component.preload
? // 同步
route.component
: // 异步,支持 dynamicImport
route.component.preload().then(component => component.default);
}
})
.filter(c => c);
const loadedComponents = await Promise.all(matchedComponents);
// get Store
const initialProps = plugins.apply('modifyInitialProps', {
initialValue: {},
});
// support getInitialProps
const promises = loadedComponents.map(component => {
if (component && component.getInitialProps) {
return component.getInitialProps({
isServer: true,
...props,
...initialProps,
});
}
return Promise.resolve(null);
});
return Promise.all(promises);
};
serverRender = async (ctx = {}) => {
// ctx.req.url may be `/bar?locale=en-US`
const [pathname] = (ctx.req.url || '').split('?');
// global
global.req = {
url: ctx.req.url,
};
const location = createLocation(ctx.req.url);
const activeRoute = findRoute(router.routes, pathname);
// omit component
const { component, ...restRoute } = activeRoute || {};
// router context hook
// get current router status 40x / 30x, share with server
const context = {};
// TODO: getInitialProps timeout handle
const initialData = await getInitialProps(pathname, {
route: restRoute,
// only exist in server
req: ctx.req || {},
res: ctx.res || {},
context,
location,
});
// 当前路由(不包含 Layout)的 getInitialProps 有返回值
// Page 值为 undefined 时,有 getInitialProps 无返回,此时 return dva model
const pageData = initialData[initialData.length - 1];
if (pageData === undefined) {
initialData[initialData.length - 1] = plugins.apply('initialProps', {
initialValue: pageData,
});
}
// reduce all match component getInitialProps
// in the same object key
// page data key will override layout key
const props = Array.isArray(initialData)
? initialData.reduce(
(acc, curr) => ({
...acc,
...curr,
}),
{},
)
: {};
const App = React.createElement(
StaticRouter,
{
location: ctx.req.url,
context,
},
React.createElement(router.default, props),
);
// render rootContainer for htmlTemplateMap
const rootContainer = plugins.apply('rootContainer', {
initialValue: App,
});
const htmlTemplateMap = {};
const matchPath = activeRoute ? activeRoute.path : undefined;
return {
htmlElement: matchPath ? htmlTemplateMap[matchPath] : '',
rootContainer,
matchPath,
g_initialData: props,
context,
};
};
// using project react-dom version
// https://github.com/facebook/react/issues/13991
ReactDOMServer = require('react-dom/server');
}
export { ReactDOMServer };
export default (__IS_BROWSER ? null : serverRender);
// hot module replacement
if (__IS_BROWSER && module.hot) {
module.hot.accept('./router', () => {
clientRender();
});
}