Vue.js 基本のき (data, computed, methods)

NO IMAGE

この記事ではvueの基本的な記述についてまとめています。

完全vue初心者の方は公式のドキュメントを一度さらっとみてからこの記事に戻ってくることをオススメします。

Vue.jsドキュメント: https://jp.vuejs.org/v2/guide/index.html

公式でもいきなりvue-cliで始めるのではなくcdnで学習することを推奨しています。

注意点として、初心者が vue-cli で始めることは推奨しません(特に、Node.js ベースのツールについてまだ詳しくない場合)。

Vue.js: はじめに

ではvue-cliを使用して環境構築を行なっていきます。

環境構築

vue-cli: https://cli.vuejs.org/

$ npm install -g @vue/cli

$ vue create my-project

ディレクトリ構成は下記のようになっています。

my-project/
├ node_module/
├ public/
├ src/ 
├ .giignore 
├ babel.config.js 
├ package.json 
├ README.md 
└ yarn.lock

ではプロジェクトに移動して起動します。

$ cd my-project

$ yarn serve

http://localhost:8080/ にアクセスすると下記のような画面が表示されます。

ソースコードを追う

index.html

アクセスした際はpublicにあるindex.htmlをみに行きます。

<!DOCTYPE html>
<html lang="">
  <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><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> 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>

下記の記述に注目してください。そして次はmain.jsが読み込まれます。

<div id="app"></div>

index.htmlにmain.jsを読み込む趣旨の記述がないのになぜmain.jsが読み込まれるのかは今はおいておきましょう。。

main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

下記の記述に注目です。

new Vue({
render: h => h(App),
}).$mount('#app')

この記述でidがappのものをマウントしてAppを描画しています。AppはApp.vueのことを指します。ではApp.vueを確認します。

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>

下記のimgタグが http://localhost:8080/にアクセスした時に上に表示されるロゴですね。

<img alt="Vue logo" src="./assets/logo.png">

<HelloWorld msg="Welcome to Your Vue.js App"/>

この記述は

import HelloWorld from './components/HelloWorld.vue'

でインポートしたコンポーネントを描画しています。HelloWorld.vueを確認します。

HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <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
  }
}
</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>

ここにロゴの下にあるリンクなどが記載されています。

図にするとこのような形です。

ここからは実際にコードを書いてdata, computed, methodsについて説明していきます。

まず、Helloworld.vueと同じ要領でPosts.vueを作成します。

<template>
  <div>これはPostsコンポーネントです</div>
</template>

これをApp.vueで読み込みます。

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <Posts />
  </div>
</template>

<script>
import Posts from './components/Posts'

export default {
  name: 'App',
  components: {
    Posts
  }
}
</script>

data

dataオプションは関数で記述します。

export default {
  data: () => {
    return {
      // ここにデータを記述していく
    }
  }
}

実際にいくつか用意します。

data: () => {
  return {
    message: 'これはメッセージです。',
    number: 7,
    object: { id: 1, name: 'hoge' },
    array: [1, 2, 3, 4, 5]
  }
}

これをtemplateタグの中で使用するには {{ }} で囲む必要があります。

例えばmessageを表示したい場合は {{ message }} のように。

では実際にtemplateタグ内に記述していきたいのですが、その前に一つだけ。

vueファイルは下記のようにtemplateタグ直下に複数のdivを持つことができません。もし書いてしまうと次のようなエラーが出てきます。

16:3  error  The template root requires exactly one element  vue/valid-template-root

なのでvueファイルの中のルート要素は単一である必要があります。

NG

<template>
  <div></div>
  <div></div>
</template>

OK
<template>
  <div>
    <div></div>
    <div></div>
  </div>
</template>

それでは実際にdataオプションのデータを表示します。

<template>
  <div>
    {{ message }}
    <br>
    {{ number }}
    <br>
    {{ object }}
    <br>
    {{ array }}
  </div>
</template>

