「TODOアプリづくり」で知識の整理 (+ write code every day)
このスクラップについて
概要
自分が知っている知識を駆使+新しい領域にチャレンジして「TODO管理アプリ」を真面目に作ります。
このスクラップには、考えたことや知ってることをアプリ開発を通して気づいた・思い出したベースで記録していこうと思います。
目的
- 知識の整理と記録すること
- 正しいかわからないが、考えたことを残すこと
目的じゃないこと
- 人に読んでもらうために文章練習
- 基本的に人に読んでもらうときは記事にする
- スクラップは、記事にするための下書きになると思われる
※指摘・質問コメントは大歓迎です。
なぜ始めたか
仕事でかなりいろいろなことを経験して、何度か力不足を痛感して挫折しかけて、それを乗り越えてきました。
入社してから3年分の知識は、そこそこ膨大になったけれども、それらは形あるものとしてあまり残せていないのが現状です。
誰にも評価してもらってないこの知識は、本当に価値のあるものなのか。
「自分はそこそこできるようになったかも☺」という気持ちは井の中の蛙でしかないのではないか。
そこで、「TODOアプリ」を知ってる知識を整理しながら作ってみようかなと思います。
題材「TODOアプリ」なのはなぜか
- ドメイン知識がイメージしやすいこと(TODOを記録する、という基本概念はイメージしやすく見てもらうひとに説明する内容が少なく済む。)
- 小さく始めることもできる(初心者の作ってみたの題材になることもあるし、スコープを小さくして始めることが可能)
- 発展性も高い。(カンバンボードにしたり、業務で利用できるものまで昇華させてもよい。)
なんでもよかったのですが、この辺の理由からこれを選びました。
もっと作りたいものができたらそちらにするかもしれません。
最終的に人にみせることで、人から見た自分の評価がもっと輪郭がはっきりするし、入社したころの「わからないこともわからないだらけ」の自分への道しるべになるかなと思います。
また、自分の中で代表的な作ってみたものをもっていると新しいことを試すときに評価がしやすいと思うので、ずっと何か作りたいとなお持っていました。
車輪の再発明は無駄といわれますが車輪の再実装は価値があると思います。
前置きはこれくらいにして、明日から少しずつ継続的に更新していこうと思います。
全体的な進め方
- githubにパブリックリポジトリを作成して、設計と実装したものを管理する
- 考えたことやTipsはスクラップに
-
イテレーションを2週間で設定し、短期間で目標をたて、設計して、実装して、ふりかえることをしていく(ひとりアジャイル開発)- この進め方自体もふりかえり時にかえるかどうか判断する
- (3/25追記) write code every dayで毎日コツコツ積み重ねていくこととする(別のリポジトリにコミットした日はTODOアプリ関係はお休み)
プログラム練習・なにかアプリ作るときにやってること
- リポジトリを新しく作成する
- githubで作成 https://github.com/new (readmeとLICENSEファイルを作っておく)
- ローカルにクローン
PS D:\workspace\repos> git clone https://github.com/aocm/my-todo-app-challenge-prototype.git
Cloning into 'my-todo-app-challenge-prototype'...
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), done.
このタイミングでの各便利ツールの紹介とセットアップ
-
GitのGUIクライアント(なんでもいいけど、以下に一例)
- gitkraken
- sourcetree
-
VSCodeの拡張機能
- Project Manager... 画像のように、プロジェクトのパスを設定できる。簡単に別のディレクトリを開けるようになるので、VSCodeでいろいろ触るひとはおすすめ。
write code every dayにも挑戦
t_wadaさんの講演を聞いて挑戦することにしました。
Day1
- リポジトリ作成+Quasarセットアップ
Day2
- 画面レイアウト作成
Day3
- 画面レイアウトを意識して、Tasks.vueを作成
ここで気づいたが、main(デフォルト)ブランチに反映されるまで芝にならない。
Day4
やったこと
- AtomicDesignを意識して、コンポーネントを分解。
- atoms→components/atomsに作成(今回はQuasarのパーツがatomsとして、いったん)
- molecules→components/moleculesに作成(今回はタスクアイテムを配置)
- organisms→components/organismsに作成(今回はタスク一覧コンポーネントやメニュー一覧コンポーネントを配置)
- templates→layoutsに作成。organismsらを組み合わせる役目。(今回はタスクページの見た目を担う)
- pages→pagesに作成。templatesにデータを投入する役目。(今回はタスクページのデータ管理を担う。今後はここにAPI操作やStore管理する)
考えたこと
今後はStorybookでコンポーネント管理するのは決めているけど、データ管理をどうするかは悩み中。
今のかきっぷりだと、バケツリレーになってしまうが、そういうもんだろうか。要調査。
Day5
やったこと
- TaskListとTaskItemのEmitでイベント受け渡し
考えたこと
- コンテナの中で作業していると、npm(/yarn)モジュール追加がすごく遅く感じる。ホストPCはネットワークは100mbpsでているのにコンテナ内は明らかに遅い。
- Dockerのメモリ割り当て2GBしかしてないことが原因であればちょっと拡張してもう一度試す。もしくは、開発環境をコンテナからWSL2とかに変えてみようかと。
- QuasarのものにStorybookを追加することにてこずっている。そもそもStorybookを使ったことがないので、VueCliでつくったシンプルなプロジェクトで試してみたほうがいいかもしれない。
Day6(2021/2/28)
- いったんフォルダ構成を変更する。
- Quasarパターンの隣にStorybookを使ったコンポーネント管理したフロントを配置しようとおもったのだけれど、QuasarとStorybookの組み合わせがうまくいかず
Day7(2021/3/1)
Storybookがそもそも不慣れなので、Storybookの公式ページを写経しながら練習してみる
理解ふかまってから元のTODOアプリに合流する
2021/3/2
Storybookの公式ページを写経つづき
2021/3/3
Storybookの公式ページを写経つづき(composite-component)
ここでミスが。
Automated Testingの項目で真似してかくも、テスト失敗する
FAIL tests/unit/TaskList.spec.js
● renders pinned tasks at the start of the list
expect(received).not.toBe(expected) // Object.is equality
Expected: not null
22 |
23 | // We expect the pinned task to be rendered first, not at the end
> 24 | expect(firstTaskPinned).not.toBe(null);
| ^
25 | });
at Object.<anonymous>.it (tests/unit/TaskList.spec.js:24:31)
やったこと・考えたこと
-
:nth-childってなんだ?
- https://developer.mozilla.org/ja/docs/Web/CSS/:nth-child
- リスト内の要素をこのように指定できるのは初めて知った
-
セレクターの書き方がミスってるんか?
- これならnullじゃなかった
const firstTaskPinned = vm.$el.querySelector('.list-item:nth-child(1)');
- TASK_PINNEDが誤っているっぽい。
-
.list-item:nth-child(1).TASK_PINNED
... あれ、stateに渡すことはしたけどクラス指定したっけ? - 書き漏れでした。simple-componentのBuild out the states
<div class="list-item" :class="task.state">
- これならnullじゃなかった
-
TaskListのテストは通るようになったけど、(当たり前だが)スナップショットテストが落ちた。
$ vue-cli-service test:unit
PASS tests/unit/example.spec.js
PASS tests/unit/TaskList.spec.js
(node:18140) DeprecationWarning: \`storyFn\` is deprecated and will be removed in Storybook 7.0.
https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-storyfn
FAIL tests/unit/storybook.spec.js (7.666s)
略
› 3 snapshots failed from 1 test suite. Inspect your code changes or re-run jest with `-u` to update them.
Test Suites: 1 failed, 2 passed, 3 total
Tests: 3 failed, 6 passed, 9 total
Snapshots: 3 failed, 4 passed, 7 total
Time: 9.012s
Ran all test suites.
- Snapshotテストの-uってなんだ?
- https://deltice.github.io/jest/docs/ja/snapshot-testing.html
- --updateSnapshotで変更を受け入れることができるとのこと。
- -uをつけて実行→無事更新できた。
PS C:\workspace\repos\taskbox> yarn test:unit -u
yarn run v1.16.0
$ vue-cli-service test:unit -u
PASS tests/unit/example.spec.js
PASS tests/unit/TaskList.spec.js
(node:5628) DeprecationWarning: \`storyFn\` is deprecated and will be removed in Storybook 7.0.
https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-storyfn
PASS tests/unit/storybook.spec.js (7.548s)
› 3 snapshots updated.
Snapshot Summary
› 3 snapshots updated from 1 test suite.
Test Suites: 3 passed, 3 total
Tests: 9 passed, 9 total
Snapshots: 3 updated, 4 passed, 7 total
Time: 9.577s
Ran all test suites.
Done in 11.11s.
2021/3/4
VuexとStorybookのつながりが疑問だったのでこの章をやって何点か気づきがあった。
- PureTaskList.vueとTaskList.vueの分解はテスト容易性の話から行っていたが、AtomicDesignの文脈のTemplateとPagesの分解をみているようだった。AtomicDesignを実用性もたせるためにドメイン知識を持たせるorganismsからVuexを絡ませるというQiita記事を思い出した。TaskListというコンポーネントであることも踏まえると、比較的このQiita記事の考え方がStorybookと相性がいいのかもしれない。
- PureTaskList.vueにemitの受け口つけてないけどどうするんだろうと思っていたが$listenersが肝っぽい。(これを外したら処理がよばれなくなった。)
$listeners考察
<template>
<PureTaskList :tasks="tasks" v-on="$listeners" @archive-task="archiveTask" @pin-task="pinTask" />
</template>
<template v-else>
<Task v-for="task in tasksInOrder" :key="task.id" :task="task" v-on="$listeners" />
</template>
listeners" を使って特定の子要素に送ることができます listeners プロパティを使うことで、コンポーネントの全てのイベントリスナを v-on="
TaskList.vueのイベントリスナを全部PureTaskListコンポーネントに送り付け、
PureTaskList.vueはそれを受け取ってそのリスナふくめてTaskコンポーネントに送り付けている?
現時点だとTaskList.vueのv-on="$listeners"はなくてもうごくが、さらに親(TaskList.vueを含めたコンテナ・Page)が作られたときなどに親から渡されたときにいい感じにバケツリレーしてくれる、という感じだろうか。
Tips propsに書いていない属性
Vueでは、propsに書いてないattributeはコンポーネントのルート要素に設定される。。。知らなかった、、、
なるほど。
これのソースを探したらv3の記事しか見当たらなかったけど、たぶんv2系でも同じってことなんだと思われる。
When a component returns a single root node, non-prop attributes will automatically be added to the root node's attributes.
(DeepLで翻訳:コンポーネントが単一のルート・ノードを返す場合、非プロップ属性はルート・ノードの属性に自動的に追加されます。)
2週間分のまとめ。
2021/3/5
エラーで表示が分岐する方法を記載している。
あまりerrorのもとになる情報をStoreで管理すればいいので、とても扱いやすいと率直に感じた
2021/3/6
QuasarにStorybookを追加する方法がわからなかったので、方向転換としてまずVueCLIのVue2デフォルトプリセットで作成して試してみることにする。
※とりあえずvue createまで
2021/3/7
Vue2のデフォルトアプリにStorybookを追加してみる
2021/3/8
既存のVueアプリへStorybookを追加する方法がわかってきたので、とりあえずQuasarアプリにもStorybookを追加・起動するところまで。
2021/3/9
storyを追加してみる。
ただし、Quasarのスタイルが全く効かない。
2021/3/10
ただし、Storyで起動しようとするとTaskListがエラーでてしまう。
<q-page-container>がトップレベルにあるのが問題なのか?
※エラーログを控え損ねたが、この日のコミットログでチェックアウトして試せば確認できるはず
参考記事
2021/3/11
なぜexport const actionsDataと、エクスポートしているのかわかっていなかったが子供コンポーネントのアクションをそのまま使いたいときに便利なんだと気づく。
写経をちゃんと考えてやっていないことがばれる。。。
2021/3/12
メニュー一覧コンポーネントもStorybookにしてみる。
汎用的なコンポーネントなので、入力によって表示が大きく変わるのでStorybookの強みが出るなと思った。
Storyもうちょっと加えてもよかったかもしれない。
2021/3/13
layouts(AtomicDesignでいうTemplateの役割で区切っている)がStoryで表示できるのはすごくいいなと思った。
課題なのが、コンポーネントの解決がうまくいかない。
Storybookだとちゃんと相対で表記しないといけなく、Quasarだとcomponentsからでエイリアスが組めている。
Webpackのコンフィグをいじった(2021/3/10のコミット)のに、なぜうまくいかないかがよくわからない。。
JavaScriptの理解不足が痛い。
- import TaskList from 'components/organisms/TaskList.vue'
- import MenuList from 'components/organisms/MenuList.vue'
+ import TaskList from '../components/organisms/TaskList.vue'
+ import MenuList from '../components/organisms/MenuList.vue'
2021/3/14
Storeのファイルをexampleのコピペで作ろうとしていたが、
「quasarはコマンドでStoreのファイルを生成ができる」ということを思い出したの利用してコミット。
$ quasar new store tasks
2021/3/15
Vuexの書き方がプロジェクトごとにいろいろあって、Quasarのデフォルトの書き方の使い方がぱっとみで理解できなかった。
根本的な使い方をちゃんと学ばないといけないと感じた。
2021/3/16
この内容で一回コンポーネントをつくると、エラーになってしまったため、一回コンポーネントをわけずに作成。
q-footerを一番トップレベルに宣言しているからだろうか。
<q-footer bordered class="bg-white text-primary">
<q-list class="rounded-borders">
<q-item>
<q-item-section>
<q-input color="" v-model="text" label="add tasks">
<template v-slot:prepend>
<q-icon name="add_tasks" />
</template>
</q-input>
</q-item-section>
</q-item>
</q-list>
</q-footer>
TypeError: Cannot read property 'view' of undefined
at VueComponent.fixed (http://localhost:6006/vendors~main.6aec0e0f20b9a40ac5b0.bundle.js:115681:21)
at Watcher.get (http://localhost:6006/vendors~main.6aec0e0f20b9a40ac5b0.bundle.js:200594:25)
at Watcher.evaluate (http://localhost:6006/vendors~main.6aec0e0f20b9a40ac5b0.bundle.js:200699:21)
at VueComponent.computedGetter [as fixed] (http://localhost:6006/vendors~main.6aec0e0f20b9a40ac5b0.bundle.js:200949:17)
at VueComponent.classes (http://localhost:6006/vendors~main.6aec0e0f20b9a40ac5b0.bundle.js:115711:20)
at Watcher.get (http://localhost:6006/vendors~main.6aec0e0f20b9a40ac5b0.bundle.js:200594:25)
at Watcher.evaluate (http://localhost:6006/vendors~main.6aec0e0f20b9a40ac5b0.bundle.js:200699:21)
at Proxy.computedGetter (http://localhost:6006/vendors~main.6aec0e0f20b9a40ac5b0.bundle.js:200949:17)
at Proxy.render (http://localhost:6006/vendors~main.6aec0e0f20b9a40ac5b0.bundle.js:115758:19)
at VueComponent.Vue._render (http://localhost:6006/vendors~main.6aec0e0f20b9a40ac5b0.bundle.js:199663:22)
ここ最近更新できてませんでしたが、なるべくコミットはし続けていました。
この空白の1か月半分の活動を簡単にまとめると、
- TODOアプリにサーバーサイド追加(JavaでSpringBootで、DDD的なモデリングと実装をお試し)
- FizzBuzzをVue3で実装してみた https://github.com/aocm/vue3-fizzbuzz
- Vue3 + Cypressの調査
- ふりかえり闇鍋アプリの修正 https://github.com/aocm/yaminabe-app
だいたいフロント側はVue3とCypress、サーバーサイドはDDD(ついでにSpringBoot)に興味があった感じ。
TODOアプリのために参考にしたブログ
松岡さん(little_hands)のブログ
◆- 松岡さんのブログにはタスク管理をネタに様々な問題の例示をおこなっているので非常に勉強になる
- 例)DDDで複数集約間の整合性を確保する方法(サンプルコードあり) ドメイン駆動設計
ドメイン駆動設計のための Spring の上手な使い方
- DDDとSpringの相性を整理していた時に見つけた、増田さんのSlideshare。SpringBootふくめSpringでDDDやモデリングするときは見直したい。
モデリングもしないでアジャイルとは何事だ
- Iwao Haradaさんの記事。「"設計する"と"設計書をかく"は違う」ということをうまく自分の言葉にできなくて悩んでいたときに見つけた。