あっくんブログ Written by Akihiro Tsuji

Vue3のプロジェクトによる開発

Vue.js プログラミング

こんにちは、あっくんです。最近転職クエストというサービスを利用することにしました。エンジニア転職に特化しているみたいです。客観的に自分の就職活動がどうなのか?エンジニア転職を勝ち取られた方のインタビューやポートフォリオも参考にできそうです。厳しい状況ですが、コツコツ積み上げてみます。今回はVue3のプロジェクトで開発をやっていきましょう。Viteを使って、どのファイルがどういう役割をしているのか見ていきましょう。

プロジェクトで開発

Vue3はSPA(Single Page Application, 1枚のページで完結するWebアプリケーション)開発で多用されています。しかし、たくさんのページやファイルを利用するWebアプリケーションの開発にも活用されています。

vite-appプロジェクトをチェック

ターミナルで↓のようにコマンドを打ち込みます。

npm init vite-app vite-app

Viteで作成するときのappの中身(vite-app) 

ファイル類
index.htmlWebページのファイル。このファイルにアクセスしてページを表示する。
package.jsonプロジェクトの設定情報
packate-lock.jsonプロジェクトの設定に関するファイル

フォルダ内のフォルダ類
「public」フォルダ外部に公開されるファイルをまとめるところ。
favicon.icoアイコンファイル

「src」フォルダ
ファイルmain.jsアプリケーションのプログラム
ファイルApp.vueアプリケーションで使うコンポーネントファイル
フォルダ「assets」フォルダロゴのイメージファイル
フォルダ「components」フォルダコンポーネントファイル

「node_modules」フォルダ
プロジェクトで使うパッケージがまとめられています。

「distフォルダ」
ビルドして生成された公開されるファイルが保存されています。

プロジェクトとWebサイトの違い

プロジェクトはプロジェクトの動作の確認を行った時、「npm run serve」,「npm run dev」といったコマンドを実行して、プロジェクトを実行していました。プロジェクトそのものがWebサーバーとして実行されるようになっています。
Webサイトはフォルダ内のHTMLファイルやイメージファイルなどをWebサーバーにアップロードするというやり方です。HTMLファイルはただサーバーにおいておくだけで、アドレスにアクセスしたらこのファイルをロードして表示するということができます。
つまり、プロジェクトはプロジェクトをWebサーバーとして実行するためのプログラムが含まれています。

ビルド後はすっきり?

プロジェクトは、一般的なWebサーバーで公開しようと思った場合は、ビルドという作業が必要となります。ビルドすることで、実際に公開するファイル類が生成されます。ビルド後は、スクリプトなどが変換されて、中を見てもわからない状態になっています。プログラムの作成はビルドする前の状態で行う必要があり、ビルド後のファイルを直接編集することはありません。
つまり、Vue3では「ビルド後に生成されたファイルを編集する」ということはないと考えましょう。

main.jsの役割

作成するアプリケーションの内容は、基本的に「src」フォルダ内にまとめれています。「main.js」ファイルはプロジェクトをWebアプリケーションとして起動した際、最初に実行されるスクリプトです。

import { createApp } from 'vue' import App from './App.vue' import './index.css' createApp(App).mount('#app')
Code language: JavaScript (javascript)

importについて

「import」というのは、モジュールからオブジェクトなどを読み込んで使えるようにするためのものです。Vueモジュールにある「createApp」というメソッドと、App.vueというファイル、そしてindex.cssというスタイルシートのファイルを読み込んでいます。
importした後に、createApp.mountでVue3のアプリケーション・オブジェクトを生成しid=”app”にマウントしています。

App.vueをチェックする

プロジェクトでは、App.vueのように「.vue」という拡張子のファイルが使われます。これは、Vue3のコンポーネントを定義しているファイルです。

<template>タグと<script>タグに分かれています。

template>タグについて

<template>タグは、コンポーネントのテンプレートの内容です。レンダリングされるときは<template>タグは消えて、この中の内容が表示されます。
<HelloWorld>タグは、HelloWorldというコンポーネントを表示するタグです。App.vueでは、HelloWorld.vueというファイルを読み込んで、利用します。

export defaultについて

export default {   ・・いろいろ書いてあるろ書いてあるいてある }
Code language: JavaScript (javascript)

export default」はスクリプトファイルをimportでインポートしたときに、デフォルトで用意されるものを指定するものです。

import 変数 from 'App.vue'

export defaltの{}部分に記述された内容が変数に取り出されるのです。

コンポーネントの中身

{ name: 'App', components: { HelloWorld } }
Code language: CSS (css)

nameはコンポーネントの名前です。componentsは使用するコンポーネントをまとめたものです。HelloWorldコンポーネントを用意しています。Vue3のコンポーネントは、設定情報をまとめたオブジェクトを引数にしてcreateAppします。その設定情報のオブジェクトが、importで用意されています。
ざっくりですが、「コンポーネントの設定内容などを、まとめてexport defaltすればうまくうごいてくれるらしい」という感じです。

compnentから.vueへ

