From 4ea27ebaefcc33c018901b8aad14fc145bfe3043 Mon Sep 17 00:00:00 2001 From: c0dedance <2627702283@qq.com> Date: Sun, 22 Oct 2023 13:49:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20ssg=20=E5=A4=9A=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E6=89=93=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/node/build.ts | 75 +++++++++++++++++++++------------ src/node/cli.ts | 2 + src/node/plugin-routes/index.ts | 7 +++ src/runtime/ssr-entry.tsx | 6 ++- 4 files changed, 61 insertions(+), 29 deletions(-) diff --git a/src/node/build.ts b/src/node/build.ts index 47bd93b..e9addca 100644 --- a/src/node/build.ts +++ b/src/node/build.ts @@ -4,10 +4,11 @@ import { build as viteBuild } from 'vite' import fs from 'fs-extra' // import ora from 'ora' import { CLIENT_ENTRY_PATH, SERVER_ENTRY_PATH } from './constant' +import { createVitePlugins } from './vitePlugins' import type { InlineConfig } from 'vite' import type { RollupOutput } from 'rollup' import type { SiteConfig } from 'shared/types' -import { createVitePlugins } from './vitePlugins' +import type { Route } from './plugin-routes' // const spinner = ora() @@ -18,40 +19,59 @@ export async function build(root: string = process.cwd(), config: SiteConfig) { const serverEntryPath = pathToFileURL( path.resolve(root, './.temp/ssr-entry.cjs') // TODO: 统一化打包后缀 ).toString() - const { render } = await import(serverEntryPath) + const { render, routes } = await import(serverEntryPath) // 3. 服务端渲染,产出HTML - await renderPage(root, render, clientBundle) + await renderPage({ root, render, clientBundle, routes }) } -export async function renderPage( - root: string, - render: () => string, +export async function renderPage({ + root, + render, + clientBundle, + routes, +}: { + root: string + render: (pagePath: string) => string clientBundle: RollupOutput -) { - const appHtml = render() + routes: Route[] +}) { const clientChunk = clientBundle.output.find( (chunk) => chunk.type === 'chunk' && chunk.isEntry ) - + const renderIndexHTML = (appHtml: string) => + `\ + + + + + + title + + + +
${appHtml}
+ + + `.trim() console.log(`Rendering page in server side...`) + await Promise.all( + routes.map(async (r) => { + // 渲染路由对应的页面 + const appHtml = render(r.path) + // 组件HTML嵌入到模板中 + const html = renderIndexHTML(appHtml) + // htlm文件名处理 + const outputFilePath = r.path.endsWith('/') + ? `${r.path}index.html` + : `${r.path}.html` + + const outputPath = path.join(root, 'build', outputFilePath) + // ensure build dir + await fs.ensureDir(path.dirname(outputPath)) + // write html file + await fs.writeFile(outputPath, html) + }) + ) - const html = `\ - - - - - - title - - - -
${appHtml}
- - -`.trim() - // ensure build dir - await fs.ensureDir(path.resolve(root, 'build')) - // write html file - await fs.writeFile(path.resolve(root, './build/index.html'), html) // remove .temp await fs.remove(path.resolve(root, './.temp')) } @@ -92,6 +112,7 @@ async function resolveBuildConfig({ build: { ssr: isSSR, outDir: isSSR ? '.temp' : 'build', + // outDir: isServer ? path.join(root, '.temp') : path.join(root, 'build'), rollupOptions: { input: isSSR ? SERVER_ENTRY_PATH : CLIENT_ENTRY_PATH, output: { diff --git a/src/node/cli.ts b/src/node/cli.ts index 31972ca..e2fb0fd 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -1,3 +1,4 @@ +import path from 'path' import { cac } from 'cac' import { build } from './build' import { version } from '../../package.json' @@ -27,6 +28,7 @@ cli cli .command('build [root]', 'build for production') .action(async (root: string) => { + root = path.resolve(root) const config = await resolveConfig(root, 'build', 'production') await build(root, config) }) diff --git a/src/node/plugin-routes/index.ts b/src/node/plugin-routes/index.ts index 3c38b48..dd8e79e 100644 --- a/src/node/plugin-routes/index.ts +++ b/src/node/plugin-routes/index.ts @@ -1,3 +1,4 @@ +import React from 'react' import { Plugin } from 'vite' import { RouteService } from './RouteService' @@ -5,6 +6,12 @@ interface PluginOptions { root: string } +export interface Route { + path: string + element: React.ReactElement + filePath: string +} + const CONVENTIONAL_ROUTE_ID = 'rpress:routes' // 本质: 把文件目录结构 -> 路由数据 diff --git a/src/runtime/ssr-entry.tsx b/src/runtime/ssr-entry.tsx index 9f04f51..6f6c6b8 100644 --- a/src/runtime/ssr-entry.tsx +++ b/src/runtime/ssr-entry.tsx @@ -4,10 +4,12 @@ import { StaticRouter } from 'react-router-dom/server' import App from './App' // For ssr component render -export function render() { +export function render(pagePath: string) { return renderToString( - + ) } + +export { routes } from 'rpress:routes'