English | 简体中文
使用 vite 和 egg 实现 ssr 或者 csr
- 使用插件默认配置,并配置
vite
默认字段 这将会将vite的root
根目录设置在/client
:
//config/config.default.ts
import {EggAppConfig, PowerPartial} from "egg";
const config: PowerPartial<EggAppConfig> = {
vite: {}
}
export default config;
- 添加
tegg-vite-plugin
到plugin.ts
//config/plugin.ts
import {EggPlugin} from 'egg';
import * as path from 'path';
const plugin: EggPlugin = {
teggVite: {
enable: true,
package: '@seasonjs/tegg-vite-plugin',
},
};
export default plugin;
vite
配置继承于 vite config 的 InlineConfig 类型,
细节可以参考: vite doc:createserver.
但是与之不同的是为了方便ssr,增加了 teggSSR
字段:
interface ViteConfig extends InlineConfig {
teggSSR?: {
html?: string //html 模板路径
entry?: string //ssr js 服务文件路径
}
}
- 添加
vite
的默认配置字段 将会使用默认的配置到/client
如果你使用默认配置,将会吧入口设置到 your-project-root/client/index.html
并且使用默认的ssr入口渲染方法
//config/config.default.ts
import {EggAppConfig, PowerPartial} from "egg";
import * as path from 'path';
const config: PowerPartial<EggAppConfig> = {
vite: {
server: {middlewareMode: 'ssr'},
}
}
export default config;
你也可以自定义配置
//config/config.default.ts
import {EggAppConfig, PowerPartial} from "egg";
import * as path from 'path';
const config: PowerPartial<EggAppConfig> = {
vite: {
server: {middlewareMode: 'ssr'},
teggSSR: {
html: path.reslove('../client/index.html'),// 你的项目/client/index.html
entry: path.reslove('../client/ssr-entry.ts')// 你的项目/client/ssr-entry.ts
}
}
}
export default config;
- 把你的处理方法放入Controller,如果你使用的是这个插件的默认ssr render:
[注意!!!]: default handler 还没实现
//controller/SSRController.ts
import {
Context,
EggContext,
HTTPController,
HTTPMethod,
HTTPMethodEnum,
HTTPQuery,
} from '@eggjs/tegg';
import { EggLogger } from 'egg';
@HTTPController()
export class SSRController {
@HTTPMethod({
method: HTTPMethodEnum.GET,
path: '/*',
})
async hello(@Context() ctx: EggContext) {
this.logger.info('access url: %s', ctx.url);
ctx.viteSSRRender(ctx)
}
}
除此之外我更建议你定制自己的处理方法,其实也并不复杂:
//controller/SSRController.ts
import {
Context,
EggContext,
HTTPController,
HTTPMethod,
HTTPMethodEnum,
HTTPQuery,
} from '@eggjs/tegg';
import {Application, EggLogger} from 'egg';
@HTTPController()
export class SSRController {
@Inject()
private app: Application;
@HTTPMethod({
method: HTTPMethodEnum.GET,
path: '/*',
})
async hello(@Context() ctx: EggContext) {
this.logger.info('access url: %s', ctx.url);
const url = ctx.req.originalUrl
try {
// 1. 读取 index.html
let template = fs.readFileSync(
path.resolve(__dirname, 'index.html'),
'utf-8'
)
// 2. 应用 Vite HTML 转换。这将会注入 Vite HMR 客户端,
// 同时也会从 Vite 插件应用 HTML 转换。
// 例如:@vitejs/plugin-react-refresh 中的 global preambles
template = await this.app.vite.transformIndexHtml(url, template)
// 3. 加载服务器入口。vite.ssrLoadModule 将自动转换
// 你的 ESM 源码使之可以在 Node.js 中运行!无需打包
// 并提供类似 HMR 的根据情况随时失效。
const {render} = await this.app.vite.ssrLoadModule('/src/entry-server.js')
// 4. 渲染应用的 HTML。这假设 entry-server.js 导出的 `render`
// 函数调用了适当的 SSR 框架 API。
// 例如 ReactDOMServer.renderToString()
const appHtml = await render(url)
// 5. 注入渲染后的应用程序 HTML 到模板中。
const html = template.replace(`<!--ssr-outlet-->`, appHtml)
// 6. 返回渲染后的 HTML。
ctx.status = 200;
ctx.set('Content-Type', 'text/html');
ctx.body = html;
} catch (e) {
// 如果捕获到了一个错误,让 Vite 来修复该堆栈,这样它就可以映射回
// 你的实际源码中。
app.vite.ssrFixStacktrace(e);
ctx.logger.error(e);
ctx.res.status(500);
ctx.body = e.message;
}
}
}
支持所有 node > 14.0.0 并且 egg > 2.0.0
MIT Copyright © 2021, seasonjs