6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vue.jsの基礎

Last updated at Posted at 2020-02-08

Vue.jsの基本的な使い方

  • Reactは単方向データバインディングで一方通行なのに対し、Vueは双方向データバインディングで双方向にデータを流す仕組みになっている。
    • 双方向データバインディングの場合はjsの中身が変わったらすぐに反映される
    • 単方向データバインディングの場合はrenderなどの描画の処理が単方向であるため即時反映とはならない

環境構築 ---未完成

  • vue.esn.jsというtemplate機能のないランタイム限定ビルドがあるので、これも必要に応じてnpmでインストールする

基本的な書き方

  • new Vueでインスタンス生成
  • elでセレクターを書き込み、スコープを指定する
  • dataプロパティはオブジェクトの形で自分が使いたいプロパティを定義する
    • データオブジェクトの中の値を出し入れするための入れ物のようなもの
  • dataで定義したプロパティはhtmlで使うことができる

ファイルのベースづくり

app.js
import Vue from 'vue'

new Vue({
  el: '#app1',
  data: {
    message: 'vueのテンプレートの構文。{{}}で囲って処理がかける'
  }
})

htmlの方では{{}}の中でテンプレートの構文やif文など、またインスタンス化したdataのプロパティなども使える

index.html
<div id="app1">
  {{ message }}
</div>

v-bindを使った属性のバインド

  • 単方向のデータバインディング
  • dataの変更に応じて表示されるが、HTML側の入力でdataが変更されることはない
  • v-bind:とすることでインスタンスで定義したdataのプロパティが属性として出力される
  • v-bind:は省略可能で、:のみで書くことができる
  • class属性を渡すときは連想配列の形で渡す
  • その連想配列の値がtruefalseかでプロパティ名が付くどうかが判別される
  • この例の場合、activetext-dangertrueなのでclass="active text-dangerとして入ってくる

text-dangerのように-を使っている場合''または""で囲む必要がある

app.js
import Vue from 'vue'

new Vue({
  el: '#app2',
  data: {
    message: 'このページをロードしたのは ' + new Date().toLocalString(),
    classObject: {
      active: true,
      'text-danger': true
    }
  }
})
index.html
<div id="app2">
<!-- title属性は補足的な情報を与えるときに使用し、ポインタを重ねると吹き出し(ツールチップ)が表示される -->
  <span v-bind:title="message" :class="classObject">
    この文字にロードした日付が表示される
  </span>
</div>

v-if, v-else-if, v-elseを使った条件分岐で表示非表示

v-ifのみでの条件分岐

app.js
import Vue from 'vue';

new Vue({
  el: '#app3',
  data: {
    isShow: true
  }
})
  • isShowの名称は任意
  • v-ifの中でtruefalseかを判定し、trueであれば、そのタグ自体を表示し、falseであれば、そのタグ自体を非表示にする
    ※ 非表示の場合DOM自体がなくなる(DOMの残るdisplay:noneとは異なる)
htmlindex.html
<div id="app3">
  <span v-if="isShow">v-ifを使ったDOMの表示非表示</span>
</div>

v-if, v-else-if, v-elseを使った条件分岐

app.js
new Vue({
  el: '#app3',
  data: {
    isShow: 'a'
  }
})
index.html
<div id="app3">
  <span v-if=" isShow === 'a' ">v-ifを使った条件分岐</span>
  <span v-else-if=" isShow === 'b' ">v-ifと同階層に書く</span>
  <span v-else>v-ifと同階層に書く</span>
</div>

v-showを使った要素の表示非表示

  • v-showによる要素は常に描画され、v-ifと異なりDOMを維持する
  • styleだけ非表示になっているだけ(display: none;)
  • v-showtrueなら表示、falseなら非表示(display:none;)
app.js
new Vue({
  el: '#app3',
  data: {
    isShow: true
  }
})
index.html
<div id="app3">
  <span v-show="isShow">v-showで表示</span>
  <span v-show="!isShow">v-showで非表示に</span>
</div>

v-forを使ったループ処理

