アーカイブ

マークアップエンジニアから​見た​Nuxt.jsの​可能性

GitHub Edit Page
この記事は公開から1年以上が経過しています。内容が一部古い箇所があります。

この記事はVue.js #2 Advent Calendar 2017の 21 日目の記事です。

去年も参加させていただいたのですが、今年は4つもカレンダーが作成されて Vue.js の勢いすごいですね。

今回、自分がこの記事で書く内容は特にテック的なことではなく、Nuxt.jsへの感想記事や思い出記事みたいなのになりますので、Nuxt.jsの技術的な内容を期待していた方はすみません。こういった人間もいるのか程度にご覧いただけたら、という感じです。

今回、自分がこの記事で書く内容は特にテック的なことではなく、Nuxt.jsへの感想記事や思い出記事みたいなのになりますので、Nuxt.jsの技術的な内容を期待していた方はすみません。こういった人間もいるのか程度にご覧いただけたら、という感じです。

テンプレートエンジン・プリプロセッサの多様化

いきなりVue.jsNuxt.jsという主題から脱線してしますがご了承ください。

Node.js というものが世に出てから、おそらく、宗教上の理由などを抜きにすれば、HTML や CSS を素で書くことはなくなったと思います。いわゆるテンプレートエンジン・プリプロセッサの時代。

人々は HTML をPug(Jade)で書いたり、CSS をLessSassStylusといったもので書くようになったおかげで作業効率は格段にあがったと思います。ここに各種タスクランナーを組み合わせれば更に恩恵を受けられるものになることでしょう。

とくにPugというものと出会って、マークアップエンジニアとしての自分は今まで SSI や php での処理しかできなかった「共通化」部分を実現して静的に出力できるというのは感激ものでした。たとえば、

doctype html
html(lang="ja")
	head
		meta(charset="UTF-8")
		meta(http-equiv="X-UA-Compatible" content="IE=edge")
		meta(name="viewport" content="width=device-width, initial-scale=1")
	body
		block contents

みたいな感じにして

extends _tmpl

block contents
	h1 Hello World!

な感じで出力すると

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>
  <body>
    <h1>Hello World!</h1>
  </body>
</html>

みたいな感じになります。あとは変数での宣言も出来るので先程の_tmpl.pugに以下のようにparam という変数を設定して

doctype html
html(lang="ja")
	head
		meta(charset="UTF-8")
		meta(http-equiv="X-UA-Compatible" content="IE=edge")
		meta(name="viewport" content="width=device-width, initial-scale=1")
		block param
		title #{title}
		meta(name="description" value=description)
		meta(name="keywords" value=keywords)
	body
		block contents

以下のようにして出力すると

extends _tmpl

block param
	- var title = "なにかのサイト"
	- var keywords = "キーワード1, キーワード2, キーワード3";
	- var description = "なにかしらの説明";

block contents
	h1 Hello World!

こんな感じで、ページ毎に共通のものと、そのページ独自のメタデータなどを静的ファイルとして入れ込むことができます。

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>なにかのサイト</title>
    <meta name="keywords" content="キーワード1, キーワード2, キーワード3" />
    <meta name="description" content="なにかしらの説明" />
  </head>
  <body>
    <h1>Hello World!</h1>
  </body>
</html>

これを今まで手動ですべて管理していた身と考えれば革新的だったのがわかると思います。

フロントエンドとの境界線をどう超えるか問題

自分がマークアップエンジニアとして仕事をしてからは、上記のようなテンプレートエンジンやプリプロセッサを駆使して業務を行っていたのですが、まれに JS 開発におけるフロントエンド領域の仕事依頼がくることがあります。

マークアップ上ではせいぜいVanillaJsjQueryを使用して身軽なアニメーションや状態の管理などは行えました。しかしガッツリとしたフロントエンド業務であれば、様々なモジュールを駆使して、npmコマンドやbrowserifywebpackなどを駆使してバリバリ開発しているわけで、そういったものを通常では使用しない人間にとっては、環境設定の時点で大きな障壁となっていたりします。

