見出し画像

某アイシングクッキー屋のサイトを、アイシングクッキー界一ナウい技術スタックなサイトにリニューアルした話

青森県弘前市にあるアイシングクッキー専門店enのWeb担当をしておりますfukuiretuと申します。タイトルの通り、先日アイシングクッキー専門店enのサイトをリニューアルしまして、本記事はその際の移行記になります。

先に軽くお店の紹介を...

enはデザインへのこだわりはもちろん、安心安全に美味しく食べて頂けることを目指し、着色に天然色素を使用、原材料に国産バター・国産小麦を使用するなど味と品質にも重きを置いています。

デザインの一例としては、例えば会社や自社サービスのロゴをモチーフにしたクッキー。
暖かくなってきてイベントなども多くなるシーズンですので、ノベルティなどにオススメです!

また、ウェディングドレスのクッキーは6月はジューンブライドということで来月結婚式を挙げる方も多いかと思いますが、プチギフトとしてオススメです!

他にもnoteとInstagramで作品を公開しているので、良かったら覗いてみてください。
■note: https://note.mu/en0315
■Instagram: https://www.instagram.com/en_0315/

Why?

リニューアル前はameba ownd上で運用していて、無料で利用できるし大きな不満はなかったのですが、

・独自ドメインをSSLで運用したい
・ページ表示のパフォーマンスを上げたい
・SEO対策を自由にしたい
・デザインを自由にしたい(特にヘッダーの会員登録導線を消したい)
・オフラインでもページが表示できるようにしたい(PWA)
・(新しい技術などを試す実験台がほしい)

など、自分でコントロールしたい部分が増えてきたというのが主な理由で、
今回のリニューアルで一応全て解決しました。
ランニングコストはリニューアル前と同じくドメイン(e-n.shop)代のみです。
(現状のサイト運用で同じような悩みを抱えている特に青森県民の方、ちょっと話を聞いてみたいなどもしあればお気軽にご連絡ください!)

How?

リニューアルの要件(と自分の欲求)を満たす構成を考えて、Nuxt.jsを静的サイトジェネレーターとして利用し、生成された静的サイトをNetlifyで運用するようにしました。
以降では、技術スタックや要件・機能をどのように実現(解決)しているのかなどを備忘録も兼ねてダイジェストで紹介したいと思います。
特にこれからNuxt.jsでサイトを構築してみようかなと思ってる方の手助けになれば幸いです。

※基本的な使い方などは公式のドキュメントが充実しているので本記事では割愛します。

■技術スタック
・フロントエンド: Nuxt.js(Vue.js), ver 1.4.0
・AltJS: TypeScript, ver 2.8.1
・CSSフレームワーク: Bulma, ver 0.7.1
・ホスティングサーバー: Netlify

■どのように実現しているのか

●独自ドメイン, SSL

