お勉強用リポジトリ
https://github.com/yuukanehiro/Vue-Study
Udemyと参考書をやってみてメモ
もくじ
きほんのき
はろわ!
hello.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <title>Vue.js</title> </head> <body> <div id="app"> <p>{{ message }}</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> <script src="js/hello.js"></script> </body> </html>
js/hello.js
let app = new Vue({ el: '#app', data: { message: 'はろわ!' } })
v-bind
bind.html
<div id="app"> <a v-bind:href="url">優技録</a> </div>
js/bind.js
let app = new Vue({ el: '#app', data: { url: 'https://www.yuulinux.tokyo' } })
v-model 双方向データバインディング
vmodel.html
<div id="app"> <input type="text" v-model="data"> <p>{{ data }}</p> </div>
js/vmodel.js
let app = new Vue({ el: '#app', data: { data: '優技録だよ', } })
computed
computed.html
<div id="app"> <p>{{ localEmail }}</p> </div>
js/computed.js
let app = new Vue({ el: '#app', data: { email: 'YUU3@example.net' }, computed: { localEmail: function() { return this.email.split('@')[0].toLowerCase() + '@' +this.email.split('@')[1]; } } })
methods
methods.html
<div id="app"> <p>{{ localEmail }}</p> </div>
js/methods.js
let app = new Vue({ el: '#app', data: { email: 'YUU3@example.net' }, methods: { localEmail: function() { return this.email.split('@')[0].toLowerCase() + '@' +this.email.split('@')[1]; } } })
v-on
on.html
<div id="app"> <button v-on:click="onclick">押して!</button> <p>{{ now }}</p> </div>
js/on.js
let app = new Vue({ el: '#app', data: { now: '', }, methods: { onclick: function() { this.now = new Date().toLocaleString(); } } })
v-if
vif.html
<div id="app"> <p>{{ message }}</p> <p v-if="toggle"> <a v-bind:href="url">優技録</a> </p> </div>
js/vif.js
let app = new Vue({ el: '#app', data: { message: 'こんにちわん!', url: 'https://www.yuulinux.tokyo/', toggle: true } })
v-for
vfor.html
<div id="app"> <p>{{ message }}</p> <p v-if="toggle"> <a v-bind:href="url">優技録</a> </p> <ul> <li v-for="language in languages">{{ language }}</li> </ul> </div>
js/vfor.js
let app = new Vue({ el: '#app', data: { message: 'こんにちわん!', url: 'https://www.yuulinux.tokyo/', toggle: true, languages: [ 'JavaScript', 'PHP', 'Python' ] } })
component
component.html
<div id="app"> <hello-component></hello-component> </div>
js/component.js
Vue.component('hello-component', { template: '<p>こんにちゃ!</p>' }) let app = new Vue({ el: '#app' })
computedとmethodsの違い
- computed
・getter, setter
・キャッシュされる - methods
・getter
・キャッシュされない
computed2.html
<div id="app"> <p> base price: <input type="text" v-model="basePrice"> </p> <p> tax included price: <input type="text" v-model="taxIncludedPrice"> </p> </div>
js/computed2.js
let app = new Vue({ el: '#app', data: { basePrice: 100 }, computed: { taxIncludedPrice: { get: function() { return parseInt(this.basePrice * 1.08); // 整数値を返す }, set: function(taxIncludedPrice) { this.basePrice = Math.ceil(taxIncludedPrice / 1.08); // 小数点以下を切り上げてからbasePriceプロパティに代入 } } } })
watch
watch.html
<div id="app"> <p> {{ message }} </p> <p> <input type="text" v-model:value="message"> </p> <pre>{{ $data }}<pre> </div>
js/watch.js
let app = new Vue({ el: '#app', data: { message: '優技録' }, watch: { message: function(newValue, oldValue) { console.log('new: %s, old: %s', newValue, oldValue) } } })
lodashライブラリ
- _.debounce(関数名, ミリ秒)
画面同期やサーバ問い合わせでミリ秒待ってから、関数名を実行する
クラスのデータバインディング
classbind.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <title>Vue.js</title> <style> .large { font-size: 36px; } .text-danger { color: red; } </style> </head> <body> <div id="app"> <p> Hello <span v-bind:class="{large: isLarge, 'text-danger': hasError}">優技録</span> </p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> <script src="js/classbind.js"></script> </body> </html>
js/classbind.js
let app = new Vue({ el: '#app', data: { isLarge: true, hasError: true } })
セレクトボックス
単体選択
select-single.html
<div id="app"> <select v-model="selected"> <option disable value="">選択してちょ</option> <option>りんご</option> <option>ばなな</option> <option>しゅーまい</option> </select> {{ selected }} </div>
select-single.js
let app = new Vue({ el: '#app', data: { selected: '' } })
複数選択
select-multiple.html
<div id="app"> <select v-model="selected" multiple> <option disable value="">選択してちょ</option> <option>りんご</option> <option>ばなな</option> <option>しゅーまい</option> </select> {{ selected }} </div>
select-multiple.js
let app = new Vue({ el: '#app', data: { selected: [] } })
簡単なアプリをつくろう
todolist
todolist.html
<!DOCTYPE html> <html lang="ja"> <head> <style> #app ul { list-style: none; } #app li > span.done { text-decoration: line-through; } </style> <meta charset="UTF-8" /> <title>Vue.js</title> </head> <body> <h2>ToDo list</h2> <div id="app"> <form v-on:submit.prevent> <input type="text" v-model="newItem"> <button v-on:click="addItem"> 追加だ! </button> </form> <ul> <li v-for="(todo, index) in todos"> <input type="checkbox" v-model="todo.isDone"> <span v-bind:class="{ done: todo.isDone }">{{ todo.item }}</span> <button v-on:click="deleteItem(index)">削除</button> </li> </ul> <pre>{{ $data }}</pre> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> <script src="js/todolist.js"></script> </body> </html>
js/todolist.js
let app = new Vue({ el: '#app', data: { newItem: '', todos: [] }, methods: { addItem: function(event) { // 入力が空の場合はアーリーリターン if (this.newItem == '') { return; } var todo = { item: this.newItem, isDone: false }; this.todos.push(todo); // タスク追加 this.newItem = ''; // クリア }, deleteItem: function(index) { this.todos.splice(index, 1); // splice(削除を開始する配列のインデックス, 削除する個数) } } })
bitcoin
bitcoin.html
<!DOCTYPE html> <html lang="ja"> <head> <style> #app ul { list-style: none; } #app li > span.done { text-decoration: line-through; } [v-cloak] { display: none; } </style> <meta charset="UTF-8" /> <title>Vue.js</title> </head> <body> <h2>BitCoin App</h2> <div id="app"> <section v-if="hasError">Errorが起きたよ</section> <section v-else> <div v-if="loading"> Now Loading... </div> <ul v-cloak> <li v-for="(rate, currency) in bpi"> {{ currency }} : {{ rate.rate_float | currencyDecimal }} </li> </ul> </section> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios@0.18.0/dist/axios.min.js"></script> <!-- ●←Axiosを読み込み --> <script src="js/bitcoin.js"></script> </body> </html>
js/bitcoin.js
let app = new Vue({ el: '#app', data: { bpi: null, hasError: false, loading: true }, mounted: function() { axios.get('https://api.coindesk.com/v1/bpi/currentprice.json') .then(function(response) { // console.log(response); // console.log(response.data.bpi); //console.log(response.data.bpi.EUR.rate_float); this.bpi = response.data.bpi; }.bind(this)) // このクラスにバインド。bindで指定しないとthen()の中のthisがWindowオブジェクトを意味してしまう .catch(function(error) { console.log(error); this.hasError = true; }.bind(this)) .finally(function() { // 通信が全て終わった時に、loadingをfalseにする this.loading = false; }.bind(this)) }, filters: { currencyDecimal(value) { return value.toFixed(2); } } })
Qiita App
index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Real time search</title> </head> <body> <div id="app"> <p> <input type="text" v-model="keyword"> </p> <p> {{ message }} </p> <ul> <li v-for="item in items"> <a v-bind:href="item.url">{{ item.title }}</a> Likes: {{ item.likes_count }} </li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios@0.18.0/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js"></script> <script src="js/main.js"></script> </body> </html>
js/main.js
var app = new Vue({ el: '#app', data: { items: null, keyword: '', message: '' }, watch: { keyword: function(newKeyword, oldKeyword) { // console.log(newKeyword); this.message = '入力が終わるのを待っています...'; this.debouncedGetAnswer(); } }, created: function() { this.keyword = 'JavaScript'; this.getAnswer(); this.debouncedGetAnswer = _.debounce(this.getAnswer, 1000) }, methods: { getAnswer: function() { if(this.keyword === '') { this.items = null; this.message = ''; return; } this.message = 'Loading...'; var vm = this; var params = { page: 1, per_page: 20, query: this.keyword } axios.get('https://qiita.com/api/v2/items', { params} ) .then(function(response) { vm.items = response.data; console.log(response); }) .catch(function(error) { vm.message = 'Error! + error'; }) .finally(function() { vm.message = ''; }) } } })
Vue-Cli
インストール
$ npm install -g @vue/cli
プロジェクト作成
$ vue create my-cli $ cd my-cli
ライブラリのインストール
$ vue add element $ vue add router $ vue add vuex
$ npm install -g @vue/cli-service-global
サーバ立ち上げ
$ npm run serve DONE Compiled successfully in 12040ms 22:47:27 App running at: - Local: http://localhost:8080/ - Network: http://192.168.11.2:8080/ Note that the development build is not optimized. To create a production build, run npm run build.
http://localhost:8080/
Vuex
Vuex 公式のサンプルアプリ
https://jsfiddle.net/n9jmu5v7/1269/
html
<div id="app"> <p>{{ count }}</p> <p> <button @click="increment">+</button> <button @click="decrement">-</button> </p> </div>
js
// make sure to call Vue.use(Vuex) if using a module system const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment: state => state.count++, decrement: state => state.count-- } }) new Vue({ el: '#app', computed: { count () { return store.state.count } }, methods: { increment () { store.commit('increment') }, decrement () { store.commit('decrement') } } })
モジュール
https://vuex.vuejs.org/ja/guide/modules.html
const moduleA = { state: () => ({ ... }), mutations: { ... }, actions: { ... }, getters: { ... } } const moduleB = { state: () => ({ ... }), mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } }) store.state.a // -> `moduleA` のステート store.state.b // -> `moduleB` のステート
Udemyの講座やる
$ vue create udemy-vuejs $ npm run build $ npm run serve
main.js
import Vue from 'vue'; import App from './App.vue'; Vue.config.productionTip = false console.log(App); new Vue({ render: h => h(App), }).$mount('#app');
Appオブジェクトが確認できた。
コンポーネント props, $emit
main.js
import Vue from 'vue'; import App from './App.vue'; Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app');
App.vue
<template> <div> <LikeHeader :total-number="number"></LikeHeader> <LikeNumber :total-number="number" @my-click="incrementNumber"></LikeNumber> <LikeNumber :total-number="number"></LikeNumber> </div> </template>> <script> import LikeHeader from './components/LikeHeader.vue'; import LikeNumber from './components/LikeNumber.vue'; export default { components: { LikeHeader, LikeNumber }, data() { return { number: 10 } }, methods: { incrementNumber(value) { this.number = value; } } } </script>
components/LikeHeader.vue
<template> <div> <h2>いいね{{ number() }}</h2> </div> </template> <script> export default { props: ["totalNumber"], methods: { number() { return this.totalNumber; } } } </script>> <style scoped> div { border: 1px solid blue; } </style>>
components/LikeNumber.vue
<template> <div> <p>いいね{{ halfNumber }} <button @click="increment">+1</button> </p> </div> </template> <script> export default { props: { totalNumber: { type: Number } }, computed: { halfNumber() { return this.totalNumber / 2; } }, methods:{ increment() { //this.totalNumber += 1; this.$emit("my-click", this.totalNumber + 1) } } } </script> <style scoped> div { border: 1px solid red; } </style>>