【超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インスタンスでのみ使用できる
書き方
<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>
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が反映されちゃってる。
あるテンプレートにだけ適用したいと思っていたのに...
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>
src/App.vue
のstyleを消すと以下のようになったり。
styleタグとbodyタグの中で属性を指定することで、cssの適用範囲を指定している。
div[data-v-276663f0] <div data-v-276663f0 ....>