あっくんブログ Written by Akihiro Tsuji

Vue3のComposition APIを使おう

Vue.js プログラミング

こんにちは、あっくんです。Googleアナリティクスのことをよくわかっていないですが、自分の作ったアプリケーションをGoogleアナリティクスで検証できるようにしたいと思い、Googleタグマネージャの設定を行ってHTMLのheadとbodyにGoogleタグマネージャのタグを貼り付けてGoogleアナリティクスとトラッキングコードと紐付けてできました。普通に嬉しかったです。自分のアプリケーション自体訪問数ほぼゼロなのですが、就活でアプリケーションを見ていただいているか気になったので、設置してみました。

今回はCompositon APIを使っていきましょう。

コンポーネントは複雑すぎる

コンポーネントはprops,data,算出プロパティ,ウォッチャ,methodsなどのメソッドで、きれいに組み立てるのが思ったよりも大変です。複雑になりすぎたコンポーネントの問題を解消するためにVue3の開発チームが作り出した仕組みが「Composition API」です。

propsとsetupだけ!

Composition APIは散らばった要素をもう一度1つにまとめ直すことを考えて作られました。

export default { props: 値, setup(props) { …..処理…….. } }
Code language: CSS (css)

propsはコンポーネントのタグに設定できる属性を管理します。それ以外はsetupで処理します。

コンポーネントを作ってみる。

Composition APIを作ってコンポーネントを作成しましょう。composition_appプロジェクトをViteで作ってやっていきましょう。

開発するディレクトリでターミナルに↓のように記述してcomposition_appを作って、npmをインストールします。

npm init vite-app composition_app

cd composition_app
npm install

srcにあるcomponentsのHelloWorld.vueを書き換えましょう。

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <p>{{ msg }}</p> </div> </template> <script> export default { props: { title: String, msg: String, }, setup(props) { console.log(props); }, }; </script>
Code language: HTML, XML (xml)

ここでは、propsにtiteとmsgの2つの属性を用意し、setupでは、propsの値をconsole.logに出力しています。これでデベロッパーツールのコンソールでpropsの内容が確認できます。

HelloWorldを表示する

App.vueとindex.htmlを書き換えます。

<template> <div id="app"> <HelloWorld title="Composition API" msg="This is Composition API sample." /> </div> </template> <script> import HelloWorld from "./components/HelloWorld.vue"; export default { name: "app", components: { HelloWorld, }, }; </script>
Code language: HTML, XML (xml)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Compositon API App</title> <!-- CSS only --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous" /> <!-- JavaScript Bundle with Popper --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous" ></script> </head> <body> <h1 class="bg-secondary text-white h4 p-3">Vue3 Composition API</h1> <div class="container"> <div id="app"></div> </div> <script type="module" src="/src/main.js"></script> </body> </html>
Code language: HTML, XML (xml)

20210915

Image from Gyazo

↑HelloWorldコンポーネントを表示します。

ここでは、<HelloWorld />タグにtitleとmsgという属性を用意しています。これがHelloWorldのpropsに渡され、{{}}で表示しています。

JSXでも使える?

componentsフォルダ内に「Hello.jsx」を作成して確認していきましょう。

export default { name: 'HelloJSX', props: { title: String, msg: String }, setup(props) { return () => <div class="alert alert-primary"> <h1>{props.title}</h1> <p>{props.msg}</p> </div> } }
Code language: JavaScript (javascript)

JSXを使うときreturnにアロー関数を用意します。

これを表示するために、App.vueを修正します。