プロジェクトではコンポーネントは.vueファイルとして、コンポーネントを定義するようになります。.vue
ファイルでは、表示するタグとコンポーネントの実装部分(export default)を分けて記述します。componentでは、templateの値として、出力内容を用意する必要がありましたので、テキスト値として出力内容を作成するのはわかりにくいですね。

index.htmlについて

index.htmlは「public」フォルダにあります。Webページ全体をデザインしています。

<!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>Vite App</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"></div> <script type="module" src="/src/main.js"></script> </body> </html>
Code language: HTML, XML (xml)

htmlファイルには、scrpt>タグとdiv id=”app”>があるくらいです。
ここでVue3が実際に機能しているか確認します。Viteではターミナルメニューから「npm run dev」と実行します。

Image from Gyazo

↑ターミナルからnpm run devを実行する。

Image from Gyazo

↑localhost:3000にアクセスするとWebページが表示されます。

何がどうなっている?

1.表示されるWebページは、index.htmlとして用意されています。JavaScript関係のタグでmain.jsを読み込み、これで、Vue3の処理が実行されています。

2.main.jsでは、Vue3のアプリケーション・オブジェクトが作成されます。ここで、App.vueのAppコンポーネントが組み込まれます。

3.Appコンポーネント(App.vue)では、その内部で更にHelloWorldコンポーネントを組み込んでいます。

ざっくりですが、「Webページに、Appというコンポーネントを組み込み、その中で更に、HelloWorldコンポーネントを組み込んで表示しています。

index.htmlを修正する。

<!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>Vite 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> <script src="https://unpkg.com/vue@next"></script> </head> <body> <h1 class="bg-secondary text-white h4 p-3">Vue3 Vite</h1> <div class="container"> <div id="app"></div> </div> <script type="module" src="/src/main.js"></script> </body> </html>
Code language: HTML, XML (xml)

まず、BootstrapをCDNからロードして利用できるようにします。そして、<h1>でタイトルを表示しています。

HelloWorodコンポーネントを修正する

ここでは、「src」フォルダ内の「components」の「HelloWorld.vue」を↓のように書き換えます。

<template> <div class="alert alert-info"> <h2>{{title}}</h2> <p>{{message}}</p> </div> </template> <script> export default { name: 'HelloWorld', props: { title: String, message: String, } } </script>
Code language: HTML, XML (xml)

templateに{{title}},{{message}}を用意しています。propsはプロパティを設定しておくものです。

App.vueを修正する

HelloWorlfコンポーネントを利用するApp.Vueを修正します。

<template> <div id="app"> <HelloWorld title="Hello" message="*これは、Vue3のサンプルプロジェクトです。"/> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'App', components: { HelloWorld } } </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑アクセスするとこのようなWebページが表示されます。

ターミナルで「npm run dev」を実行してlocalhost:3000にアクセスします。すると画面に「Hello」と日本語のメッセージが表示されます。これが、Appに組み込まれたHelloWorldのコンポーネントの表示です。

<HelloWorld title="Hello"message="*これは、Vue3のサンプルプロジェクトです。"/>

titleとmessage属性に値を設定しています。これが、HelloWorldのpropsに渡され、{{title}}と{{message}}に表示されています。このように、{{}}による値の埋め込みとprops、コンポーネントタグの属性の組み合わせることで、表示内容に関する細かな情報を渡して表示することができます。

v-onによるイベントの利用

HelloWorld.vueを書き換えます。

<template> <div class="alert alert-info"> <h2>{{title}}</h2> <p>{{message}}</p> <hr> <input type="text" class="form-control" v-model="input"> <button class="btn btn-info mt-2" v-on:click="doAction">Click</button> </div> </template> <script> export default { name: 'HelloWorld', props: { title: String, message: String, }, data() { return { message: 'お名前は?', input:'no name', } }, methods: { doAction() { this.message = 'こんにちは、' + this.input + 'さん!' } } } </script>
Code language: HTML, XML (xml)

テンプレート部分にinput>タグとbutton>タグを用意しています。これらを使って入力と処理を実行します。name,props,data,methodsといった値を用意しています。

・・HelloWorldを利用する

App.vueのtemplate>を書き換えます。

コード App.vue

Image from Gyazo

↑入力フィールドに名前を書いてボタンをクリックするとメッセージが表示されます。

テンプレートの修正

<input type="text" class="form-control" v-model="input">
<button class="btn btn-info mt-2" v-on:click="doAction">Click</button>

<input>ではv-modelを使って、inputにバインドしています。<button>ではv-on:clickでdoActionに割り当てています。

コンポーネントの修正

data() { return { message: 'お名前は?', input:'no name', } },
Code language: JavaScript (javascript)

.vueでコンポーネントを定義する場合も、componentと同じように関数の形で定義します。ここでは、messageinputの2つの値を用意しています。

methodsについて

methods: { doAction() { this.message = 'こんにちは、' + this.input + 'さん!' } }
Code language: JavaScript (javascript)

this.inputを使って、this.messageの値を変更しています。dataの値はthisから直接使えるようになっています。

AppからHelloWorldに値を渡す

