iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0
Kubernetes

從零到一:使用 K8S + GitOps 打造異構技術棧的資料分析平台系列 第 5

[Day 5] 前端視覺化:React + Echarts 的極致呈現 —— 如何流暢地在前端渲染數萬個數據點的晶圓地圖。

  • 分享至 

  • xImage
  •  

Day 5: 前端視覺化:React + Echarts 的極致呈現

如何流暢地在前端渲染數萬個數據點的晶圓地圖。

1. 視覺化的挑戰

對 Wafer BI 來說,最核心的功能莫過於「晶圓熱圖 (Wafer Heatmap)」。一片晶圓上可能有成千上萬個晶粒 (Die),如果用傳統 DOM 元素去渲染,瀏覽器直接卡死給你看。所以我們導入 ECharts,靠 Canvas 繪製撐住流暢度。

先看成果——這是深色主題下的晶圓詳情頁,滑鼠移到任何一個 Die 上都有即時 Tooltip 顯示座標與量測值:

https://ithelp.ithome.com.tw/upload/images/20260807/20182549tEpkKQNqWA.png

▲ Dark Mode 晶圓熱圖:hover 任一 Die 即時顯示座標與 Thickness 數值

2. 容器化:跟 Java、Python 一樣,也是多階段構建

前兩天分別把 Java 跟 Python 服務裝進了容器,React 前端當然也不例外——這個系列的主軸是 K8S,任何要進叢集的服務都得先變成 Image。services/frontend/Dockerfile 一樣是多階段構建,只是這次的第二階段不是「跑起來」,而是「把靜態檔案交給 Nginx」:

# Stage 1: Build
FROM node:22-slim AS build

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .
RUN npm run build

# Stage 2: Production
FROM nginx:alpine

# Copy the built files from the build stage
COPY --from=build /app/dist /usr/share/nginx/html

# Add nginx config to handle SPA routing if needed
RUN echo 'server { \
    listen 80; \
    location / { \
        root /usr/share/nginx/html; \
        index index.html; \
        try_files $uri $uri/ /index.html; \
    } \
}' > /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

跟 Day 3 的 COPY pom.xml / Day 4 的 COPY requirements.txt 是同一個道理——先 COPY package*.json 裝依賴,再 COPY . . 複製原始碼,改一行 UI 程式碼不會讓 npm install 重跑。第二階段用 nginx:alpine 這個超輕量基底,只裝進 build 完的靜態檔(dist/),Node.js、npm、TypeScript 編譯器全部留在第一階段,不會進到最終 Image 裡。

try_files $uri $uri/ /index.html 這行是 SPA (Single Page Application) 路由的關鍵:React Router 的前端路由(例如 /wafer/Lot1/1)在伺服器端根本不存在對應的檔案,Nginx 找不到就會回退(fallback)到 index.html,剩下的路由交給 React Router 在瀏覽器端接管——少了這行,重新整理頁面會直接 404。

實際建置過程,npm install 裝了 184 個套件、vite build 把 TypeScript 編譯加打包一次搞定:

https://ithelp.ithome.com.tw/upload/images/20260807/201825497ugt790Ttq.png

▲ docker build 前端 Image:npm install 14.3 秒、tsc + vite build 4.43 秒完成,產物壓縮後只有 460KB

3. 晶圓熱圖實作大解密

在專案的 services/frontend/src/App.tsx 中,我們定義了 getHeatmapOption 函數,這就是畫出那張晶圓圖的核心魔法。

以下是簡化後的核心配置程式碼:

const getHeatmapOption = (data: any, isThumbnail = false) => {
  if (!data) return {};
  const gridSize = 30; // 假設網格大小為 30x30
  const categories = Array.from({ length: gridSize }, (_, i) => i + 1);
  
  return {
    tooltip: { 
      show: !isThumbnail, 
      formatter: (params: any) => {
        const [x, y, val] = params.data;
        return `Die (${x}, ${y})<br/>Value: ${val.toFixed(2)}`;
      }
    },
    // 隱藏 X 軸與 Y 軸,讓畫面更乾淨
    xAxis: { type: 'category', data: categories, show: false },
    yAxis: { type: 'category', data: categories, show: false, inverse: true },
    
    // 定義色彩映射,根據數值高低顯示從藍到紅的漸層
    visualMap: {
      min: data.min,
      max: data.max,
      calculable: true,
      orient: 'horizontal',
      left: 'center',
      bottom: '0%',
      inRange: {
        color: ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']
      }
    },
    
    // 畫一個大圓圈,模擬晶圓的邊界 (Wafer Edge)
    graphic: [{
      type: 'circle',
      shape: { cx: '50%', cy: '50%', r: isThumbnail ? '40%' : '42%' },
      style: { 
        fill: 'transparent', 
        stroke: 'rgba(150, 150, 150, 0.5)', 
        lineWidth: 2,
        shadowBlur: 10,
        shadowColor: 'rgba(0,0,0,0.3)'
      },
      silent: true,
      left: 'center',
      top: 'center',
      z: 10
    }],
    
    // 將實際的測試數據映射為熱圖座標點
    series: [{
      name: 'Wafer Map',
      type: 'heatmap',
      data: data.data.map((item: any) => [item[0] - 1, item[1] - 1, item[2]]),
      label: { show: false },
      itemStyle: { borderWidth: 0 }
    }]
  };
};

