mt_coff's log

メモとか雑に

Vue.jsでゆるくAtomic Designをやる

これは Aizu Advent Calendar 2018 の 22 日目の記事です。
20 日目は @4rotsugd さんでC++ゲーム開発録 - 1週間で音ゲー制作、23 日目は @yt8492 さんのRaspberry Pi 3でAndroid Things入門です。

adventar.org アドベントカレンダーは初めてでプログラミング初心者なのでお手柔らかにお願いします 🙇🙇 🙇
遅れてしまってすみません!!なんでもはしないけど許して下さい。

はじめに

本記事では Vue.js を使って Atomic Design でゆるく UI を作っていきます。Vue.js についての詳細やロジカルな部分にはあまり触れないのでご了承ください。ざっくりやっていきます。結構雑な理解です。

Atomic Design とは

ざっくり説明すると Brad Fros 氏によって提唱されている UI の設計手法です。UI を

  • Atoms
  • Molecules
  • Organisms
  • Templates
  • Pages

の5段階で構成します。上から下に行くに従って抽象的なインターフェースから具象的なインターフェースとなります。
以下で簡単にそれぞれどのようなものか説明します。

Atoms

全ての UI を構成する基礎で、例としてはフォームのボタンや入力欄などが挙げられ、UI として機能が壊れない最小の要素です。

Molecules

Atoms の組み合わせで構成されます。比較的単純で再利用可能なように作成します。単純な機能を持つことが可能です。
こちらでは検索欄が例に挙げられています。後述の Organisms に分類したくなるかもしれません。ただ、検索欄自体は複数箇所で使用が想定でき、配置することによって意味が明確するはずです。例えば、ヘッダーに配置すればページもしくはその Web サイト全体の検索だと直感的にわかりますし、ユーザー一覧の上部に配置されていればユーザーを検索するんだなということがわかるかと思います。(プロダクトによって変えるべきだとは思います。)

Organisms

Atoms と Molecules の組み合わせ構成され、コンテンツとして完結しているような比較的複雑な UI の構成要素となります。例としては ヘッダーやフッターなどが挙げられます。

Templates

Atoms、Molecules、Organisms を組み合わせてページの雛形とするのが Templates です。実際に画像や文章などは流し込ません。

Pages

Templates に実際にコンテンツを流し込んがものが Pages です。文章や画像など実際に表示することができます。イメージとしては Templates というクラスのインスタンスを生成してできたものが Page でしょうか。(ホンマか?)

Vue.js で Atomic Design を実践してみる

ここまでで Atomic Design についてざっくり(本当にざっくりと雑に)と説明が終わりました。ここで抑えていて欲しいのは Atomic Design はデザインのための手法であってコンポーネントを作成するための手法ではないということです。ですから実際にコーディングしていく上では具象的な層から抽象的な層のデザインを決定してみたり、状態をどこに持たせるか?など考えることは多いです。
今回は、できる限り CSS は各 Atoms で適用して、状態はコンテンツとして完成し始める Organisms から持たせるという方針でやりたいと思います。ここは個人やプロダクトに寄る部分になるかなと思います。
実際のソースコードは以下に置いておきます。(名前か被るコンポーネントは My をつけてますが実際のプロダクトではやらないでくださいね!)
github.com

セットアップなど

まずはプロジェクトを作成するところから始めましょう。vue cli を使用してプロジェクトの雛形を作成します。

$ vue create project-name

でプロジェクトが作成できます。実行するといくつか設定をどうするか聞かれますがここは個人で好きなのを選択していいと思います。私はPrettier が入る構成にするのをおすすめします。(オートフォーマットが非常に便利なため)

$ vue add storybook

Storybookを導入して置くとコンポーネントカタログを作成でき便利です。が今回は Storybook でのコンポーネントカタログの作成方法は述べません。基本的な初期設定はvue cliがやってくれます。

そして/src/components以下にAtomsMoleculesOrganismsPagesを作成しておきましょう。それぞれのディレクトリー以下でコンポーネントを定義していくことになります。
なぜTemplateを作らないかといえばデータの流し込みは Vue の機能(もし他のフラームワークを使用するならその機能)を使用してデータを流し込むためです。逆にTemplateを利用すると冗長な形になります。

