じゃこscript

好きな言葉は炙りしめ鯖です

2018年の振り返り、来年の抱負

晦日ということで、タイトル通り改めて2018年を振り返ってみる。
といっても大きなイベントだけのピックアップ。

2月

フルートのレッスンセンターに通い始める

フルート自体を始めたは2017年の10月からだったが、どうせやるならプロに教わって練習した方が確実に上達するだろう…ということで通い始めた。
体験レッスンの時点で悪い癖を指摘してもらったり、独学で躓いていた箇所をあっさり吹けるように指導してもらうなど、やはりプロはゴイスーなのだと感じた。
月4回のレッスンで¥18,000はそこそこ痛い出費だが、それに見合うだけの価値はあると思っている。
レッスン内容としては、40分のうち最初の20分をエチュード、後半の20分をぼくが吹きたい曲の練習って感じ。
先生は音大卒のとても優しいお姉様で、指導もわかりやすい。
(いないと思うけど)フルート初心者で教室を探している人がいたら、新宿のムラマツフルートがオススメです。土日の枠もあるしね。

7月

人生初の一人暮らし

実家を出たのは20歳ぐらいの頃だったが、これまでずっとルームシェアだったため、一人暮らしというものを経験したことがなかった。
(ルームメイトが実家に帰るタイミングなどは除く)
ちょうど今年、それまで住んでいた物件の更新時期がやってきたのだが、次の更新時には2人共アラサー男子になっていることを考えるとそろそろねぇ…?ということで一人暮らしを始めることに。
とはいえ住んでいた地域の住心地が抜群に良かった(アクセスetc)ため、7丁目から6丁目に引っ越すことにした。徒歩3分くらいのとこ。

ルームシェアから一人暮らしに変わって感じたことというと、思ったより一人暮らしって楽なんだなーと。
ルームシェア中は「ぼくが洗濯、あなたは掃除」のように家事を完全に分担することがなく、手が空いている方がよしなにやるゆるゆる分担だったので、体感0.5家事が1家事になったくらいのイメージ。
0→1は経験値的な話だったりで多分めっちゃ大変だと思うけど、0.5→1は単純に頻度が増えるくらいで特に問題はなかった。料理好きだし。
そういった意味では冷蔵庫を100%自分用に使えるのはデカイなあと思った。
ちなみに間取りは2DKだけど、寝室は3.5畳しかない(加えて台形なのでデッドスペースだらけになってる)し、仕事部屋はPCデスクに防音室(後述)とピアノを置いているのでとても狭い。めちゃくちゃ狭い。

11月

n回目の転職

Nがいくつなのかは直接聞いてください(震え声)
不動産系ベンチャーからHR系スタートアップに転職した。
転職の契機としては、転職した方が面白そうだったから。

これだけ書くとクソ人間にしか見えないが、実際「面白そう」という価値基準は自分の中でとても重要な指標になっていて、
具体的には「(社会問題の解決手段になりそうだし実現したら世の中もっと楽しくなるし立ち上げメンバーとして色々苦しみながらチャレンジできそうだし)面白そう」という感じだったりする。
前の会社に対する不満がないかと言えばウソになるが、そこまで表立った不満はなかった。
ただ、組織が大きくなっていく上で、成長のベクトルが自分が会社に望んでいたものから反れはじめていたので、辞めるならそろそろかなーと思い始めていた。
SaaS事業を謳いつつ大口のクライアントからのカスタマイズ案件という名の受託業務をこなしている状態で、売上は模範的な成長曲線を描いていたものの、
果たしてサービス提供者としての「良い」サービスを作れていたかどうかがわからなくなっていてやっぱつれぇわって感じだった。ちゃんと言えたじゃねえか。
やっぱり自分の作ったものを多くの人に使ってほしいし、愛着を持って育てていきたい。
でもこの「受託で安定したキャッシュを産んでいく」こと自体は全く悪くなくて、むしろその体制を作ったことは本当にすごいと思うし(一部の社員に偏って負担はかかっていたものの)、 こういった組織にフィットする人材は必ずいるので、このまま続けていって問題ないと思う。
ただそこに自分が働いていて楽しいか?って言われるとうーん違うんじゃねって感じだったので転職した。 繰り返しになるが、会社に良い・悪いは存在しなくて、単に合う・合わないの問題だったということかなあ。

