# VUE
###### tags: `PID`
# setup
## CDN(開發版本)
```
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
```
## CDN(生產版本)
```
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
```
# 基本教學
## 呼叫預設資料內容
### html
```
<div id="app">
{{ message }}
</div>
```
### js
```
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
```
## 用VUE影響標籤屬性, 可以簡寫成 :title
### html
```
<div id="app-2">
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
</div>
```
### js
```
var app2 = new Vue({
el: '#app-2',
data: {
message: '页面加载于 ' + new Date().toLocaleString()
}
})
```
## 條件式, 由seen(資料名稱)的布林值控制顯示
### html
```
<div id="app-3">
<p v-if="seen">现在你看到我了</p>
</div>
```
### js
```
var app3 = new Vue({
el: '#app-3',
data: {
seen: true // false
}
})
```
## 迴圈
### html
```
<div id="app-4">
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
```
### js
```
var app4 = new Vue({
el: '#app-4',
data: {
todos: [
{ text: '学习 JavaScript' },
{ text: '学习 Vue' },
{ text: '整个牛项目' }
]
}
})
```
## DOM事件處理, 可以簡寫成 @click
### html
```
<div id="app-5">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">反转消息</button>
</div>
```
### js
```
var app5 = new Vue({
el: '#app-5',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
```
## 顯示端(html)影響資料顯示
### html
```
<div id="app-6">
<p>{{ message }}</p>
<input v-model="message">
</div>
```
### js
```
var app6 = new Vue({
el: '#app-6',
data: {
message: 'Hello Vue!'
}
})
```
## 組建化應用
### 定義新標籤(組建)-->todo-item
### html
```
<ol>
<todo-item></todo-item>
</ol>
```
### js
```
Vue.component('todo-item', {
template: '<li>这是个待办项</li>'
})
var app7 = new Vue(...)
```
### 定義新標籤的屬性-->todo
### js
```
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
```
### 利用 v-bind 新增新屬性
### html
```
<div id="app-7">
<ol>
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id"
></todo-item>
</ol>
</div>
```
### 輸入需要循環顯示的資料
### js
```
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
var app7 = new Vue({
el: '#app-7',
data: {
groceryList: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其它什么人吃的东西' }
]
}
})
```
### 結果會等於以下
### html
```
<div id="app-7">
<ul>
<li v-for="todo in groceryList">
{{ todo.text }}
</li>
</ul>
</div>
```
### js
```
var app7 = new Vue({
el: '#app-7',
data: {
groceryList: [
{ text: '蔬菜' },
{ text: '奶酪' },
{ text: '随便其它什么人吃的东西' }
]
}
})
```
# 數據應用
## 前置
### html
```
<div id="app">
{{ a }}
{{ b }}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
```
### js
```
// 通常用vm(Vue Model)來代表一個Vue的實例, 可以在Vue裡面撈外面先設定好的資料
var vm = new Vue({
el: '#app',
data: data
})
```
## 在Vue外面先設定好資料
### js
```
var data = { a: 1 }
```
## 更改先宣告的資料會影響到Vue裡面的資料
### js
```
// 也可以先宣告後更改(a: 1-->2)
data.a = 2;
```
## 也可以直接更改Vue的資料
### js
```
// 或是更改Vue裡面的資料(a: 2-->3)
vm.a = 3;
```
## 但是不能在原有的宣告裡新增來顯示
### js
```
// 但是不能再加入其他資料(畫面上不會顯示新增的 b 資料, 但是直接更改Vue裡面的可以)
vm.b = 1;
```
## Object.freeze()
### js
```
// Object.freeze(data): 在此函式過後就無法再被更改(指的是外在因素, 例如: v-on)
Object.freeze(data);
```
## Vue內建功能
### js
```
// Vue 也有許多功能是以 $ 作為開頭, 以區分使用者自定義
// vm.$data: 可以看現在vm裡的data
// vm.$el: 可以看Vue運用在哪個位置
// vm.$watch: 查詢資料變化
vm.$watch('a', function(newVal, oldVal){
console.log(newVal,oldVal);
})
// console結果 3,4
vm.a = 4;
```
# 生命週期(鉤子)
## vue可以在每個步驟中進行動作, 例如: 監聽, 編譯
## created: 在一個實例被創建後執行
### js
```
var app = new Vue({
el: '#app',
data: {
a: 'hello'
},
created: function(){
console.log('a is:' + this.a)
}
})
```
## 不可以用箭頭函示, 因為箭頭函式沒有this的概念
## 其他的例如: mounted(元素掛載, 有el), updated(DOM更新完成), destroyed(銷毀)
## 也可以在前面加上before, 字首大寫, 不用加ed
# Vue語法
## v-once: 有這個標籤屬性的不會被更改
### html
```
<div id="app">
<span>{{ a }}</span><br>
<span v-once>{{ a }}</span>
</div>
```
### js
```
var app = new Vue({
el: '#app',
data: {
a: 'hello'
},
created: function(){
console.log('a is:' + this.a)
}
})
app.a = 'change';
```
## 如果要透過Vue加入HTML的話, 必須要再需加入的地方加入標籤屬性 v-html
### html
```
<div id="app1">
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
</div>
```
### js
```
var app1 = new Vue({
el: '#app1',
data: {
rawHtml: '<span style="color: red">This should be red</span>'
}
})
```
## html 中的 VUE 可以加入JS的單向運算
### html
```
<div id="app2">
<span>{{ a + 2 }}</span>
<span>{{ ok ? "YES" : "NO" }}</span>
<span>{{ message.split('').reverse().join('') }}</span>
<div v-bind:id="'list' + id">id = list1</div>
</div>
```
### js
```
var app2 = new Vue({
el: '#app2',
data: {
a: 3,
ok: true,
message: 'It will be reverse',
id: 1
}
})
```
## 由VUE來控制html的顯示
### html
```
<div id="app3">
<p>如果v-if裡面的值是true就會看到<span v-if="seen">這句話</span></p>
</div>
```
### js
```
var app3 = new Vue({
el: '#app3',
data: {
seen: true
}
})
```
# 計算和偵聽器
## Vue可以通過cpmputed 來設定要計算的函示並且return 顯示
### html
```
<div id="app">
<p>原本的句子 {{ message }}</p>
<p>計算後句子 {{ reverseMessage }}</p>
</div>
```
### js
```
var app = new Vue({
el: '#app',
data: {
message: 'Hello'
},
computed:{
reverseMessage: function(){
return this.message.split('').reverse().join('')
}
}
})
```
## 也可以用表達式(方法)來達到一樣的效果
### html
```
<div id="app1">
<p>原本的句子 {{ message }}</p>
<p>表達式句子 {{ reverseMessage() }}</p>
</div>
```
### js
```
var app1 = new Vue({
el: '#app1',
data: {
message: 'Hello'
},
methods: {
reverseMessage: function(){
return this.message.split('').reverse().join('')
}
}
})
```
## 計算和方法最大的差異在於: 計算式儲存於緩存, 所以不會再更動, 除非有響應式依賴發生變化
## 實際應用
### html
```
<div id="app2">{{ fullName }}</div>
```
### js
```
var app2 = new Vue({
el: '#app2',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
```
## computed中每個計算方法都是預設為getter, 但是其實有另一個setter需要自行建立, setter可以設定該計算方法的設定
### js
```
var app3 = new Vue({
el: '#app3',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: {
get: function(){
return this.firstName + ' ' + this.lastName
},
set: function (newValue) {
var names = newValue.split('')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
})
```
## watch可以觀察資料的變化
### html
```
<div id="app4">
<p>
ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
```
### js
```
var app4 = new Vue({
el: '#app4',
data: {
question: '',
answer: 'Ask some question'
},
watch: {
question: function (newQ, oldQ) {
this.answer = 'Waiting...'
}
}
})
```
# class&style
## 利用布林值控制是否添加class
### html
```
<div id="app" v-bind:class="{ active: isActive }">this GREEN if isActive is true</div>
```
### js
```
var app = new Vue({
el: '#app',
data: {
isActive: true
}
})
```
## 可以對class傳送一個陣列來增加多的類別
### html
```
<div id="app1" v-bind:class="[active1, active2]">There are two class</div>
```
### js
```
var app1 = new Vue({
el: '#app1',
data: {
active1: 'done',
active2: 'box'
}
})
```
## 可以設定style樣式再帶入
### html
```
<div id="app2" :style="styleObject">TEXT</div>
```
### js
```
var app2 = new Vue({
el: '#app2',
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
})
```
# 條件式
## v-else可以顯示如果不通過要顯示的
### html
```
<div id="app">
<h1 v-if="awsome">True it</h1>
<h1 v-else>False it</h1>
</div>
```
### js
```
var app = new Vue({
el: '#app',
data: {
awsome: true
}
})
```
## 也可以利用v-else-if來做更細部的篩選
### html
```
<div id="app1">
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else-if="type === 'C'">C</div>
<div v-else>NOT A,B,C</div>
</div>
```
### js
```
var app1 = new Vue({
el: '#app1',
data: {
type: 'D'
}
})
```
## 可以用key來獨立重複元素, 避免復用
## 與v-if相似的是v-show, 兩者差別在v-if只有在true才會運作(善用於不太會一直更改的地方), v-show則是會直接運作(善用於頻繁更改的地方)
# 列表
## v-for 可以做迴圈顯示
### html
```
<ul id="app">
<li v-for="item in items" :key="item.message">
{{ item.message }}
</li>
</ul>
```
### js
```
var app = new Vue({
el: '#app',
data: {
items: [
{message: 'test1'},
{message: 'test2'}
]
}
})
```
## v-for也有父子層關係
### html
```
<ul id="app1">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
```
### js
```
var app1 = new Vue({
el: '#app1',
data: {
parentMessage: 'parent',
items: [
{message: 'test1'},
{message: 'test2'}
]
}
})
```
## in 可以替換成 of
## 也可以對類別做迴圈
### html
```
<ul id="app2">
<li v-for="(value, name) in object">
{{ name }} : {{ value }}
</li>
</ul>
```
### js
```
var app2 = new Vue({
el: '#app2',
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Done',
public: '2020-08-10'
}
}
})
```
## 嘗試在工作管理區域打入 app.items.push({message: 'test3'})
## 函式篩選
### html
```
<ul id="app3">
<li v-for="n in evenNumbers">
{{ n }}
</li>
</ul>
```
### js
```
var app3 = new Vue({
el: '#app3',
data: {
numbers: [1, 2, 3, 4, 5, 6]
},
computed: {
evenNumbers: function(){
return this.numbers.filter(function (number){
return number % 2 == 0;
})
}
}
})
```
## 此方法與上面相同
### html
```
<ul id="app4">
<li v-for="set in sets" v-if="set % 2 == 0">
{{ set }}
</li>
</ul>
```
### js
```
var app4 = new Vue({
el: '#app4',
data: {
sets: [1, 2, 3, 4, 5, 6, 7, 8]
}
})
```
## 可以在html中使用template等待js啟用
### html
```
<ul id="app5">
<template v-for="set in sets">
<li>{{ set }}</li>
</template>
</ul>
```
### js
```
var app5 = new Vue({
el: '#app5',
data: {
sets: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
})
```
# DOM事件, v-on
## 更改data
### html
```
<div id="app">
<button v-on:click="count+=1">Add 1</button>
<p>The button click {{ count }} times</p>
</div>
```
### js
```
var app = new Vue({
el: '#app',
data: {
count: 0
}
})
```
## 呼叫函式
### html
```
<div id="app1">
<button v-on:click="greet">Greet</button>
</div>
```
### js
```
var app1 = new Vue({
el: '#app1',
data: {
name: 'Tommy'
},
methods: {
greet: function (){
alert('Hello ' + this.name)
}
}
})
```
## 事件也有修飾
## .once 事件只會處理一次
### html
```
div id="app4">
<button v-on:click.once="alert">Just Once</button>
</div>
```
### js
```
var app4 = new Vue({
el: '#app4',
methods: {
alert: function(){
alert('Just this time')
}
}
})
```
## .stop 防止事件冒泡
### html
```
<div id="app5">
<div @click="outer">
<div @click="middle">
<button @click.stop="inner">click me</button>
</div>
</div>
</div>
```
### js
```
var app5 = new Vue({
el: '#app5',
methods: {
outer: function(){
alert('這是外面的div');
},
middle: function(){
alert('這是中間的div');
},
inner: function(){
alert('這是裡面的button');
}
}
})
```
## .prevent 不會發生預設事件
### html
```
<div id="app6">
<a href="https://tw.yahoo.com/" @click.prevent="changeMsg">Click me</a>
<p>{{ msg }}</p>
</div>
```
### js
```
var app6 = new Vue({
el: '#app6',
data: {
msg: 'Hi'
},
methods: {
changeMsg: function(){
this.msg = 'Cant go to yahoo';
}
}
})
```
## .capture 冒泡捕捉, 類似反向冒泡, 許要每一層都加上
### html
```
<div id="app7">
<div @click.capture="outer">
<div @click.capture="middle">
<button @click.capture="inner">click me</button>
</div>
</div>
</div>
```
### js
```
var app7 = new Vue({
el: '#app7',
methods: {
outer: function(){
alert('這是外面的div');
},
middle: function(){
alert('這是中間的div');
},
inner: function(){
alert('這是裡面的button');
}
}
})
```
## .self 只會發生在自己的範圍, 不包含子元素
# 表單
## v-model 可以用表單來控制資料
### html
```
<div id="app">
<input type="text" v-model="msg">
<p>Message is: {{ msg }}</p>
</div>
```
### js
```
var app = new Vue({
el: '#app',
data: {
msg: 'Hello'
}
})
```
## 可以利用checkbox控制布林值
### html
```
<div id="app1">
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
</div>
```
### js
```
var app1 = new Vue({
el: '#app1',
data: {
checked: false
}
})
```
## 利用多個checkbox控制資料
### html
```
<div id="app2">
<input type="checkbox" id="check1" value="apple" v-model="buyList">
<label for="check1">apple</label>
<input type="checkbox" id="check2" value="milk" v-model="buyList">
<label for="check2">milk</label>
<input type="checkbox" id="check3" value="egg" v-model="buyList">
<label for="check3">egg</label>
<ul>
<li v-for="item in buyList">{{ item }}</li>
</ul>
</div>
```
### js
```
var app2 = new Vue({
el: '#app2',
data: {
buyList: []
}
})
```
## 利用單選選項控制資料
### html
```
<div id="app3">
<input type="radio" id="A" value="A" v-model="checkOne">
<label for="A">A</label>
<input type="radio" id="B" value="B" v-model="checkOne">
<label for="B">B</label>
<p>Checked: {{ checkOne }}</p>
</div>
```
### js
```
var app3 = new Vue({
el: '#app3',
data: {
checkOne: ''
}
})
```
## 由下拉式選單控制
### html
```
<div id="app4">
<select name="" id="" v-model="selected">
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
</select>
<span>SELECT: {{ selected }}</span>
</div>
```
### js
```
var app4 = new Vue({
el: '#app4',
data: {
selected: ''
}
})
```
## 可以在html中設定預設的布林值轉化
### html
```
<div id="app5">
<input type="checkbox" v-model="check" true-value="YES" false-value="NO">
<span>{{ check }}</span>
</div>
```
### js
```
var app5 = new Vue({
el: '#app5',
data: {
check: ''
}
})
```
## 表單的事件綁定也有修飾符, .lazy: 需要點擊非作用區域才會發生
### html
```
<div id="app6">
<input type="text" v-model.lazy="msg">
<p>{{ msg }}</p>
</div>
```
### js
```
var app6 = new Vue({
el: '#app6',
data: {
msg: 'Hello'
}
})
```
## .number: 把數字變成字串格式
### html
```
<div id="app7">
<input type="text" v-model.number="num">
<p>{{ num }}</p>
</div>
```
### js
```
var app7 = new Vue({
el: '#app7',
data: {
num: 0
}
})
```
## .trim: 去除首尾的空白字元
### html
```
<div id="app8">
<input type="text" v-model.trim="num">
<p>{{ num }}</p>
</div>
```
### js
```
var app8 = new Vue({
el: '#app8',
data: {
num: ''
}
})
```
# 組建
## 初始建立
### html
```
<div id="app">
<button-counter></button-counter>
</div>
```
### js
```
Vue.component('button-counter', {
data: function(){
return {
count: 0
}
},
template: '<button v-on:click="count++">{{ count }} clicked</button>'
})
var app = new Vue({
el: '#app'
})
```
## 組建可以重複使用, 但是都是獨立的, 資料不會互相影響
## 組建中的data必須是函式, 除非要組建之間互相影響
## 可以用props來設定屬性
### html
```
<div id="app1">
<blog-post title="Hello"></blog-post>
<blog-post title="Vue"></blog-post>
<blog-post title="Hi"></blog-post>
</div>
```
### js
```
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
var app1 = new Vue({
el: '#app1'
})
```
## 以下效果與上面一樣
### html
```
<div id="app2">
<blog-post v-for="post in posts" :key="post.id" :title="post.title"></blog-post>
</div>
```
### js
```
var app2 = new Vue({
el: '#app2',
data: {
posts: [
{id: 1, title: 'Hello'},
{id: 2, title: 'Vue'},
{id: 3, title: 'Hi'}
]
}
})
```