Skip to content

Commit

Permalink
feat: ssg 多路由打包
Browse files Browse the repository at this point in the history
  • Loading branch information
c0dedance committed Oct 22, 2023
1 parent 98a6c0c commit 4ea27eb
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 29 deletions.
75 changes: 48 additions & 27 deletions src/node/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -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) =>
`\
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>title</title>
<meta name="description" content="xxx">
</head>
<body>
<div id="root">${appHtml}</div>
<script type="module" src="/${clientChunk?.fileName}"></script>
</body>
</html>`.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 = `\
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>title</title>
<meta name="description" content="xxx">
</head>
<body>
<div id="root">${appHtml}</div>
<script type="module" src="/${clientChunk?.fileName}"></script>
</body>
</html>`.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'))
}
Expand Down Expand Up @@ -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: {
Expand Down
2 changes: 2 additions & 0 deletions src/node/cli.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import path from 'path'
import { cac } from 'cac'
import { build } from './build'
import { version } from '../../package.json'
Expand Down Expand Up @@ -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)
})
Expand Down
7 changes: 7 additions & 0 deletions src/node/plugin-routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import React from 'react'
import { Plugin } from 'vite'
import { RouteService } from './RouteService'

interface PluginOptions {
root: string
}

export interface Route {
path: string
element: React.ReactElement
filePath: string
}

const CONVENTIONAL_ROUTE_ID = 'rpress:routes'

// 本质: 把文件目录结构 -> 路由数据
Expand Down
6 changes: 4 additions & 2 deletions src/runtime/ssr-entry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<StaticRouter location="/guide">
<StaticRouter location={pagePath}>
<App />
</StaticRouter>
)
}

export { routes } from 'rpress:routes'

0 comments on commit 4ea27eb

Please sign in to comment.