v-forの値に変数名を割り当てる(今回はtodo)
todo in todosとすることでtodoにjsファイルで定義したtodosの一つ一つの値のデータが入る
dataプロパティの値を配列リテラルの形式で渡すことで、配列一つ一つを回せる

app.js
import Vue from 'vue';

new Vue({
  el: '#app4',
  data: {
    todos: [
      {text: 'v-forで'},
      {text: 'htmlを'},
      {text: 'ループ生成'}
    ]
  }
})
index.html
<div id="app4">
  <ol>
    <li v-for="todo in todos">
      {{ todo.text }}
    </li>
  </ol>
</div>

<!-- 出力結果 -->
<ol>
  <li>v-forで</li>
  <li>htmlを</li>
  <li>ループ生成</li>
</ol>

v-onを使ったイベント発火

v-on:のあとにイベント名を指定し、イベント後の発火したいメソッドを指定
v-on:は省略可能で、@のみで書くことができる

  • vueではmethodsプロパティを定義でき、viewの中で使いたい関数を定義する
    • methodsは関数の置き場所として用いられる
    • methodsは関数なので、呼べばそのまま実行される
    • methodsで算出された値はキャッシュされない
  • changeMessageという関数名は任意
  • methodsプロパティ内でthisを使うとdataプロパティにアクセスできる

this.messagemessageにアクセスできる

app.js
import Vue from 'vue';

new Vue({
  el: '#app5',
  data: {
    message: 'Hello Vue.js'
  },
  methods: {
    changeMessage: function() {
      this.message = this.message + '変更しました'
    }
  }
})
index.html
<div id="app5">
  <p>{{ message }}</p>
  <button v-on:click="changeMessage">メッセージを変える</button>
</div>

<!-- 出力結果 -->
メッセージを変える
  <!-- クリック -->
  Hello Vue.js変更しました

v-onでtoggle

v-onでは、必ずしもmethodを使わなければいけないというわけではない
以下はtoggle処理の例である

<div id="app5">
  <button v-on:click="show = !show">
    Toggle
  </button>
</div>

v-model 双方向データバインディング

  • 双方向のデータバインディング
  • dataの変更に応じて表示され、HTMLでフォームの入力を行った際にもdataの値の変更が可能
  • input要素やtextarea要素、select要素に双方向 (two-way) データバインディングを作成
  • v-modeldataのプロパティを指定すると、jsファイルのプロパティと描画が連携される
  • 双方向データバインディングでは入力フォームの値が変わると描画も変わるため、pタグの中身もすぐに反映される
  • 下の例ではinputタグの中にmessageの値がvalueの形で入る
  • ユーザーの入力データが自動で更新される
app.js
import Vue from 'vue';

new Vue({
  el: '#app6',
  data: {
    message: '双方向データバインディング'
  }
})
index.html
<div id="app6">
  <p>{{ message }}</p>
  <input type="text" v-model="message">
</div> 

computed 算出プロパティ

  • computedは関数の処理を書く算出のプロパティである
  • methodsと似ているが、computeddataプロパティの変更に依存して描画に反映される
  • dataやcomputedの値が変化することで自動的に実行
  • computedはjsの方で算出された値が常に結果がキャッシュされており再利用できる。dataの変更(thisの変更)をthisで監視しているため、dataプロパティに変更があった場合のみ描画に反映される
    • thisを持たないcomputeddataの変更を参照できず、ずっと初回のキャッシュを表示し続ける
  • methodsは再描画されるたびに処理が実行される
app.js
import Vue from 'vue';

new Vue({
  el: '#app7',
  data: {
    isShow: true
  }
  computed: {
    showString: function() {
      return (this.isShow) ? Date.now() : 'isShowはfalse'
    },
    showString2() {
      return Date.now()
    }
  },
  methods: {
    showStringMethods() {
      return (this.isShow) ? Date.now() : 'isShowはfalse'
    },
    showStringMethods2() {
      return Date.now()
    }
  }
})

computedの場合は()は不要

index.html
<div id="app7">
  <input type="checkbox" v-model='isShow'>
  <p>isShow: {{ isShow }}</p>
  <p>showString: {{ showString }}</p>
  <p>showString2: {{ showString2 }}</p>
  <p>showStringMethods: {{ showStringMethods() }}</p>
  <p>showStringMethods2: {{ showStringMethods2() }}</p>