ポイント

2 点ほど実装に入る前に Tips があります。まず 1 つ目ですが Vue ではすでに HTML 要素で使用されている名前はコンポーネントの名前として使用できないことです。
2 つ目はv-modelを独自定義したコンポーネントで使用するにはvalue属性をvalueプロパティにバインドし、inputイベントにて新しい値で独自のinputイベントを発行する必要があるということです(v-model@input:valueの糖衣構文)。具体的には以下のように定義します。(一例です。他にも記述方法はあります)

<template>
  <input :value="value" @input="onInput"></input>
</template>

<script>
export default {
  name: 'my-input'
  props: ["value"],
  methods: {
    // 親からさらに親へ伝えるときはeventではなくnewValueを渡すと良い
    onInput(event) {
      this.$emit('input', event.target.value)
    }
  }
}
</script>
// 上記を提起することで以下のように使用可能
<template>
  <my-input @v-model="hoge"></my-input>
</template>
<script>
import MyInput from "path/MyInput.vue"
export default {
  name:"fuga",
  components: {
    MyInput
  }
}
</script>

それでは実際に各 1 つずつコンポーネントの実装を追っていきましょう。

Atoms

inputは上記の例にあるので button を使ってみたいと思います。以下のような形になると思います。

<template>
  <button class="button" @click="onClick"><slot /></button>
</template>

<script>
  export default {
    name: "my-button",

    methods: {
      onClick() {
        this.$emit("click");
      }
    }
  };
</script>
// スタイルは省略

クリックイベントの発火を親に伝えて親でイベントをハンドリングするようにするだけですね。簡単!<slot></slot> を使うことにより<my-buton>hoge</my-buton> のような形でボタンに表示するテキストを制御できます。 また、なぜthis.$emit("click")しているかといえば親でイベントを制御しないと URL からどの動作をするか判断したり、イベントの数だけボタンを定義することになるためです。また、ボタンの色やサイズなどこの中で決定してもいいですし、共通のスタイルだけ当てて色やサイズは親コンポーネントで決定するとより柔軟に扱えると思います。

Molecules

リンクをリストにしたものを扱ってみましょう。

<template>
  <ul>
    <li v-for="(link, key) in linkList" :key="key">
      <link-list-item :to="link">{{ key }}</link-list-item>
    </li>
  </ul>
</template>

<script>
  import LinkListItem from "@/components/Atoms/LinkListItem.vue";

  export default {
    name: "link-list",
    components: {
      LinkListItem
    },
    props: {
      linkList: {
        type: Object,
        required: true
      }
    }
  };
</script>

Atoms にrouter-linkをラップしたLinkListコンポーネントがあることとします。単純に親から遷移先の名前と URL のペアの Array を受け取ってそれをv-forを使用し、リストにしています。

Organisms

Molecules でみたLinkList を使用してみましょう。

<template>
  <nav>
    <my-title>Menu</my-title>
    <link-list :link-list="linkList" />
  </nav>
</template>

<script>
  import MyTitle from "@/components/Atoms/MyTitle.vue";
  import LinkList from "@/components/Molecules/LinkList.vue";

  export default {
    name: "side-nav",
    components: {
      MyTitle,
      LinkList
    },
    data() {
      return {
        linkList: { home: "/", page1: "page1", page2: "page2", page3: "page3" }
      };
    }
  };
</script>

こんな形でサイドメニューを定義してみました。MyTitleコンポーネントは見出しとして使用する想定です。ここでようやく状態を持つことを考えます。このコンポーネントでは遷移先に関する情報をもたせています。API からのレスポンスでリンクを生成したりする際に、より上位のコンポーネントで取得しこのコンポーネントに渡してもいいですが、これはここだけで完結可能と考えることができるのでもしリンクを API から取得するのであればこのコンポーネントでの定義も問題ないです。場合によって使い分けましょう。

Pages

