LoginSignup
9
1

【下り最速は】Astro使って阿部寛のホームページよりも早いホームページを作りたい【この俺だ】

Last updated at Posted at 2023-12-21

はじめに

皆さんも怒りに震えたことはないだろうか?

外でバリバリスマホ使っていたら出てくる、アイツのことだ。

そう、速度制限。

IMG_0097.jpeg

かくいう僕も、今月、5月にahamoに変えてから初めて速度制限を喰らってしまった。

原因は引っ越し先の前住人が光回線を契約変更or解約をしておらず、家にネットが引けなかったから。

引っ越しする時は、ネット回線の契約変更をちゃんとしましょうね。(怒)

結果、あっという間に20ギガを使いたおし、WifiがないところではYouTube再生すらできない状態になり、最近はコワーキングスペースを渡り歩きながらプロダクトを作る日々・・・・

とはいえ家に帰らないわけにもいかず、僕は家で久しぶりにあのウェブサイトを訪れることにしてみた。

皆さんご存知、阿部寛のホームページである。

最速表示速度0.09秒!「阿部寛のホームページ」

阿部寛のホームページといえば、その90年代ネット黎明期を彷彿とさせるレトロなデザインとネットの速度制限中でも全くストレスなく見れる読み込みの速さで有名である。

(みてみたい人は「阿部寛 ホームページ」でググってみてね)

一部の界隈では、あまりにも表示が早すぎるので「いかに遅く表示させるか」を競う逆リアルタイムアタックなるものまで出される始末。

その読み込みの早さの秘訣は、ソース(htmlや画像)の少なさにある。

index.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<meta name="GENERATOR" content="JustSystems Homepage Builder Version 20.0.6.0 for Windows">
<meta http-equiv="Content-Style-Type" content="text/css">
<title>阿部寛のホームページ</title>
</head>
<frameset cols=18,82>
  <frame src="menu.htm" marginheight="0" marginwidth="0" scrolling="auto" name="left">
  <frame src="top.htm" marginheight="0" marginwidth="0" scrolling="auto" name="right">
  <noframes>
  <body></body>
  </noframes>
</frameset>
</html>

トップページはわずか15行のhtmlでできており、cssも最低限。

画像も1枚しかなく、全てのリソースを合わせてもわずか147kB!

IMG_0086.jpeg

iPhoneで撮ったうちのワンコの画像↑が551kBなので、この画像の大体3分の1程度のリソースしかないことになる。

実際にデベロッパーツールで何度かリロードして速度を測ったところ、

スクリーンショット 2023-12-21 10.33.24.png

image.png

まさかの0.09秒。

0.1秒切ってきやがった・・・

もはや早すぎてポルナレフ状態である。

そこで僕はこう思った。

「これより早いホームページ作ったら、WEB最速名乗っても良くね・・・?」(迷推理)

ここから僕のどこまでも最速を追い求めるWeb制作が始まった。

Astro is 何 ?

Astroを一言で言うならば、

「軽量爆速なWebサイトを開発できる新しいフレームワーク」 である。

もうちょっと詳しく知りたい人は、この方の記事をみていただくとわかりやすいかと思う。

Reactなど人気のUIフレームワークを使いながら作成でき、ビルド時にホームページを軽くするために、不必要なJavaScriptをなるべく排除することで高速なホームページを作成できるという2022年頃に開発された新しいフレームワークのようだ。

新しいから日本語のリソースは少ないものの、公式Docsは多言語対応しており、英語が苦手な人でも比較的とっつきやすい。

ちょうど12月に起業する予定で「会社のホームページを作らないとなー」と思っていたので、今回はAstroの公式テンプレートの一つであるSTONEを改造して、Firebase Hostingを使ってデプロイすることにした。

果たしてどこまで「阿部寛のホームページ」に追いつけるのか。。。

スクリーンショット 2023-12-21 11.48.21.png

「打倒!阿部寛のホームページ」作戦概要(こだわり)

  • WebPファイルの多用
    WebPファイルとは2010年にGoogleが開発した画像の新しい形式のことで、この形式にするとJPGやPNGよりも最大26%サイズを小さくできるというWeb用の拡張子である。faviconなどそもそも小さくて影響が少なそうな画像ファイルを除き、ページ内で使われる画像を積極的にWebP形式にすることで、読み込みのスピードをさらに減らそうと試みた。

  • Alpine.jsの採用
    とはいえ、画面遷移など多少のアニメーションがないと会社のホームページとしては物足りない。
    そこで今回はAlpine.jsというVue, React, Angular 等の SPAツールの 超シンプル版を使うことで必要最低限のJavaScriptの記述でアニメーションを実装することにした。

