打造我一直想要的 SSG:11ty、Vite 和 JAM 三明治

已發表: 2022-03-10
快速總結 ↬早在 2020 年 1 月,Ben Holmes 就開始著手做幾乎每個 Web 開發人員每年都會做的事情:重建他的個人網站。 在這篇文章中,他分享了他如何從絕對零開始構建自己的構建管道並創建 Slinkity 的故事。

我不了解你,但我已經被我們這些天擁有的所有 Web 開發工具所淹沒。 無論你喜歡 Markdown、純 HTML、React、Vue、Svelte、Pug 模板、Handlebars、Vibranium——你都可以將它與一些 CMS 數據混合起來,得到一個不錯的靜態站點雞尾酒。

我不會告訴你應該使用哪些 UI 開發工具,因為它們都很棒——取決於你項目的需要。 這篇文章是關於為任何場合尋找完美的靜態網站生成器; 讓我們可以使用 markdown 之類的無 JS 模板來啟動,並根據需要引入組件驅動的交互性“孤島”。

我在這裡將一年的學習經驗提煉成一篇文章。 我們不僅要討論代碼(又名 duct-taping 11ty 和 Vite 一起),而且我們還將探索為什麼這種方法對 Jamstackian 問題如此普遍。 我們會談到:

  • 靜態站點生成的兩種方法,以及我們為什麼要彌合差距;
  • Pug 和 Nunjucks 等模板語言仍然有用;
  • 當 React 或 Svelte 等組件框架應該發揮作用時;
  • Vite 的全新熱重載世界如何幫助我們以幾乎零配置將 JS 交互性帶入我們的 HTML;
  • 這如何補充 11ty 的數據級聯,將 CMS 數據帶入您可能需要的任何組件框架或 HTML 模板。

因此,事不宜遲,以下是我關於可怕的構建腳本、捆綁器突破和意大利麵條代碼管道磁帶的故事(最終)給了我我一直想要的 SSG:一個名為 Slinkity 的 11ty、Vite 和 Jam 三明治!

靜態站點生成的巨大差異

在深入研究之前,我想討論一下靜態站點生成中的兩個“陣營”。

在第一個陣營中,我們有“簡單”的靜態站點生成器。 這些工具不會帶來 JavaScript 包、單頁應用程序以及我們所期待的任何其他流行語。 他們只是確定了 Jamstack 的基礎:從您喜歡的任何 CMS 的 JSON blob 中提取數據,並將該數據滑入純 HTML 模板 + CSS。 Jekyll、Hugo 和 11ty 等工具在這個陣營中占主導地位,讓您將降價和流動文件的目錄變成一個功能齊全的網站。 主要優勢:

  • 學習曲線淺
    如果您了解 HTML,那麼您就可以開始了!
  • 快速構建時間
    我們不會處理任何復雜的事情,因此每條路線都可以快速構建。
  • 即時互動
    客戶端上沒有(或很少)要解析的 JavaScript。

現在在第二個陣營,我們有“動態”靜態站點生成器。 這些引入了 React、Vue 和 Svelte 等組件框架,為您的 Jamstack 帶來交互性。 這些實現了在構建時將 CMS 數據與您的站點路由相結合的相同核心承諾。 主要優勢:

  • 為交互性而生
    需要動畫圖像輪播? 多步驟形式? 只需添加 HTML、CSS 和 JS 的組件化塊。
  • 狀態管理
    Svelte 商店的 React Context 之類的東西允許在路由之間無縫共享數據。 例如,您的電子商務網站上的購物車。

這兩種方法都有明顯的優點。 但是,如果你從第一個陣營中選擇了一個 SSG,比如 Jekyll,只是在你的項目開始六個月後才意識到你需要一些組件式交互性怎麼辦? 或者你為那些強大的組件選擇了 NextJS 之類的東西,只是為了適應 React 的學習曲線,或者靜態博客文章中不必要的 JavaScript 知識庫?

在我看來,很少有項目完全適合一個陣營或另一個陣營。 它們存在於一個範圍內,隨著項目需求的發展,不斷地支持新的功能集。 那麼我們如何找到一個解決方案,讓我們第一個陣營的簡單工具開始,在需要時從第二個陣營逐漸添加功能?

