Nuxt.jsでAMP HTMLをgenerateする方法

エンジニア向けの記事です。それも、Nuxt.jsでgenerate(出力)するファイルをAMPにしたい!というエンジニア向けです。

前提

・特にamp用のmodulesとかは使いません。色んなところに落ちてるコードを引っ張って作った感じです。
・単一ファイルコンポーネントで作ってます。なのでCSSは「ページ用スタイル」「共通スタイル」みたいな感じで分かれてます。
・SCSS使ってます。共通スタイルはコンパイル後にstyleタグに突っ込みます。

コード

とにかくAMP化に必要な部分だけに絞ってます

/nuxt.config.js
やってること:ページ生成するときにAMP化するjsを呼び出してます

import ampify from './plugins/ampify'
...
export default {
 mode: 'universal',
...

  head: {
   base: {
     href: 'router.base'
   },
   ...
   link: [
     { hid: 'canonical', rel: 'canonical', href: '/' },
     ...
   ]
  },
  ...
  hooks: {
   // This hook is called before serving the html to the browser
   'render:route': (url, page, { req, res }) => {
     page.html = ampify(page.html)
   }
  },
  ...
}

/pages/*.vue
やってること:ページごとにcanonical urlを最適なものに変えてます
※hrefはいい感じにフルパス入れといてください。ここをいい感じにする方法は今度別の記事で書きます

<template>
</template>

<script>
export default {
  head() {
    link: [{
      hid: 'canonical',
      rel: 'canonical',
      href: `${process.env.SITE_URL}${this.$router.history.base}${this.$route.path}`
    }]
  }
}
</script>
​

/plugins/ampify.js
やってること:CSSとかJSとか色々整理してAMPとして使えるようにしてます。ここが一番のキモです。
※ファイル名は適当なのでなんでもいいです

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>'
const fs = require('fs')

module.exports = (html) => {
 // CSSを一つのstyleタグにまとめる
 let styleConcat = ''
 html = html.replace(/<style.*?>(.*?)<\/style>/gi, (_match, main) => {
   styleConcat += main
   return ''
 })

 // 共有CSSを最後に追加
 const globalStyle = fs.readFileSync('assets/css/style.css', 'utf8')
 html = html.replace('</head>', `<style amp-custom>${styleConcat}${globalStyle}</style></head>`)

 // charsetを消す
 html = html.replace(/@charset "UTF-8";/gi, '')

 // AMP HTMLしか使わないので、amphtmlのlinkは消す
 html = html.replace(/<link[^>]*rel="(?:amphtml)?"[^>]*>/gi, '')

 // ⚡マークをHTMLタグに追加
 html = html.replace(/<html/gi, '<html ⚡ id="html"')

 // baseタグを上に持ってくる
 let baseTag = ''
 html = html.replace(/<base href="(.*?)">/gi, (_match) => {
   baseTag = _match
   return ''
 })
 html = html.replace('</title>', `</title>${baseTag}`)

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

 // ld+json以外のJSを消す
 html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, (match) => {
   if (match.includes('type="application/json"') || match.includes('type="application/ld+json"') || match.includes('cdn.ampproject.org')) {
     return match.replace(/&quot;/g, '"')
   }
   return ''
 })

 // AMP用script読み込み準備
 const ampScript = '<script async src="https://cdn.ampproject.org/v0.js"></script>'

 // AMPボイラープレートとAMP用scriptをheadの終了直前には
 html = html.replace('</head>', ampScript + ampBoilerplate + '</head>')

 return html
}

/assets/scss/style.scss

好きなscssを書く(共通CSSとかの、最優先で効いてほしいスタイルをここで当てる)。
全ページにあたるので注意

/package.json
やること:パッケージ管理
※パッケージ管理はyarn使ってます。npm使っている人はファイル名とか違うのでご注意。
※他にも必要なパッケージあったらごめんなさい

{
 ...
 "scripts": {
   "dev": "node-sass --include-path assets assets/scss/style.scss assets/css/style.css --output-style compressed -w & nuxt",
   "generate": "node-sass --include-path assets assets/scss/style.scss assets/css/style.css --output-style compressed & nuxt generate",
   ...
 },
 "dependencies": {
   "nuxt": "^2.6.3",
   ...
 },
 "devDependencies": {
   "fs": "^0.0.1-security",
   "node-sass": "^4.13.1",
   "sass-loader": "^7.1.0",
   ...
 }
}

コマンド

yarn run dev

ローカルがAMP化して立ち上がります

yarn run generate

distにページが生成されます

まとめ

とりあえずamp化するための関数組んで、ビルドフックで呼び出せばOKです。
実際は他にもあった方がいいものとかがあるので、それは別の記事で解説します。

参考にしたサイト

https://medium.com/the-web-tub/making-a-website-with-nuxt-vue-and-amp-3ef904819de6
https://www.aintek.xyz/posts/create-amp-with-nuxtjs (non sslなので気をつけて)


いいなと思ったら応援しよう!