image.png

実はここが今回最大の難所で、Stoneのテンプレートをそのままクローンした場合、何も触っていない状態からLayout.astroで

Property 'isDark' does not exist on type '{}'.

というエラーが出て全くビルドできない。

他にも引数が足りないという表示が出たり、関数の引数が指定されてないやら、そもそもAlpine.jsがAstroプロジェクトの中で定義されていないといったAlpine.js関連のエラーが多数出てくるのだが、Alpine.jsの細かい仕様なんかAstroDocsに書かれているはずもなく、質問を調べてもissueで聞いてもなしのつぶて。

解決策としては、typesフォルダをルートディレクトリに作成し、types.d.tsを下記のように設定した。

types/types.d.ts
// @ts-ignore
declare module '@alpinejs/collapse' {
    // インポートされたモジュールの型定義
    const collapse: any;
    export default collapse;
}

// @ts-ignore
declare module 'alpinejs' {
    // Alpineの型定義
    export interface Alpine {
        plugin: (plugin: any) => void;
        store: (name: string, value: object) => any;
        start: () => void;
    }
    const alpine: Alpine;
    export = alpine;
}

// @ts-ignore
declare global {
    interface Window {
        Alpine: typeof import('alpinejs').Alpine;
    }
    interface Alpine {
        $store: {
            theme: {
                isDark: boolean;
                toggle: () => void;
            };
        };
    }
}

また、Alpine.jsを使っているLayout.astroも下記のように修正する。

src/layouts/Layout.astro
---
import '@fontsource/inter/latin-400.css';
import '@fontsource/inter/latin-500.css';
import { ViewTransitions } from 'astro:transitions';
import Footer from '../components/layout/Footer.astro';
import Header from '../components/layout/Header.astro';

interface Props {
  description: string;
  title: string;
}

const { description, title } = Astro.props;
---

<!DOCTYPE html>
<html lang="en" x-cloak x-data :class="{ 'dark': $store.theme.isDark }">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
    <link rel="icon" type="image/png" sizes="32x32" href="favicon-32×32.png" />
    <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
    <link rel="manifest" href="/site.webmanifest" />
    <meta name="generator" content={Astro.generator} />
    <meta name="description" content={description} />
    <title>{title}</title>
    <ViewTransitions />
  </head>
  <body
    class="bg-primary-50 text-primary-950 dark:bg-primary-950 dark:text-primary-200 antialiased transition"
  >
    <Header />
    <main>
      <slot />
    </main>
    <Footer />
    <style is:global>
      [x-cloak] {
        display: none !important;
      }
    </style>
    <!-- Alpine.jsの読み込み時に、type="module"とlang="ts"をつけることで、typescriptを使うことができる -->
    <script type="module" lang="ts">
      //インポート先にhttps://esm.sh/をつけることで、CDNからモジュールを読み込むことができる
      import Alpine from 'https://esm.sh/alpinejs';
      import collapse from 'https://esm.sh/@alpinejs/collapse';
      Alpine.plugin(collapse);

      Alpine.store('theme', {
        isDark: false,
        init() {
          this.isDark = (() => {
            if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
              return localStorage.getItem('theme') === 'dark';
            }
            if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
              return true;
            }
            return false;
          })();
        },
        toggle() {
          this.isDark = !this.isDark;
          localStorage.setItem('theme', this.isDark ? 'dark' : 'light');
        },
      });
      window.Alpine = Alpine;
      Alpine.start();
    </script>
  </body>
</html>

最後に、同じくsrcフォルダにあるenv.d.tsをこのように修正する

src/env.d.ts
/// <reference types="astro/client" />
import 'alpinejs';
declare module '@alpinejs/collapse' {
    export default function (): void;
}
declare global {
    interface Window {
        Alpine: any;
    }
}
declare module 'alpinejs' {
    export interface Alpine {
        plugin: (plugin: any) => void;
        //引数をanyにすると、エラーが出なくなる
        store: (name: string, value: object) => any;
        start: () => void;
    }
    const Alpine: Alpine;
    export default Alpine;
}

