Skip to content

Commit

Permalink
feat: optimize island arch
Browse files Browse the repository at this point in the history
  • Loading branch information
sanyuan0704 committed Aug 28, 2022
1 parent 66f34c0 commit b296f6c
Show file tree
Hide file tree
Showing 13 changed files with 56 additions and 34 deletions.
4 changes: 2 additions & 2 deletions 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 <theme.Layout />;
return <Layout />;
}
26 changes: 16 additions & 10 deletions src/client/app/client-entry.tsx
@@ -1,23 +1,30 @@
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<string, ComponentType<any>>;
// 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');
if (!containerEl) {
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(<App />);
} else {
const islands = document.querySelectorAll('[__island]');
Expand All @@ -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])
);
}
Expand Down
8 changes: 8 additions & 0 deletions 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!
);
17 changes: 11 additions & 6 deletions src/client/app/ssr-entry.tsx
Expand Up @@ -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`,
{
Expand All @@ -22,6 +22,11 @@ React.createElement = (type: ElementType, props: any, ...children: any[]) => {
};

// For ssr component render
export function render() {
return renderToString(<App />);
export function render(): { appHtml: string; propsData: any[] } {
ISLAND_PROPS = [];
const appHtml = renderToString(<App />);
return {
appHtml,
propsData: ISLAND_PROPS
};
}
11 changes: 3 additions & 8 deletions src/client/app/type.d.ts
@@ -1,13 +1,8 @@
/// <reference types="vite/client" />

declare module '/@island/theme*' {
import { ComponentType } from 'react';
import { ComponentType, Component } from 'react';

const theme: {
Layout: ComponentType<any>;
};

export default theme;
export const Layout: ComponentType<any>;
export const islands: Record<string, ComponentType<any>>;
}

declare module window {}
2 changes: 1 addition & 1 deletion 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 (
<div>
Expand Down
8 changes: 7 additions & 1 deletion 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
};
2 changes: 1 addition & 1 deletion src/client/theme/layout/Layout/index.tsx
Expand Up @@ -4,7 +4,7 @@ export const Layout: React.FC = () => {
return (
<div>
<h1>This is Layout page</h1>
<Counter count={1} __islandId="Counter" />
<Counter count={1} __island />
</div>
);
};
3 changes: 2 additions & 1 deletion src/client/tsconfig.json
Expand Up @@ -4,6 +4,7 @@
"module": "ESNext",
"outDir": "../../dist/client",
"jsx": "preserve",
"skipLibCheck": true
"skipLibCheck": true,
"allowSyntheticDefaultImports": true
}
}
2 changes: 1 addition & 1 deletion src/node/build/bundle.ts
Expand Up @@ -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,
Expand Down
5 changes: 3 additions & 2 deletions src/node/build/render.ts
Expand Up @@ -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
) {
Expand All @@ -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 = `
<!DOCTYPE html>
<html>
Expand All @@ -27,6 +27,7 @@ export async function renderPage(
</head>
<body>
<div id="root">${appHtml}</div>
<script id="island-props">${JSON.stringify(propsData)}</script>
<script type="module" src="/${clientChunk?.fileName}"></script>
</body>
</html>`.trim();
Expand Down
1 change: 0 additions & 1 deletion src/node/plugin.ts
Expand Up @@ -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 = {
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Expand Up @@ -8,6 +8,7 @@
"outDir": "dist",
"skipLibCheck": true,
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"jsx": "react",
"lib": ["ESNext", "DOM"]
Expand Down

0 comments on commit b296f6c

Please sign in to comment.