好吧,讓我們來看看我的學習之旅。

注意:如果您已經購買了使用 11ty 構建靜態網站的靜態模板,請隨時跳到多汁的代碼演練。

跳躍後更多! 繼續往下看↓

從組件到模板和 Web API

早在 2020 年 1 月,我就著手做幾乎每個 Web 開發人員每年都會做的事情:重建我的個人網站。 但這一次會有所不同。 我挑戰自己,雙手綁在背後建立一個網站,不允許任何框架或構建管道!

作為一個 React 愛好者,這不是一件簡單的事情。 但是我昂首挺胸,開始從絕對零開始構建自己的構建管道。 我可以從我的個人網站的 v1 中分享很多寫得不好的代碼……但如果你這麼勇敢,我會讓你點擊這個自述文件。 相反,我想專注於我學到的更高層次的外賣,讓我自己餓死我的 JS 內疚的樂趣。

模板比您想像的要遠得多

我在這個項目中遇到了一個正在恢復的 JavaScript 垃圾。 我喜歡使用基於組件的框架來滿足一些與靜態站點相關的需求:

  1. 我們想將我的網站分解為可重用的 UI 組件,這些組件可以接受 JS 對像作為參數(也稱為“道具”)。
  2. 我們需要在構建時獲取一些信息以進入生產站點。
  3. 我們需要從文件目錄或內容的胖 JSON 對像生成一堆 URL 路由

列表取自我個人博客上的這篇文章。

但是您可能已經註意到……這些都不需要客戶端 JavaScript。 像 React 這樣的組件框架主要是為了處理狀態管理問題而構建的,比如 Facebook Web 應用程序首先激發了 React。 如果您只是將您的網站分解為一口大小的組件或設計系統元素,那麼像 Pug 之類的模板也可以很好地工作!

以這個導航欄為例。 在 Pug 中,我們可以定義一個“mixin”來接收數據作為 props:

 // nav-mixins.pug mixin NavBar(links) // pug's version of a for loop each link in links a(href=link.href) link.text

然後,我們可以在我們網站的任何地方應用該 mixin。

 // index.pug // kinda like an ESM "import" include nav-mixins.pug html body +NavBar(navLinksPassedByJS) main h1 Welcome to my pug playground

如果我們用一些數據“渲染”這個文件,我們將得到一個漂亮的index.html來提供給我們的用戶。

 const html = pug.render('/index.pug', { navLinksPassedByJS: [ { href: '/', text: 'Home' }, { href: '/adopt', text: 'Adopt a Pug' } ] }) // use the NodeJS filesystem helpers to write a file to our build await writeFile('build/index.html', html)

當然,這並沒有為你的 mixin 提供範圍 CSS 或你想要的有狀態的 JavaScript 之類的細節。 但它比 React 之類的東西有一些非常強大的好處:

  1. 我們不需要我們不了解的精美捆綁器。
    我們只是手動編寫了pug.render調用,並且我們已經準備好部署站點的第一條路線。
  2. 我們不會向最終用戶發送任何 JavaScript。
    使用 React 通常意味著發送一個大的 ole 運行時供人們的瀏覽器運行。 通過在構建時調用pug.render類的函數,我們將所有 JS 保留在我們身邊,同時在最後發送一個乾淨的.html文件。

這就是為什麼我認為模板是靜態網站的一個很好的“基礎”。 儘管如此,能夠接觸到我們真正從中受益的組件框架會很好。 稍後再談。

推薦閱讀:Zara Cooper如何使用 Pug 創建更好的 Angular 模板

您不需要框架來構建單頁應用程序

當我在做的時候,我還想在我的網站上進行一些性感的頁面轉換。 但是我們如何在沒有框架的情況下完成這樣的事情呢?

帶有垂直擦除過渡的交叉淡入淡出
帶有垂直擦除過渡的交叉淡入淡出。 (大預覽)

好吧,如果每個頁面都是它自己的.html文件,我們就不能這樣做。 當我們從一個 HTML 文件跳轉到另一個 HTML 文件時,整個瀏覽器都會刷新,所以我們不能有很好的淡入淡出效果(因為我們會簡單地將兩個頁面顯示在彼此的頂部)。