自分自身としてもそうしたフロントへの境界線をどう飛び越えていくかについて思い悩みつつも、先人が敷いてくれた環境設定というレールの上で、どのような軌跡をおってきたかを逐一調べながらやるしかなかったので、いきなりバリバリと進められるわけでもなく、どうやって段階を追ってやっていけばいいのかをヤキモキしていた時期がありました。

Vue.js との出会い

そんな自分でも何かうまく扱えるフレームワークはないかと調べてみた所、どうやら Vue.js というものが分かりやすいという情報を仕入れ、昨年に、稚拙ではありますが、SPA なるものを作り上げるところまでできました。(去年の記事)

なるほどこうして動かせるのかと理解を進めるのと同時に、「vue-cli」というコマンドラインから簡単にプロジェクトを作れるものを知って、参入障壁というかどうやっていけばいいのかの理解を進めるのにはもってこいでした。

もちろん他のフレームワークでも近しいものがあるかもですが、フレームワークの明快さと馴染みのある感じ、そしてvue-cliという存在において、Vue.jsは自分にとって道を切り開いてくれる開拓者のような存在でした。

Nuxt.js を知る

そんな中、Vue.jsからの派生フレームワークで今年バージョン 1.0 がリリースされたNuxt.jsというものの存在を知りました。

Screenshot from Gyazo

最初はVue.jsを使って SPA や SSR といった開発が容易にできる! といった評判を聞いており、自分は「へ〜こういうのがリリースされたのね」という感覚で受け取っていて、あとで触ってみるかなーという感じで、知った当時はそれほど興味なりはそこまでといった感じでした。

時は変わって 10 月某日、さくらインターネットさんに開催していただいた「さくらの夕べ 「さくらインターネットのエンジニアは Vue.js をこんな風につかってます」に Vue.js 関連の話をやるとのことで参加してきました。=> (レポート記事)

その中で@esukei さんが Nuxt.js に関連してを紹介していただいたのですが、ひとつ自分の中で引っかかったとある機能がありました。

それは 静的ファイルを出力する というnuxt generateコマンドがあるということでした。そしてそれをもとにウェブサイトを作れるのではないか? という内容をおっしゃられていました。

先述したように、これまでマークアップエンジニアとしての私は各種テンプレートエンジンやプリプロセッサ、そしてタスクランナーを使い業務を行っていました。

それはそれで問題なかったのですが、CSS の管理問題コンポーネントとしての共通化、そしてJS 部分との連携も考えねばならないのに、それぞれを別個として出力させて、合わせたときにどうなるかを考えなければならなかったというのがあります。 そういった部分を解消しうる静的なマークアップ環境が整う都合のいいものは何か無いだろうか…と考えていました。

その問題をおおよそ解決しうるプロダクトとしてNuxt.js、そしてnuxt generateというものがなのではないか、と感じました。

Nuxt.js の良さそうな所

ディレクトリ構成が明瞭的

Nuxt.js は取っ組みやすいスターターキットを用意してくれているスグレモノでvue-cliを使えるようにして以下で作成できます。

$ vue init nuxt-community/starter-template test
? Project name test
? Project description Nuxt.js project
? Author yamanoku <0910yama@gmail.com>

   vue-cli · Generated "test".

   To get started:

     cd test
     npm install ## Or yarn
     npm run dev

で上記プロジェクト名やプロジェクト説明、著者の入力をエンターでタンタンターンッとやって、作成したディレクトリ(上記で言うと test)に移動するとこのような構成になっています。

├── assets
├── components
├── layouts
├── middleware
├── pages
├── plugins
├── static
└── store

ディレクトリの詳細は公式ページを参照いただければ、と思いますが、単純にページのみをつくる想定で考えると、

├── components(パーツ集)
├── pages(ページ単位)
└── static(画像ほか静的なファイル置き場)

