ryota21silvaの技術ブログ

Funna(ふんな)の技術ブログ

これまで学んだ技術の備忘録。未来の自分が救われることを信じて

【超Vue.js】ゼロから始めるコンポーネント【セクション6 】

74. コンポーネントを使用して、再利用可能なVueインスタンスを作る

コンポーネントの中でデータを使う時は、データを関数にする必要がある。

以下はグローバルコンポーネント

Vue.component('my-component', {
  data: function(){
    return {
        number: 12
    }
  },
  template: '<p>いいね{<200c>{number}}</p>'
})
 
new Vue({
    el: '#app',
})

75 dataはなぜコンポーネントにおいて関数である必要があるのか?

なぜ、コンポーネント内では、dataを関数にする必要があるの?

コンポーネント内のdataをオブジェクトにしてしまうと、そのコンポーネントを使用している3つのビューにおいて、オブジェクトのデータが参照によって共有されてしまう
つまり、同じデータを共有するということ(=一つのメモリに共有するということ)

例えば、一つのmy-componentをいじると、他のmy-componentも変わってしまう。

ex.) 以下のようにコンポーネントのdataをオブジェクトにした場合、ボタンを押すとmy-component全部の数字の値が増えてしまう。

<div id="app">
  <my-component></my-component>
  <my-component></my-component>
  <my-component></my-component>
</div>

var data = {
number: 12
}

Vue.component('my-component', {
data: function(){
    return data
  },
  template: '<p>いいね{{number}}<button @click="increment">+1</button></p>',
  methods: {
  increment: function() {
    this.number += 1;
  }
  }
})

new Vue({
el: '#app',
})

関数にすると、thisを使えば、押したmy-componentの値だけが増える。

Vue.component('my-component', {
data: function(){
    return {
    number: 12
    }
  },
  template: '<p>いいね{{number}}<button @click="increment">+1</button></p>',
  methods: {
  increment: function() {
    this.number += 1;
  }
  }
})

76. コンポーネントにおける、ローカル登録とグローバル登録の違いを理解する

グローバル登録

// コンポーネントをVueインスタンスの外側に置いてる。
Vue.component('my-component', {
data: function(){
    return {
    number: 12
    }
  },
  template: '<p>いいね{{number}}<button @click="increment">+1</button></p>',
  methods: {
  increment: function() {
    this.number += 1;
  }
  }
})

new Vue({
el: '#app',
})

new Vue({
el: '#app2',
})

ローカル...特定のVueインスタンスでのみ使用できる

書き方
  • 変数を作ってその中にグローバルコンポーネントの中で書いたようなオブジェクトを書く。

  • インスタンスの中でcomponentオプションを書く

  • キーに反映させたいhtmlの記述、バリューに変数名

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<div id="app">
  <my-component></my-component>
  <my-component></my-component>
  <my-component></my-component>
  <hr>
</div>

<div id="app2">
  <my-component></my-component>
  <my-component></my-component>
  <my-component></my-component>
</div>

// 変数の設定
var component = {
  data: function(){
      return {
        number: 12
      }
    },
  template: '<p>いいね{{number}}<button @click="increment">+1</button></p>',
  methods: {
      increment: function() {
      this.number += 1;
      }
  }
}

new Vue({
el: '#app',
        // コンポーネントオプション
        components: {
        // キーに反映させたいhtmlの記述、バリューに変数名
          'my-component': component
     }
})

new Vue({
el: '#app2',
})

77. 「 import App from ‘./App.vue'」の意味を理解する

(main.js)
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

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

import App from './App.vue'とは

App.vueコンポーネントのオブジェクトをimportしている。
template、script、styleに分かれてる.Vueファイル(=単一ファイルコンポーネント)をimportすることで、main.jsコンポーネントのオブジェクト(グローバルコンポーネントでいう第二引数で取るオブジェクト)をAppという形で利用できるようになる。
render: h => h(App)と書ける。
3つのセクションに分けれるというのが良いところ。

