[English](./README.md) | 简体中文
# @seasonjs/tegg-vite-plugin
使用 vite 和 egg 实现 ssr 或者 csr
### 只需两部即可启动
1. 使用插件默认配置,并配置`vite` 默认字段 这将会将vite的`root`根目录设置在`/client`:
```typescript
//config/config.default.ts
import {EggAppConfig, PowerPartial} from "egg";
const config: PowerPartial = {
vite: {}
}
export default config;
```
2. 添加 `tegg-vite-plugin` 到 `plugin.ts`
```typescript
//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;
```
### egg 配置文件的字段
`vite` 配置继承于 vite config 的 [InlineConfig](https://vitejs.dev/guide/api-javascript.html#inlineconfig) 类型,
细节可以参考: [vite doc:createserver](https://vitejs.dev/guide/api-javascript.html#createserver).
但是与之不同的是为了方便ssr,增加了 `teggSSR`字段:
```typescript
interface ViteConfig extends InlineConfig {
teggSSR?: {
html?: string //html 模板路径
entry?: string //ssr js 服务文件路径
}
}
```
## 开启 SSR
1. 添加 `vite` 的默认配置字段 将会使用默认的配置到 `/client`
如果你使用默认配置,将会吧入口设置到 `your-project-root/client/index.html`
并且使用默认的ssr入口渲染方法
```typescript
//config/config.default.ts
import {EggAppConfig, PowerPartial} from "egg";
import * as path from 'path';
const config: PowerPartial = {
vite: {
server: {middlewareMode: 'ssr'},
}
}
export default config;
```
你也可以自定义配置
```typescript
//config/config.default.ts
import {EggAppConfig, PowerPartial} from "egg";
import * as path from 'path';
const config: PowerPartial = {
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;
```
2. 把你的处理方法放入Controller,如果你使用的是这个插件的默认ssr render:
__[注意!!!]: default handler 还没实现__
```typescript
//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)
}
}
```
除此之外我更建议你定制自己的处理方法,其实也并不复杂:
```typescript
//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(``, 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
## 贡献者
[@Cyberhan123](https://github.com/cyberhan123)
## 协议
[MIT](LICENSE)
Copyright © 2021, seasonjs