最悪これだけでもページ作れると思います。必要なものは適時追加いただければという感じで。

ここで良いなと感じる部分はどこに何を担当しているのかを一見して分かりやすいというのがあります。なのでこれはここに入れておくというルールも固定化できると思います。

詳細な環境構築が不要

ここから開発をはじめるにあたり、まずnpm iyarnでモジュール一覧をダウンロードするのですが、通常であればwebpackだったりgulpなりのビルドツールやタスクランナーを設定することがあると思いますがそれらは不要です。

必要なモジュールほかはnpm i -Dyarn add -Dで追加してimportなどで導入できるのですが、ここから CSS フレームワークなども簡単に設定できます。例えば

yarn add -D bulma

このように追加した後に、以下nuxt.config.jsに以下を追記すると、

module.exports = {
  head: {
    css: ['bulma/css/bulma.css'],
  },
};

全ページ共通でbulma.cssが使えます。

1ソース完結、scoped CSS 革命

Vue.jsをベースとしているので、1ソースの中で、htmlcssJavaScriptをそれぞれ書ける利点があります。さらに、htmlにはpugcssにはsasslessstylusのプリプロセッサ言語も使用できちゃいます。 コンポーネント設計や AtomicDesign における個々のパーツを管理する設計思想が流行ってはいますが、Nuxt.jsはそれを実現しやすいものだと思っています。

そして CSS 管理においてはscoped CSSを使用すると、コンポーネントやレイアウト、ページ単体の CSS 管理ができます。

Screenshot from Gyazo

こうすると

Screenshot from Gyazo

クラスにユニーク名が付与されてこういうことができます(画像のは Nuxt.js ではないのですがイメージとして)。

@yassh さん投稿による「vue-loader の Scoped CSS のスタイルが子コンポーネントのルート要素に効いてしまって辛い」といった仕様の問題もありますが、使いこなせば強力な武器になるのではないかなと思います。

ES6/ES7 も対応できる&トラインスパイルしてくれる

JavaScript のナウい書き方にもバベルで対応しているので、レガシーな書き方からも脱せそうです。

<head>情報も簡単に設定できる

こうした JS フレームワークにおいて、meta情報などのhead要素を弄るのはあまり情報がなかった、bundle された js を置く index.html に設定するだけと思うのですが、共通であったり各種ページでの個別設定も簡易的にできます。

先程登場したnuxt.config.jsで以下設定できます。

module.exports = {
  head: {
    title: 'test', // <title>部分
    meta: [
      // metaタグ関連
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '説明文説明文' },
    ],
    link: [
      //linkタグ関連
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
    ],
  },
};

さらに個別ページでは以下のような個別設定も可能です。

<template>
  <h1>{{ title }}</h1>
  <!-- => <h1>Hello World!</h1> -->
</template>

<script>
  export default {
    data() {
      return {
        title: 'Hello World!',
      };
    },
    head() {
      return {
        title: this.title, // => <title>Hello World!</title>
        meta: [
          {
            hid: 'description',
            name: 'description',
            content: 'My custom description',
          },
          // => <meta name="description" content="My custom description">
        ],
      };
    },
  };
</script>

PWA 化も楽々導入。

話題の PWA に関してもpwa-moduleを追加すると利用できちゃいます。以下 manifest 設定と icon.png をstaticに追加しておくと簡単に作成できます。

module.exports = {
  modules: ['@nuxtjs/pwa'],
  manifest: {
    name: 'testApp',
    short_name: 'shortName',
    title: 'test',
    'og:title': 'ogtest',
    description: 'appDescription',
    'og:description': 'ogDescription',
    lang: 'ja',
    theme_color: '#ffffff',
    background_color: '#ffffff'
  }
  workbox: {
    dev: true, //開発でもPWAができる
  }
}

これでオフラインでも確認できる静的ページを作成できちゃいます。

Nuxt.js の悩ましい所