</div>

<!-- 出力結果 -->
isShow: true
showString: 123456789
showString2: 123456789
showStringMethods: 123456789
showStringMethods2: 123456789
  <!-- checkbox click -->
  isShow: true
  <!-- 関数内でthisをを持つため、dataの変更を監視して反映 -->
  showString: isShowはfalse 
  <!-- dataが変更されてもthisがないので反映されず、初回のキャッシュ内容保持 -->
  showString12: 123456789
  <!-- methodsはdataに依存せず毎回再描画がされるため更新される -->
  showStringMethods: isShowはfalse 
  showStringMethods2: 234567890

v-htmlでサニタイズを無効化

vue.jsでは自動でサニタイズされ、タグは文字列として読み込まれる
タグとして読み込ませたい場合はサニタイズを無効化するv-htmlを使う

app.js
import Vue from 'vue';

new Vue({
  el: '#app10',
  data: {
    script: '<p style="color:red">タグとして表示</p>'
  }
})
index.html
<div id="app10">
  <p>{{ script }}</p>
  <p v-html="script"></p>
</div>

<!-- 出力結果 -->
<p style="color:red">タグとして表示</p>
タグとして表示  (color:red; 適用)

トランジションとアニメーション

アニメーションにはEnterアニメーションとLeaveアニメーションがある

  • Enter: 非表示から表示に変わるときのアニメーション
    • 変化前の状態のclassはv-enter, 変化後のclassはv-enter-to
    • 変化過程にアニメーションを設定したいときのclassはv-enter-active
  • Leave: 表示から非表示に変わるときのアニメーション
    • 変化前の状態のclassはv-leave, 変化後のclassはv-leave-to
    • 変化過程にアニメーションを設定したいときのclassはv-leave-active

transitionタグでname属性を付けた場合はv-の箇所をその名前に変更できる

app.js
import Vue from 'vue';

new Vue({
  el: '#app11',
  data: {
    show: true
  }
})
index.html
<style>
  .fade-enter-active, .fade-leave-active {
    transition: opacity .5s;
  }
  .fade-enter, .fade-leave-to { /* .fade-leave-active below version 2.1.8 */
    opacity: 0;
  }
</style>

<div id="app11">
  <button v-on:click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>

コンポーネントの使い方

コンポーネントとは名前付きの再利用可能なVueインスタンスである(部品のようなもの)

  • Vue.componentでコンポーネントの登録

    • 第一引数にコンポーネント名を指定する、第二引数にコンポーネントに定義したい値(data, templateなど)を設定する
    • コンポーネントでdataを定義するときは関数で定義する
      ※ 関数ではなくオブジェクトの形にすると複数のコンポーネントで値を共有する形になる。関数にすることで、個々のコンポーネントに値をもたせることができる
  • コンポーネント登録後にnew Vueでインスタンス化する

app.js
// 登録する
Vue.component('sample-component' {
    template: '<div>this is sample</div>'
})

// インスタンス生成
new Vue({
    el: '#app'
})

コンポーネントを挿入したいところに呼び出すコンポーネントのタグを記述し、app.jsを読み込む

sample.html
<div id="app">
    <sample-component></sample-component>
</div>

<script src="app.js"></script>

HTMLが置換され、次のようになる

<div id="app">
    <div>this is sample</div>
</div>

templateについて

temaplateはコンポーネントのHTMLを渡す
htmlファイルでtempalteのタグが挿入される

app.js
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">clicked {{ count }} times</button>'
})
new Vue({el: '#app12'})

コンポーネント名button-counterにコンポーネントが割り当てられ、templateのHTMLが挿入される

index.html
<div id="app12">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

コンポーネントの登録を省略

slotを使用していなければ下記のように省略できる

// 省略なし
<template>
    <div id="app">
        <app-header></app-header>
    </div>
</template>


// 省略あり
<temaplte>
    <div id="app">
        <app-header />
    </div>
</template>

単一ファイルコンポーネントを使うとき

