慣性聚合 高效追蹤和閱讀你感興趣的部落格、新聞、科技資訊
閱讀原文 在慣性聚合中打開

推薦訂閱源

博客园 - 司徒正美
V
V2EX
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
月光博客
月光博客
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python)
格式化顯示還是不夠。我建立了一個帶有真正表格視圖的 JSON & XML 閱覽器。
Aleksandre M · 2026-05-24 · via DEV Community

我所嘗試過的每一個JSON檢視器都做相同的事:它美化輸出。增加縮排、著色鍵、摺疊括號。對於10行的配置有用 — 對於開發者每天處理的實際數據結構則沒用,那是一個物件的陣列

一個美化輸出的50行API響應仍然是你上下滾動500行的內容。你不能說__JHSNS_SEG_e984f6ba_4__"只顯示管理員" 不需要重新讀取每一條記錄。您無法按日期排序。您無法跨行搜尋.

這不是查看器問題 — 這是格式錯誤。物件的陣列想要成為 表格

┌────┬───────┬───────┬────────┐
│ ID │ NAME  │ ROLE  │ ACTIVE │
├────┼───────┼───────┼────────┤
│ 1  │ Alice │ admin │ ● Yes  │
│ 2  │ Bob   │ user  │ ● Yes  │
│ 3  │ Carol │ user  │ ● No   │
│ 4  │ Dave  │ admin │ ● Yes  │
└────┴───────┴───────┴────────┘

進入全螢幕模式 退出全螢幕模式

可排序的欄位。可搜尋的列。點擊任何列以展開完整的嵌套詳情。相同數據 — 但您掃描 它而不是 讀取 它.

所以我建立了 prettyjsonxml.com — 一個 JSON 和 XML 查看器,將陣列轉換為真正的表格(還有可摺疊的樹狀視圖、格式化、最小化、base64 圖片預覽)。一個 HTML 檔案。沒有後端。沒有建構步驟。完全在瀏覽器中運行;你的數據永遠不會離開你的電腦。

我沒有預期的:讓表格視圖在 9 MB API 响應上實際變得快速,比看起來的難得多。這裡是未經編輯的故事.

天真的版本:有效,然後無效

V1 是直接的:

const data = JSON.parse(text);
renderTable(data);

function renderTable(items) {
  const tbody = document.querySelector('tbody');
  items.forEach(row => {
    const tr = document.createElement('tr');
    // ... build cells
    tbody.appendChild(tr);
  });
}

進入全屏模式 退出全屏模式

對我測試的 5 行示例看起來很漂亮。然後我粘貼了一個真正的 API 响應。瀏覽器凍結了1.5秒.

凍結有兩個來源.

  1. JSON.parse在9 MB的區塊上凍結主線程約500毫秒.
  2. 創建30,000 × 2列(主 + 詳細)= 60,000個DOM節點凍結約1500毫秒.

你無法讓它們變得快,但你可以讓它們不凍結UI.

第一階段:content-visibility: auto — 單行獲勝

如果告訴瀏覽器可以,現代瀏覽器會跳過對屏幕外內容的佈局。一條CSS規則:

.data-table tr.row-main {
  content-visibility: auto;
  contain-intrinsic-size: auto 40px;
}

進入全螢幕模式 退出全螢幕模式

content-visibility: auto 告訴瀏覽器:"在這個元素即將滑入視野之前,不必煩惱計算它的佈局。" contain-intrinsic-size 提供了一個暫存的身高,以便捲動條仍然代表整個文件.

總渲染時間沒有改變 — 工作仍然進行 — 但 感知效能提升了,因為瀏覽器可以優先繪製可見部分。非常少使用。適用於約95%的現代瀏覽器.

第二階段:使用 Web Workers 進行 JSON.parse — 這個反直覺的教訓

下一次凍結是JSON.parse本身。傳統智慧:用 Web Worker 將昂貴的解析作業移離主執行緒。

const worker = new Worker(URL.createObjectURL(new Blob([`
  self.onmessage = (e) => {
    const parsed = JSON.parse(e.data);
    self.postMessage(parsed);
  };
`], { type: 'application/javascript' })));

worker.postMessage(largeJsonString);
worker.onmessage = (e) => render(e.data);

進入全螢幕模式 離開全螢幕模式

完成。主執行緒保持反應靈活。對吧?

實際上感覺更慢了。

原因如下:當工作執行緒透過回傳解析過的物件時postMessage,主執行緒必須 結構化克隆 整個物件圖才能接收它。對於一個包含30,000個物件的陣列,這個克隆需要300–500毫秒 — 同樣也在主執行緒

所以我就成功地將500毫秒的 JSON.parse 移到了主執行緒之外,並增加了400毫秒的結構化克隆。淨收益:約100毫秒。而使用者感知到的是卡頓稍後 在流程中,在他們點擊預期能立即得到結果的按鈕之後.

