文件

Listener 系統

_Listener({ svg, lazyload }) 如何建立全域的 IntersectionObserver,用於圖片延遲載入與 SVG 內嵌。

進入點

window._Listenersrc/prototype/window.ts)是唯一的進入點。無參數呼叫會同時啟用兩個 observer;傳入物件時則各自獨立控制——target.svg 決定是否執行 setLazyloadListener()target.lazyload 決定是否執行 setSvgListener()。兩個設定函式(src/listener/LazyloadListener.tssrc/listener/SVGListener.ts)皆具冪等性:各自檢查模組層級的 lazyloadListenersvgListener 旗標,已初始化則直接返回,因此多次呼叫 _Listener是安全的。

RJS 建構子(src/model/RJS.ts)在每次實例化時也會呼叫 $window._Listener(body[listener] || {}),因此任何使用 RJS 的頁面都會依該實例的 listener 選項設定監聽器。

graph TB
    subgraph Listener
        Init["_Listener(target)"] --> SVGGate{"target.lazyload?"}
        Init --> LazyGate{"target.svg?"}
        SVGGate -->|true| SVGObs["setSvgListener(): new IntersectionObserver"]
        LazyGate -->|true| LazyObs["setLazyloadListener(): new IntersectionObserver"]
        LazyObs --> LoadImg["img[lazyload] 進入視窗 → fetch 並替換 src"]
        SVGObs --> ReplaceSVG["temp-svg[src] 進入視窗 → fetch 並內嵌 <svg>"]
    end

Lazyload Observer

setLazyloadListener() 建立一個 IntersectionObserver,並立即附加至 document.body 中所有既有的 img[lazyload]:not([lazyload=''])。透過 "img"._({ lazyload: url })(見 API 參考)新建立的 img 元素,只要該 observer 已存在,會在建立當下就註冊至同一個 observer。

當延遲載入的圖片進入視窗範圍時:

  1. observer 立即取消監聽該元素(一次性)。
  2. 讀取 lazyload 屬性作為真正的圖片 URL。
  3. url.$$200(true) fetch 該 URL,並解析出圖片的 object-URL blob。
  4. 成功時將 img.src 設為解析出的 blob URL;失敗時 img.src 會 fallback 為內建的「圖片無法顯示」inline SVG data URI(src/data.ts 中的 photo404)。
  5. 無論成功或失敗都會移除 lazyload 屬性。

SVG 內嵌 Observer

setSvgListener() 建立第二個 IntersectionObserver,附加至所有既有的 temp-svg[src]:not([src='']) 元素。當符合的元素進入視窗範圍時:

  1. src.$$200() fetch 該 SVG URL(不強制轉換為圖片 blob,因為 SVG 回應以文字方式處理)。
  2. 將取得的文字解析至一個未掛載的 <div>,並取出其第一個子節點(<svg> 元素)。
  3. 新的 <svg> 會繼承佔位元素的 idclassList 中的每個 class,以及 onclick handler。
  4. <svg> 會被插入至佔位元素之前,佔位元素隨後被移除。
  5. 任一步驟失敗(fetch 或解析)時,佔位元素的 innerHTML 會被替換為

RJS 渲染過程中的重入註冊

RJS.ts#SET_DOM 在走訪 DOM 樹時,也會就地觀察新建立的子元素:任何帶有 lazyload class 的子節點會註冊至 lazyloadObserver,任何帶有 svg class 且 tagNameSPAN 的子節點會註冊至 svgObserver——這使得 :for 迴圈或 :path 載入片段所產生的元素,無需為每個片段額外呼叫 _Listener() 即可參與 lazyload/SVG 內嵌。

EN