Netlifyは独自ドメインの利用もSSL(Let's Encrypt)の利用も無料です。
また、
・ネイキッドドメインからサブドメインへのリダイレクトの設定が可能
・httpからhttpsのリダイレクトの設定が可能
となっています。管理画面は全て英語ですが、わかりやすいUIなので迷うことなく設定できました。

パフォーマンス
ローカルでLighthouse(ver 2.10.0.3002)を実行しながらちまちまと。
リリース時にLighthouseを流した結果は以下の通りです。

画像はJPEGMiniで圧縮しました。
NetlifyがCDNで配信してくれることもあり、かつ静的サイトなのでかなり描画は高速になりました。

●オフラインでの動作
Workboxを利用し、オフライン(対応ブラウザのみ)での動作を実現しています。Nuxt用の拡張モジュールであるnuxt-community/pwa-moduleを利用すると簡単に導入が可能です。導入手順としては、

1. npm(yarn)で@nuxt/pwa-moduleをインストールする
2. nuxt.config.jsに以下を定義する

・
・
・
modules: ['@nuxtjs/pwa']

これだけです。これだけで、静的ファイル生成時に出力された
・jsファイル
・dist/_nuxt配下
・/(ルートパス)配下
は自動的にキャッシュ対象となります。
サイトの特性上、日常的にオフラインで見たいようなものではないのでそこまで需要があるわけではないですが、地下鉄利用時など電波の安定状況に左右されずにページを表示できるのはそこそこ便利だと思います。

Workboxに関することは以下の記事を参考にさせて頂きました🙏
PWAをNuxt.jsで簡単に体験する
Mercari Web版にWorkboxでService Workerを導入する話

※Workboxを利用するために必要なServiceWorkerが、localhost以外だとhttpsが必須なので導入の際はご注意ください。

●Google Analytics
サイト運用において必ず導入するであろうGoogle Analytics
こちらもNuxt用の拡張モジュールであるnuxt-community/analytics-moduleを利用することで簡単に導入が可能です。導入手順は、
1. npm(yarn)で@nuxt/analytics-moduleをインストールする
2. nuxt.config.jsに以下を定義する

・
・
・
modules: [
  ['@nuxtjs/google-analytics']
],
'google-analytics': {
  id: 'UA-xxxxxxxx-x'
}

これでページトラッキングがされるようになります。(SPAでもちゃんとトラッキングされます)

●sitemap.xml
SEO対策としてサイト運用においてはほぼ必ず導入しますよね。
こちらもNuxt用の拡張モジュールであるnuxt-community/sitemap-module
利用することで簡単に導入が可能です。導入手順は、
1. npm(yarn)で@nuxt/sitemap-moduleをインストールする
2. nuxt.config.js以下を定義する

・
・
・
modules: ['@nuxtjs/sitemap'],
sitemap: {
  path: '/sitemap.xml',
  hostname: 'https://www.e-n.shop',
  cacheTime: 1000 * 60 * 15,
  gzip: true,
  generate: true,
  routes: [
    '/order',
    '/lesson',
    '/about'
   ]
}

これで完了です。

●構造化データマークアップ
こちらもSEO対策です。
Nuxt.jsのheadタグ生成は内部的にはvue-metaを利用しているため
vue-metaの仕様に従い、nuxt.config.jsに以下のように設定を追加します。以下は実際のサイトで利用している設定です。

head: {
 __dangerouslyDisableSanitizers: ['script'],
 script: [{
   innerHTML: `{
     "@context": "http://schema.org",
     "@type": "Organization",
     "name": "アイシングクッキー専門店en",
     "url": "https://www.e-n.shop",
     "logo": "https://www.e-n.shop/images/logo_ogp.png",
     "contactPoint": {
       "@type": "ContactPoint",
       "contactType": "Customer service",
   	  "email": "info@e-n.shop",
   	  "url": "https://www.e-n.shop/about"
 	  },
     "sameAs": [
       "https://www.instagram.com/en_0315/",
       "https://minne.com/@en-cookies",
       "https://note.mu/en0315/"
     ]
   }`,
   type: 'application/ld+json'
 }]
}

構造化データマークアップに関しては以下の記事が参考になります🙏
SEO強化のサポート役「構造化データマークアップ」って何? そのやり方は?

●Instagramで運用している写真をスライダーで表示

Instagramから提供されている https://api.instagram.com/v1/users/self/media/recent を利用してデータを取得し、SwiperのVue用ライブラリであるvue-awesome-swiperを利用して実現しています。ただ、以下のアナライザ結果をみると...

swiper.jsがかなりの割合(容量)を占めてることがわかります。実現したいことに大してあまりコスパがよろしくないので、もう少し軽量なライブラリを探すか自前で頑張ろうかなと思っています。
アナライザの利用方法は公式ドキュメントをご参照ください。

InstagramのAPIで取得した画像はデフォルトでは前述のWorkboxのキャッシュ非対象となるため、nuxt.config.jsに以下の設定を追加しています。

・
・
・
workbox: {
  runtimeCaching: [
    {
      urlPattern: 'https://scontent.cdninstagram.com/*',
      handler: 'cacheFirst',
      method: 'GET',
    },
  ],
}

また、APIで取得したデータは静的サイトに含めるような処理(pageコンポーネントのascynDataで取得)にしているので、Instagramに投稿された画像がリアルタイムには反映されません。反映するには再度ビルドしてデプロイが必要です。この辺は、サイトの特性上リアルタイム性が求められないので、パフォーマンスを優先しました。
(httpクライアントはaxios-moduleを使用しています)

●Informationにnoteに書いた記事一覧を表示

元々はowndのブログ機能をお店からのお知らせとして利用していたのですが、記事をnoteに移行しリンクからnoteの該当記事に遷移するようにしました。
noteにはrss機能が実装されているので、rssからデータを取得して一覧表示させるのが普通かと思うのですが、現状noteは投稿日時が変更できない仕様のため、rssで返却される投稿日時をそのまま使えない問題があり、現状は以下のような形で手動でyamlに定義したものを読み込んで表示しています。

infoArticles:
 - publishedDate: 2018-05-17
   title: lesson開始のお知らせ
   url: https://note.mu/en0315/n/nabf76d5370c3
 - publishedDate: 2017-12-10
   title: 2017年 年末年始休業のお知らせ
   url: https://note.mu/en0315/n/n711917fb62f7
 - publishedDate: 2017-06-13
   title: 本日(2017/06/13)正式にOPENしました
   url: https://note.mu/en0315/n/nbb5393c60488

他のブログから移行を考えているユーザからの要望もそこそこ多いので、
このあたりの仕様は中の人の特権を活かし、noteカイゼンチームに相談したいなぁと思ってます。

■その他の技術要素

●TypeScript
Nuxt.jsにTypeScriptを導入しようと思うと若干の手間が必要です。
最低限必要な手順としては、
1. nuxt.config.jsのbuildオプションに設定を追加する
2. Nuxt.jsがTypeScriptに対応していないので型ファイルを自前で用意する
です。

1.は、 https://github.com/nuxt/nuxt.js/blob/dev/examples/typescript/modules/typescript.js を参考に、
2.は、https://ja.nuxtjs.org/api/contexthttps://ja.nuxtjs.org/api/ をもとに型定義をしました。
参考までにgistに型定義ファイルを置いておくので気になる方は見てみてください。

また、LintはVue + TypeScriptなプロジェクトにESLintを導入するを参考にTSLintではなくESLintをベースにしています🙏

Bulma
同じお気持ちの方がいて安心しました。

リセットCSS
ressを使用しています。たまたまリリース前に、2018年のCSSリセットはress.cssを選べばよさそうの記事を読んだことがきっかけです🙏

●AtomicDesign
コンポーネントの設計手法としてAtomicDesignを採用しています。後追いで取り込んだので全てがAtomicDesignに沿ったコンポーネント設計にはなっていないのですが、大体半分くらいは...といった感じです。
ディレクトリ構成は以下のように、components配下にatoms, molecules, organismsを切る形にしています。

src
├── components
  ├── atoms
  ├── molecules
  └── organisms

粒度の一例ですが、例えば


は見出しを表現するコンポーネント(Heading)としてatomsに切り出し、

はカードを表現するコンポーネント(CardItem)としてmolecules、それを組み合わせてメニューとするコンポーネント(CardItemMenu)としてorganismsに切り出しています。

また、冗長になりがちなifの分岐はrender関数を使うことで見通しがよくなるような作りにしています。このあたりはsoussune - 40. CTOの技術選定でもSCOUTERさんの実例として話されていて非常に参考になりました🙏
現状JSXは使ってないんですが、後学のために今後使っていきたいところ。

AtomicDesignについてはつい先日発売したばかりの、Atomic Design ~堅牢で使いやすいUIを効率良く設計する がオススメです。手前味噌ですが、書評も合わせてどうぞ。Vue.js からみた AtomicDesignも大変参考になりました🙏

最後に

予めある程度のレールが敷かれていることでbabelなどの設定周りやライブラリの選定、ディレクトリ構成などに時間を要することが少ないので、最初の取っ掛かりで頓くことがほとんどないことがNuxt.jsの1つのメリットだと感じました。(Railsを初めて触った時の感覚に近い。中級以上やレールをはずれようとすると同じように辛みがあるのもおそらく同じだろう...)

ナウい技術を使うこと自体がWebサイトの本質ではないので、当たり前ですが要件やリソース、チームメンバーのスキルセットなど状況に応じてバランスの良い選択を取るのがベストですが、コードを書く上での気持ちよさや、本質的な部分を作ることに集中できると感じたNuxt.js(Vue.js)は個人的に推していきたいし、今後も活用していきたい気持ちです。

今後もアイシングクッキー界ーナウいサイトを目指してやっていきます。

弊noteをよりよいサービスにするために、技術書購入や勉強会・セミナー参加の費用にあてたいと思います🙏