親コンポーネント側で値を操作すると、v-bindでバインドされた子コンポーネント側の属性が更新されて、それに伴い子コンポーネントの表示も更新されます。

<template> <div id="app"> <HelloWorld v-bind:title="message"/> <hr> <button class="btn btn-primary" v-on:click="doAction">change title</button> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'App', components: { HelloWorld }, data() { return { message: 'HELLO', } }, methods: { doAction() { var input = prompt('new title:') this.message = input } }, } </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑「change title」のボタンをクリックし、表れたダイアログでテキストを入力し、OKすると、HelloWorldのタイトルが変更されます。

App側の操作で、HelloWorldコンポーネントの表示が更新されます。

v-bindによる値のバインド

<HelloWorld v-bind:title="message"/>

titleの値にAppコンポーネントのdataに用意されているmessageがバインドされます。ボタンクリック時のイベント処理で、このmessageの値を変更します。

doAction() { var input = prompt('new title:') this.message = input }
Code language: JavaScript (javascript)

これで、入力されたテキストは戻値として変数inputに代入されます。これをthis.messageに設定します。これで、this.messageが変更され、バインドされたHelloWorld>のtitle属性が変更され、HelloWorldのタイトルが変更されました。

子コンポーネントから親コンポーネントへ

子コンポーネントに用意されている処理から、親コンポーネントに値を渡す場合は、「$emit」というメソッドを使います。

子コンポーネント
this.$emit( イベント, 引数の値…….)

親コンポーネント側での子コンポーネントタグの書き方は↓のようになります。

子コンポーネントのタグ
<コンポーネント v-on:イベント=メソッド/>

親コンポーネントのメソッド
v-onで指定したメソッドをmethods内に用意します。このとき、$emitで渡す引数をうけとるように引数を用意しておきます。

HelloWorldからAppを呼び出す

<template> <div class="alert alert-info"> <h2>{{title}}</h2> <p>{{message}}</p> <hr> <input type="text" class="form-control" v-model="input"> <button class="btn btn-info mt-2" v-on:click="doAction">Click</button> </div> </template> <script> export default { name: 'HelloWorld', props: { title: String, message: String, }, data() { return { message: 'お名前は?', input:'no name', } }, methods: { doAction() { this.message = 'こんにちは、' + this.input + 'さん!' this.$emit('result-event', this.input) } } } </script>
Code language: HTML, XML (xml)

doActionメソッドのなかで、this.$emitを実行しています。ここで「result-event」というイベントを呼びだし、、this.inputの値を引数として渡しています。

App.vueを修正する

<template> <div id="app"> <HelloWorld v-bind:title="message" v-on:result-event="appAction"/> <hr> <p>{{result}}</p> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'App', components: { HelloWorld }, data() { return { message: 'HELLO', result:'no event' } }, methods: { appAction(message) { this.result = '(*** you send:"' + message + '" .***)' } } } </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑ボタンクリックメッセージを更新するとボタンの下に(you send:”○○”.)と表示されます。

HelloWorld側のボタンをクリックすることで、親コンポーネントであるApp側の表示が更新されます。

$emitでresult-eventを発火→result-eventが発生し、appActionメソッドを呼び出す。」という流れです。

テンプレート参照

テンプレート参照は、↓のように記述します。

ref="名前"

このようにref属性をつけて記述されたテンプレートのHTML要素は、「this.$ref.名前」という形で、そのHTML要素のオブジェクト(JavaScriptのElementと呼ばれるオブジェクト)に直接アクセスできるようになります。
refでIDを指定して得た参照はビルドされた後も、参照したHTML要素を指し示すようにできているのです。

テンプレート参照を使う

<template> <div className="alert alert-info"> <h2>{{title}}</h2> <p ref="msg">{{message}}</p> <button class="btn btn-primary" v-on:click="DoAction">Click</button> </div> </template> <script> export default { name: 'HelloWorld', data() { return { title: 'HelloWorld', message: 'This is smple message.', } }, mounted() { this.counter = 0 }, methods: { DoAction() { this.counter++ this.$refs.msg.innerHTML += '<h6>counter:' + this.counter + '</h6>' } } } </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑ボタンをクリックすることで、「countd:○○」という表示が増えていく。

テンプレート参照をチェック

p ref="msg">{{message}}

<p>タグは、”msg”という名前で参照されます。

DoAction() { this.counter++ this.$refs.msg.innerHTML += '<h6>counted:' + this.counter + '</h6>' }
Code language: JavaScript (javascript)

this.counter++で1増やした後、this.$refs.msgのinnerHTMLに<h6>のタグを追加しています。これでどんどん表示が増えていきました。Vueの仕組みを飛ばして、HTMLを直接操作できるようになるのです。
しかしHTML要素を直接操作するということはVue3によって構築されるている内部構造を破壊する危険もあります。つまり、テンプレート参照は、「いざとなれば、直接操作する方法もある」という風に考えておいたほうが良いともいます。

ここまで読んでいただいてありがとうございます。掌田津耶乃さんのVue.js3超入門で勉強をしています。語りかけるような文章ですごいわかりやすいです。本当におすすめです。