上述した点はプロダクトを利用する面で良さ気なところでしたが、すぐに導入するには難しいところもあります。

minify される点

yarn generatedistディレクトリが作成されると、生成されたhtmlは基本 minify されています。 そのままでよければいいのですが、出力されたファイルを確認したいときに可読性がわるくブラウザのデベロッパーツールで minify 解除するなどしないと階層化されている内容をみれないです。 どうやら minify 制御はnuxt.config.js内ではhtml-miniferを使用しているのでgenerate内で以下のように設定してみると多少なりの制御はできるみたいです。ただ思い通りの調整をするにはもう少し調査が要りそうです。

  generate: {
    minify: {
      collapseBooleanAttributes: true,
      collapseWhitespace: true,
      decodeEntities: true,
      minifyCSS: true,
      minifyJS: true,
      processConditionalComments: true,
      removeAttributeQuotes: false,
      removeComments: false,
      removeEmptyAttributes: true,
      removeOptionalTags: true,
      removeRedundantAttributes: true,
      removeScriptTypeAttributes: false,
      removeStyleLinkTypeAttributes: false,
      removeTagWhitespace: false,
      sortAttributes: true,
      sortClassName: true,
      trimCustomFragments: true,
      useShortDoctype: true
    },
  }

関連: https://nuxtjs.org/api/configuration-generate#minify

同ディレクトリ内に違うファイルを作成できない

これは自分の知見不足なのかもしれませんが

pages
├── index.vue
└── one
    ├── two.vue
    └── index.vue

このような構成の場合にgenerateを動かすと

├── index.html
└── one
    ├── two
    │   └── index.html
    └── index.html

という感じでone/two.htmlというファイルは作成されません。この辺は router の設定次第なのかなと思いますが、少々不便です。

Vue.js を前提としているための学習コスト&他フレームワークで再利用ができない

当たり前な部分ではありますが、JavaScript部分を動かすには多少なりともVue.jsを理解しておく必要はあります(個人的な感想ではありますが他のものと比較すれば理解はしやすいかなと思いますが)。 また、他のフレームワークやモジュールなどを利用することもできないので、もし Vue.js 以外のフレームワークを使っていたりするときは選定する技術にするかを考える必要はあるかなと思います。

まだまだ情報が少ないので知見を集め続けなければならない

公式日本語ドキュメントもあるNuxt.jsなのですが、まだまだ出て日が浅いのもあるのと、制作事例も少ないため「こうするにはどうすればいい?」という情報共有がまだ少ないかなという印象です。 上述した不明点もすぐ調べてわかれば良いのですが多少のググり力がないと厳しい or 見つからない場合もあるので、「こうしたときにどうすればいいのか?」というのを出来る限り埋めておかないと厳しいかなとは思います。 公式の Issueなどを眺めてみるとどうすればいいのかなどの話はちらほら見れます。

導入は難しいかもしれないが、動かしてみる価値はある。

メリット面とデメリット面を自分なりに上げてみましたが、1つのプロジェクトを無事完結するためにはまだまだ難はあるかなと思います。もしかしたら、前よりも効率が落ちる可能性もあるかもしれません。ですが、動かしてみる価値はあるのではと思っています。

今後Nuxt.jsを使った案件として実績をあげるためにも、まずは個人的に動かしてみるのと、これまでに携わった案件でどこまで代用できるのか、というのを考えてみると良いのかなと思っています。そうすればどの点で有利でどの点で不利になるかが明確になるんじゃないかなと感じます。

今回はマークアップの視点で考えてみましたが、SPA や SSR を簡単にやってみるなど「試してみたかったことをアレコレやってみる」最初のきっかけとしてはかなり導入の敷居が低いと思うので、どんどん利用者や利用実績が増えていけばいいなぁと感じています。

アーカイブ記事のため、内容に関する更新依頼は受け付けておりませんが、誤字や脱字などありましたらご連絡ください。

この記事に関する修正依頼
トップへ戻る