From b296f6c8408a77d15239f238797174b2e5486f11 Mon Sep 17 00:00:00 2001 From: yangxingyuan Date: Sun, 28 Aug 2022 10:50:38 +0800 Subject: [PATCH] feat: optimize island arch --- src/client/app/app.tsx | 4 +-- src/client/app/client-entry.tsx | 26 ++++++++++++------- src/client/app/island-inject.ts | 8 ++++++ src/client/app/ssr-entry.tsx | 17 +++++++----- src/client/app/type.d.ts | 11 +++----- src/client/theme/components/Counter/index.tsx | 2 +- src/client/theme/index.ts | 8 +++++- src/client/theme/layout/Layout/index.tsx | 2 +- src/client/tsconfig.json | 3 ++- src/node/build/bundle.ts | 2 +- src/node/build/render.ts | 5 ++-- src/node/plugin.ts | 1 - tsconfig.json | 1 + 13 files changed, 56 insertions(+), 34 deletions(-) create mode 100644 src/client/app/island-inject.ts diff --git a/src/client/app/app.tsx b/src/client/app/app.tsx index eeeecb76..b2d0527d 100644 --- a/src/client/app/app.tsx +++ b/src/client/app/app.tsx @@ -1,6 +1,6 @@ -import theme from '/@island/theme'; +import { Layout } from '/@island/theme'; import React from 'react'; export function App() { - return ; + return ; } diff --git a/src/client/app/client-entry.tsx b/src/client/app/client-entry.tsx index 1792ac14..27aa1a9d 100644 --- a/src/client/app/client-entry.tsx +++ b/src/client/app/client-entry.tsx @@ -1,16 +1,21 @@ import { hydrateRoot, createRoot } from 'react-dom/client'; import { App } from './app'; -import React, { createElement } from 'react'; -import { Counter } from '../theme/components/Counter'; +import React, { ComponentType, createElement } from 'react'; +import { islands } from '/@island/theme'; -// island 数据,后续单独放到一个 bundle 中 -// @ts-ignore -window.ISLANDS = { - Counter -}; +// Type shim for window.ISLANDS +declare global { + interface Window { + ISLANDS: Record>; + // The state for island. + ISLAND_PROPS: any; + } +} -// @ts-ignore -window.ISLAND_PROPS = [{ count: 1 }]; +window.ISLANDS = islands; +window.ISLAND_PROPS = JSON.parse( + document.getElementById('island-props')!.textContent! +); async function renderInBrowser() { const containerEl = document.getElementById('root'); @@ -18,6 +23,8 @@ async function renderInBrowser() { throw new Error(`#root element not found`); } if (import.meta.env.DEV) { + // App Will be tree shaking in production. + // So complete application code is removed and only island component code is reserved. createRoot(containerEl).render(); } else { const islands = document.querySelectorAll('[__island]'); @@ -26,7 +33,6 @@ async function renderInBrowser() { const [id, index] = island.getAttribute('__island')!.split(':'); hydrateRoot( island, - // @ts-ignore createElement(window.ISLANDS[id], window.ISLAND_PROPS[index]) ); } diff --git a/src/client/app/island-inject.ts b/src/client/app/island-inject.ts new file mode 100644 index 00000000..14feb377 --- /dev/null +++ b/src/client/app/island-inject.ts @@ -0,0 +1,8 @@ +import theme from '/@island/theme'; + +export const Islands = theme.islands; + +window.ISLANDS = Islands; +window.ISLAND_PROPS = JSON.parse( + document.getElementById('island-props')!.textContent! +); diff --git a/src/client/app/ssr-entry.tsx b/src/client/app/ssr-entry.tsx index 0087a41f..7aafa11f 100644 --- a/src/client/app/ssr-entry.tsx +++ b/src/client/app/ssr-entry.tsx @@ -2,14 +2,14 @@ import { renderToString } from 'react-dom/server'; import { App } from './app'; import React from 'react'; -const ISLAND_PROPS = []; +let ISLAND_PROPS: any[] = []; const originalCreateElement = React.createElement; // @ts-ignore React.createElement = (type: ElementType, props: any, ...children: any[]) => { - if (props && props.__islandId) { - const id = props.__islandId; + if (props && props.__island) { ISLAND_PROPS.push(props); - delete props.__islandId; + delete props.__island; + const id = type.name; return originalCreateElement( `div`, { @@ -22,6 +22,11 @@ React.createElement = (type: ElementType, props: any, ...children: any[]) => { }; // For ssr component render -export function render() { - return renderToString(); +export function render(): { appHtml: string; propsData: any[] } { + ISLAND_PROPS = []; + const appHtml = renderToString(); + return { + appHtml, + propsData: ISLAND_PROPS + }; } diff --git a/src/client/app/type.d.ts b/src/client/app/type.d.ts index b4691b1c..705b0889 100644 --- a/src/client/app/type.d.ts +++ b/src/client/app/type.d.ts @@ -1,13 +1,8 @@ /// declare module '/@island/theme*' { - import { ComponentType } from 'react'; + import { ComponentType, Component } from 'react'; - const theme: { - Layout: ComponentType; - }; - - export default theme; + export const Layout: ComponentType; + export const islands: Record>; } - -declare module window {} diff --git a/src/client/theme/components/Counter/index.tsx b/src/client/theme/components/Counter/index.tsx index 7703c9a9..26f992b5 100644 --- a/src/client/theme/components/Counter/index.tsx +++ b/src/client/theme/components/Counter/index.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import React from 'react'; -export function Counter(props: { count?: number; __islandId: string }) { +export function Counter(props: { count?: number; __island: boolean }) { const [count, setCount] = useState(props.count || 0); return (
diff --git a/src/client/theme/index.ts b/src/client/theme/index.ts index 4f44068f..067a298e 100644 --- a/src/client/theme/index.ts +++ b/src/client/theme/index.ts @@ -1,2 +1,8 @@ import { Layout } from './layout/Layout'; -export default { Layout }; +import { Counter } from './components/Counter/index'; + +export { Layout }; + +export const islands = { + Counter +}; diff --git a/src/client/theme/layout/Layout/index.tsx b/src/client/theme/layout/Layout/index.tsx index 4ce336ff..65ee946d 100644 --- a/src/client/theme/layout/Layout/index.tsx +++ b/src/client/theme/layout/Layout/index.tsx @@ -4,7 +4,7 @@ export const Layout: React.FC = () => { return (

This is Layout page

- +
); }; diff --git a/src/client/tsconfig.json b/src/client/tsconfig.json index 6cc13bae..3471e888 100644 --- a/src/client/tsconfig.json +++ b/src/client/tsconfig.json @@ -4,6 +4,7 @@ "module": "ESNext", "outDir": "../../dist/client", "jsx": "preserve", - "skipLibCheck": true + "skipLibCheck": true, + "allowSyntheticDefaultImports": true } } diff --git a/src/node/build/bundle.ts b/src/node/build/bundle.ts index 0ec7cd9a..d9f6590e 100644 --- a/src/node/build/bundle.ts +++ b/src/node/build/bundle.ts @@ -8,12 +8,12 @@ export const okMark = '\x1b[32m✓\x1b[0m'; export const failMark = '\x1b[31m✖\x1b[0m'; export async function bundle(root: string) { - console.log(CLIENT_ENTRY_PATH); const resolveViteConfig = (isServer: boolean): InlineConfig => ({ mode: 'production', root, plugins: [createIslandPlugins()], build: { + minify: false, ssr: isServer, outDir: isServer ? TEMP_PATH : 'dist', cssCodeSplit: false, diff --git a/src/node/build/render.ts b/src/node/build/render.ts index 7bcba3be..f6d1bb0a 100644 --- a/src/node/build/render.ts +++ b/src/node/build/render.ts @@ -5,7 +5,7 @@ import type { RollupOutput } from 'rollup'; import { okMark } from './bundle'; export async function renderPage( - render: () => string, + render: () => { appHtml: string; propsData: string }, root: string, clientBundle: RollupOutput ) { @@ -15,7 +15,7 @@ export async function renderPage( const { default: ora } = await dynamicImport('ora'); const spinner = ora(); spinner.start(`Rendering page in server side...`); - const appHtml = render(); + const { appHtml, propsData } = render(); const html = ` @@ -27,6 +27,7 @@ export async function renderPage(
${appHtml}
+ `.trim(); diff --git a/src/node/plugin.ts b/src/node/plugin.ts index 6107c8d5..49f1736d 100644 --- a/src/node/plugin.ts +++ b/src/node/plugin.ts @@ -7,7 +7,6 @@ import { } from './constants'; import reactPlugin from '@vitejs/plugin-react'; import fs from 'fs-extra'; -import { createContext } from 'react'; export function createIslandPlugins() { const islandPlugin: Plugin = { diff --git a/tsconfig.json b/tsconfig.json index 03cc0183..68957984 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "outDir": "dist", "skipLibCheck": true, "noUnusedLocals": true, + "allowSyntheticDefaultImports": true, "resolveJsonModule": true, "jsx": "react", "lib": ["ESNext", "DOM"]