前置き
・LaravelついでにVue.jsを使うことになった
・公式のドキュメントを流し読み
・とりあえずコンポーネントで分けてみるか
・カオスwwww
というわけで、根本的にどうVue.jsを使っていいか分かっていないようなので、
どうすれば「俺はVue.jsを使ってるぜっ!」
と言えるのか試行錯誤してみる。
事の始まり
簡単な検索ページでも作ってみる
<div>
id<input type="text">
name<input type="text">
</div>
<div>
<input type="button" value="検索">
</div>
<br />
<div>
<table border="1">
<tr>
<th>id</th>
<th>name</th>
</tr>
<tr>
<td>1</td>
<td>たかし</td>
</tr>
</table>
</div>
どうコンポーネント分けようかな
こ・・こんな感じ?
とりあえずツリー風に書いて、役割を明確に・・
▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂うわあああああああ
クールになるんだ
OK落ち着け俺。
何か根本的に間違っている。
情報を整理してリトライしよう。
Vue.jsはMVVM
(M)odel
データだったり、ロジックだったり。
Vue.jsでいうと、dataオブジェクトやmethodsのこと。
props、computed、watchもModelに入るのかな?
(V)iew
DOM要素のこと。
Vue.jsでいうと、elのこと。
templateやrenderもそうなのかな?
(V)iew(M)odel
ModelとViewの仲立ちをもの。
Vue.jsでいうと、Vueインスタンスのこと。
考察
Vueインスタンスにdataオブジェクトを内包できるから勘違いしたような気がする。
→明確にViewModelとModelを分けてみよう。
ViewModelであるVueインスタンスと、Modelは切り離すべきではなかろうか( ̄ヘ ̄)ウーン
→用意するModelは、入力と結果の二つってところか。
検索ロジックってどっちに入れよう・・。
入力値も使うし、結果も格納しないといけないんだよな・・。
→ロジック用のModelを用意してみる。
データ用のModelにはSetterGetterを用意すればいいな。
んで、ロジックModelで入力と結果のModelに入れ込めばいいんだ。
こんなところか。
Viewに関しては、コンポーネントの分け方の問題なのかなぁ・・。
コンポーネント
ぼくのかんがえるこんぽーねんと
なんか・・部品・・ですよね・・。
はい、この曖昧さが悲劇を生んだような気がします。
公式より
基本的な HTML 要素を拡張して再利用可能なコードのカプセル化を助けます。
高いレベルでは、コンポーネントは Vue.js のコンパイラが指定された振舞いを加えるカスタム要素です。
場合によっては、特別な is 属性で拡張されたネイティブな HTML 要素の姿をとることもあります。
使い回しが聞きやすいように、独自タグ作るやーつ。
と読める。
使い回しが効きそうなところはコンポーネントにしましょう。
サイトのデザインって統一感持たせたいんだから、
言ってしまえば全部使い回しが効くように作るべきだよね。
じゃあ
「使い回しが効く部品」
で考えることにしよう。
コンポーネント再検討
・画面全体のテンプレート
・入力部(ラベル+テキストボックス)
・ボタン
・テーブル
に分けてみました。
全体像を再検討
コンポーネントとモデルを繋げる線がViewModel(Vueインスタンス)になるのかな・・?
作ってみる
入力モデル
require('./bootstrap');
window.Vue = require('vue');
module.exports = new Vue({
data: {
id: '',
name: ''
},
methods: {
getId: function() {
return this.id;
},
setId: function(val) {
this.id = val;
},
getName: function() {
return this.name;
},
setName: function(val) {
this.name = val;
}
},
setMethods: function() {
return this.methods;
}
});
解説)
特にありません。
再検討したものをそのまま実装してます。
検索結果格納用モデル
require('./bootstrap');
window.Vue = require('vue');
module.exports = new Vue({
data: {
list: []
},
methods: {
getList: function() {
return this.list;
},
setList: function(val) {
this.list = val;
}
},
setMethods: function() {
return this.methods;
}
});
解説)
特にありません。
再検討の時と違うのは、結果は配列で持たせるように変更してます。
ロジック用モデル
// 不要としました
解説)
検索ロジックはViewModelに依存させました。
なぜか?
多くの値(Model等)を用いたり、返したりするロジックを外出しすると、
ViewやViewModelの記載が複雑になりました。
一意性の高いロジックは外出しせずに、都度その場で定義した方が良さそうです。
<!-- モデルをコンポーネントに渡さなければならないため、記載や可読性が複雑になる -->
<my-button label="検索" v-on:prop-click="search(modelInput, modelResult)"></my-button>
入力部コンポーネント
<template>
<span>
{{label}}<input type="text" v-model="value">
</span>
</template>
<script>
export default {
props: ["label"],
data: function() {
return {value: ""};
},
watch: {
value: function(val, oldval) {
this.$emit("prop-setter", val);
}
}
}
</script>
解説)
入力値が変わるたびに、親から受けとったModelのsetterを実行。
ボタンコンポーネント
<template>
<input type="button" v-bind:value="label" v-on:click="click()">
</template>
<script>
export default {
props: [ "label" ],
methods: {
click: function() {
this.$emit("prop-click");
}
}
}
</script>
解説)
ボタンを押したら、受け取ったメソッドを実行するだけ。
テーブルコンポーネント
<template>
<div>
<table border="1">
<tr>
<th v-for="header in propHeaders">{{header}}</th>
</tr>
<tr v-for="data in propDatas">
<td>{{data[0]}}</td>
<td>{{data[1]}}</td>
</tr>
</table>
</div>
</template>
<script>
export default {
props: [ "prop-headers", "prop-datas" ]
}
</script>
解説)
受け取ったヘッダと値を使ってテーブル表示。
(app.js)ViewModelの定義等
require('./bootstrap');
window.Vue = require('vue');
import ModelInput from './model_input.js';
import ModelResult from './model_result.js';
//import LogicSearch from './logic_search.js';
Vue.component('my-text', require('./components/text.vue'));
Vue.component('my-button', require('./components/button.vue'));
Vue.component('my-table', require('./components/table.vue'));
const input = new Vue({
el: '#input',
data: ModelInput.$data,
methods: ModelInput.$options.setMethods()
});
const button = new Vue({
el: '#button',
data: {
modelInput: ModelInput,
modelResult: ModelResult
},
methods: {
search: function() {
var result = [];
result.push([this.modelInput.getId(), this.modelInput.getName()]);
result.push([2, "たかし"]);
this.modelResult.setList(result);
}
}
});
const table = new Vue({
el: '#table',
data: ModelResult.$data
});
解説)
各コンポーネントがモデルを使用できるように、VueオブジェクトとModel紐づけてます。
ビュー
<div id="input">
<my-text label="id" v-on:prop-setter="setId"></my-text>
<my-text label="name" v-on:prop-setter="setName"></my-text>
</div>
<div id="button">
<my-button label="検索" v-on:prop-click="search"></my-button>
</div>
<br />
<div id="table">
<my-table v-bind:prop-headers="['id', 'name']" v-bind:prop-datas="list"></my-table>
</div>
解説)
コンポーネントを使って描画。
まとめ
・最初に比べるとだいぶマシになった。
・誰か参考になる情報やリンクを教えてください。