はじめに
はじめまして、飯田です。
フリューにはフロントチーム設立と同時に2018年6月に入社しました。
主にピクトリンクのフロント面を触っていて、現在はVue.jsを勉強しています。
Vue.jsは「わかりやすい」「フロント初学者に最適」などと言われてはいますが、今までアニメーションをつけるためにふんわりjQueryを書いていた程度の私には敷居が高く、難しく感じました。
そんな自分がどのような流れでVue.jsというものを学んでいるのかを少しずつ書いていこうと思います。
最初にやったこと
公式ドキュメントに目を通す
https://jp.vuejs.org/v2/guide/index.html
Vueは公式ドキュメントが日本語化されているのでありがたいなあ、ととりあえず読んでみる…も、自分の知識が足りず、説明に当たり前のように出てくるカタカナの開発用語やプログラミング用語が分からず、コードを見てなにができるのかなんとなく分かっても文章自体はあまり理解できませんでした。
ベースの知識があまりない状態で新しい技術を習得しようとするときのあるあるなのかもしれません。
正直挫折しそうになってしまったので書籍に頼ることにしました。
書籍を読む
通称「猫本」
公式ドキュメントの内容をもっとわかりやすく書いてくれている本です。
7章以降は飛ばしました。
こちらはVue.jsの基礎中の基礎がかなり噛み砕いて説明されておりわかりやすかったです。
説明にも特に難しい用語が使われていないので最後までスラスラ読めました。
個人的に最初から難しい説明を読むのが嫌ならこの本から入ると良いと思います。
全体を読むというよりは、わからない部分や知りたい部分のみ都度目を通しています。
コンポーネントやVue Router、Vuexについての説明は最初は読まずに飛ばしました。
また、学習過程でわからないことが出てきた際に公式ドキュメントや上述の本の必要な部分を都度読み返しています。
とりあえず簡単なものを書いてみる
まずは公式ドキュメントや本を見ながら、基礎的なものを手を動かして書いていました。
私はWebページでVue.jsをどのように使えば良いのかうまくイメージができなかったので、自分が今まで書いたjQueryをVue.jsで置き換えるとどうなるかを試したりもしていました。
- dataを作ってそのまま表示してみる
- v-modelを使ってフォームに入力した内容を表示してみる
- v-ifを使って要素の出しわけをする
- v-onを使ってボタンをクリックしたときにイベントを発火させてみる
- v-forを使ってリストの内容を繰り返してみる
そして上記がなんとなく理解できたら、実際の業務で使えそうなものを書いて動かしていました。
- 文字数をカウントするフォーム(v-model)
- 押したボタンによって内容を切り替える(v-on + v-if)
- Todoリスト(色々なものの組み合わせ。書籍やあらゆる記事で解説されているので基礎の応用かなと思っている)
などなど
Vue Componentについて
プロジェクト上で練習用ブランチを切って過去に作ったキャンペーンページを練習でVue.jsに直していたのですが、Pull Requestで「コンポーネントに分けた方がいいよ」とレビューをいただきました。
すっ飛ばしていた公式ドキュメントや書籍のVue Componentについての説明をやっとここで読みました。
ボタンなどの何度も使うような部品をコンポーネントとして定義し、都度呼び出すことによって簡単に使い回せるようにするイメージです。
公式ドキュメント
https://jp.vuejs.org/v2/guide/components.html
// HTML <div id="#app"> // コンポーネントの呼び出し <hoge-component></hoge-component> <hoge-component></hoge-component> <hoge-component></hoge-component> </div> // Vue.js <script> var HogeComponent = { template: '<p>{{ Message }}</p>', // templateの内容が表示される data() { return { Message: 'この部分がコンポーネントだよ' } } } new Vue:({ el: '#app', components: { 'hoge-component': HogeComponent // ここにコンポーネントの設定を記述するのを忘れない } }) </script>
表示
作成したHogeComponent
を<hoge-component></hoge-component>
部分で表示させています。
コンポーネント名をケバブケースで記述することによって呼び出すことが可能です。
慣れないうちは割とやりがちな注意点
ちゃんと表示されないんだけど!?というときに確認すること。
- Vue Componentを使う際はdataをfunctionにする / returnを記述する
- スペルミスをしていないか(プロパティ名、コンポーネント名、あらゆる場所でよくやりがち)
- Vueインスタンス内の
components:
の中にコンポーネントの設定を記述するのを忘れていないか
vue-cliを使ってみる
※あらかじめnode.jsとnpmのインストールが必要です。この記事では省略します。
ある程度の規模のプロジェクトでVue.jsの開発環境を一から構築するのは大変です。
Vue.jsのアプリケーション開発に必要な環境や設定を色々と整えてくれて、雛形ファイルも用意してくれるのがvue-cliというなんかすごいやつです。
試しにローカル環境で使ってみます。
インストール
$ npm install -g @vue/cli
プロジェクトの作成
$ vue create my-project
my-project
部分はプロジェクト名にあたるのでお好きな名前で。
色々聞かれますがとりあえずEnter連打で良いと思います。
ローカルサーバーの起動
$ cd my-project
$ npm run serve
作成したプロジェクトのディレクトリに移動し、ローカルサーバーを起動します。
http://localhost:8080/
にアクセスし、上記の画面が出れば起動成功です。
初めて使うときはこちらの記事を参考にさせていただきました。
https://qiita.com/567000/items/dde495d6a8ad1c25fa43
ファイル構造について
vue-cliを使う際は、単にHTMLファイルにVue.jsを書いていたときとは違う点が多々あります。
先ほど作成したプロジェクトのディレクトリを開き、主要なファイルを見てみます。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <title>my-project</title> </head> <body> <noscript> <strong>We're sorry but my-project doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>
import Vue from 'vue' import App from './App.vue' // 表示させたいVueファイルをimportする Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app') // 表示させたいVueファイルをどの要素内に表示するか
どのVueファイルの内容をindex.html
のどの要素内に表示させるかを設定しています。
上記では#app
という要素にApp.vue
の内容を表示させています。
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js App"/> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'app', components: { HelloWorld } } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
Vueファイルにはtemplate
、script
、style
がまとめて書かれています。
これを単一ファイルコンポーネントと呼びます。
単一ファイルコンポーネントのメリット
Vue Componentを独立した一ファイルにすることができます。
vue-cliで作成したプロジェクト内のVueファイルは既にこの形になっています。
公式ドキュメント(説明が難しい…)
https://jp.vuejs.org/v2/guide/single-file-components.html
単一ファイルコンポーネントの利点は、一ファイルにtemplate
、script
、style
をまとめて記述することができる点です。
先ほどのsrc/App.vue
をもう一度見てみます。
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js App"/> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'app', components: { HelloWorld } } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
template、script、styleがまとめて書かれているのがわかると思います!
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js App"/> </div> </template>
<template>
タグ直下は必ずひとつのブロック要素で囲みます。(エラーになります)
ここでHelloWorld
という名前のコンポーネントを表示しています。
msg=
部分についてはApp.vueからHelloWorldコンポーネントに表示させたい文字列を渡しているのですが、次のセクションで説明します!
<script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'app', components: { HelloWorld } } </script>
name:部分は省略可能ですが、書いた方が良いと思います。
他の単一ファイルコンポーネントを読み込むときは、<script>
タグ内でファイルをimportします(変数名 + ファイルパス)
components:
内に読み込んだコンポーネントの変数名を記述するのも忘れないようにします。
<style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
デフォルト言語はCSSですが、lang
属性を使用することによって他の言語をサポートしてくれます(普段書き慣れているSCSSなどで書ける)
scoped
を付けることによって、記述したスタイルをそのコンポーネントにのみ適用させることができます。
その場合は下記のように書くことができます。
<style lang="scss" scoped> #app { background: #000; .hoge { text-align: center; } } </style>
コンポーネント同士のデータの受け渡し
先ほど触れた通り、App.vue
のtemplate
部分で読み込んでいたコンポーネントHelloWorld
部分にmsg=
ではじまる謎の記述がありました。
ここで、HelloWorld.vue
という単一ファイルコンポーネントを読み込んでいるApp.vue
を親コンポーネント、読まれているファイルであるHelloWorld.vue
を子コンポーネントと呼びます。
親コンポーネント側(App.vue
)のこの部分で子コンポーネント側(HelloWorld.vue
)のmsg
部分にWelcome to Your Vue.js App
という文字列を渡しています。
<HelloWorld msg="Welcome to Your Vue.js App"/>
ここで、コンポーネントとして読み込んでいるsrc/components/HelloWorld.vue
を見てみます。
<template> <div class="hello"> <h1>{{ msg }}</h1> // ここでmsgの内容を表示する <p> For a guide and recipes on how to configure / customize this project,<br> check out the <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>. </p> <h3>Installed CLI Plugins</h3> <ul> <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li> <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li> </ul> <h3>Essential Links</h3> <ul> <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li> <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li> <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li> <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li> <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li> </ul> <h3>Ecosystem</h3> <ul> <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li> <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li> <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li> <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li> <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li> </ul> </div> </template> <script> export default { name: 'HelloWorld', props: { msg: String // 受け取ったmsgのデータ型 } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> h3 { margin: 40px 0 0; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>
このとき、子コンポーネントに親コンポーネント側から受け取りたいデータについて記述する必要があります。
このときに使うのがpropsで、script内に以下のように書きます。
<script> export default { name: 'HelloWorld', props: { msg: String // ここに受け取るものとデータ型を記述する } } </script>
props
を記述することで親コンポーネントから文字列を受け取ることができました。
template
内の{{ msg }}
部分で受け取った文字列が表示されています。
ここまでを表示で確認すると以下のような構造になります。
注意点
単一ファイルコンポーネントを作成するときの命名規則
ちなみにピクトリンクのフロントチームではファイル名をパスカルケースに統一しています(公式ガイドに合わせています)
同じ階層にあるファイルのパス指定には./
必須
vue-cliでファイルをimportする際、同じ階層のファイルのパス指定に./
を含めないとエラーになってしまいます。
import Hoge from './Hoge.vue'
長くなってしまいましたが、Part.1ではVue.jsを学ぶに当たって最初に何をしたか、Vue Component、vue-cli、単一ファイルコンポーネント、propsについて説明してみました。
Part.2では実際にプロジェクト上のコンテンツをVue.jsに直した話を書いていこうと思います。