---
# System prepended metadata

title: VUE
tags: [PID]

---

# 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'}
        ]
    }
})
```