Skip to content

Commit

Permalink
feat: server side render
Browse files Browse the repository at this point in the history
  • Loading branch information
chenjun1011 committed Aug 1, 2019
1 parent 9277be0 commit 052298e
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 77 deletions.
1 change: 1 addition & 0 deletions packages/rax-plugin-ssr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"lodash.clone": "^4.5.0",
"lodash.clonedeep": "^4.5.0",
"rax-pwa-webpack-plugin": "^1.0.0-0",
"rax-server-renderer": "^1.1.0",
"rax-ssr-dev-server": "^1.0.0",
"webpack": "^4.38.0"
}
Expand Down
45 changes: 26 additions & 19 deletions packages/rax-plugin-ssr/src/getEntries.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
const path = require('path');
const fs = require('fs-extra');
const qs = require('querystring');

const SSRLoader = require.resolve('./loader');

module.exports = (rootDir) => {
const appDirectory = rootDir;
const appSrc = path.resolve(appDirectory, 'src');

const entries = {};
const absoluteAppPath = path.join(appDirectory, 'src/app.js');
const absoluteAppJSONPath = path.join(appDirectory, 'src/app.json');
const absoluteDocumentPath = path.join(appDirectory, 'src/document/index.jsx');
const absoluteShellPath = path.join(appDirectory, 'src/shell/index.jsx');

const files = fs.readdirSync(path.resolve(appSrc, 'pages'));
files.map((file) => {
const absolutePath = path.resolve(appSrc, 'pages', file);
const pathStat = fs.statSync(absolutePath);
const appJSON = require(absoluteAppJSONPath);
const routes = appJSON.routes;

if (pathStat.isDirectory()) {
const relativePath = path.relative(appDirectory, absolutePath);
entries[file] = './' + path.join(relativePath, '/');
}
});
const entries = {};

routes.forEach((route) => {
const entry = route.name || route.component.replace(/\//g, '_');
const absolutePagePath = path.resolve(appSrc, route.component);

const documentPath = path.resolve(appSrc, 'document/index.jsx');
if (fs.existsSync(documentPath)) {
entries._document = documentPath;
}
const query = {
path: route.path,
absoluteDocumentPath,
absoluteShellPath,
absoluteAppPath,
absolutePagePath,
absoluteAppJSONPath,
// errorPath: path.join(appDirectory, 'src/pages/error/index.jsx'), // 从 route 中读取
// assetsManifestPath: pathConfig.assetsManifest
};

const shellPath = path.resolve(appSrc, 'shell/index.jsx');
if (fs.existsSync(shellPath)) {
entries._shell = shellPath;
}
entries[entry] = `${SSRLoader}?${qs.stringify(query)}!${absolutePagePath}`;
});

return entries;
};
2 changes: 1 addition & 1 deletion packages/rax-plugin-ssr/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module.exports = ({ chainWebpack, registerConfig, rootDir, onHook, log }) => {
}

onHook('after.dev', () => {
runSSRDev(ssrConfig, log);
runSSRDev(ssrConfig, rootDir, log);
});
});
};
97 changes: 97 additions & 0 deletions packages/rax-plugin-ssr/src/loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
const { parse } = require('querystring');
const fs = require('fs');

module.exports = function(content) {
const query = typeof this.query === 'string' ? parse(this.query.substr(1)) : this.query;

const {
absoluteDocumentPath,
absoluteShellPath,
absoluteAppPath,
absolutePagePath,
absoluteAppJSONPath
} = query;

const hasShell = fs.existsSync(absoluteShellPath);
const shellStr = hasShell ? `import Shell from '${absoluteShellPath}'` : 'const Shell = function (props) { return props.children };';

return `
import { createElement } from 'rax';
import renderer from 'rax-server-renderer';
import App from '${absoluteAppPath}';
import Page from '${absolutePagePath}';
import Document from '${absoluteDocumentPath}';
import appJSON from '${absoluteAppJSONPath}';
${shellStr}
async function renderComponentToHTML(req, res, Component) {
const ctx = {
req,
res
};
const shellData = await getInitialProps(Shell, ctx);
const appData = await getInitialProps(App, ctx);
const pageData = await getInitialProps(Component, ctx);
const initialData = {
shellData,
appData,
pageData
};
const contentElement = createElement(Shell, null, createElement(App, {
routerConfig: {
defaultComponet: Component,
routes: appJSON.routes
}
}));
const contentHtml = renderer.renderToString(contentElement);
const documentProps = {
pageHtml: contentHtml,
pageData: JSON.stringify(initialData)
};
await getInitialProps(Document, ctx);
const documentElement = createElement(Document, documentProps);;
const html = '<!doctype html>' + renderer.renderToString(documentElement);
return html;
}
export async function render(req, res) {
const html = await renderToHTML(req, res);
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.send(html);
}
export async function renderToHTML(req, res) {
const html = await renderComponentToHTML(req, res, Page);
return html;
}
async function getInitialProps(Component, ctx) {
if (!Component.getInitialProps) return null;
const props = await Component.getInitialProps(ctx);
if (!props || typeof props !== 'object') {
const message = '"getInitialProps()" should resolve to an object. But found "' + props + '" instead.';
throw new Error(message);
}
if (Component.defaultProps) {
Component.defaultProps = Object.assign({}, props, Component.defaultProps);
} else {
Component.defaultProps = props;
}
return props;
}
`;
};
17 changes: 16 additions & 1 deletion packages/rax-plugin-ssr/src/runSSRDev.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
const path = require('path');
const chalk = require('chalk');
const address = require('address');
const deepmerge = require('deepmerge');
const webpack = require('webpack');
const SSRDevServer = require('rax-ssr-dev-server');