我們需要一種方法來“獲取”我們要導航到的任何地方的 HTML 和 CSS,並使用 JavaScript 將其動畫化到視圖中。 這聽起來像是單頁應用程序的工作! 為此,我使用了一個簡單的瀏覽器 API 組合:

  1. 使用事件偵聽器攔截所有鏈接點擊。
  2. fetch API 獲取您要訪問的任何頁面的所有資源,然後將我想要動畫化的部分抓取到視圖中:導航欄之外的內容(我希望在動畫期間保持靜止)。
  3. 網絡動畫 API 將新內容動畫化為關鍵幀。
  4. history API 使用window.history.pushState({}, 'new-route')更改瀏覽器 URL 欄中顯示的路由。 否則,看起來您從未離開過上一頁!

為清楚起見,這裡是使用簡單的查找和替換(源文章)的單頁應用程序概念的可視化說明:

逐步的客戶端路由過程: 1. 返回中等稀有漢堡包,2. 我們使用 fetch API 請求一個做得好的漢堡包,3. 我們按摩響應,4. 我們提取 'patty' 元素並應用它到我們的當前頁面。
逐步的客戶端路由過程: 1. 返回中等稀有漢堡包,2. 我們使用 fetch API 請求一個做得好的漢堡包,3. 我們按摩響應,4. 我們提取 'patty' 元素並應用它到我們的當前頁面。 (大預覽)

注意您也可以從我的個人網站訪問源代碼。

當然,React et al 和你選擇的動畫庫的一些配對可以做到這一點。 但是對於像淡入淡出過渡這樣簡單的用例……Web API 本身就非常強大。 如果您想在 Pug 或純 HTML 等靜態模板上實現更強大的頁面轉換,Swup 等庫將為您提供很好的服務。

11ty 帶來了什麼

在這一點上,我對我的小 SSG 感覺很好。 當然,它無法在構建時獲取任何 CMS 數據,並且不支持按頁面或按目錄的不同佈局,也沒有優化我的圖像,也沒有增量構建。

好吧,我可能需要一些幫助。

鑑於我從 v1 中學到的所有東西,我認為我有權放棄“無第三方構建管道”規則並使用現有工具。 事實證明,11ty 擁有我需要的功能寶庫!

  • 在構建時使用.11ydata.js文件獲取數據;
  • 來自_data文件夾的全局數據可用於我的所有模板;
  • 在開發過程中使用 browsersync 進行熱重載;
  • 支持精美的 HTML 轉換;
  • …以及無數其他好東西。

如果您嘗試過 Jekyll 或 Hugo 等簡單的 SSG,您應該對 11ty 的工作原理有一個很好的了解。 只有區別嗎? 11ty 完全使用 JavaScript。

11ty 基本上支持所有模板庫,因此很高興將我所有的 Pug 頁面呈現為.html路由。 它的佈局鏈接選項也有助於我的人造單頁應用程序設置。 我只需要一個用於所有路線的script ,以及一個“全局”佈局來導入該腳本:

 // _includes/base-layout.html <html> <body> <!--load every page's content between some body tags--> {{ content }} <!--and apply the script tag just below this--> <script src="main.js"></script> </body> </html> // random-blog-post.pug --- layout: base-layout --- article h2 Welcome to my blog p Have you heard the story of Darth Plagueis the Wise?

只要main.js完成了我們探索的所有鏈接攔截,我們就有了頁面轉換!

哦,還有數據級聯

所以 11ty 幫助清理了我從 v1 開始的所有意大利麵條代碼。 但它帶來了另一個重要的部分:一個乾淨的 API 來將數據加載到我的佈局中。 這是 Jamstack 方法的基礎。 除了使用 JavaScript + DOM 操作在瀏覽器中獲取數據之外,您還可以:

  1. 在構建時使用 Node.js 獲取數據。
    這可能是對某些外部 API 的調用、本地 JSON 或 YAML 導入,甚至是您網站上其他路由的內容(想像一下,每當添加新路由時都會更新目錄)。
  2. 將這些數據放入您的路線中。 回想一下我們之前編寫的.render函數:
 const html = pug.render('/index.pug', { navLinksPassedByJS: [ { href: '/', text: 'Home' }, { href: '/adopt', text: 'Adopt a Pug' } ] })