現職は神楽坂にあるスタートアップで、世界的な人材不足問題を解決していくサービスを作っている会社。働いている人が一人残らず優の秀なので、スキル不足を痛感する毎日だけどめちゃめちゃワクワクしながら仕事してる。
ポジションとしてはエンジニアとデザイナーの中間的な立ち位置で、デザイナーさんと連携しつつフロント(React)やバックエンド(Rails)の実装したり、時々自分でデザインしたり…といった感じ。
あれ、これ前職と変わってないじゃん!!

27歳男子、防音室を買う

フルートはサックスやトランペットなどとは異なり、ミュートがない。
加えて高音域なので、賃貸物件で練習した日にはそれはそれは苦情の嵐である。
なので練習する時には近くのカラオケ内で練習しているのだが、

  • 休日、休前日などは混んでて入れないことがある
  • 会社帰りに練習する場合、会社にフルートを持っていく必要がある
  • タバコ臭い

などの理由により、レンタルの防音室(組み立てるやつ)の導入することに。
カラオケってなんだかんだそこそこお金かかるし、なら防音室買ってもトントンなんじゃね?という心づもり。
7月に引っ越した時も、「防音室を設置してもいい物件」というくそ無茶振りを仲介会社にかまして困らせた記憶はまだ新しい。
ただ設置にあたって少々問題があり、ぼくが購入したのは1.2m四方の防音室だったのだが、防音用に分厚い壁や床板などを使用している都合上、自分の採寸と合わないスペースがあったようで、防音室と壁の間にデッドスペースが生じてしまった。くぅ〜〜。

防音室を設置したことにより、生で吹くと騒音レベルだったフルートの音が大分小さくなった気がする。
スマホをベランダに置いて録音してみた(防音室+窓)けれど、ほぼほぼ聞こえなくなっていた。やったね。

余談だけどこのカラオケ、

  • 持ち込み可でドリンクオーダー制とかない
  • 料金も従量制(部屋にいた時間分だけ払う、延長の連絡とかもない)
  • 会計も支払い用のモニタに会員カードのQRコードをかざして払うだけ

なので、コミュ障にとっては神的な存在であった。全国展開してほしい。
ちなみに1年間で70回ほど通っていたが、1曲も歌うことはなかった…

2019年の抱負

というわけでクソほど飛び飛びの振り返りになったが、来年何したいかというお話を書いていく。

楽団に入る

フルートを吹きはじめて1年ちょいということで、人前で楽器を演奏する経験を積んでいきたい。
なので、アマチュアの楽団に所属しようと思っている。モチベーションにもなるしね。
でもフルートって意外と買い手市場らしい。世知辛い。

会社を(サービスを)成長させる

これを達成するには打ち手…というかやらなきゃいけないことがたくさんあって、それを全部がむしゃらにやっていきましょうというお気持ち。

  • マネジメント
    • これまでプレイヤーとして仕事することがほとんどで、誰かと仕事する上で効率的な進め方とか考えたことがあまりなかった。でも人数の少ないスタートアップだからこそ最大効率で戦うべきなので、知識を身に付けつつ実践あるのみ。
  • 経営
    • マネジメントと同じような話になるけど、最大効率化する上で必要だと思うので。商学部なんだからちゃんと授業聞いておけばよかった…
  • デザイン
    • 現状インハウスデザイナーがいない状態なので、本職のデザイナーレベルとまではいかずとも、軽めの業務はできるレベルまで持っていきたい。デザイナーさんとの仕事が進めやすくなるという副次的メリットもあるよね。
    • UIトレーシングをちょくちょくやっているので継続していく。個人で作ったサービスを設計から見直してレビューしてもらう。
  • エンジニアリング
    • サービスを磨いていく上で必要な武器が心もとない&性能が良くないので、磨いていきたい。
    • 個人サービスは引き続き作っていく。ブログやイベントなど、自分の知見を他人に公開する場を設ける。

自動車免許取る

ゆるキャン△見てキャンプデビューした結果必要だと感じたので。

Vue-test-utilsではじめてのフロントエンドテストを書きました

背景

会社では主にディレクションやフロントエンドを担当することが多いのですが、恥ずかしながらフロントエンドのテストコードを書いたことがありません…
(そんな状態でフロントエンドエンジニアとして名乗るのもおこがましいですが…)。

