cover

軟體工程的歷史就是一部「痛點驅動」的演進史。每一個新工具的誕生,幾乎都是因為上一代方案有著讓人無法忍受的問題。

先講結論

很多人學技術只學「怎麼用」,卻不問「為什麼會有這個東西」。結果面對技術選型毫無判斷力——什麼火用什麼。理解了演進鏈,你就能明白每個技術「解決了什麼」和「犧牲了什麼」。這篇是系列的第一篇,講 DOM 操作和前端架構的演進。

系列文章:本篇:DOM 與架構非同步、CSS 與狀態管理API 設計與部署

DOM 操作的演進:原生 JS → jQuery → 框架

2005 年的黑暗歲月

如果你在 2005 年寫前端,光是選取一個 class 為 “item” 的元素就要寫這種東西:

var items = [];
var allElements = document.getElementsByTagName('*');
for (var i = 0; i < allElements.length; i++) {
  if (allElements[i].className === 'item') {
    items.push(allElements[i]);
  }
}

因為 IE6 不支援 getElementsByClassName。而且每個瀏覽器的 DOM API 都不一樣——IE 用 attachEvent,其他用 addEventListener。CSS 的 opacity 在 IE 要用 filter: alpha(opacity=50)

前端工程師的工時不是花在功能開發,而是花在「讓它在 IE6 上也能跑」。這不是工程,這是苦行。

jQuery:救世主降臨(2006)

jQuery 把所有瀏覽器差異包裝起來,提供統一 API:

$('.item').addClass('active').fadeIn(300);
$.get('/api/users', function(data) { $('#user-list').html(data); });
$('#btn').on('click', function() { $(this).toggleClass('active'); });

鏈式呼叫、跨瀏覽器相容、選擇器引擎——jQuery 在鼎盛時期被 70% 以上的網站使用。它不是框架,它是基礎設施。

jQuery 的崩壞:spaghetti code

但當 Web App 變複雜,jQuery 的問題就暴露了:

$('#search-btn').on('click', function() {
  $.get('/api/products?q=' + $('#search-input').val(), function(data) {
    $('#product-list').empty();
    data.forEach(function(product) {
      $('#product-list').append('<div>' + product.name + '</div>');
    });
    // DOM 是新建的,事件要重新綁
    $('.add-to-cart').off('click').on('click', function() {
      addToCart($(this).data('id'));
      updateCartCount();
      // ...更多手動 DOM 操作
    });
  });
});

資料邏輯、DOM 操作、事件處理全部攪在一起。沒有元件概念、沒有資料流、手動保持畫面和資料同步。當 App 長到一定規模,沒有人搞得清楚哪段 code 改了哪段 DOM。

框架的回應:讓 UI 和資料自動同步

2010 年代初,三個框架幾乎同時出現:

Angular(2010) 用 two-way data binding——資料改了畫面自動更新。聽起來美好,但雙向綁定在複雜應用中會導致你不知道是誰改了什麼值。

React(2013) 完全不同的思路:Virtual DOM + 單向資料流。你告訴它「畫面該長什麼樣」,它算出最小的 DOM 更新。Component 的概念讓 UI 變成可重用的積木。

Vue(2014) 走漸進式路線——學習曲線比 Angular 平緩,比 React 更接近傳統 HTML。

最終業界達成共識:Component-based architecture 是正確方向。 這個思維至今仍然主宰整個前端生態。

前端架構的演進:MPA → SPA → SSR → Islands

MPA:最初的模樣

Web 最初全是 MPA(Multi-Page Application)。點連結 → 伺服器渲染 HTML → 整頁重載。SEO 天然好、邏輯簡單。但體驗?每次操作都白閃一下,狀態丟失。

2004 年 Gmail 展示了不用整頁重載就能操作郵件的體驗,人們意識到:Web 可以像原生 App 一樣流暢。

SPA:前端的黃金年代

隨著 React/Angular 普及,SPA 爆發。整個 App 一個 HTML 頁面,路由切換和畫面更新全在客戶端,資料透過 API 取得。

使用者體驗絲滑了,但新問題來了:

  • SEO 爆死:搜尋引擎看到的只有 <div id="root"></div>
  • 首屏白到天荒地老:使用者要先下載一大包 JS,等它跑完畫面才出來
  • 所有渲染工作都推給客戶端,伺服器的算力浪費了

SSR:回到伺服器,帶著新武器

Next.js(2016)代表業界的反思:第一次請求由伺服器渲染好 HTML(解決 SEO 和首屏問題),然後客戶端 hydrate(保留 SPA 流暢體驗)。

代價?伺服器負擔增加、部署變複雜、Hydration 本身也有效能問題——瀏覽器要重新跑一遍 JS 來「啟動」已經渲染好的 HTML。

Islands:只 hydrate 需要互動的部分

Astro(2021)更激進:大部分內容保持純 HTML,只有需要互動的部分(搜尋框、動態表單)才載入 JS。一個內容網站可能只需要幾 KB 的 JS,而不是幾百 KB。

每一次架構演進,都是在效能、互動性、開發者體驗之間尋找新的平衡點。 沒有哪個方案是萬能的——選擇取決於你的產品需求。


jQuery 解決了瀏覽器不一致,React 解決了 UI 同步,Next.js 解決了 SSR。每一代都站在上一代的肩膀上——同時也創造了新的問題留給下一代。


延伸閱讀