From 300e30e5d59ee3e9695dff375413c9732882b950 Mon Sep 17 00:00:00 2001 From: fxy060608 Date: Mon, 3 May 2021 16:41:49 +0800 Subject: [PATCH] wip: ssr --- packages/vite-plugin-uni/lib/ssr/env.js | 25 ++++++++ packages/vite-plugin-uni/lib/ssr/render.js | 59 +++++++++++++++++++ .../src/configResolved/plugins/index.ts | 2 +- .../src/configResolved/plugins/mainJs.ts | 42 ++++++++++--- packages/vite-plugin-uni/src/utils/index.ts | 1 + packages/vite-plugin-uni/src/utils/ssr.ts | 42 ++++--------- 6 files changed, 131 insertions(+), 40 deletions(-) create mode 100644 packages/vite-plugin-uni/lib/ssr/env.js create mode 100644 packages/vite-plugin-uni/lib/ssr/render.js diff --git a/packages/vite-plugin-uni/lib/ssr/env.js b/packages/vite-plugin-uni/lib/ssr/env.js new file mode 100644 index 000000000..5c9e9ea9e --- /dev/null +++ b/packages/vite-plugin-uni/lib/ssr/env.js @@ -0,0 +1,25 @@ +const context = (() => { + if (typeof globalThis !== 'undefined') { + return globalThis + } else if (typeof self !== 'undefined') { + return self + } else if (typeof window !== 'undefined') { + return window + } else { + return Function('return this')() + } +})() +// assign defines +const defines = __DEFINES__ +Object.keys(defines).forEach((key) => { + const segments = key.split('.') + let target = context + for (let i = 0; i < segments.length; i++) { + const segment = segments[i] + if (i === segments.length - 1) { + target[segment] = defines[key] + } else { + target = target[segment] || (target[segment] = {}) + } + } +}) diff --git a/packages/vite-plugin-uni/lib/ssr/render.js b/packages/vite-plugin-uni/lib/ssr/render.js new file mode 100644 index 000000000..bc0c351ac --- /dev/null +++ b/packages/vite-plugin-uni/lib/ssr/render.js @@ -0,0 +1,59 @@ +import { renderToString } from '@vue/server-renderer' + +let AppInstance + +function createApp(App) { + AppInstance = createVueSSRApp(App).use(plugin) + AppInstance.mount = () => {} + return AppInstance +} + +export async function render(url, manifest) { + const app = AppInstance + const router = app.router + + // set the router to the desired URL before rendering + router.push(url) + await router.isReady() + + // passing SSR context object which will be available via useSSRContext() + // @vitejs/plugin-vue injects code into a component's setup() that registers + // itself on ctx.modules. After the render, ctx.modules would contain all the + // components that have been instantiated during this render call. + const ctx = {} + const html = await renderToString(app, ctx) + + // the SSR manifest generated by Vite contains module -> chunk/asset mapping + // which we can then use to determine what files need to be preloaded for this + // request. + const preloadLinks = renderPreloadLinks(ctx.modules, manifest) + return [html, preloadLinks] +} + +function renderPreloadLinks(modules, manifest) { + let links = '' + const seen = new Set() + modules.forEach((id) => { + const files = manifest[id] + if (files) { + files.forEach((file) => { + if (!seen.has(file)) { + seen.add(file) + links += renderPreloadLink(file) + } + }) + } + }) + return links +} + +function renderPreloadLink(file) { + if (file.endsWith('.js')) { + return '' + } else if (file.endsWith('.css')) { + return '' + } else { + // TODO + return '' + } +} diff --git a/packages/vite-plugin-uni/src/configResolved/plugins/index.ts b/packages/vite-plugin-uni/src/configResolved/plugins/index.ts index cbb1b9356..f9ba07987 100644 --- a/packages/vite-plugin-uni/src/configResolved/plugins/index.ts +++ b/packages/vite-plugin-uni/src/configResolved/plugins/index.ts @@ -105,7 +105,7 @@ export function initPlugins( 0, 'pre' ) - addPlugin(plugins, uniMainJsPlugin(options), 1, 'pre') + addPlugin(plugins, uniMainJsPlugin(config, options), 1, 'pre') addPlugin(plugins, uniPagesJsonPlugin(config, options), 1, 'pre') addPlugin(plugins, uniManifestJsonPlugin(config, options), 1, 'pre') diff --git a/packages/vite-plugin-uni/src/configResolved/plugins/mainJs.ts b/packages/vite-plugin-uni/src/configResolved/plugins/mainJs.ts index 90aee79a6..092a3d322 100644 --- a/packages/vite-plugin-uni/src/configResolved/plugins/mainJs.ts +++ b/packages/vite-plugin-uni/src/configResolved/plugins/mainJs.ts @@ -1,29 +1,53 @@ import path from 'path' import slash from 'slash' -import { Plugin } from 'vite' +import { Plugin, ResolvedConfig } from 'vite' import { VitePluginUniResolvedOptions } from '../..' +import { generateSSRRenderCode } from '../../utils' -export function uniMainJsPlugin(options: VitePluginUniResolvedOptions): Plugin { +export function uniMainJsPlugin( + config: ResolvedConfig, + options: VitePluginUniResolvedOptions +): Plugin { const mainPath = slash(path.resolve(options.inputDir, 'main')) const mainJsPath = mainPath + '.js' const mainTsPath = mainPath + '.ts' const pagesJsonJsPath = slash(path.resolve(options.inputDir, 'pages.json.js')) + const isSSR = config.server.middlewareMode return { name: 'vite:uni-main-js', - transform(code, id) { + transform(code, id, ssr) { if (id === mainJsPath || id === mainTsPath) { - let wrapperCode = `function createApp(rootComponent,rootProps){return createVueApp(rootComponent, rootProps).use(plugin)}` - if (code.includes('createSSRApp')) { - code = code.replace('createSSRApp', 'createVueSSRApp') - wrapperCode = `function createSSRApp(App){return createVueSSRApp(App).use(plugin)}` + if (!isSSR) { + code = createApp(code) } else { - code = code.replace('createApp', 'createVueApp') + code = ssr ? createSSRServerApp(code) : createSSRClientApp(code) } return { - code: `import { plugin } from '@dcloudio/uni-h5';import '${pagesJsonJsPath}';${wrapperCode};${code}`, + code: `import { plugin } from '@dcloudio/uni-h5';import '${pagesJsonJsPath}';${code}`, map: this.getCombinedSourcemap(), } } }, } } + +function createApp(code: string) { + return `function createApp(rootComponent,rootProps){return createVueApp(rootComponent, rootProps).use(plugin)};${code.replace( + 'createApp', + 'createVueApp' + )}` +} + +function createSSRClientApp(code: string) { + return `function createApp(rootComponent, rootProps) {const app = createVueSSRApp(rootComponent, rootProps).use(plugin);const oldMount = app.mount;app.mount = (selector) => app.router.isReady().then(() => oldMount.call(app, selector));return app;};${code.replace( + 'createApp', + 'createVueSSRApp' + )}` +} + +function createSSRServerApp(code: string) { + return `${generateSSRRenderCode()};${code.replace( + 'createApp', + 'createVueSSRApp' + )}` +} diff --git a/packages/vite-plugin-uni/src/utils/index.ts b/packages/vite-plugin-uni/src/utils/index.ts index eb160998a..7b9cb7b65 100644 --- a/packages/vite-plugin-uni/src/utils/index.ts +++ b/packages/vite-plugin-uni/src/utils/index.ts @@ -1,3 +1,4 @@ +export * from './ssr' export * from './filter' export * from './features' export * from './easycom' diff --git a/packages/vite-plugin-uni/src/utils/ssr.ts b/packages/vite-plugin-uni/src/utils/ssr.ts index ebb95a019..a8e872633 100644 --- a/packages/vite-plugin-uni/src/utils/ssr.ts +++ b/packages/vite-plugin-uni/src/utils/ssr.ts @@ -1,3 +1,6 @@ +import path from 'path' +import fs from 'fs-extra' + function serializeDefine(define: Record): string { let res = `{` for (const key in define) { @@ -10,35 +13,14 @@ function serializeDefine(define: Record): string { } export function generateSSREnvCode(define: Record): string { - return envCode.replace('__DEFINES__', serializeDefine(define)) + return fs + .readFileSync(path.join(__dirname, '../../lib/ssr/env.js'), 'utf8') + .replace('__DEFINES__', serializeDefine(define)) } -const envCode = `const context = (() => { - if (typeof globalThis !== 'undefined') { - return globalThis; - } - else if (typeof self !== 'undefined') { - return self; - } - else if (typeof window !== 'undefined') { - return window; - } - else { - return Function('return this')(); - } -})(); -// assign defines -const defines = __DEFINES__; -Object.keys(defines).forEach((key) => { - const segments = key.split('.'); - let target = context; - for (let i = 0; i < segments.length; i++) { - const segment = segments[i]; - if (i === segments.length - 1) { - target[segment] = defines[key]; - } - else { - target = target[segment] || (target[segment] = {}); - } - } -});` +export function generateSSRRenderCode() { + return fs.readFileSync( + path.join(__dirname, '../../lib/ssr/render.js'), + 'utf8' + ) +} -- GitLab