…但不是每次都用我們的數據調用pug.render ,我們讓 11ty 在幕後執行此操作。

當然,我的個人網站沒有太多數據。 但是為我所有的個人項目創建一個.yaml文件感覺很棒:

 # _data/works.yaml - title: Bits of Good Homepage hash: bog-homepage links: - href: https://bitsofgood.org text: Explore the live site - href: https://github.com/GTBitsOfGood/bog-web text: Scour the Svelt-ified codebase timeframe: May 2019 - present tags: - JAMstack - SvelteJS - title: Dolphin Audio Visualizer ...

並通過任何模板訪問該數據:

 // home.pug .project-carousel each work in works h3 #{title} p #{timeframe} each tag in tags ...

來自使用 create-react-app 的“客戶端渲染”世界,這是一個相當大的啟示。 不再向瀏覽器發送 API 密鑰或大型 JSON Blob。

我還為我網站的版本 1 添加了一些用於 JavaScript 獲取和動畫改進的好東西。 如果你很好奇,這裡是我的 README 所在的位置。

在這一點上我很高興,但有些東西不見了

我放棄了基於 JS 的組件並採用了模板(通過動畫頁面轉換來啟動),這讓我走得很遠。 但我知道這不會永遠滿足我的需求。 還記得我把我們踢開的那個巨大的鴻溝嗎? 好吧,在我的構建設置(堅定地在 #1 陣營)和 JS 化交互的天堂(Next、SvelteKit 和 #2 陣營的更多)之間顯然仍然存在溝壑。 說我要添加:

  • 帶有打開/關閉切換的彈出模式,
  • 一個基於組件的設計系統,如 Material UI,具有範圍樣式,
  • 一個複雜的多步形式,可能由狀態機驅動。

如果您是純 JS 純粹主義者,那麼您可能對所有這些用例都有無框架的答案。 但是 JQuery 不再是常態是有原因的! 創建離散的、易於閱讀的 HTML 組件、作用域樣式和 JavaScript“狀態”變量片段具有吸引力。 React、Vue、Svelte 等為調試和測試提供瞭如此多的細節,以至於直接的 DOM 操作無法完全匹配。

所以這是我的百萬美元問題:

我們可以使用直接的 HTML 模板開始,然後在我們想要的地方逐步添加 React/Vue/Svelte 組件嗎?

答案是肯定的。 讓我們試試看。

11ty + Vite:天作之合️

這是我在這裡想像的夢想。 無論我想在哪裡插入一些交互的東西,我都想在我的模板中留下一個小標誌來“把 X React 組件放在這裡”。 這可能是 11ty 支持的簡碼語法:

 # Super interesting programming tutorial Writing paragraphs has been fun, but that's no way to learn. Time for an interactive code example! {% react './components/FancyLiveDemo.jsx' %}

但請記住,一體式 11ty(故意)避免:一種捆綁所有 JavaScript 的方法。 來自捆綁的 OG 公會,您的大腦可能會跳到在這裡構建 Webpack、Rollup 或 Babel 進程。 構建一個大的 ole 入口點文件,並輸出一些漂亮的優化代碼,對嗎?

好吧,是的,但這可能會涉及很多。 例如,如果我們使用 React 組件,我們可能需要一些 JSX 加載器,一個用於轉換所有內容的精美 Babel 進程,一個用於 SASS 和 CSS 模塊導入的解釋器,幫助實時重新加載的東西等等。

如果有一個工具可以查看我們的.jsx文件並確切地知道如何處理它們。

輸入:維特

Vite 最近一直是全城的話題。 它旨在成為使用 JavaScript 構建幾乎任何東西的多合一工具。 這是您在家嘗試的示例。 讓我們在機器上的某個地方創建一個空目錄並安裝一些依賴項:

 npm init -y # Make a new package.json with defaults set npm i vite react react-dom # Grab Vite + some dependencies to use React

現在,我們可以創建一個index.html文件作為我們應用程序的“入口點”。 我們將保持非常簡單:

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h1>Hello Vite! (wait is it pronounced "veet" or "vight"...)</h1> <div></div> </body> </html>

唯一有趣的是中間的div id="root" 。 這將是我們的 React 組件的根!

如果您願意,您可以啟動 Vite 服務器以在瀏覽器中查看我們的純 HTML 文件。 只需運行vite (或npx vite ,如果您的終端中沒有配置該命令),您將看到以下有用的輸出:

 vite vX.XX dev server running at: > Local: http://localhost:3000/ > Network: use `--host` to expose ready in Xms.

就像 Browsersync 或其他流行的開發服務器一樣,每個.html文件的名稱對應於我們服務器上的一個路由。 所以如果我們將index.html重命名為about.html ,我們將訪問http://localhost:3000/about/ (是的,你需要一個斜杠!)

現在讓我們做一些有趣的事情。 除了那個index.html文件,添加某種基本的 React 組件。 我們將在這裡使用 React 的useState來演示交互性:

 // TimesWeMispronouncedVite.jsx import React from 'react' export default function TimesWeMispronouncedVite() { const [count, setCount] = React.useState(0) return ( <div> <p>I've said Vite wrong {count} times today</p> <button onClick={() => setCount(count + 1)}>Add one</button> </div> ) }

現在,讓我們將該組件加載到我們的頁面上。 這就是我們必須添加到我們的index.html的全部內容:

 <!DOCTYPE html> ... <body> <h1>Hello Vite! (wait is it pronounced "veet" or "vight"...)</h1> <div></div> <!--Don't forget type="module"! This lets us use ES import syntax in the browser--> <script type="module"> // path to our component. Note we still use .jsx here! import Component from './TimesWeMispronouncedVite.jsx'; import React from 'react'; import ReactDOM from 'react-dom'; const componentRoot = document.getElementById('root'); ReactDOM.render(React.createElement(Component), componentRoot); </script> </body> </html>

是的,就是這樣。 無需自己將我們的.jsx文件轉換為瀏覽器就緒的.js文件! 無論 Vite 在哪裡看到.jsx導入,它都會自動將該文件轉換為瀏覽器可以理解的內容。 在開發中工作時甚至沒有distbuild文件夾; Vite 動態處理所有內容——每次我們保存更改時都會重新加載熱模塊。

好的,所以我們有一個非常強大的構建工具。 我們怎樣才能把它帶到我們的 11ty 模板中?

與 11ty 一起運行 Vite

在我們開始討論好東西之前,讓我們討論一下並排運行 11ty 和 Vite。 繼續將 11ty 作為開發依賴項安裝到上一節中的同一項目目錄中:

 npm i -D @11ty/eleventy # yes, it really is 11ty twice

現在讓我們做一點飛行前檢查,看看 11ty 是否正常工作。 為避免混淆,我建議您:

  1. 從之前刪除那個index.html文件;
  2. TimesWeMispronouncedVite.jsx移動到新目錄中。 說, components/
  3. 為我們的網站創建一個src文件夾;
  4. 將模板添加到該src目錄以供 11ty 處理。

例如,包含以下內容的blog-post.md文件:

 # Hello world! It's markdown here

您的項目結構應如下所示:

 src/ blog-post.md components/ TimesWeMispronouncedVite.jsx

現在,從終端運行 11ty,如下所示:

 npx eleventy --input=src

如果一切順利,您應該會看到如下構建輸出:

 _site/ blog-post/ index.html

其中_site是我們的默認輸出目錄,而blog-post/index.html是我們為瀏覽而精心轉換的 markdown 文件。

通常,我們會運行npx eleventy --serve來啟動開發服務器並訪問該/blog-post頁面。 但是我們現在正在使用 Vite 作為我們的開發服務器! 這裡的目標是:

  1. 讓 110 構建我們的 markdown、Pug、nunjucks _site目錄。
  2. 將 Vite 指向同一個_site目錄,這樣它就可以處理 React 組件、花哨的樣式導入以及 11ty 沒有處理的其他內容。

所以這是一個兩步構建過程,11ty 交付了 Vite。 這是你需要在“watch”模式下同時啟動 11ty 和 Vite 的 CLI 命令:

 (npx eleventy --input=src --watch) & npx vite _site

您還可以在兩個單獨的終端中運行這些命令,以便於調試。

運氣好的話,您應該能夠訪問http://localhost:3000/blog-post/ (同樣,不要忘記尾部的斜杠!)以查看已處理的 Markdown 文件。

使用簡碼進行部分補水

讓我們簡要介紹一下短代碼。 是時候重新審視之前的語法了:

 {% react '/components/TimesWeMispronouncedVite.jsx' %}

對於那些不熟悉短代碼的人:它們與函數調用大致相同,函數返回一個 HTML 字符串以滑入您的頁面。 我們的簡碼的“解剖”是:

  • {% … %}
    包裝器表示短代碼的開始和結束。
  • react
    我們稍後將配置的短代碼函數的名稱。
  • '/components/TimesWeMispronouncedVite.jsx'
    我們的簡碼函數的第一個(也是唯一一個)參數。 你可以有盡可能多的參數。

讓我們連接我們的第一個短代碼! 將.eleventy.js文件添加到項目的基礎中,並為我們的react短代碼添加此配置條目:

 // .eleventy.js, at the base of the project module.exports = function(eleventyConfig) { eleventyConfig.addShortcode('react', function(componentPath) { // return any valid HTML to insert return `<div>This is where we'll import ${componentPath}</div>` }) return { dir: { // so we don't have to write `--input=src` in our terminal every time! input: 'src', } } }

現在,讓我們用我們的新短代碼為我們的blog-post.md 。 將此內容粘貼到我們的降價文件中:

 # Super interesting programming tutorial Writing paragraphs has been fun, but that's no way to learn. Time for an interactive code example! {% react '/components/TimesWeMispronouncedVite.jsx' %}

如果你運行一個快速的npx eleventy ,你應該在/blog-post/index.html下的_site目錄中看到這個輸出:

 <h1>Super interesting programming tutorial</h1> <p>Writing paragraphs has been fun, but that's no way to learn. Time for an interactive code example!</p> <div>This is where we'll import /components/TimesWeMispronouncedVite.jsx</div>

編寫我們的組件簡碼

現在讓我們用那個短代碼做一些有用的事情。 還記得我們在試用 Vite 時編寫的script標籤嗎? 好吧,我們可以在我們的簡碼中做同樣的事情! 這次我們將使用componentPath參數來生成導入,但其餘部分幾乎相同:

 // .eleventy.js module.exports = function(eleventyConfig) { let idCounter = 0; // copy all our /components to the output directory // so Vite can find them. Very important step! eleventyConfig.addPassthroughCopy('components') eleventyConfig.addShortcode('react', function (componentPath) { // we'll use idCounter to generate unique IDs for each "root" div // this lets us use multiple components / shortcodes on the same page idCounter += 1; const componentRootId = `component-root-${idCounter}` return ` <div></div> <script type="module"> // use JSON.stringify to // 1) wrap our componentPath in quotes // 2) strip any invalid characters. Probably a non-issue, but good to be cautious! import Component from ${JSON.stringify(componentPath)}; import React from 'react'; import ReactDOM from 'react-dom'; const componentRoot = document.getElementById('${componentRootId}'); ReactDOM.render(React.createElement(Component), componentRoot); </script> ` }) eleventyConfig.on('beforeBuild', function () { // reset the counter for each new build // otherwise, it'll count up higher and higher on every live reload idCounter = 0; }) return { dir: { input: 'src', } } }

現在,調用我們的短代碼(例如{% react '/components/TimesWeMispronouncedVite.jsx' %} )應該輸出如下內容:

 <div></div> <script type="module"> import Component from './components/FancyLiveDemo.jsx'; import React from 'react'; import ReactDOM from 'react-dom'; const componentRoot = document.getElementById('component-root-1'); ReactDOM.render(React.createElement(Component), componentRoot); </script>

使用(npx eleventy --watch) & vite _site訪問我們的開發服務器,我們應該會找到一個精美的可點擊計數器元素。

流行語警報——部分水化和島嶼建築

我們只是以最簡單的形式展示了“島嶼架構”。 這就是我們的交互式組件樹不必消耗整個網站的想法。 相反,我們可以在整個應用程序中根據我們實際需要交互性的位置來創建迷你樹或“島嶼”。 有一個基本的鏈接登陸頁面,沒有任何狀態需要管理? 偉大的! 不需要交互式組件。 但是你有一個可以從 X React 庫中受益的多步驟表單嗎? 沒問題。 使用類似react短代碼的技術來啟動Form.jsx島。

這與“部分補水”的想法密切相關。 如果您使用像 NextJS 或 Gatsby 這樣的組件式 SSG,您可能已經聽說過“水合作用”這個詞。 簡而言之,這是一種方法:

  1. 首先將您的組件渲染為靜態 HTML。
    這使用戶在最初訪問您的網站時可以查看一些內容。
  2. 用交互性“水合”這個 HTML。
    這是我們連接狀態鉤子和渲染器的地方,好吧,讓按鈕點擊真正觸發一些東西。

這種 1-2 的衝擊使得 JS 驅動的框架對於靜態站點是可行的。 只要用戶在你的 JavaScript 解析完成之前有一些東西要查看,你就會在這些燈塔指標上獲得不錯的分數。

好吧,直到你不這樣做。 “水合”整個網站可能會很昂貴,因為您需要準備好處理每個最後一個 DOM 元素的 JavaScript 包。 但是我們的雜亂無章的短代碼技術並沒有覆蓋整個頁面! 相反,我們“部分地”水合那裡的內容,僅在必要時插入組件。

別擔心,所有這些都有一個插件:Slinkity

讓我們回顧一下我們在這裡發現的內容:

  1. Vite 是一個非常強大的打包工具,無需額外配置即可處理大多數文件類型( jsxvuesvelte等等)。
  2. 簡碼是一種將 HTML 塊插入到我們的模板中的簡單方法,組件樣式。
  3. 我們可以使用簡碼在任何我們想要使用部分水合的地方渲染動態的、交互式的 JS 包。

那麼優化的生產構建呢? 正確加載作用域樣式? 哎呀,使用.jsx創建整個頁面? 好吧,我已經將所有這些(以及更多!)捆綁到一個名為 Slinkity 的項目中。 我很高興看到社區對該項目的熱情接待,親愛的讀者,我希望你自己來試一試!

試試快速入門指南

Astro的也很棒

著眼於尖端技術的讀者現在可能至少想到過一次 Astro。 而且我不能怪你! 它的構建考慮了一個非常相似的目標:從純 HTML 開始,在需要的地方插入有狀態的組件。 哎呀,他們甚至可以讓你開始Vue 中編寫 React 組件或HTML 模板文件中編寫 Svelte 組件! 這就像 MDX Xtreme 版。

但是,他們的方法有一個相當大的成本:您需要從頭開始重寫您的應用程序。 這意味著一種基於 JSX 的新模板格式(您可能不習慣),一個全新的數據管道,目前缺少一些細節,以及解決問題時的一般錯誤。

但是使用 Slinkity 之類的工具來調製 11ty + Vite 雞尾酒? 好吧,如果您已經有一個 11ty 站點,Vite 應該無需任何重寫就可以安裝到位,並且短代碼應該涵蓋許多與.astro文件相同的用例。 我承認它現在遠非完美。 但是,嘿,到目前為止它一直很有用,如果您想避免在站點範圍內重寫,我認為這是一個非常強大的選擇!

包起來

到目前為止,這個 Slinkity 實驗已經很好地滿足了我的需求(你們中的一些人也是如此!)。 隨意使用適用於您的 JAM 的任何堆棧。 我很高興能分享我一年的構建工具放蕩的結果,我很高興看到我們如何彌合巨大的 Jamstack 鴻溝。

延伸閱讀

想要更深入地研究部分水合作用、ESM 或 SSG? 檢查這些:

  • 島嶼建築
    Jason Format 的這篇博文真正開啟了關於 Web 開發中“孤島”和“部分水化”的討論。 它充滿了有用的圖表和這個想法背後的哲學。
  • 使用定制的靜態站點生成器簡化您的靜態
    另一篇 SmashingMag 文章將引導您從頭開始製作基於節點的網站構建器。 這對我來說是一個巨大的啟發!
  • ES Modules 如何重新定義 Web 開發
    一篇關於 ES Modules 如何改變 Web 開發遊戲的個人帖子。 這進一步探討了網絡上導入語法的“當時和現在”。
  • Web組件簡介
    關於什麼是 Web 組件、影子 DOM 的工作原理以及 Web 組件在哪些方面被證明有用的出色演練。 使用本指南將自定義組件應用於我自己的框架!