初めてのVue④コードの整理
コンポーネントを使ってわかりやすくコードが書けるようになったので、以前使ったコードをいくつかのファイルに分けて書いてみましょう。今回は複雑に思うかもしれませんが、塊で考えれば分かりやすいと思います。
- Vueのコードをわかりやすく書きたい
- 親と子のデータの受け渡し方を学びたい
- コンポーネントをうまく使えるようになりたい
今回は下記のコードを整理していきます。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Document</title>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="app3">
<section v-show="inProgressAssingments.length">
<h2>In Progress Assingments</h2>
<ul>
<li v-for="assingment in inProgressAssingments":key="assingment.id">
<label>
{{assingment.name}}
<input type="checkbox" v-model="assingment.complete">
</label>
</li>
</ul>
</section>
<section v-show="completeAssingments.length">
<h2>Completed</h2>
<ul>
<li v-for="assingment in completeAssingments":key="assingment.id">
<label>
{{assingment.name}}
<input type="checkbox" v-model="assingment.complete">
</label>
</li>
</ul>
</section>
</div>
<script type="module">
let app3 = {
data(){
return{
assingments: [
{name: 'task1', complete: false, id: 1 },
{name: 'task2', complete: false, id: 2 },
{name: 'task3', complete: false, id: 3 },
]
}
},
computed: {
inProgressAssingments(){
return this.assingments.filter(assingment => ! assingment.complete);
},
completeAssingments(){
return this.assingments.filter(assingment => assingment.complete);
}
}
};
Vue.createApp(app3).mount('#app3');
</script>
</body>
</html>
そうですね、少し前に使ったコードです。
これを下記の4つのファイルに分けていきます。
まずはindex.htmlです。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Document</title>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="app3">
</div>
<script type="module">
import app3 from './js/components/App.js';
Vue.createApp(app3).mount('#app3');
</script>
</body>
</html>
app3が使えるようにしただけです。ほとんど何もなくなりますね。
続いて、App.jsからimportしているのでApp.jsを見ていきましょう。
import Assingments from "./Assingments.js";
export default {
components: {Assingments},
template: `
<assingments></assingments>
`,
}
いろんなインスタンスのハブになるイメージで作っています。
Assingmentsを使えるようにしてassingmentsタブを使っているだけです。
続いてAssingments.jsを見ていきましょう。
mport AssingmentList from "./AssingmentList.js";
export default{
components: { AssingmentList },
//ポイント①:assingmentsにフィルターした配列、titleにタイトルを渡してます。これらは子(AssingmentList)の中で使います
template: `
<assingment-list :assingments="filters.inProgress" title="In Progress"></assingment-list>
<assingment-list :assingments="filters.completed" title="Completed"></assingment-list>
` ,
data(){
return{
assingments: [
{name: 'task1', complete: false, id: 1 },
{name: 'task2', complete: false, id: 2 },
{name: 'task3', complete: false, id: 3 },
]
}
},
//ポイントではないですが、こんな書き方もできるということで書き換えました。
computed: {
// inProgressAssingments(){
// return this.assingments.filter(assingment => ! assingment.complete);
// },
// completeAssingments(){
// return this.assingments.filter(assingment => assingment.complete);
// },
filters(){
return{
inProgress: this.assingments.filter(assingment => ! assingment.complete),
completed: this.assingments.filter(assingment => assingment.complete)
};
}
},
}
やっとコードが出てきました。
元のデータはここにあって、フィルターしたデータを子のファイルでへ送ってます。
続いて、AssingmentsList.jsを見ていきましょう。
import Assingment from "./Assingment.js";
export default{
components:{ Assingment },
//ポイント①:親から受け取ったassingmentsやtitleを使ってテンプレートを書いてます。
template: `
<section v-show="assingments.length">
<h2>{{ title }}</h2>
<ul>
//ポイント②:assingmentsの中のそれぞれの要素は孫ファイル(Assingment)に渡してます
<assingment
v-for="assingment in assingments"
:key="assingment.id"
:assingment="assingment"
>
</assingment>
</ul>
</section>
`,
//ポイント③親からのデータを受け取れるようにpropsを定義してます。
props: {
assingments: Array,
title: String,
}
}
Propでデータ型を設定しておくことを忘れないようにしましょう。
続いて、Assingment.jsを見ていきましょう。
export default {
template: `
<li>
<label>
//ポイント①:受け取ったassingmentオブジェクトを使ってテンプレートを書いています
{{assingment.name}}
<input type="checkbox" v-model="assingment.complete">
</label>
</li>
`,
//ポイント②:propsの設定も忘れずに!
props: {
assingment: Object
}
}
これでまとまりごとにファイルが分けれたのでコードが書きやすくなりました。
親と子のコミュニケーション
親から子にデータを渡すときはpropsが使えるのですが、子から親の時はどうすればいいのでしょうか?
新しくAssingmentを追加するときを例に見ていきましょう。
コードを書いていきます。
import AssingmentList from "./AssingmentList.js";
import AssingmentCreate from "./AssingmentCreate.js";
export default{
components: { AssingmentList, AssingmentCreate},
template: `
<assingment-list :assingments="filters.inProgress" title="In Progress"></assingment-list>
<assingment-list :assingments="filters.completed" title="Completed"></assingment-list>
//ポイント:assingment-createを追加
<assingment-create :assingments="assingments"></assingment-create>
` ,
data(){
return{
assingments: [
{name: 'task1', complete: false, id: 1 },
{name: 'task2', complete: false, id: 2 },
{name: 'task3', complete: false, id: 3 },
]
}
},
computed: {
filters(){
return{
inProgress: this.assingments.filter(assingment => ! assingment.complete),
completed: this.assingments.filter(assingment => assingment.complete)
};
}
},
}
export default{
template:`
//ポイント①:submitした時にページが再読み込みされないようpreventしてます
<form @submit.prevent="add">
<div>
<input v-model="newAssingment" placeholder="New assingment">
<button type="submit">Add</button>
</div>
</form>
` ,
props:{
assingments: Array
},
//ポイント②:addが発火した時に使う変数を定義してます。
data(){
return{
newAssingment:''
}
},
//ポイント③:addが発火した時の処理を書いています。
methods:{
add(){
this.assingments.push({
name: this.newAssingment,
complete: false,
id: this.assingments.length +1
});
this.newAssingment='';
}
}
}
AssingCreateのインスタンスにもpropでAssingmentListの配列が渡っているのがわかると思います。
これは少し変な感じがします。
原因は子から親にpropsを使ってアクセスしているからです。
ではどうするかというと、emitを使って書き換えます。
export default{
template:`
<form @submit.prevent="add">
<div>
<input v-model="newAssingment" placeholder="New assingment">
<button type="submit">Add</button>
</div>
</form>
` ,
// props:{
// assingments: Array
// },
data(){
return{
newAssingment:''
}
},
//ポイント:emitを使ってaddを発火します、引数はnewAssingmentを使ってくださいと書きます
methods:{
add(){
this.$emit('add', this.newAssingment);
// this.assingments.push({
// name: this.newAssingment,
// complete: false,
// id: this.assingments.length +1
// });
this.newAssingment='';
}
}
}
親ではどうやってデータを受け取っているのでしょうか?
import AssingmentList from "./AssingmentList.js";
import AssingmentCreate from "./AssingmentCreate.js";
export default{
components: { AssingmentList, AssingmentCreate},
template: `
<assingment-list :assingments="filters.inProgress" title="In Progress"></assingment-list>
<assingment-list :assingments="filters.completed" title="Completed"></assingment-list>
//ポイント①:もともとassingmentsを定義してたとこにaddを定義
<assingment-create @add="add"></assingment-create>
` ,
data(){
return{
assingments: [
{name: 'task1', complete: false, id: 1 },
{name: 'task2', complete: false, id: 2 },
{name: 'task3', complete: false, id: 3 },
]
}
},
computed: {
filters(){
return{
inProgress: this.assingments.filter(assingment => ! assingment.complete),
completed: this.assingments.filter(assingment => assingment.complete)
};
}
},
//ポイント②:ここでassingmentsへの追加の処理を行う。nameには子から渡されたnewAssingmentが入る。
methods: {
add(name){
this.assingments.push({
name: name,
complete: false,
id: this.assingments.length +1
});
// this.newAssingment='';
}
}
}
子から親にアクセスするときはemit、親から子にアクセスするときはpropと覚えておきましょう。