現職でもエンジニアの数が少なく、これまでフロントエンドのテストコードを書く習慣がありませんでした。
そんな環境でプロダクトが大きくなるにつれ、改修に伴う動作チェックのコストが少しずつ肥大化していき、
つらみが増してきたのでテストを書いてみよう!と決意した次第です。

主に Vue.js を使う機会が多いため、Vueコンポーネントのテストコードを書いて最低限の品質を担保できるようになることを目標としたいと思います。
(欲を言えばフロントエンドのテストにおけるお作法とかも学びたい…!)

概要

  • Vue.js公式が提供している vue-test-utils について調べる
  • テストコードのサンプルを書いてみる
  • フロントエンドのテストコードにおいて、何を担保すべきかを調べる

vue-test-utilsとは

vue-test-utilsは Vue.js 向けの公式単体テストライブラリです。

https://vue-test-utils.vuejs.org/ja/ より

avoirazという非公式のテストライブラリを開発していた方が中心となって公式テストライブラリを作った、という背景があるようです。

テストを書いてみる

セットアップ vue-test-utils の使い方を体験したい場合は、基本設定としてデモリポジトリをクローンし、依存関係をインストールしてください。

Vue Test Utils | はじめるより

公式がサンプルを用意してくれてますね。
というわけで早速環境構築を進めていきたいと思います。

$ git clone https://github.com/vuejs/vue-test-utils-getting-started
$ cd vue-test-utils-getting-started
$ npm install

アプリケーションのコードは counter.js と、そのテストとなる test.js のみのシンプルな構造ですね。
インストールが完了したので、とりあえずテストを実行してみます。

$ npm test