app.js
// 登録する
Vue.component('sample-component', require('./components/SampleComponent.vue').default);

// インスタンス生成
new Vue({
    el: '#app'
})
./components/SampleComponent.vue
// template作成
<template>
    //
</template>

// このtemplateを使ったときの共通の処理を追記する
<script>
export default {
    data: {
        //
    },
    mounted() {
        //
    }
}
</script>

propsで親コンポーネントから受け継ぐ

  • propsを使うことでコンポーネントの親から子へ値を受け渡せる
  • propsは配列の形式で属性を定義し、親コンポーネントからその属性値を受け継いでdataのプロパティのように振る舞う
app.js
Vue.component('blog-post', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
})
new Vue({el: '#app13'})
index.html
<div id="app13">
  <!-- 親コンポーネント(コンポーネントの受け渡し先となる生成元のタグのこと) -->
  <!-- このタグを親コンポーネントといい、作成したコンポーネントを子コンポーネントという -->
  <blog-post title="title属性をpropsで渡せる"></blog-post>
  <blog-post title="title属性をpropsで渡せる"></blog-post>
  <blog-post title="title属性をpropsで渡せる"></blog-post>
</div>

オブジェクトでない配列を指定する場合

  • 子が親からデータを受け取るとき、配列で指定するか、オブジェクトの配列として指定するかの2通りある
  • 簡潔に列挙できるが、データの情報が記述できず、itemもvalueも同様に扱われる
parent.vue
<child
    :param0="item",
    :param1="value"
>
child.vue
props: [
    'item',
    'value'
]

オブジェクトの配列として受け取る場合

  • 詳細に記述できるため、違いを明記できる
child.vue
props: {
    item: {
        type: Object
    },
    value: {
        type: Number,
        default: 0
    }
}

emitで子から親へデータを伝達

  • 子から親へのデータの伝搬では$emitと$onを一対として使用するカスタムイベントを活用する
  • 親コンポーネントからこのイベントを監視する場合はv-on:, @でイベント名を紐付けて使用する

子コンポーネント

child.vue
<template>
    <div class="item">
        <span>{{ value }}</span>
        <button @click="updateEvt">追加</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            value: 0
        }
    },
    methods: {
        plus0ne() {
            this.value += 1
            // $emitの第2, 第3...引数で変数を指定するとその変数を親が受け取ることができる
            this.$emit('update', this.value)
        }
    }
}
</script>

親コンポーネント

parent.vue
<template>
    <counter @updated="updateEvt" />
</template>

<script>
import Child from './Child.vue'
export default {
    components: {
        Child
    },
    methods: {
        // 子から変数を受けとることができる
        updateEvt(value) {
            console.log('The value updated' + value)
        }
    }
}
</script>

イベントとメッセージを親コンポーネントに渡す方法

  • v-on:click=$emit('カスタムイベント名')のように$emitで任意のカスタムイベント名を指定することで、そのイベントをイベント名として親子コンポーネントで通知することができる

    • この例ではv-on:カスタムイベント名=func()とするとclickイベントが通知される
  • templateはバッククォートで囲むことで階層構造で文字列を書くことができる(ES6より)

app.js
Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <button v-on:click="$emit('enlarge-text')">
        Enlarge text
      </button>
      <div v-thml="post.content"></div>
    </div>
  `
})
new Vue({
  el: '#app14',
  data: {
    posts: [
      {
        id: 1,
        title: 'sample post1',
        content: '<p>サンプル投稿のコンテント</p>'
      },
      {
        id: 2,
        title: 'sample post2',
        content: '<p>サンプル投稿のコンテント</p>'
      },
      {
        id: 3,
        title: 'sample post3',
        content: '<p>サンプル投稿のコンテント</p>'
      },
      
    ]
  }
})
index.html
<div id="app14">
  <div id="blog-posts-events-demo">
    <div :style="{ fontSize: postFontSize + 'em' }">
      <blog-post
        v-for="post in posts"
        v-on:enlarge-text="fontSizeScale()"
        v-bind:key="post.id"
        v-bind:post="post"
      ></blog-post>
    </div>
  </div>
</div>
6
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?