Web Workers 是用於 CPU 繫結工作,其結果不必回傳. 當 95% 的工作量是解析後的物件本身時,結構複製的成本會主導收益。

我保留了用於格式化/最小化的工人(輸出是字串,複製成本低廉)。對於解析然後渲染的流程,它幾乎沒有產生淨收益。真正的修復在其他地方.

第三階段:虛擬滾動 — 實際的修復

對於有30,000行的表格,你不需要渲染30,000行。你只需要渲染用戶能看到的約50行,並在用戶滾動時替換它們.

<table> 的陷阱在於:你不能position: absolute 列 (表格佈局不允許這樣)。改用 spacer 列

<table>
  <thead>...</thead>
  <tbody>
    <tr style="height:850px"></tr>   <!-- spacer for rows above viewport -->
    <tr>row 21</tr>
    <tr>row 22</tr>
    ...
    <tr>row 70</tr>
    <tr style="height:1200px"></tr>  <!-- spacer for rows below viewport -->
  </tbody>
</table>

Enter fullscreen mode Exit fullscreen mode

在每次滾動事件中,重新計算哪些列是可見的並替換它們:

function onScroll() {
  const tbodyRect = tbody.getBoundingClientRect();
  const viewTop = Math.max(0, -tbodyRect.top);
  const viewBottom = viewTop + scrollContainer.clientHeight;

  const startRow = Math.max(0, Math.floor(viewTop / ROW_HEIGHT) - 10);
  const endRow   = Math.min(items.length, Math.ceil(viewBottom / ROW_HEIGHT) + 10);

  // Remove old visible rows, build new ones from items[startRow..endRow]
  // Adjust spacer heights so scrollbar position stays correct
}

Enter fullscreen mode Exit fullscreen mode

對於一個30,000行的JSON陣列,這從"完全無反應的tab"變為"順滑的60 fps滾動。"即使在更謙卑的尺寸下——比如幾百行——優勢在於搜尋和排序變得即時,因為它們現在運作在JavaScript陣列上,而不是通過遍歷千個DOM節點。

我花費20分鐘解決的bug:我的虛擬滾動器聆聽到window.scroll。但我的頁面有body { overflow: hidden }<main> { overflow: auto } — 所以window.scroll 從未發生。實際的滾動事件來自<main>

// Walk up to find the nearest ancestor that actually scrolls
function findScrollContainer(el) {
  let p = el.parentElement;
  while (p && p !== document.body) {
    const oy = getComputedStyle(p).overflowY;
    if ((oy === 'auto' || oy === 'scroll') && p.scrollHeight > p.clientHeight) return p;
    p = p.parentElement;
  }
  return window;
}

Enter 全螢幕模式 Exit 全螢幕模式

在執行時總是解析滾動容器。不要假設它是window

textarea 捕獲

一個我沒預期的凍結:將一個 9 MB 的字串指定給<textarea>.value它自己會阻擋主執行緒約300–500毫秒。即使大部分的文本內容在滾動視窗之外,瀏覽器也必須計算其佈局。

修復:為了檔案>5 MB,請將文本區域清空,並顯示一個帶有樣式的「載入狀態」面板。

if (file.size > 5 * 1024 * 1024) {
  editor.value = '';
  showLoadedOverlay(file.name, file.size);
} else {
  editor.value = text;
}

進入全螢幕模式 退出全螢幕模式

數據仍然可以解析並呈現在檢視器中 — 只是沒有在可編輯的文本區域裡。對於9 MB的文件,沒有人還想手動編輯的。

我會做不同的改變

  • 從虛擬滾動開始。 不要把它作為第三階段添加。它是唯一可以擴展的東西。其他一切都是修飾。
  • 質疑「把它移到Worker」的直覺。 Worker 非常適合計算,你不需要回傳。不適合 parse-then-clone 的流程.
  • 在適用的地方使用 content-visibility: auto. 基本上免費.
  • 萬一用實際的生產數據測試. 我的 5 行測試案例隱藏了所有有趣的 bug.

單文件的事情

我一直在跟自己爭論是否要加上一個編譯步驟。"直接打包就好了,直接拆分成模組,直接加 TypeScript…" 那個原型的技術上更簡潔,但實質上更糟糕 — 現在我必須托管更多文件,擔心緩存破壞,維護一套工具鏈.

對於一個主張是"100% 在你的瀏覽器中,沒有伺服器"的工具,把它作為一個你可以Save As 和離線運行是正確的產品決策。實用性優先於純粹性。

最終:~225 KB 單個 HTML,無依賴,無建構,直接從 Cloudflare Pages 提供服務。


如果你想要嘗試它:prettyjsonxml.com — 貼上任何 JSON 或 XML,以可排序表格或可摺疊樹狀視圖查看。我建構它是因為我需要它。分享它是因為或許你也需要。

你為大數據用戶介面使用過哪些性能技巧?總是好奇那些令人驚訝的技巧。