さくらのレンタルサーバ15周年!ロゴデザインコンテスト
さくらのレンタルサーバ15周年!ロゴデザインコンテスト
2019.03.12
#4
Nuxt de Portfolio

【Ch.3】コンポーネントを整理する【Nuxt de Portfolio】

ライダー

こんにちは、フロントエンドエンジニアのライダーです。

今回は「Nuxt de Portfolio」連載のチャプター3となります。

前回の記事で作成したコンポーネントをもっと便利に活用していきましょう!

振り返り

ここまでは Nuxt を学習しつつ、ページとレイアウトを中心に、サイト全体をざっくり作ってきました。ディレクトリ構成はこんな感じになっていると思います。

components/
	common/TheHeader.vue
	common/TheFooter.vue
pages/
	index.vue
	about/index.vue
layouts/
	default.vue
	error.vue

CSSはベースをグローバルに登録して、コンポーネントは scoped を活用してマークアップを進めました。一方で冗長になってしまった部分があります。

  • @import "~assets/scss/variables" が各コンポーネントに出現した
  • .article-body {} が各コンポーネントに出現した
  • h2 {} が各コンポーネントに出現した

基本的にどれも、「頻出するものを、ひとつ(一箇所)にまとめる」方針で問題なさそうですね。

SCSS変数を全コンポーネントで使えるようにする

@import "~assets/scss/variables" が各コンポーネントに出現し、記述が非常に面倒です。ここでは、どのコンポーネントでも使えるように、https://www.npmjs.com/package/nuxt-sass-resources-loader を使いグローバルへ登録します。

npm install --save-dev nuxt-sass-resources-loader

nuxt.config.js

/*
  ** Nuxt.js modules
  */
modules: [
	['nuxt-sass-resources-loader', '~/assets/scss/_variables.scss']
],

こうすることで、 _variables.scss はたびたびインポートせずとも、どこでも使えるようになりました。今回は存在しませんが、 _mixin.scss なども登録すると面倒が減っていいですね。

グローバルで使えるようになったので、各コンポーネントで記述していた

@import "~assets/scss/variables"

は、すべて削除していきましょう。

修正・追加・削除等があったファイル

  • [修正] layouts/error.vue
  • [修正] pages/about/index.vue
  • [修正] pages/index.vue
  • [修正] nuxt.config.js
  • [修正] components/common/TheFooter.vue
  • [修正] components/common/TheHeader.vue

ArticleBody コンポーネントを作成する

次は、 <slot> を活用し、このクラスを持ったコンポーネントを外に切り出していきます。

components/common/ArticleBody.vue を作成してください。

<template>
  <div class="article-body">
    <slot></slot>
  </div>
</template>

<style scoped lang="scss">
	.article-body {
    h3 {
      font-size: 20px;
      margin: 30px 0 20px;
    }

    p {
      line-height: 1.75;

      + p {
        margin-top: 20px;
      }

      a {
        color: $color-blue;
      }
    }

    ul {
      list-style: circle;
      margin-top: 20px;
      margin-left: 20px;

      li {
        &:nth-of-type(n+2) {
          margin-top: 0.5em;
        }
      }
    }

    img {
      max-width: 100%;
    }
  }
</style>

<slot> とは?

今回、コンポーネントとして共通化したい部分は外枠(つまり <div class=“article-body”)であり、中身はコンポーネントの呼び出し元によって変わってきます(記事の中身はサイト全体で同じではないですよね)。

そんなときに、呼び出される共通コンポーネント(今回の ArticleBody )に <slot>をもたせておくと、呼び出し元から <slot> へ挿入することができます。

具体的には、<slot> をもつ ArticleBody に対し、ArticleBodyを呼び出すページコンポーネントが記事内容にあたるHTMLを挿入している部分になります。

ArticleBody コンポーネントを使う

この ArticleBody コンポーネントを pages/index.vue に組み込みます。コンポーネントの使い方は、TheHeader, TheFooter を使ったときと同様です。

pages/index.vue