> vue-test-utils-getting-started@1.0.0 test /Users/jaco/Development/vue-test-utils-getting-started
> jest

 PASS  ./test.js
  Counter
    ✓ renders the correct markup (4ms)
    ✓ has a button (7ms)
    ✓ button should increment the count (2ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        0.597s, estimated 1s
Ran all test suites.

テストが通っていることが確認できました。
では各ファイルの実装を見ていきましょう。

// counter.js

export default {
  template: `
    <div>
      <span class="count">{{ count }}</span>
      <button @click="increment">Increment</button>
    </div>
  `,

  data () {
    return {
      count: 0
    }
  },

  methods: {
    increment () {
      this.count++
    }
  }
}

ボタンを押すとカウンターがインクリメントされるだけのコンポーネントですね。

// Import the mount() method from the test utils
// and the component you want to test
import { mount } from '@vue/test-utils'
import Counter from './counter'

describe('Counter', () => {
  // Now mount the component and you have the wrapper
  const wrapper = mount(Counter)

  it('renders the correct markup', () => {
    expect(wrapper.html()).toContain('<span class="count">0</span>')
  })

  // it's also easy to check for the existence of elements
  it('has a button', () => {
    expect(wrapper.contains('button')).toBe(true)
  })

  it('button should increment the count', () => {
    expect(wrapper.vm.count).toBe(0)
    const button = wrapper.find('button')
    button.trigger('click')
    expect(wrapper.vm.count).toBe(1)
  })
})

テストコードにコメントまでつけてくれている…!
気になるところをもうちょっと調べてみます。

const wrapper = mount(Counter)

mount メソッドによって、 import したコンポーネントのラッパオブジェクトを生成します。
mount以外にも shallowMount メソッドが存在し、
- mount: 子コンポーネントをスタブ化せずにマウントする - shallowMount: 子コンポーネントをスタブ化してマウントする といった違いがあるようです。

基本的にはこのラッパオブジェクトに定義されているメソッドを利用してテストコードを実装していくみたいですね。

console.log(wrapper) するとわかりますが、 wrapper.vm 経由でVueインスタンスにアクセスすることができます。
ラッパオブジェクトのプロパティ・メソッドについては公式ドキュメントにまとめられています。

console.log(wrapper)

// 以下はテスト結果の一部
VueWrapper {
  vnode: [Getter/Setter],
  element: [Getter/Setter],
  options: { attachedToDocument: false, sync: true },
  version: 2.4,
  vm:
    VueComponent {
      _uid: 0,
      ...

基本的な流れとして、
1. ラッパオブジェクトを生成する
2. テストケースに合わせてラッパオブジェクトのプロパティ・メソッドを利用し、テストコードを記述する
といった感じでしょうか。 この辺はテストコードを書いたり読んだりして学んでいきたいです。

フロントエンドのテストで何をテストすればいいの問題

実はこの辺りの疑問についても公式ドキュメントでサポートしてくれています。
Vue.jsの公式ドキュメントの充実具合は本当にすごいですね…

UI コンポーネントでは、コンポーネントの内部実装の詳細に集中しすぎて脆弱なテストが発生する可能性があるため、完全なラインベースのカバレッジを目指すことはお勧めしません。

代わりに、コンポーネントのパブリックインターフェイスを検証するテストを作成し、内部をブラックボックスとして扱うことをお勧めします。単一のテストケースでは、コンポーネントに提供された入力(ユーザーのやり取りやプロパティの変更)によって、期待される出力(結果の描画またはカスタムイベントの出力)が行われることが示されます。

たとえば、ボタンがクリックされるたびに表示カウンタを 1 ずつインクリメントする Counter コンポーネントの場合、そのテストケースはクリックをシミュレートし、描画された出力が 1 つ増加したのか検証します。カウンタは値をインクリメントし、入力と出力のみを扱います。

このアプローチの利点は、コンポーネントのパブリックインターフェイスが同じままである限り、コンポーネントの内部実装が時間の経過とともにどのように変化してもテストは合格になります。

このトピックは、Matt O'Connell による偉大なプレゼンテーションで詳細に説明されています。

Vue Test Utils | ガイドより

…恥ずかしながらぶっちゃけよくわかってないので、わたしなりに噛み砕いてみます(一度自分で検討をつけてみて、知見のある方に伺ってみようと思います)。

UI コンポーネントでは、コンポーネントの内部実装の詳細に集中しすぎて脆弱なテストが発生する可能性があるため、完全なラインベースのカバレッジを目指すことはお勧めしません。 代わりに、コンポーネントのパブリックインターフェイスを検証するテストを作成し、内部をブラックボックスとして扱うことをお勧めします。単一のテストケースでは、コンポーネントに提供された入力(ユーザーのやり取りやプロパティの変更)によって、期待される出力(結果の描画またはカスタムイベントの出力)が行われることが示されます。

内部のメソッドまでテストするのではなく、コンポーネントの振る舞いをテストしよう、ということですかね。

× コンポーネント内部の◯◯というメソッドが正しく動いていることをテストする
◯ 呼び出し側から見て、(内部で何が起こってるのかは知らないけど)コンポーネントが正しく動いていることをテストする

サンプルコードに置き換えると、

× incrementメソッドを実行するとcountプロパティが加算されることをテストする
◯ @clickを発火するとcountプロパティが加算されることをテストする

このようなイメージです。
@clickを発火するとspanの中身が加算されることをテストする でもいいのでは?と思ったのですが、

it('renders the correct markup', () => {
  expect(wrapper.html()).toContain('<span class="count">0</span>')
})

このテストで count が正しくviewに反映されていることが担保されているので、
よりシンプルに、 @click イベントによる挙動をテストしているのではないかと思います。

たとえば、ボタンがクリックされるたびに表示カウンタを 1 ずつインクリメントする Counter コンポーネントの場合、そのテストケースはクリックをシミュレートし、描画された出力が 1 つ増加したのか検証します。カウンタは値をインクリメントし、入力と出力のみを扱います。

このアプローチの利点は、コンポーネントのパブリックインターフェイスが同じままである限り、コンポーネントの内部実装が時間の経過とともにどのように変化してもテストは合格になります。

@clickというインターフェースが変わらない限り、incrementメソッドの名前や内部のロジックが変わったとしても、@clickを発火することで値がインクリメントされることはテストできるよね

ということでしょうか?
ここちょっと自信ないので要確認です…

また、技術書典で購入させていただいた、@mya-akeさんの現場で使えるVue.js tips集によると、

基本的にはホワイトボックステストというよりもブラックボックステストをする感覚がよいと思います。 具体的には次の3つあたりをテストすると効果が高そうです。

  1. 無事にマウントされること
  2. イベントが発火し期待した結果となること
  3. propsで受け取る値により期待した結果となること

とのことです。
propsコンポーネントのインターフェースの1つですし、ブラックボックステストとしての観点でテストしておいた方が良さそうですね。

※内容を記載することについては@mya-akeさんから承諾をいただくことができました。ありがとうございました!
※余談ですがこのtips集、とーーーーーーっても実用的でした!
(個人的に今年の技術書典で1番買って良かったと思っています( ◜◡◝ ))

所感

めちゃめちゃ長くなってしまいました…
あらためて、Vue.jsはドキュメント周りがしっかりしているな…と感じました。
vue-test-utilsJestKarma といったテストランナーを組み合わせることで真価を発揮するようなので、
いずれはこの辺りについても調べてみようと思います。

テストについては本当にド素人なので、ここ間違ってるよ〜・こうするといいかもねといったアドバイスをいただけるととても嬉しいです。

Dockのエフェクトをジニーからスケールに変更したらMacのフリーズ率が激減した話

背景

タイトル通りです。
1ヶ月ほど前に会社で新しいMBPを支給してもらったのですが、それからアプリケーションを最小化する際にPCがフリーズする現象が多発してしまい…
酷い時には1日に5~6回再起動することになり、原因を調べることになりました。

概要

PC新調前後それぞれの環境は以下の通りです。

新調前

MacBook Pro (Late2014) 15inch
OS Sierra

新調後

MacBook Pro (Late2017) 13inch
OS High Sierra

※メモリやCPUについては確かほぼ変わらなかった記憶があります。

調査

Mac Application minify freeze などググるものの、これといった解決策は見当たらず…
High Sierra が未だに動作が不安定という話は耳にしていたため、OSの問題かなぁと諦めようとしたその時、

後輩「どうでもいいですけど、ジニーエフェクトって無駄に挙動凝ってて好きじゃないんですよねー」
わたし「ほう…?」

ダメ元で Dockの最小化エフェクトをジニーからスケールに変更 したところ、これが当たりだったようです。
以後フリーズすることはなくなりました。

所感

確かにジニーエフェクトは開発側の「どう?ヌルっと動いてすごいやろ?」感がありますね!!
(いるかどうかはさておき)同じような現象に悩んでる方のお役に立てれば幸いです。

会社でフロントエンド研修課題をつくりました(その3. とりあえず実装してみる)

背景

こんな流れで研修課題を作ることになりました。

概要

  • 課題内容を考えてみる
  • 環境構築手順をまとめる
  • 実際にアプリを作ってみる ←いまここ

実際にアプリを作ってみる

前回で環境構築が完了したので、今回からはコードを書いていきましょう。
ゴールは Vue.js+Vuexを使ったTodoアプリ です。
とはいえ、いきなりコンポーネント化しつつVuexも使いつつ…というのもハードルが高いので、
少しずつ段階を踏んでいくようにしましょう。

  1. Vue.jsのみで1ファイルでTodoアプリを作る
  2. コンポーネント化を進める(ファイル分割)
  3. Vuexで状態管理する
  4. Vuelidateでリアルタイムバリデーションを実装する
  5. テストコードを書く

こんな感じですかね。

では、まず1ファイルでTodoアプリを作ってみましょう。

Vue.jsのみで1ファイルでTodoアプリを作る

というわけで出来上がったコードがこちらになります(3分クッキング感)。

App.vue

<template>
  <div class='todo'>
    <ul>
      <li v-for="item in items" :key="item.index">
        <label :class="{ checked: item.isChecked }">
          <input type="checkbox" v-model="item.isChecked"> {{ item.title }}
        </label>
      </li>
    </ul>
    <div>
      <input type="text"
        placeholder="タスクを入力してみよう"
        v-model="newItemTitle" />
    </div>
    <div>
      <button @click="addTodo(newItemTitle)">タスクを追加する</button>
      <button @click="deleteTodo(newItemTitle)">チェックをつけたタスクを削除する</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  data: function() {
    return {
      items: [
        {
          title: '夕飯の材料を買いに行く',
          isChecked: false
        },
        {
          title: 'お風呂の掃除をする',
          isChecked: true
        },
        {
          title: 'ゴミ出しをする',
          isChecked: false
        }
      ],
      newItemTitle: '',
    }
  },
  methods: {
    addTodo(newTitle) {
      let item = {
        title: newTitle,
        isChecked: false
      }
      this.items.push(item)
    },
    deleteTodo() {
      this.items = this.items.filter(item => {
        return item.isChecked == false
      })
    }
  }
}
</script>

f:id:ia_isier:20180526011559p:plain

画面はこんな感じです。
見た目が非常にアレですが、とりあえずこのまま進めていきましょう。

コンポーネント化を進める(ファイル分割)

次の段階として、ボタンをコンポーネント化しましょう。
インターフェースとしては、テキストクリックした時のイベント 辺りを渡せれば十分ですかね。

App.vue

<template>
  <div class='todo'>
      // 略
    <div>
      <TodoButton @buttonClick="addTodo(newItemTitle)" :text="'タスクを追加する'"></TodoButton>
      <TodoButton @buttonClick="deleteTodo" :text="'チェックをつけたタスクを削除する'"></TodoButton>
    </div>
  </div>
</template>

<script>
import TodoButton from './components/Button'

export default {
  name: 'App',
  components: {
    TodoButton: TodoButton
  },
  // 略
}
</script>

components/Button.vue

<template>
  <button @click='onClick()'>{{ text }}</button>
</template>

<script>
export default {
  name: 'Button',
  props: {
    text: {
      type: String,
      default: 'default',
    },
  },
  methods: {
    onClick: function() {
      this.$emit('buttonClick')
    },
  },
}
</script>

コンポーネント化しただけなので、特に見た目は変わりません。
次は Vuexで状態管理する ですが、これは導入からやると長くなりそうなので今回はここまでにします。

所感

書いてて「良い研修課題ってなんだろう…?」と何度も頭を捻ってしまいました。
わかりやすさと楽しさとスキルの身につけやすさ、これらが同居するようなものを作れるといいのですが…

会社でフロントエンド研修課題をつくりました(その2. 環境構築)

背景

こんな流れで研修課題を作ることになりました。

概要

  • 課題内容を考えてみる
  • 環境構築手順をまとめる ←いまここ
  • 実際にアプリを作ってみる

環境構築手順をまとめる

研修課題なので、サクッと作れて何かトラブった時にサクッと壊せるのがベターですね。
というわけでvue-cliを使います。
こういう「とりあえずでいいからvue試してみたいねん」って時にめっちゃ役立ちます。
もしかしたら本番のプロダクトでも実運用できるのかな…?要調査ですね。

では早速使っていきましょう!

vue-cliのインストール

$ yarn global add @vue/cli
$ vue create vue-project
?  Your connection to the the default npm registry seems to be slow.

上記のコマンドを実行するだけでプロジェクトを作成してくれます。
途中いくつか質問を受けるので、適宜回答していきましょう。

注: ネット上でよく見る vue init ◯◯(テンプレート名) ◯◯(プロジェクト名) ですが、init コマンドは最新の3 系では古くなっているため、 create コマンドを使用するといいみたいです。

Use https://registry.npm.taobao.org for faster installation? (Y/n)

デフォルトで参照している npm package registry では接続が遅くなるとのこと。
こっちの方が速いぜ!と勧めてくれているので Yes と答えておきましょう。

? Please pick a preset: (Use arrow keys)
❯ default (babel, eslint)
  Manually select features

babeleslint も用意されているデフォルトのプリセットで問題ありません。 default を選択。

? Pick the package manager to use when installing dependencies: (Use arrow keys)
❯ Use Yarn
  Use NPM

パッケージマネージャーは yarn でいいですね。
諸々必要なパッケージのインストールが始まります。

✨  Creating project in /Users/jaco/Development/vue-cli.
🗃  Initializing git repository...
⚙  Installing CLI plugins. This might take a while...

yarn install v1.3.2
info No lockfile found.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
success Saved lockfile.
✨  Done in 52.59s.

🚀  Invoking generators...
📦  Installing additional dependencies...

yarn install v1.3.2
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
success Saved lockfile.
✨  Done in 2.97s.

⚓  Running completion hooks...

🎉  Successfully created project vue-project.
👉  Get started with the following commands:

 $ cd vue-project
 $ yarn serve

至れり尽くせりですね。
指示通りにコマンドを実行しましょう。
(どうでもいいですが、何故か「いたせりつくせり」だと思っていました…)

$ cd vue-project
$ yarn serve
 DONE  Compiled successfully in 3570ms


  App running at:
  - Local:   http://localhost:8080/
  - Network: http://192.168.179.5:8080/

  Note that the development build is not optimized.
  To create a production build, run yarn build.

内部で webpack-dev-server が立ち上がっているため、自動でブラウザ上で http://localhost:8080/ を開いてくれます。

f:id:ia_isier:20180526003345p:plain

これで環境構築は終了です。良い時代になりましたね。
次回からは実際にコードを書いていこうと思います。

会社でフロントエンド研修課題をつくりました(その1. 何をテーマにするか)

背景

現職のインターン生はほぼほぼバックエンドを担当しているのですが、
先日「フロントエンドも最低限開発できるようになりたい」という要望を受けました。

フロントエンドにおける研修課題のようなものはなかった(バックエンドはある)ため、いっちょやったるかーという次第です。
インターン生にヒアリングしてみたところ、

以上の需要があるようでした(コーディングも楽しいと思うんだけどなあ。。。)。

こういった研修課題は一種の集合知というか、作ってFBもらって改善して…というサイクルを回していくことで効率よく質を上げることができるので、
まずはお試しでプロトタイプのようなものを作ることを目標としたいと思います。

概要

おおまかにこんな流れで進めてみます。

  • 課題内容を考える ← いまここ
  • 環境構築手順をまとめる
  • 実際に課題を作ってみる

課題内容を考える

楽しく学べて、仕事に役立つスキルが身につくというのが自分の考える研修の理想像です。
要素を分解すると、

楽しく学べる→自分の関心がある要素が詰まっている
仕事に役立つ→実務で使えるノウハウが身につけることができる

こんな感じですかね。

わたしからインターン生に期待する(身につけてほしい・身につけると役に立つ)スキルとしては、

  • HTML/CSSで最低限のコーディングができる
    • とはいえゼロベースで新規機能のフロント実装…というのも厳しいので、「コーディング規約とスタイルガイドを参考にコーディングができる」と定義しておきます。
  • 要件を元にCSS, Javascriptを使って必要な機能を実装することができる

以上が挙げられます。
これに、

というインターン生からの要望を織り込んでみましょう。

あれ…Vue.js+Vuexを使ったTodoアプリでイケそうな気がしてきました。

現職ではAPIから取得した設定値に基づいてコンポーネントを配置するケースが多いため、
API制御周りも含めようと考えてました。
が、良いサンプルが思いつかなかったので、その辺は折を見て導入できればと。。。

というわけで、
Vue.js+Vuexを使ったTodoアプリ という、
いんたーねっつで検索かければいくらでも出てきそうな内容の研修課題となりましたが、とりあえずこの方向性で作ってみようと思います。
次回は環境構築編です。

localstorageに意図せずundefinedを突っ込んだらハマりました

localStorage を久しぶりに使う機会があって、ハマったのでメモしておきます。

背景

// hogeはobjectを想定しています
localStorage.setItem('item', JSON.stringify(hoge));

~~~~~~
let item = JSON.parse(localStorage.getItem('item'));
console.log(item); // object?

もちろんAPIから取得した object が表示されることを期待していたのですが、Uncaught Syntaxerror: Unexpected token u が発生。
調べてみると undefinedJSON.parse すると発生するようですね。
というわけで

// hogeはAPIからfetchしてきたobjectを想定しています
localStorage.setItem('item', JSON.stringify(hoge));

~~~~~~
let moge = localStorage.getItem('item');
if (typeof moge === undefined) return;
let item = JSON.parse(moge);
console.log(item); // object?

これなら大丈夫だろうと思いきや、ログには undefined が…
なんで undefined の判定入れてるのにすり抜けるんだろう?というかなんで JSON.parse() できてるの…

解決法

10分ほど悩みましたが、よく考えたら

localStorage.setItem('item', JSON.stringify(hoge));

ここで文字列として突っ込んでるんですよね。

console.log(typeof item); // String

ですよねー。
localStorageに String として突っ込んだ undefined はパースしても Stringとしての undefined だよ、という話でした。

まあAPIの返り値からちゃんとチェックしろという話なんですよね…これ…
localStorageの仕様を身体で覚えることができたということでとりあえずは良しとします…