今までのものを組み合わせ、API 通信などを行ったりして各ページを作成するだけですので割愛させていただきます。ポイントとしては共通部分はくくりだしてベースとなっているファイル(例: App.vue)に置いてみたり、必要なレイアウトが複雑であればレイアウトを Atoms として切り出してみたりすると良いかもしれません。

まとめ

簡単にですが Atomic Design についての説明と Vue.js による実装をしてきましたが、いかがでしたでしょうか?コンポーネントの分類が難しかったり、私自身の理解が甘かったりしますが一通りの概念を把握してもらえれば幸いです。実際のソースではこの記事内で紹介していないコンポーネントも実装してありますのでクソコードだと思っていただいても OK ですし参考になればとても喜びます。

余談

冒頭に書いてあるプログラミング初心者は大嘘です(多分)

おわり

参考

HerokuにGo 1.11をデプロイして動かすときに躓いたのでメモ

HerokuにGo v1.11.1をデプロイするときに躓いたのでメモ

何が起きたか

go mod を利用しライブラリを導入しHerokuにデプロイ使用とした。 go.modgo.sumディレクトリに存在し、ビルドパックにheroku/goを指定しいざデプロイ!!したらアプリがクラッシュした。

解決策

venderディレクトリがプロジェクトルートに存在する必要があるみたい。なので go mod venderで生成してやった。その後にデプロイ、無事にアプリが動作した。
ちなみにドキュメントを読むことで venderが必要なことがわかったので今回はドキュメントをちゃんと読めた私は進歩している(正気か?

まとめ

HerokuでGoアプリケーション系の記事はバージョンが古いのばかりな気がするので公式のドキュメント & buildpackのドキュメントを読みましょう。デプロイ後にアプリが動かないことに気が付き、ドキュメントを読んで対処できました。めでたい🎉

jest-puppeteer.config.jsはプロジェクトルートになければならない

まとめ

ドキュメント読みましょう

configで設定したコマンドが動かなかった

e2eテストの環境環境構築をしていて以下のように書いていた

// jest-puppeteer.config.js
module.exports = {
  server: {
    command: "PORT=3000 yarn dev",
    port: 3000 
  }
}

こいつをtest/e2e/に入れていたがコマンドが実行されることなく起動しなかった.

解決策としてはプロジェクトルートjest-puppeteer.config.jsを入れればよかった...
ドキュメントにも書いてあるのでしっかりと読むべきだったが, jest-puppeteerを使ってみた系の記事をいくつか見ていてどの記事もディレクトリに関する指定が書かれていない(本当か?)ため日本語の情報に飛びつく私はドキュメントを読むまでに時間がかかった...かなしい...
他にもsetupFilesにファイルを指定しないで動かしたりしていたので気をつけたい.

Jestでcoverageがでなかったので自分の対処法メモ

現象

Jestを使用してテストを行っていたが、jest.conf.jsに以下のように書いていてうまくカバレッジが出なかった。

collectCoverageFrom: [
  "src/**/*.{js, ts, tsx}",
  // その他設定
]

これでカバレッジを見ると

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |        0 |        0 |        0 |        0 |                   |
----------|----------|----------|----------|----------|-------------------|

こんな感じになってしまった。

解決策

上記のコードでは{js, ts, tsx}のようにカンマの後にスペースが入っている。
これだとダメだったようで{js,ts,tsx}のようにすると正常にカバレッジが表示された。
スペースが入ると動かなくなるものには気をつけていきたい💪

応用情報技術者試験に受かってしまった

TL; DR

f:id:mt_coff:20180623235616p:plain
点数が低いけれども受かってしまった...

何故受けたか

大学在学中に応用情報を取得しておきたかったのと普段絡んでる方々が取得済みで自分が持ってないのがなんだかなぁ...という感じ

合格まで

実は29年度の秋期を申し込んでいたが、週三十時間労働と夏バテで勉強ができず、当日も体調が悪かったため会場に向かえなかった(午前のボーダーを超えるくらいはできてたはず...)
改めて春期で申し込みを行い春休みを使いざっくりと勉強してちゃんと受験した。
午前対策は過去問道場のみで、午後は↓ を全部流し読みした。

2018 応用情報技術者 午後問題の重点対策 (午後問題対策シリーズ)

2018 応用情報技術者 午後問題の重点対策 (午後問題対策シリーズ)

当日は午前問題は6割を超える確信を持てるくらいには解けていて(点数は低いが)午後が鬼門だった。
午後問題は

  • 情報セキュリティ
  • プログラミング
  • システムアーキテクチャ
  • データベース
  • 組込みシステム
    を選択した。
    どの分野も午後問題の重点対策でそれなりにできていたはずなのだが、データベースが壊滅状態、組み込みは他の問題に時間をかけたため問題文をしっかり読めず適当な解答を生成してしまった。
    その反面情報セキュリティはあっさり解けたし、システムアーキテクチャも同様に簡単な印象だった。
    プログラミングは時間を半分以上費やしほぼ全部自身のある答えを書くことができた。(これが他の足を引っ張っている)
    したがって、午後はセキュリティとアーキテクチャとプログラミングだけでほぼ点数をカバーしているのではないかと言うのが自分の持っている感想。

感想

カバーする範囲が広くしんどかった。特にネットワーク系は大学の講義の影響もあって全く触りたくなかったので午前問題でも勉強しなかったのは反省している。午後に関しても、プログラミングにあまりにも時間をかけすぎたのと、視野が狭くなっていて別の問題を選択してみるという行為が完全に頭から抜け落ちていたのは改善点だと思う。
上位の資格も挑戦していく気はあるのでインターンが始まる前に(行けるかわからないが)どれを受けるか決めて勉強を始めたいと思う。もっとちゃんと勉強します。

VSCodeがtsconfigで定義したpathsを使うと出たエラーの解決策メモ

importを楽にするためにtsconfig.jsonに以下の設定を追記していた。

{
  "paths": {
    "@/*": ["src/*"]
}

しかし、VSCodeにてimport hoge from "@/fuga"のように利用していたらモジュールが見つからないとエラーを吐いていた。
もちろん、コードはしっかりと動くので問題はなかったがエラーが出続けるのは精神衛生上悪いので解決策を探していた。

解決策

以下の拡張を導入することで解決した。 marketplace.visualstudio.com
結局よく原因がわかっていない🤔(vue-cliで生成したプロジェクトではエラーを吐かなかったため)

anyenvの導入でハマったのでメモ

anyenvの導入にハマってしまったので自分用にメモ.

anyenvとは

github.com

pyenvやrbenvなどの複数バージョンの管理を行えるツールを管理できるツールでコレがあることによって自分で設定ファイルに設定を追加せずに**envを利用刷ることができる.便利.

第一のハマりポイント - 既に .**envが存在する

README通りに導入はできたが,どうにも**envが上手く動作しなかった.
ローカル,グローバルどちらに使いたいバージョンを設定してもうまく適用されなかった.
見つけたのがこちらの記事↓

qiita.com

どうも過去に**envを導入したことがあってhomeディレクトリ下に.**envディレクトリがあると上手く動作しないことが原因らしい.

第二のハマリポイント - 設定の記述するファイル

anyenvの導入には

export PATH="$HOME/.anyenv/bin:$PATH"
eval "$(anyenv init -)"

を自分の設定ファイルに追加する必要がある.
自分は.zshenv上記設定を追加していて(なぜかは忘れた)上手く動作しなかった.
pyenvを使っていた時は上手く動作していたので中々気づくことができずにハマってしまった...
どうやら/etc/zprofile 内で /usr/libexec/path_helper -sを実行してinitで追加されるパスを上書きしているのが悪いようだ.
.zshrceval $(anyenv init -)を追加することで解決した.設定ファイルの読み込み順の問題みたいなので/etc/zprofileを読み込まなくするか,以降に読み込まれるファイルに設定を追加すると解決するみたい.

まとめ

2つの原因でハマっていてかなり困った.初期化処理が何をしているか理解すれハマらずにすんだと思うので,これからはその辺りも理解してやっていきたい💪