<template>
  <div class="page-index">
    <section class="about">
      <h2>夏目漱石</h2>
      <ArticleBody>
        <img src="~/assets/images/portrait.png" alt="夏目漱石">
        <p>日本の小説家、評論家、英文学者。本名、夏目 金之助(なつめ きんのすけ)。江戸の牛込馬場下横町(現在の東京都新宿区喜久井町)出身。俳号は愚陀仏。詳細は<a href="<https://ja.wikipedia.org/wiki/夏目漱石>" target="_blank">ウィキペディア</a>を。</p>
      </ArticleBody>
    </section>

    <section class="works">
      <h2>代表作</h2>
      <ArticleBody>
        <p>『吾輩は猫である』(1905年)がデビュー作である。</p>
        <ul>
          <li>『吾輩は猫である』(1905年)</li>
          <li>『坊っちゃん』(1906年)</li>
          <li>『草枕』(1906年)</li>
          <li>『三四郎』(1908年)</li>
        </ul>
      </ArticleBody>
    </section>
  </div>
</template>

<script>
  import ArticleBody from '~/components/common/ArticleBody';

  export default {
    components: {
      ArticleBody
    }
  }
</script>

<style scoped lang="scss">
  .about,
  .works {
    h2 { ... }
  }

	/* ここにあった .article-body を削除 */
</style>

新たに、コンポーネントを使うための処理を追加します。CSSでは、実際のスタイルは pages/index.vue ではなく、 components/common/ArticleBody.vue がもつようになるため、

.article-body { ... }

は削除しておきます。

テンプレート内、 <div class="article-body"> だった部分を <ArticleBody> へ変更します。閉じタグの方も変更を忘れずに。

今はこれを、 pages/index.vue で行いましたが、 pages/about.vuelayouts/error.vue でも同様のことを行います。

下記が置き換えの手順です。

  • ページ/レイアウトコンポーネントのスタイルから .article-body {} を削除する
  • ArticleBodyscript でインポートし、components へ登録する
  • div.article-bodyArticleBody へ置き換える

すると、いままで各コンポーネントで記述して冗長になっていた部分が、ひとつのコンポーネントとしてシンプルで扱いやすくなりました。

修正・追加・削除等があったファイル

  • [追加] components/common/ArticleBody.vue
  • [修正] layouts/error.vue
  • [修正] pages/about/index.vue
  • [修正] pages/index.vue

.section-title を作成する

h2 {
  ...
  &::after { ... }
}

コンポーネントの切り出し方法は学んだので、今度は直接CSSにグローバルで登録してみましょう。

assets/scss/_section-title.scss を作成します。

.section-title {
  display: flex;
  align-items: center;
  margin: 50px 0 30px;
  font-size: 30px;

  &::after {
    content: '';
    display: block;
    flex: 1;
    height: 1px;
    margin-left: 10px;
    background-color: $color-black;
  }
}

また、各コンポーネントで、クラス名無しだった h2 に、クラスを付与し、スタイルから h2 {} を削除します。

<h2 class="section-title"></h2>

そのあと、 assets/scss/style.scss で、作成したファイルをインポートします。

@charset "utf-8";

@import '_variables';
@import '_base';
@import '_section-title';

こうすることで、またひとつ冗長な部分が減りました。

全体的にみても、かなりすっきりしています。ほとんど共通のスタイルを利用しているため、 <style> 自体が必要なくなったコンポーネントばかりですね。

 

error.vue の例

<template>
  <div class="layout-error">
    <section class="error">
      <h2 class="section-title">404</h2>
      <ArticleBody>
        <h3>名前はまだ無い</h3>
        <p>アクセスされたページは存在しないか、すでに削除されています。</p>
      </ArticleBody>
    </section>
  </div>
</template>

見事、テンプレートだけが残り、スタイルはすべて共通化されています。

修正・追加・削除等があったページ

  • [追加] assets/scss/_section-title.scss
  • [修正] layouts/error.vue
  • [修正] pages/about/index.vue
  • [修正] pages/index.vue
  • [修正] assets/scss/style.scss

おわりに

いかがでしたか? これでコンポーネントをすっきり整理することができましたね。次回はいよいよ、最終調整ののち Netlify へデプロイし、サイトを公開していきます。お楽しみに!