Vue.jsとか使って時間管理マトリックスのWEBアプリを作ってみた
作ってみた。リポジトリは下記。
Vue.jsとかSVG周りの勉強がてらなんか作るかーと思って作ってみた。
時間管理マトリックスってなんぞや?って人は下記を参照されたし。
概要
左下に入力項目の表示非表示ボタンがある。
ボタンを押すと左上に可変の入力欄があり、そこに項目名を入力、重要度と緊急度を数値の入力欄かスライダーで変更できるようにしている。
各値が変更されればそれに合わせてSVGのtext要素の値、座標も変更するといった感じだ。
今はLocalStorageに変更した値を保存するようにしているので再度開きなおしてもそのまま前回の画面のまま編集できるようにしている。
見た目はもっと良くしたいなーと思いながらも今の優先順位的にそこまで入れ込む気はないので手は付けず。
ぶつかった問題とかTipsとか
初めてGithub Pages使ってみた!
各リポジトリのSettingsからGitHub PagesのSourceを選択するだけでページを公開することが出来て便利。
基本的にはmasterブランチのファイルが公開されるが変更する方法もあるっぽい?(ここよく分かってない)
masterブランチにビルド後のJSファイルも含める形になる。
仕方ないとは思うけど何かいい方法がないかな。
あとmasterにマージしてからファイルが反映されるまで少し時間がかかる模様。
体感数分。
スプレッド演算子がSyntaxErrorになる
VuexのVueコンポーネントへのマッピング時の...mapState等がSyntaxErrorになった。
最終的にoptions取り除いて.babelrcを作成した。
// before { loader: "babel-loader", options: { presets: [ ['env', {'modules': false}] ] } } // after { loader: "babel-loader", }
{ "presets": ["es2015"], "plugins": ["transform-object-rest-spread"] }
webpack4のモード
webpack4からはmodeを指定することが出来るようになっていて、そこで開発用かプロダクション用か切り替えることが出来る。
もちろんwebpack.config.jsにmodeを追加することも出来るがwebpackコマンドの引数に追加することも出来る。
なので、下記のように指定してあげるとnpm run ***
で使い分けが出来て便利。
"scripts": { "dev": "webpack --mode development", "build": "webpack --mode production", }
LocalStorage
Local Storageには文字列で登録されるので、そのままオブジェクトを突っ込むと[object Object]となってしまう。
const obj = {x: 1, y: 2}; localStorage.setItem('items', obj); localStorage.getItem('items'); // [object Object]
なので適宜、設定時と取得時にJSONの変換を行う必要がある。
// 設定時 const obj = {x: 1, y: 2}; localStorage.setItem('items', JSON.stringify(obj)); // 取得時 JSON.parse(localStorage.getItem('items')); // {"x":1,"y":2}
Vue.js、Vuex
Vue.jsのwatch
監視する値が何回層かの配列やオブジェクトだったときにウォッチャではdeepというプロパティを定義してtrueにすると内部の値も監視対象としてくれます。
data() { return { items: [ {x: 0, y:0}, ], }; }, // 内部監視あり watch: { items: { handler() { // 処理 }, deep: true, } } // 内部監視なし watch: { items() { // 処理 }, }
SVGの描画時マウントされていないために参照できない問題。
ref の登録タイミングに関する重要な注意事項として、参照自体は、render 関数の結果として作成されているため、最初の描画においてそれらにアクセスすることができません。それらはまだ存在しておらず、$refs はリアクティブではなく、従ってデータバインディングのためにテンプレートでそれを使用すべきではありません。
地味に躓いた。
text要素の幅を取得して、背景にその幅分のrect要素を広げたかったのだけど、うまくいかなかった。
最終的にはVueのmouted()でフラグを有効にし、それの真偽で処理をするようにした。
data() { return { isMounted: false, }; }, mounted() { this.isMounted = true; }, // ~~~~~~~~ // if (this.isMounted) { // 特定要素の幅取ったり }
Vuexの各アクションなどのキー名
Vuexでミューテーションやアクションのキーに定数を使用することを提案している箇所がある。
これは本当に好みの問題かもしれない。
一応使って実装してみた。
こんな感じ。
// types.js export const set = 'set'; // mutations.js import * as types from './types'; export default { [types.set](state, items) { state.items = items; } } // app.vue this.$store.commit(types.set, items); // 文字列のままだと this.$store.commit('set', items);
定義名はmutationとaction、getterに適用。
gettersはプロパティに生えるから使わなくてもいいかもしれない。
this.$store.getters[types.initialItems]; this.$store.getters.initialItems;
VuexのmapStateとmapGettersを産出プロパティに
通常産出プロパティにスプレッド演算子の関係でmapStateとmapGettersは設定できないんだけど無理やりやってみた。
同じキー名のプロパティがあった場合、後方の値で上書きされるので注意。
computed: Object.assign(mapState([ 'items', ]), { ...mapGetters([ types.urgentPosition, types.importantPosition, ]), }), }
これからやってみたいこと
このアプリは遊びで試してみたかったのでそんな時間をかけるつもりはないもののやりたことは色々ある。
- 文字が枠外に出たりするのでちゃんと文字列が枠内に収まるように
- 型の導入
- LocalStorage以外にサーバーと連携してDBに保存するとか
- なんかもっとかっこいい見た目にしたい!!!
ぶっちゃけ見た目や使い勝手悪すぎて自分のUI周りの実装力の低さを思い知ってしまった。
ここはこれからどれだけ勉強するか悩みどころ。