ポートフォリオのパフォーマンス向上のためにやった/やってること

## やったこと

ポートフォリオをリニューアルするのに際し、とにかく最初の描画速度を上げたかった、理由は「他にこだわりどころがなかったから」だ

作ったものは[こちら(一部未完成)](https://kusunokisouta.com)
[リポジトリ](https://github.com/Umisyo/Umisyo.github.io)

## 環境

- 開発環境:Nuxt(generate)
- ホスティング:firebase
    - 多分有名所のホスティングサービスの中で最速だと思っています。

## WebPの使用

[WebPについて](https://ja.wikipedia.org/wiki/WebP)

Chrome系のブラウザでしか描画できないが、ファイルサイズが圧倒的に小さくなるので、それだけパフォーマンスが向上する。

画像自体のホスティングは[Cloudinary](https://cloudinary.com)というCDNを利用した。
Cloudinaryを使うと、WebPに対応していないブラウザでは自動的に別の形式の画像を配信することができます。

## AMP対応

[AMPについて](https://amp.dev)

AMPを使うと、スマホでGoogleの検索ページからアクセスしたときに自動的にGoogleクローラがキャッシュしたAMP対応HTMLにアクセスしてくれます。検索した時点でキャッシュされるので表示が爆速になります。

今回は、indexのみamp版のページを用意することにしました。
Nuxt generateを使っているので、amp htmlの形式に合わせたHTMLを生成するためにはhooksのタイミングでHTMLを置換する処理を走らせる必要があります。
こんな感じ

```nuxt.config.js
import ampify from './src/plugins/ampify'

export default {
  ...
  hooks: {
    // This hook is called before saving the html to flat file
    'generate:page': page => {
      page.html = createAsciiArt(page.html)
      if (page.route === '/amp') {
        page.html = ampify(page.html)
      }
    },
    // This hook is called before serving the html to the browser
    'render:route': (url, page, { req, res }) => {
      page.html = createAsciiArt(page.html)
      if (page.route === '/amp') {
        page.html = ampify(page.html)
      }
    }
  }
```

```src/plugins/ampify.js
const ampScript =
  '<script async src="https://cdn.ampproject.org/v0.js"></script>'
const ampBoilerplate =
  '<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>'

module.exports = html => {
  html = html.replace(/<html/gi, '<html ⚡')

  let styleConcat = ''
  html = html.replace(
    /<style[^>]*data-vue-ssr[^>]*>(.*?)?<\/style>/gi,
    (match, sub) => {
      styleConcat += sub
      return ''
    }
  )
  html = html.replace(
    '</head>',
    `<style amp-custom>${styleConcat}</style></head>`
  )

  html = html.replace(/<link[^>]*rel="(?:preload|prefetch)?"[^>]*>/gi, '')

  html = html.replace(/<link[^>]*rel="(?:amphtml)?"[^>]*>/gi, '')

  html = html.replace(/\s*data-(?:[^=>]*="[^"]*"|[^=>\s]*)/gi, '')

  html = html.replace(
    /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
    match => {
      return /application\/ld\+json/gi.test(match) ? match : ''
    }
  )

  html = html.replace('<link rel="amphtml" href="/">', '')

  // Replace img tags with amp-img
  html = html.replace(/<img([^>]*)>/gi, (match, sub) => {
    return `<amp-img ${sub} layout="responsive"></amp-img>`
  })

  // Add AMP script before </head>
  html = html.replace('</head>', ampScript + ampBoilerplate + '</head>')

  return html
}
```

これでgenerate時に `/amp` 以下のページだけamp対応に置換してくれます。
imgをamp-imgに置換はしていますが、コーディングの際にタグに直接 `width` と `height` を指定して上げる必要があります（ちなみに、`width`と`height`を直接指定するとサイズの計算を事前に済ませてくれるので画像読み込み時のガクガクがなくなります。普通の`img`でも指定してあげると、体感速度が上がって良いです）
また、Styleは一つのタグでまとめなくては行けないため、generate時に一つのタグ内に収まるように、assetsの配下にcssを集約しましょう。タグ一つにまとめることでレンダリング速度も多少向上します。

また、通常のHTMLとAMP HTMLで相互にリンクを貼る必要があるため、以下のようにします。
amp側からは先程の置換で一緒にリンクも張っています

```nuxt.config.js
export default {
   link: [
       {rel: 'amphtml', href: './amp'}
   ]
```

今回はindexのみamp対応にしているため、indexからのみ貼ったほうがいいのですが、めんどくさかったので全ページに貼っています（）

generateしたら[こちら](https://search.google.com/test/amp?hl=ja)でテストしてあげると良いです。

## imgにdecoding="async"をつける
`decoding="async"`と明示的に書くことによって、画像の描画をコンテンツの表示を邪魔しないように非同期的に行ってくれます。
また、Chrome限定ですが `loading="lazy"` もつけてあげると良いでしょう。