これでAlpine.jsとLayout.astro上のエラーは解決され、無事ビルドすることができた。

  • FireBase HostingのCDNを使って加速
    FireBaseやVercelなど多くのCloudHostingサービスに用いられる技術の1つにCDN(Content Delivery Network)というものがある。
    これは平たく言ってしまえば、
    「大元のサーバーとの間に世界各地にある代理サーバーを噛ませて、それら代理サーバーにも複製したコンテンツを置いておくことで表示・配信を加速し、負担を軽くしよう」
    というシステムだ。
    FireBase Hostingはこのシステムを導入することによって、動画など大容量コンテンツや事業をグローバルに展開し、世界各地から多くのアクセスが見込まれるサイトも安定した速度で供給できるという強みがある。
    今回はお名前.comで取得した 「cor-jp.com」 というカスタムドメインをHostingに追加して、今後自社のコーポレートサイトとして活用できるようにしながらデプロイしていこうと思う。

その他Astro v4.0へのバージョンアップに伴いtsconfig.jsonにあるExperimental Flagsなども編集したが、詳細は下記GitHubリポジトリとStoneテンプレートリポジトリを見比べていただきたい。

(もし他にも試してみてダメだったことがあったら教えてください、追記します)

そして、完成したサイトがこちらになる。

では、実際に速度制限中の僕のスマホで表示してみよう。

RPReplay_Final1703143090.gif

おお、早い!
これは「阿部寛のホームページ」といい勝負ができそうだ!

-そして今、戦いの幕が開く-・・・

決戦、阿部寛のHP VS 弊社のHP

今回はこのようなレギュレーションで競うことにした。

  • 勝負はChromeデベロッパーツールの測定を各10回ずつリロード、読み込み終了タイムで計測
  • 初回は重たいのでリロード一回目から測る
  • 環境をなるべく同じにするために連続した時間(開始してから両方終了するまで5分以内)で測る。
  • 10回やってそれぞれの最速タイムで勝負を決める。

では、いざ・・・・!

先攻/弊社ホームページ

ぬおおおおおおお!

画面収録 2023-12-21 16.57.47.gif

結果は、、、

回数 読み込み終了タイム
1回目 127ms
2回目 131ms
3回目 116ms
4回目 183ms
5回目 80ms
6回目    83ms
7回目          98ms
8回目          97ms
9回目          142ms
10回目          143ms

なんと!0.08 秒が出た!!
Astro SUGEEEE!!!

・・・・しかし、勝負はまだ終わっていない!
あのホームページに勝ってこそ、真の最速っ!

いざ!!

後攻/阿部寛のホームページ

とりゃああああああ!

画面収録 2023-12-21 17.16.49.gif

結果は、、、

回数 読み込み終了タイム
1回目 117ms
2回目 186ms
3回目 108ms
4回目 118ms
5回目 185ms
6回目    115ms
7回目          167ms
8回目          157ms
9回目          148ms
10回目          112ms

か・・・・勝ったぁぁぁぁ!!

しかしさすがは90msを出していた爆速ホームページ、幸運の女神がこちらに微笑んだ程度の辛勝だった。。。。

結論

  • Astroで作ったWEBサイトは、阿部寛のホームページよりも早いWebサイトを作ることができる。

  • ただし誤差単位なので計測次第では普通に負ける。

  • やっぱり最新技術と張り合える「阿部寛のホームページ」はスゴい。

以上、「Astro使って阿部寛のホームページよりも早いホームページを作りたい」でした。




・・・え、「なんでアドベントカレンダーこんなにギリギリになったんだ」って?

image.png

最後に(蛇足)

ここまで読んでいただきありがとうございます。
現在私は今年10月にG's ACADEMYを卒業後、12月にtoB向けWeb&Mobileアプリの自社&受託開発、Pythonによるクローリングや分析、機械学習やLLMを利用したITコンサルタントなどを主に行う会社、Cor.incを立ち上げました。

企業文化として「それぞれの個性や価値観を理解しあい、競争ではなく共創できる社会を作り上げていく会社」を目指しています。

現在絶賛この理念に共感していただける仲間を募集していますので、ちょっとでも気になった方は下のByNameにある連絡先やX(Twitter)Facebookで気軽に話しかけてみてください、僕が喜びます。
(Facebookだけはたまに返事が遅れることがあります、すみません🙇)

今回作ったHPからの連絡はまだコンタクトフォームが完成していないので、もうしばらくお待ちください!
今年中には日本語対応&コンタクトフォームとChatBotを追加実装する予定です笑

これからもガンガンQiitaやZenn、NoteにYouTubeでも発信していきますので、どうぞよろしくお願いいたします!

9
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1