iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 26
1
Modern Web

從 0 開始建一個 Static Site Generator系列 第 26

Day 26: 載入圖片

這系列的程式碼在 https://github.com/DanSnow/ironman-2020/tree/master/static-site-generator

這篇用到的圖片來源,授權是 CC0 ,作者是 Snapwire

圖片是網頁上一個很重要的元素, Gatsby 也提供了個跟它的 GraphQL 查詢搭配的圖片最佳化,不過老實說那個有點複雜,我並沒有打算要做一個完全一樣的,這邊就來做一個比較簡單的實做,我們提供圖片的最佳化與 lazy loading 就好

圖片最佳化

這部份會使用到 imagemin 這個 Node.js 下常用的圖片最佳化的套件,不過是以 webpack loader 的形式來使用的,所以就安裝:

$ yarn add --dev image-webpack-loader

然後我們要把它再包裝成自己的 webpack loader ,這樣我們比較好加自己額外的資訊進去,不過這邊其實只有網址而已

這邊寫一個很簡單的 webpack loader

module.exports = function () {
  return `
  import url from '!!${require.resolve('file-loader')}!${require.resolve('image-webpack-loader')}!${this.resource}'

  export default { url }`
}

module.exports.raw = true

再來我們要解決在 Server 端載入圖片的問題,我們寫個程式去 hook Node.js 的 require

import Module from 'module'

export function patchRequire() {
  // 保存原本的 require
  const originalRequire = Module.prototype.require

  // 覆寫 require
  Module.prototype.require = function (id) {
    // 圖片的地方我是用 webpack alias 來載入的,所以判斷是不是 ~ 開頭,不過正常應該是要判斷副檔名
    if (id.startsWith('~')) {
      return {}
    }
    return originalRequire.call(this, id)
  }
}

這樣在 Server 端載入使用者的程式才不會出錯

lazy loading

這邊用的是很簡單的實作:

import React, { useEffect, useState } from 'react'

export function Image({ img }) {
  // server 端就直接回傳
  if (!img.url) {
    return <div>Loading...</div>
  }

  const { url } = img
  const [ready, setReady] = useState(false)

  useEffect(() => {
    // 等待圖片載完
    const img = document.createElement('img')
    img.addEventListener('load', () => setReady(true))
    img.src = url
  }, [url])

  // 圖片還沒好也顯示 Loading ,這樣才跟 Server Render 的內容一樣
  if (!ready) {
    return <div>Loading...</div>
  }

  // 顯示圖片
  return <img src={img.url} />
}

其實這篇原本還想用 sharp 提供圖片縮放的,只是我在安裝上出了點問題,另外範例程式中還有被我註解掉的用 blurhash 當 placeholder 的實作,因為 Node.js 的圖片處理速度實在是太慢了,導致重 build 非常的花時間

下一篇我想來講 incremental build


上一篇
Day 25: Static Query
下一篇
Day 27: Incremental build
系列文
從 0 開始建一個 Static Site Generator30

尚未有邦友留言

立即登入留言