dataオプションの説明については以上になります。

computed

続いてcomputedについて説明していきます。これは算出プロパティと呼ばれます。

何か入力した値を加工して表示したいときに使用します。例えば税金の計算など

実際に記述してみます。まずdataオプションの中にpriceを追加します。

templateタグ内でinput要素を追加します。

<template>
  <div>
    <input type="number" v-model="price">
    <div>値段: {{ price }}</div>
    <div>税込み: {{ priceInTax }}</div>
  </div>
</template>

<script>
export default {
  data: () => {
    return {
      price: 0,
    }
  },

ここで新しくv-modelが登場しました。

<input type="number" v-model="price">

これで双方向データバインディングが可能になります。まずデータバインディングというのは

データバインディング(データバインド、データ結合)は、データと対象を結びつけ、データあるいは対象の変更を暗示的にもう一方の変更へ反映すること、それを実現する仕組みのことである。

ウィキペディア

要はdataオプションのデータをtemplateタグ内で表示する。これが単一方向データバインディング。

それプラスユーザーの入力に合わせてデータの値を変更できるようにしたものが双方向データバインディングです。

inputでデータを入力すると即座にdataオプションのpriceが変更されていることが確認できます。

またv-modelは以下のように書くことと同じ意味になります。

<input type="number" :value="price" @input="price = $event.target.value">

:value=”price”はv-bind:value=”price”の省略形です。

v-bindディレクティブは、リアクティブに HTML 属性を更新します:

Vue.js ガイド: ディレクティブ

@input=”price = $event.target.value”> は v-on:input=”price = $event.target.value”>の省略形です。

v-on:inputというのはinputイベントのことを指します。なのでvalueの値が変わるとイベントが発火して $event.target.value の値が number に代入されます。

HTML属性を動的に変更したい場合は :HTML属性 (例 :class)

イベントの発火で処理をしたい場合は @イベント名 (例: @click)

で覚えておくといいです。

本題に戻ります。現在input要素でmessageの値を動的に変更することができました。なのでnumberの変更に合わせてcomputedプロパティを使用して追加の計算を行なっていきます。

export default {
  data: () => {
    return {
      price: 0,
    }
  },
  // 追加
  computed: {
    priceInTax() {
      const price = parseInt(this.price)
      return (price + price * 0.1) || 0
    },
  },
}

priceInTax()内で this.priceというのがdataオプションで定義したpriceになります。そしてこの中では文字列として認識されるのでparseInt()でInt型に変換しています。

そして price + price * 0.1で計算したものを返しています。

computedプロパティは必ず return が必要になります。return を記述しないと下記のようなエラーが返ってきます。

18:15  error  Expected to return a value in "priceInTax" computed property

computedプロパティの説明は以上になります。

methods

最後にmethodsについて説明していきます。

例えばボタンを用意してそのボタンがクリックされたらアラートを出すような処理を行いたい場合などに使用します。

実際にコードをみていきましょう。

まずtemplateタグ内でbuttonを用意してclickイベントを作成します。

<template>
  <div>
    <button @click="showAlert">
      Click
    </button>
  </div>
</template>

次にmethodsプロパティ内でshowAlertメソッドの中身を記述します。

  methods: {
    showAlert() {
      alert('showAlertメソッドです')
    },
  },

computedの違いとしては下記の2点があげられます。

  • 引数を受け取れる
  • returnはなくても良い
<template>
  <div>
    <button @click="showAlert('button1')">
      Click1
    </button>

    <button @click="showAlert('button2')">
      Click2
    </button>
  </div>
</template>

... 省略

  methods: {
    showAlert(button) {
      alert(`${button}のshowAlertです`)
    }
  }

上記のようにshowAlert()に引数を加えたのでshowAlert()が実行されるときに引数に応じてalertの内容が変わるようになりました。

vueで必須のdata, computed, methodsの説明は以上になります。

Vue.jsカテゴリの最新記事