注意 isThumbnail 這個參數的小心機:同一個函數同時服務「批次概覽」的 25 張縮圖(關掉 Tooltip、縮小晶圓邊界)跟「晶圓詳情」的大圖,一魚兩吃。

4. 不只熱圖:統計分析視圖

工程師光看熱圖不夠,還要看分布。「統計分析」頁面用 ECharts 的 boxplot 畫出整個批次 25 片晶圓的參數變異,搭配趨勢圖跟線性回歸:

https://ithelp.ithome.com.tw/upload/images/20260807/20182549zBz7Lot3t4.png

▲ Dark Mode 統計分析:箱型圖 (Boxplot)、批次趨勢與回歸公式

箱型圖的資料是 Day 4 的 FastAPI 後端算好的(/api/stats/{lot_id} 回傳每片晶圓的五數概括),前端只負責畫——計算歸後端、渲染歸前端,這個分工是效能的關鍵。

series: [{
  name: 'Boxplot',
  type: 'boxplot',
  data: statsData.boxplot,  // 後端算好的 [min, Q1, median, Q3, max]
}]

上面截圖裡箱型圖旁邊那條紅色虛線(回歸線),是另一組圖表疊出來的——scatter(實際散點)加一條 line(回歸線),兩個 series 畫在同一個座標系上:

series: [
  {
    name: 'Wafer Mean',
    data: regressionData.means,
    type: 'scatter',
    symbolSize: 10,
    itemStyle: { color: 'var(--accent-color)' }
  },
  {
    name: 'Regression Line',
    data: regressionData.regression_line,
    type: 'line',
    lineStyle: { width: 3, type: 'dashed', color: '#f46d43' },
    symbol: 'none'
  }
]

type: 'scatter' 標出每片晶圓的實際平均值,type: 'line' 疊上後端(/api/regression/{lot_id})算好的回歸線,symbol: 'none' 讓這條線不要在每個資料點畫一個小圓點、乾淨地只顯示線段。ECharts 的 series 是陣列,本來就是設計成「疊圖」用的——散點加趨勢線、長條加折線這種混合圖表,全部都是同一個 series 陣列裡塞不同 type 而已,不用另外拉第二個圖表元件。

這個統計分析頁面另外還掛了 toolbox(縮放、還原、存成圖片)跟 dataZoom(滑鼠滾輪縮放 + 底部拖曳軸),批次一多、晶圓數量上百片時,使用者自己框選想看的區間,不用死盯著擠成一團的資料點。

5. 微動畫與設計美學 (UI/UX)

除了 ECharts 本身的渲染能力,前端還加了主題切換(Light/Dark Mode),搭配 Lucide Icons 做出有點科技感的側邊欄。上面兩張截圖就是 Dark Mode——切換主題其實只是改 body 的一個 class,所有圖表顏色透過 CSS 變數自動跟著走。

會花這個工不是為了炫技,而是我真心相信:一個讓 User 眼睛一亮的介面,使用率會差非常多。

6. 效能優化小撇步

  • Canvas 而非 DOM/SVG:ECharts 預設的 Canvas renderer 面對上千個 heatmap cell 依然順暢。
  • 大數據模式:遇到超過十萬點的資料時,可以用 large: truelargeThreshold,犧牲一點渲染特效換取流暢操作。
  • notMerge:切換晶圓時傳 notMerge={true} 給 echarts-for-react,避免新舊 option 合併造成殘影跟記憶體堆積(這個坑我踩過,畫面疊圖的時候還以為顯卡壞了 哭啊)。

7. 小結

到今天為止,三大異構主角(Java、Python、React)都到齊,也都住進了容器。明天來談安全:API Gateway 的路由設計與 JWT 統一認證,讓這些服務只對該開的門開放。


上一篇
[Day 4] 容器化實踐 (二):Python FastAPI 與 Delta Lake 的大數據容器配置 —— 在 Python 容器中處理大數據存儲與 ACID 特性。
下一篇
[Day 6] 安全防護:API Gateway 路由設計與 JWT 統一認證機制 —— 設計微服務的門戶,實現無狀態的安全認證。
系列文
從零到一:使用 K8S + GitOps 打造異構技術棧的資料分析平台19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言