(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>

78

(src/components/LikeNumber.vue)
<template>
  <p>いいね({{ number }})</p>
</template>

<script>
export default {
  data: function() {
    return {
    number: 5
    }
  }
}
</script>

<style>
</style>

ES6の書き方

export default {
  data() {
    return {
    number: 5
    }
  }
}

79. 実際に単一ファイルコンポーネントを作成してグローバル登録する

全てのインスタンスでLikenumberコンポーネントが使えるようになる。

(src/components/LikeNumber.vue)
<template>
  <LIkeNumber></LIkeNumber>
</template>
(src/main.js)
import Vue from 'vue'
import App from './App.vue'

// グローバル登録
// コンポーネントのオブジェクトをLikeNumberという形で利用できるようになる
import LikeNumber from "./components/LikeNumber.vue";

Vue.config.productionTip = false
// グローバル登録(Vue.componentの形)
// 第一引数がコンポーネント名、第二引数にコンポーネントに関する情報をオブジェクトでセット
Vue.component('LikeNumber', LikeNumber);

new Vue({
  render: h => h(App),
}).$mount('#app')
(public/index.html)
<!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><%= 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 <script>を勝手に追加してくれる -->
  </body>
</html>

80. templateはルート要素を1つにしなければならないことに注意する

templateはルート要素を一つにしないとだめ

以下だと、divなどで囲っていないからエラー

<template>
    <p>いいね({{ number }})</p>
    <button @click="increment">+1</button>
</template>
error The template root requires exactly element

単一ファイルコンポーネントを使う時は必ず一つの要素で括ってあげなければならない

<template>
  <div>
    <p>いいね({{ number }})</p>
    <button @click="increment">+1</button>
  </div>
</template>

以下もエラー

<template>
  <div>
    <p>いいね({{ number }})</p>
    <button @click="increment">+1</button>
  </div>
  <div></div>
</template>

81. シングルファイルコンポーネントをローカル登録する

ローカル登録

Likeheader.vueをApp.vueでだけ使えるようにする
(src/components/LikeHeader.vue)
<template>
  <div>
    <h1>いいね</h1>
  </div>
</template>
(src/App.vue)
<template>
    <div>
      <LikeHeader></LikeHeader>
      <LikeHeader></LikeHeader>
      <LikeNumber></LikeNumber>
    </div>
</template>

<script>
// ローカル登録(componentsオプション?みたいな)
// LikeHeader.vueという単一ファイルコンポーネントをimportすることで、コンポーネントのオブジェクトをLikeHeaderという形で利用できるようになった
import LikeHeader from "./components/LikeHeader.vue";
export default {
  components: {
    // コンポーネント名: コンポーネント(の)オブジェクト
    LikeHeader: LikeHeader
    // ES6はkeyとvalueが同じならLikeHeaderに省略可能
  }
}
</script>

f:id:ryota21silva:20200630233147p:plain

82. componentsフォルダを作成して、綺麗なフォルダ構造にする

src直下に全部置くと煩雑になる
→componentsディレクトリを作って、その中にコンポーネントファイルをおく

83. コンポーネントの名前はケバブケースかパスカルケースにする

コンポーネント命名規則 ケバブorパスカル

パスカルケースのがよき

html要素はケバブケースだから、パスカルで書く方が見分けが付きやすい

<template>
<div>
  <!-- ケバブケース -->
  <like-header></like-header>
  <!-- パスカルケース -->
  <LikeHeader></LikeHeader>
</div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";
export default {
  components: {
    // コンポーネント名をパスカルケースにしておくと、templateではケバブ、パスカルどっちでも書ける
    LikeHeader: LikeHeader
  }
}
</script>

DOMテンプレートの中ではケバブケースじゃないとダメ

ブラウザは大文字と小文字の区別を付けられないから。
→だからhtmlはケバブケースを使っている。

DOMテンプレートとは
キャメルケース...最初が小文字、その次大文字。likeHeader

コンポーネントに使える。
しかし、Vueではキャメルケースを使うことは想定されていないらしい。
パスカルケースを推奨。

84. スコープ付きCSSと、Vue.jsがそれをどのように実装しているのか理解する

App.vueのstyleタグにCSSを書く

スコープ付きCSS
(src/components/LikeNumber.vue)
<template>
  <div>
    <p>いいね({{ number }})</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
    number: 5
    };
  },
  methods: {
    increment() {
      this.number += 1;
    }
  }
};
</script>

<!-- スタイルをつけた! -->
<style>
  div {
    border: 1px solid red;
  }
</style>
(src/App.vue)
<template>
<div>
  <like-header></like-header>
  <LikeHeader></LikeHeader>
  <LikeNumber></LikeNumber>
</div>
</template>

<script>
// ローカル登録
// LikeHeader.vueという単一ファイルコンポーネントをimportすることで、コンポーネントのオブジェクトをLikeHeaderという形で利用できるようになった
import LikeHeader from "./components/LikeHeader.vue";
export default {
  components: {
    // コンポーネント名: コンポーネント(の)オブジェクト
    // コンポーネント名をパスカルケースにしておくと、templateではケバブ、パスカルどっちでも書ける
    LikeHeader: LikeHeader
    // ES6はketとvalueが同じならLikeHeaderに省略可能
  }
}
</script>

<!-- スタイルをつけた! -->
<style>
  div {
    border: 1px solid blue;
  }
</style>

以下のように、App.vueのdivタグにも(しかも全てのdivタグに)redが反映されちゃってる。
あるテンプレートにだけ適用したいと思っていたのに... f:id:ryota21silva:20200630233828p:plain

scopedを使う

この中に書くcssは自分のコンポーネントにだけ適用させますよ、というもの。

(src/components/LikeNumber.vue)
<style scoped>
  div {
    border: 1px solid red;
  }
</style>
(src/App.vue)
<style scoped>
  div {
    border: 1px solid blue;
  }
</style>

f:id:ryota21silva:20200630234048p:plain

src/App.vueのstyleを消すと以下のようになったり。 f:id:ryota21silva:20200630234333p:plain

styleタグとbodyタグの中で属性を指定することで、cssの適用範囲を指定している。

div[data-v-276663f0]
<div data-v-276663f0 ....>

f:id:ryota21silva:20200630234239p:plain