Skip to content

Commit 809bdf6

Browse files
justquanyinnjugray
authored andcommitted
feat: support ssr for egg framework (#168)
1 parent b2fdb07 commit 809bdf6

File tree

27 files changed

+267
-95
lines changed

27 files changed

+267
-95
lines changed

packages/egg-beidou/README-ZH.md

+49-5
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66

77
### 使用方式
88

9-
- ctx.render(filepath,props) 将 filepath 文件内的 react 代码使用 props 参数进行渲染,并赋值在 ctx.body 上
10-
- ctx.renderView(filepath,props) 将 filepath 文件内的 react 代码使用 props 参数进行渲染,并将渲染结果返回
9+
- ctx.ssr(filepath,props) 将 绝对路径 filepath 文件内的 react 代码使用 props 参数进行渲染,并将渲染结果返回
1110

1211
## 配置
1312

@@ -20,21 +19,29 @@ exports.beidou = {
2019

2120
// config.default.js
2221
exports.beidou = {
23-
static: true, // 是否开启 static 渲染
22+
static: false, // 是否开启 static 渲染
2423
stream: false, // 是否开启 stream 渲染
24+
cache: true, // 是否清除require.cache (开发阶段建议关闭)
25+
onError: function(error){ // 渲染发生错误时的回调函数
26+
// do something
27+
},
28+
view: "/home/admin/project/", // 渲染文件相对路径
29+
extensions: [ '.js', 'jsx', '.ts', '.tsx' ] , // 默认文件添加后缀
2530
};
2631
```
2732

2833
## 示例
2934

3035
> 更多示例可查看单测目录下的 example 目录
3136
37+
### 方式1: 渲染打包后代码
3238
```js
39+
3340
// Controller
3441
'use strict';
3542

3643
exports.index = async function(ctx) {
37-
await ctx.render('simple/index.js', {
44+
await ctx.ssr('simple/index.js', {
3845
data: {
3946
text: 'hello world!',
4047
},
@@ -43,10 +50,47 @@ exports.index = async function(ctx) {
4350

4451
// 将数据传入 react render中的this.props
4552
exports.simple = async function(ctx) {
46-
ctx.body = await ctx.renderView('simple/index.js', {
53+
ctx.body = await ctx.ssr('simple/index.js', {
54+
data: {
55+
text: 'hello world!',
56+
},
57+
});
58+
};
59+
60+
// 使用绝对路径的方式
61+
exports.test = async function(ctx) {
62+
ctx.body = await ctx.ssr('/usr/local/project/simple/index.js', {
63+
data: {
64+
text: 'hello world!',
65+
},
66+
});
67+
};
68+
```
69+
70+
71+
### 方式2: 渲染React代码
72+
> 此插件渲染React代码的方案与北斗同构框架使用一致的方案,具体可参考北斗同构框架文档使用
73+
```js
74+
//config/plugin.js
75+
// 安装 beidou-isomorphic 与 beidou-webpack 依赖,并开启插件
76+
isomorphic: {
77+
enable: true,
78+
package: 'beidou-isomorphic',
79+
},
80+
webpack: {
81+
enable: true,
82+
package: 'beidou-webpack',
83+
env: [ 'local', 'unittest' ],
84+
}
85+
86+
// controller/index.js
87+
exports.simple = async function(ctx) {
88+
ctx.body = await ctx.ssr('simple/index.jsx', {
4789
data: {
4890
text: 'hello world!',
4991
},
5092
});
5193
};
5294
```
95+
96+

packages/egg-beidou/README.md

+48-6
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,85 @@ neet to open the plugin for using
88

99
### extends
1010

11-
- ctx.render(filepath,props) Render the `filepath` by proprs and set the result as `ctx.body` value
12-
- ctx.renderView(filepath,props) Render the `filepath` by proprs and return result for SSR
11+
- ctx.ssr(filepath,props) Render the `filepath` by proprs and return result for SSR, `filepath` is absolute path
1312

1413
## Configure
1514

1615
```js
16+
// config/pulgin.js
1717
exports.beidou = {
1818
enable: true,
1919
package: 'egg-beidou',
2020
};
2121

2222
// config.default.js
2323
exports.beidou = {
24-
static: true, // whether use static render for SSR
24+
static: false, // whether use static render for SSR
2525
stream: false, // whether use stream render for SSR
26+
cache: true, // whether open require cache
27+
onError: function(error){ // call the function when render occur error
28+
// do something
29+
},
30+
view:"/home/admin/project/",
31+
extensions: [ '.js', 'jsx', '.ts', '.tsx' ] , // file suffix
2632
};
2733
```
2834

2935
## Example
3036

3137
> see more example to test/exmaple folder
3238
39+
### case1 : render packaged code
3340
```js
3441
// Controller
3542
'use strict';
3643

3744
exports.index = async function(ctx) {
38-
await ctx.render('simple/index.js', {
45+
await ctx.ssr('simple/index.js', {
3946
data: {
4047
text: 'hello world!',
4148
},
4249
});
4350
};
4451

45-
// if you need pass server data to render
52+
// put your data into react render for react this.props
4653
exports.simple = async function(ctx) {
47-
ctx.body = await ctx.renderView('simple/index.js', {
54+
ctx.body = await ctx.ssr('simple/index.js', {
55+
data: {
56+
text: 'hello world!',
57+
},
58+
});
59+
};
60+
61+
// if you need to pass absolute path
62+
exports.test = async function(ctx) {
63+
ctx.body = await ctx.ssr('/usr/local/project/simple/index.js', {
64+
data: {
65+
text: 'hello world!',
66+
},
67+
});
68+
};
69+
```
70+
71+
### case2: render react native code
72+
> The scheme of rendering React code by this plug-in is consistent with that of Beidou isomorphic framework, which can be used with reference to Beidou isomorphic framework document.
73+
74+
```js
75+
// config/plugin.js
76+
// install egg-plugin: beidou-isomorphic & beidou-webpack and enable plugin
77+
isomorphic: {
78+
enable: true,
79+
package: 'beidou-isomorphic',
80+
},
81+
webpack: {
82+
enable: true,
83+
package: 'beidou-webpack',
84+
env: [ 'local', 'unittest' ],
85+
}
86+
87+
// controller/index.js
88+
exports.simple = async function(ctx) {
89+
ctx.body = await ctx.ssr('simple/index.jsx', {
4890
data: {
4991
text: 'hello world!',
5092
},

packages/egg-beidou/app.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
'use strict';
22

3-
const BeidouView = require('./lib/beidou');
43
const { basicPolyfill, setGlobal } = require('beidou-isomorphic/lib/polyfill');
54

6-
module.exports = (app) => {
5+
module.exports = app => {
76
basicPolyfill();
87
setGlobal(app.config.env);
9-
app.view.use('beidou', BeidouView);
108
};
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
'use strict';
2+
const fs = require('fs');
3+
const path = require('path');
4+
const is = require('is');
5+
const BeidouView = require('../../lib/beidou');
6+
const symbol = Symbol.for('beidou#renderView');
7+
module.exports = {
8+
[symbol]: null,
9+
async ssr(filepath, props) {
10+
let beidou = null;
11+
const { beidou: option } = this.app.config;
12+
if (this[symbol]) {
13+
beidou = null;
14+
} else {
15+
beidou = new BeidouView(this);
16+
}
17+
let pathArr = [ filepath, path.join(option.view || this.app.baseDir, filepath) ];
18+
if (is.function(this.remoteAsset)) {
19+
pathArr.push(await this.remoteAsset(filepath));
20+
}
21+
22+
if (option.extensions && is.array(option.extensions)) {
23+
const pathArrExt = [];
24+
25+
pathArr.forEach(v => {
26+
option.extensions.forEach(
27+
ext => {
28+
pathArrExt.push(v + ext);
29+
}
30+
);
31+
});
32+
pathArr = pathArr.concat(pathArrExt);
33+
}
34+
const p = pathArr.find(
35+
v => {
36+
return fs.existsSync(v);
37+
}
38+
);
39+
try {
40+
if (!p) {
41+
throw new Error(`${filepath} is not exsit, please check it`);
42+
}
43+
const res = await beidou.render(p, {
44+
...props,
45+
ctx: this,
46+
});
47+
return res;
48+
} catch (e) {
49+
this.coreLogger.warn(`SSRError: render ${filepath} occur exception !`, e);
50+
if (option.onError) {
51+
return await option.onError.call(this, e);
52+
}
53+
throw e;
54+
}
55+
},
56+
};

packages/egg-beidou/app/view-middlewares/custom.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const is = require('is-type-of');
44

5-
module.exports = async function (viewCtx, next) {
5+
module.exports = async function(viewCtx, next) {
66
const { Component, props } = viewCtx;
77

88
const render = Component.custom;

packages/egg-beidou/app/view-middlewares/render.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
const React = require('react');
44
const through = require('through');
55

6-
module.exports = async function (viewCtx, next) {
6+
module.exports = async function(viewCtx, next) {
77
await next();
88
const { Component, props, view, html } = viewCtx;
99
const instance = React.createElement(Component, props);

packages/egg-beidou/app/view-middlewares/script.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const is = require('is-type-of');
44

5-
module.exports = async function (viewCtx, next) {
5+
module.exports = async function(viewCtx, next) {
66
const { Component, props } = viewCtx;
77

88
const render = Component.script;

packages/egg-beidou/app/view-middlewares/style.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const is = require('is-type-of');
44

5-
module.exports = async function (viewCtx, next) {
5+
module.exports = async function(viewCtx, next) {
66
const { Component, props } = viewCtx;
77
const render = Component.style;
88
if (typeof render === 'function') {
+3-15
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
'use strict';
2-
3-
const path = require('path');
4-
5-
module.exports = appInfo => ({
2+
module.exports = () => ({
63
beidou: {
74
middlewares: [
85
'render',
96
'custom',
10-
'cache',
117
'initialprops',
128
'redux',
139
'partial',
@@ -18,16 +14,8 @@ module.exports = appInfo => ({
1814
],
1915
doctype: '<!DOCTYPE html>',
2016
cache: true,
21-
static: true,
17+
static: false,
2218
stream: false,
23-
},
24-
view: {
25-
mapping: {
26-
'.js': 'beidou',
27-
},
28-
root: `${path.join(appInfo.baseDir, 'app/views')},${path.join(
29-
appInfo.baseDir,
30-
'clients'
31-
)}`,
19+
extensions: [ '.js', 'jsx', '.ts', '.tsx' ],
3220
},
3321
});

packages/egg-beidou/lib/beidou.js

+13-12
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,32 @@ class BeidouView extends BaseView {
2626

2727
async render(...args) {
2828
this.ctx.type = 'html';
29+
if (this.options.cache === false) {
30+
this.clearCache(args[0]);
31+
}
2932
const res = await super.render(...args);
3033
return res;
3134
}
3235

33-
async renderString() {
34-
this.app.logger.info('reject');
35-
const err = new Error();
36-
err.name = 'not implemented yet!';
37-
err.status = 500;
38-
throw err;
39-
}
40-
4136
renderElementToStream(component, props = {}) {
4237
if (this.options.static) {
4338
return ReactDOMServer.renderToStaticNodeStream(component, props);
44-
} else {
45-
return ReactDOMServer.renderToNodeStream(component, props);
4639
}
40+
return ReactDOMServer.renderToNodeStream(component, props);
41+
4742
}
4843

4944
renderElement(component, props = {}) {
5045
if (this.options.static) {
5146
return ReactDOMServer.renderToStaticMarkup(component, props);
52-
} else {
53-
return ReactDOMServer.renderToString(component, props);
47+
}
48+
return ReactDOMServer.renderToString(component, props);
49+
}
50+
51+
clearCache(filepath) {
52+
const absolutePath = path.resolve(filepath);
53+
if (require.cache[absolutePath]) {
54+
delete require.cache[absolutePath];
5455
}
5556
}
5657
}

packages/egg-beidou/package.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"dependencies": {
1717
"beidou-isomorphic": "^2.0.1",
1818
"beidou-view": "^2.0.1",
19+
"is": "^3.3.0",
1920
"koa-compose": "^4.1.0",
2021
"react": "^16.7.0",
2122
"react-dom": "^16.7.0",
@@ -24,11 +25,11 @@
2425
},
2526
"devDependencies": {
2627
"autod": "^3.0.1",
27-
"egg": "^2.10.0",
28-
"egg-bin": "^4.0.0",
29-
"egg-mock": "^3.7.1",
3028
"eslint": "^5.3.0",
31-
"eslint-config-egg": "^7.0.0"
29+
"eslint-config-egg": "^7.0.0",
30+
"egg-bin": "^4.0.0",
31+
"egg": "^2.10.0",
32+
"egg-mock": "^3.7.1"
3233
},
3334
"scripts": {
3435
"autod": "autod",

0 commit comments

Comments
 (0)