module.exports = (config, log) => {
module.exports = (config, rootDir, log) => {
const webpackConfig = config.toConfig();

const absoluteAppJSONPath = path.join(rootDir, 'src/app.json');
const appJSON = require(absoluteAppJSONPath);

const distDir = config.output.get('path');
const filename = config.output.get('filename');

const routes = {};
appJSON.routes.forEach((route) => {
const pathName = route.name || route.component.replace(/\//g, '_');
routes[route.path] = path.join(distDir, filename.replace('[name]', pathName));
});

let devServerConfig = {
port: 9999,
host: address.ip(),
routes
};

if (webpackConfig.devServer) {
Expand Down
3 changes: 2 additions & 1 deletion packages/rax-plugin-ssr/src/setSSRBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ module.exports = (config, rootDir) => {
.clear()
.set('@core/app', 'universal-app-runtime')
.set('@core/page', 'universal-app-runtime')
.set('@core/router', 'universal-app-runtime');
.set('@core/router', 'universal-app-runtime')
.set('rax-server-renderer', require.resolve('rax-server-renderer'));

config.target('node');

Expand Down
61 changes: 9 additions & 52 deletions packages/rax-ssr-dev-server/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const fs = require('fs');
const express = require('express');
const RaxServer = require('rax-server');
const devMiddleware = require('webpack-dev-middleware');
const hotMiddleware = require('webpack-hot-middleware');
const httpProxyMiddleware = require('http-proxy-middleware');
Expand All @@ -18,28 +17,21 @@ class DevServer {
// eslint-disable-next-line new-cap
const router = express.Router();

const server = new RaxServer();

const {
pagesManifest,
routes,
} = this.options;

Object.keys(pagesManifest).forEach(page => {
// _document, _shell
if (page.indexOf('_') > -1) {
return;
}

router.get(`/${page}`, (req, res) => {
const pageConfig = this.getPageConfig(res, page);
server.render(req, res, pageConfig);
Object.keys(routes).forEach(routePath => {
router.get(routePath, (req, res) => {
const page = this.loadComponent(routes[routePath], res);
page.render(req, res);
});
});

if (pagesManifest.index) {
if (!routes['/'] && routes['/index']) {
router.get('/', (req, res) => {
const pageConfig = this.getPageConfig(res, 'index');
server.render(req, res, pageConfig);
const page = this.loadComponent(routes['/index'], res);
page.render(req, res);
});
}

Expand Down Expand Up @@ -68,42 +60,7 @@ class DevServer {
this.app.listen(port, callback);
}

getPageConfig(res, page) {
const {
appConfig = {},
pagesManifest,
assetsManifest,
assetsManifestPath
} = this.options;

let assets = assetsManifest || {};
if (assetsManifestPath) {
const assetsContent = fs.readFileSync(assetsManifestPath, res);
assets = JSON.parse(assetsContent);
};

const pageConfig = {
page,
...assets[page],
component: this.loadComponent(page, res),
document: {
title: appConfig.title,
component: pagesManifest._document ? this.loadComponent('_document', res) : null
},
shell: {
component: pagesManifest._shell ? this.loadComponent('_shell', res) : null
}
};

return pageConfig;
}

loadComponent(page, res) {
const {
pagesManifest
} = this.options;

const bundlePath = pagesManifest[page];
loadComponent(bundlePath, res) {
const bundleContent = this.readFileSyncFromWebpack(bundlePath, res);
const mod = eval(bundleContent);

Expand Down
1 change: 0 additions & 1 deletion packages/rax-ssr-dev-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"dependencies": {
"express": "^4.17.1",
"http-proxy-middleware": "^0.19.1",
"rax-server": "^1.0.0",
"webpack-dev-middleware": "^3.7.0",
"webpack-hot-middleware": "^2.25.0"
}
Expand Down
11 changes: 9 additions & 2 deletions packages/universal-app-runtime/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@ import { createElement } from 'rax';
import * as RaxUseRouter from 'rax-use-router';
import { createHashHistory } from 'history';
import encodeQS from 'querystring/encode';
import { isWeb } from 'universal-env';

let _history = null;

export function useRouter(routerConfig) {
const { history = createHashHistory(), routes } = routerConfig;
_history = history;
if (isWeb) {

This comment has been minimized.

Copy link
@wssgcg1213

wssgcg1213 Aug 19, 2019

Collaborator

这个原因是?

This comment has been minimized.

Copy link
@chenjun1011

chenjun1011 Aug 19, 2019

Author Collaborator

Server 端执行 createHashHistory 会报错

const { history = createHashHistory(), routes } = routerConfig;
_history = history;
}

function Router(props) {
if (routerConfig.defaultComponet) {
return createElement(routerConfig.defaultComponet, props);
}

const { component } = RaxUseRouter.useRouter(() => routerConfig);

if (!component || Array.isArray(component) && component.length === 0) {
Expand Down

0 comments on commit 052298e

Please sign in to comment.