This commit is contained in:
Mackie 2026-06-07 22:56:36 +08:00
parent 1539464396
commit 1ed574680f
6 changed files with 100 additions and 121 deletions

View file

@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"@tanstack/react-virtual": "^3.14.2",
"react": "^19.2.6",
"react-dom": "^19.2.6"
},

20
pnpm-lock.yaml generated
View file

@ -8,6 +8,9 @@ importers:
.:
dependencies:
'@tanstack/react-virtual':
specifier: ^3.14.2
version: 3.14.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react:
specifier: ^19.2.6
version: 19.2.7
@ -312,6 +315,15 @@ packages:
'@rolldown/pluginutils@1.0.1':
resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
'@tanstack/react-virtual@3.14.2':
resolution: {integrity: sha512-IpWnmCLvuymRfeeLNVXIzNEYBFLpd3drVIS91sqV78VTZFyldlChkOocZRCPp1B+Wnk09bcLNme8WaMU/9/9bQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
'@tanstack/virtual-core@3.17.0':
resolution: {integrity: sha512-gOxY/hFkPh/XQYhnThBHzkbkX3Ed+z/iushyz+R+JAr213aXxUDgQoTgTdrDpBSRsjFM73P/KfUyWmaF9WHMkQ==}
'@tybys/wasm-util@0.10.2':
resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
@ -1172,6 +1184,14 @@ snapshots:
'@rolldown/pluginutils@1.0.1': {}
'@tanstack/react-virtual@3.14.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
dependencies:
'@tanstack/virtual-core': 3.17.0
react: 19.2.7
react-dom: 19.2.7(react@19.2.7)
'@tanstack/virtual-core@3.17.0': {}
'@tybys/wasm-util@0.10.2':
dependencies:
tslib: 2.8.1

View file

@ -1,122 +1,40 @@
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from './assets/vite.svg'
import heroImg from './assets/hero.png'
import './App.css'
import { useRef } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useDashboardData } from './hooks/useDashboardData';
import './App.css';
function App() {
const [count, setCount] = useState(0)
export default function App() {
const data = useDashboardData();
const parentRef = useRef<HTMLDivElement>(null);
const rowVirtualizer = useVirtualizer({
count: data.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 35, // Consistent row height
});
return (
<>
<section id="center">
<div className="hero">
<img src={heroImg} className="base" width="170" height="179" alt="" />
<img src={reactLogo} className="framework" alt="React logo" />
<img src={viteLogo} className="vite" alt="Vite logo" />
</div>
<div>
<h1>Get started</h1>
<p>
Edit <code>src/App.tsx</code> and save to test <code>HMR</code>
</p>
</div>
<button
type="button"
className="counter"
onClick={() => setCount((count) => count + 1)}
>
Count is {count}
</button>
</section>
<div className="dashboard-container">
<h1>Market Live Stream</h1>
<div className="ticks"></div>
<section id="next-steps">
<div id="docs">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#documentation-icon"></use>
</svg>
<h2>Documentation</h2>
<p>Your questions, answered</p>
<ul>
<li>
<a href="https://vite.dev/" target="_blank">
<img className="logo" src={viteLogo} alt="" />
Explore Vite
</a>
</li>
<li>
<a href="https://react.dev/" target="_blank">
<img className="button-icon" src={reactLogo} alt="" />
Learn more
</a>
</li>
</ul>
{/* Viewport for virtualization */}
<div ref={parentRef} className="table-viewport">
<div
className="table-content"
style={{ height: `${rowVirtualizer.getTotalSize()}px` }}
>
{rowVirtualizer.getVirtualItems().map((virtualRow) => (
<div
key={virtualRow.key}
className="row"
style={{ transform: `translateY(${virtualRow.start}px)` }}
>
<span>{data[virtualRow.index].symbol}</span>
<span>${data[virtualRow.index].price}</span>
</div>
<div id="social">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#social-icon"></use>
</svg>
<h2>Connect with us</h2>
<p>Join the Vite community</p>
<ul>
<li>
<a href="https://github.com/vitejs/vite" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#github-icon"></use>
</svg>
GitHub
</a>
</li>
<li>
<a href="https://chat.vite.dev/" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#discord-icon"></use>
</svg>
Discord
</a>
</li>
<li>
<a href="https://x.com/vite_js" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#x-icon"></use>
</svg>
X.com
</a>
</li>
<li>
<a href="https://bsky.app/profile/vite.dev" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#bluesky-icon"></use>
</svg>
Bluesky
</a>
</li>
</ul>
))}
</div>
</section>
<div className="ticks"></div>
<section id="spacer"></section>
</>
)
</div>
</div>
);
}
export default App

View file

@ -0,0 +1,23 @@
import { useEffect, useState } from 'react';
export const useDashboardData = () => {
const [data, setData] = useState<any[]>([]);
useEffect(() => {
// Instantiate the worker
const worker = new Worker(new URL('../worker/data.worker.ts', import.meta.url), {
type: 'module',
});
worker.onmessage = (event) => {
if (event.data.type === 'BATCH_UPDATE') {
// Keep only the latest 1000 items to prevent memory bloat
setData((prev) => [...event.data.payload, ...prev].slice(0, 1000));
}
};
return () => worker.terminate();
}, []);
return data;
};

13
src/worker/data.worker.ts Normal file
View file

@ -0,0 +1,13 @@
// src/worker/data.worker.ts
const generateUpdate = () => ({
id: Math.random().toString(36).substring(7),
symbol: "BTC/USD",
price: (Math.random() * 50000).toFixed(2),
timestamp: Date.now(),
});
// Stream updates every 16ms (target ~60fps)
setInterval(() => {
const updates = Array.from({ length: 5 }, generateUpdate);
self.postMessage({ type: 'BATCH_UPDATE', payload: updates });
}, 16);

View file

@ -1,7 +1,11 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})
worker: {
// This ensures your worker is bundled as an ES module,
// which is required for modern browsers and deployment.
format: 'es',
},
});