<template> <div id="app"> <HelloJSX title="Composition API" msg="This is Composition API sample." /> </div> </template> <script> import HelloJSX from "./components/HelloJSX.jsx"; export default { name: "app", components: { HelloJSX, }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑JSXで表示できました。

setupのreturnについて

setupではreturnを書く場合もあります。returnした内容をもとにコンポーネントを表示されるのです。.vueファイルも、setupのrerturnで表示内容を返す関数を用意することもできます。

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <p>{{ msg }}</p> </div> </template> <script> import { h, ref } from "vue"; export default { props: { title: String, msg: String, }, setup(props) { return () => h("div", { class: "alert alert-primary" }, [ h("h1", props.title), h("p", props.msg), ]); }, }; </script>
Code language: HTML, XML (xml)

dataではなく、refを使います

コンポーネントで一番多用するのはdataの値です。しかし、「Composition API」ではdataはありません。ではどうやって、テンプレート側で利用する値を用意しますか。それは、「ref」を使って、変数として用意します。「ref」は値の参照を作成するためのものです。参照とは、その値を指し示す値のことです。つまり、その値が保管されている場所を示す値です。

変数 = ref(値)

リアクティブな値として使うためには、refを使って値を用意する必要があります。

refで値を用意する

setup(props) { const 定数= ref (値 return {定数 }
Code language: JavaScript (javascript)

setpu内でrefを使って参照を作成し、それをreturnでオブジェクトにまとめて返すようにします。この返されたオブジェクトに用意された値が、テンプレートで利用できるようになるのです。

refで値を表示する

HelloWorld.vueをrefを使って記述していきましょう。App.vueはHelloWorldを表示するように記述します。

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <p>{{ data.msg }} ({{ data.count }})</p> </div> </template> <script> import { ref } from "vue"; export default { props: { title: String, }, setup(props) { const data = ref({ msg: "This is ref-value!", count: 0, }); setInterval(() => { data.value.count++; }, 1000); return { data, }; }, }; </script>
Code language: HTML, XML (xml)
<template> <div id="app"> <HelloWorld title="Composition API" msg="This is Composition API sample." /> </div> </template> <script> import HelloWorld from "./components/HelloWorld.vue"; export default { name: "app", components: { HelloWorld, }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑表示すると数字がカウントされていきます。

refによる参照の利用

setup(props) { const data = ref({ msg: "This is ref-value!", count: 0, })
Code language: JavaScript (javascript)

refを使って、dataという定数を作成しています。このrefの引数にはmsg,countという2つのオブジェクトを用意しています。作成された定数は、returnで返します。

return { data }
Code language: JavaScript (javascript)

これでテンプレート側で利用できるようになります。

<p>{{ data.msg }} ({{ data.count }}) </p>

これでrefで作成された値が利用できるようになります。 

スクリプト側で参照を使う場合

setInterval(() => { data.value.count++ }, 1000)
Code language: JavaScript (javascript)

値を1増やしている部分は、data.count++ではなく、data.value.count++です。dataは値そのものではなく参照であるので、refで参照している値はvalueというメソッドで得ることができます。つまり、data.valueでdataが参照しているオブジェクトを取り出せるようになっています。

refとreactive

先程のrefによる参照はスクリプトないで利用するときはvalueを使う必要がありました。スクリプトとテンプレートの値の記述がことなるので、わかりにくいです。

スクリプト内
data.value.count

テンプレート内
data.count

参照を作成するのは 「ref」以外に「reactive」という関数があります。

変数 = reactive(値)

このreactiveは「リアクティブ可能な値のコピー」を返すものです。値をコピーをしてリアクティブになります。

HelloWorld.vueをreactiveを使って記述しましょう。

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <p>{{ data.msg }} ({{ data.count }})</p> </div> </template> <script> import { ref, reactive } from "vue"; export default { props: { title: String, }, setup(props) { const data = reactive({ msg: "This is ref-value!", count: 0, }); setInterval(() => { data.count++; }, 1000); return { data, }; }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑先程と同様に数字がカウントされていきます。

reactiveではスクリプト内で値を利用する部分がわかりやすくなります。

setInterval(() => { data.count++ }, 1000)
Code language: JavaScript (javascript)

テンプレートでの利用と書き方が同じになります。ただし、reactiveで作成できるのはオブジェクトのみです。数字や真偽値のような基本型の値は使えないです。がーんです。refは基本型の値でもすべて参照として取り出すことができます。
基本型の値をリアクティブにしたいときはref
オブジェクトをリアクティブにしたいときはreactive
と考えるといいかもしれません。

メソッドの利用

メソッドの利用は、setup内で変数に関数を代入しておき、この変数をテンプレートに割り当てます。HelloWorld.vueを書き換えてみましょう。

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <p class="h5">{{ data.msg }}</p> <div> <input type="number" v-model="data.num" min="0" class="form-control" /> </div> <button class="btn btn-primary m-3" v-on:click="action">Click</button> </div> </template> <script> import { ref, reactive } from "vue"; export default { props: { title: String, }, setup(props) { const data = reactive({ msg: "This is ref-value!", nmu: 0, }); const action = () => { let total = 0; for (let i = 1; i <= data.num; i++) { total += i; } data.msg = "Total:" + total; }; return { data, action, }; }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑数字を入力してクリックすると、その数字までの合計を計算して表示します。

setup内にaction関数を用意します。

const action = () => { let total = 0 for (let i = 1; i <= data.num; i++) { total += i } data.msg = "Total:" + total }
Code language: JavaScript (javascript)

アロー関数を定数に代入する形で用意しています。

return { data,action }
Code language: JavaScript (javascript)

これで、dataとactionがテンプレートで使えるようになります。

<button class=”btn btn-primary m-3″ v-on:click=”action”>Click </button>

v-on:clickにactionを指定することで、クリックすると定数actionに代入された関数が実行されます。

setupのcntextについて

setupメソッドでは、引数にpropsが渡されました。これ以外に「context」という引数が渡されます。

contextはコンポーネントに関する情報をひとまとめにしたオブジェクトです。↓のような項目が含まれています。

attrs
コンポーネントタグに用意された属性をまとめたもの

slots
コンポーネント内に含まれるスロットをまとめたもの

emit
emitされた内容をまとめたもの

これらはcontext.attrsというような形でアクセスし値を得ることができます。HelloWorld.vueを修正していきましょう。

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <p class="mt-3 h5">{{ data.msg }}</p> </div> </template> <script> import { ref, reactive } from "vue"; export default { props: { title: String, }, setup(props, context) { const data = reactive({ msg: "This is ref-value!", }); data.msg = context.attrs["msg"].toUpperCase(); return { data, }; }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑msg属性がすべて大文字になってメッセージが表示されます。

context.attrs[‘msg’]という形でmsg属性の値を取得し、それをtoUpperCaseですべて大文字にした値をdata.msgに設定してテンプレートで表示させています。

ここまで読んでいただいてありがとうございます。
今回はComposition APIについて、data,methods,
computed等を使わないでpropsとsetup(props, context)を使って記述することがわかりました。setupを使って記述がきれいになるようにということですが、もっとコードを書いて感覚的につかみたいです。
掌田津耶乃さんのVue.js3超入門で勉強をしています。語りかけるような文章ですごいわかりやすいです。本当におすすめです。