---
# System prepended metadata

title: VUE

---

# VUE
## 06-VUE中的數據代理
![](https://hackmd.io/_uploads/S1iCEwJH3.png)
![](https://hackmd.io/_uploads/rJxeBvkr2.png)
* setter
    * ![](https://hackmd.io/_uploads/BJ8gLD1r3.png)
    * 一改頁面馬上更改
    * data.name有被改到?有vm裡的就是了![](https://hackmd.io/_uploads/HyP98v1S2.png)
    * ![](https://hackmd.io/_uploads/r1d8Dv1Bn.png)，options是配置的意思，f12可以測試vm._data===data，![](https://hackmd.io/_uploads/BklaPPkrn.png)
    * ![](https://hackmd.io/_uploads/Hkti9DyB3.png)
    * 1.) script data 放進 vm._data 2.) vm._data數據setter到vm.name... 3.) 綠色部分_data內有屬性也有get set，因為view監聽，所以一發生改變，則view就會立即改變(數據截池)

## 07-事件處理
### 7-1 事件的基本使用
![](https://hackmd.io/_uploads/ryZF95yB3.png =33%x)![](https://hackmd.io/_uploads/Sk_cq9yHh.png =33%x)![](https://hackmd.io/_uploads/SkUo591r3.png =33%x)

![](https://hackmd.io/_uploads/H1kZcqyH3.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>事件的基本使用</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2>歡迎來到{{name}}學習</h2>
        <button v-on:click="showInfo">點我提示信息0</button>
        <button @click="showInfo">點我提示信息1(簡寫)(不傳參)</button>
        <button @click="showInfo2($event,66)">點我提示信息2(簡寫)(傳參)</button>
    </div>

    <script>
        const vm = new Vue({
            el:'#root',
            data:{
                name:'尚硅谷'
            },
            methods:{
                // 第一個參數一定是event,可以(a,b,c,d)=>a=event,bcd=undefind
                showInfo(event){
                    // console.log(event.target.innerText) //按鈕內容
                    // console.log(this)// vue實例
                    alert('同學您好');
                },
                showInfo2(event, number){
                    console.log(number)
                    console.log(event.target.innerText) //按鈕內容
                    // console.log(this)// vue實例
                    alert('同學您好!!');
                }
            }
        });
        
    </script>
</body>
</html>
```
### 7-2 事件修飾符
![](https://hackmd.io/_uploads/r1wy1jkrh.png)
* 冒泡事件說明 : 假設div裡面包著按鈕點擊事件，然後div本身也綁著事件，則一旦點擊按鈕事件，會一路執行到最外層事件。
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>事件修飾符</title>
    <script type="text/javascript" src="../js/vue.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.min.js"></script>
    <style>
        *{
            margin-top: 20px;
        }
        .demo1{
            background-color: red;
        }
        .box1{
            padding: 5px;
            background-color: yellow;
        }
        .box2{
            padding: 5px;
            background-color: green;
        }
        .list{
            width: 200px;
            height: 200px;
            background-color: orange;
            /* 滾動 */
            overflow: auto;
        }
        li{
            height: 100px;
        }
    </style>
</head>
<body>
    <div id="root">
        <h2>歡迎來到{{name}}學習</h2>
        <a href="https://tw.yahoo.com/" @click="showInfo">點我提示信息0</a>
        <!-- prevent: 阻止a標籤跳走預設事件，只執行自己綁定事件 -->
        <a href="https://tw.yahoo.com/" @click.prevent="showInfo">點我提示信息1</a>
        <!-- stop: 阻止事件冒泡(常用) -->
        <div class="demo1" @click="showInfo">
            <button @click.stop="showInfo">點我提示信息2</button>
        </div>
        <!-- once: 事件只能觸發一次(常用) -->
        <button @click.once="showInfo">點我提示信息2</button>
        <!-- capture: 使用事件的捕獲模式，先捕獲，再冒泡 (不常用) -->
        <!-- 捕獲階段不執行事件，冒泡: div2->div1 想要在捕獲階段就執行事件 -->
        <div class="box1" @click.capture="showMsg($event,1)">
            div1
            <div  class="box2" @click="showMsg($event,2)">
                div2
            </div>
        </div>
        <!-- self: 只有event.target是當前操作的元素才觸發事件 (不常用) -->
        <!-- 冒泡上去的target都是button -->
        <!-- self會去比對點擊的物件是否跟你本身是同個物件，像div!=button則冒泡上去的事件不會執行 -->
        <div class="demo1" @click.self="showInfo">
            <button @click.stop="showInfo">點我提示信息3</button>
        </div>
        <!-- passive: 事件的默認行為立即執行，無需等待事件回調執行完畢，搭配scroll、wheel，如果複雜任務，則滾輪或下拉都可能掛掉不會動 -->
        <!-- 不用等事件完全處理完 -->
        <ul @wheel="demo" class="list">
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
        </ul>



    </div>

    <script>
        const vm = new Vue({
            el:'#root',
            data:{
                name:'尚硅谷'
            },
            methods:{
                showInfo(e){
                    // e.preventDefault();// 可阻止a標籤跳走預設事件
                    // 也可用@click.prevent="showInfo"阻止a標籤跳走預設事件
                    alert('yahoo');
                },
                showMsg(e,msg){
                    console.log(msg);
                },
                demo(){
                    for (let i = 0; i <1000;i++) {
                        console.log('#')
                    }
                }
            }
        });    
    </script>
</body>
</html>
```
### 7-3 鍵盤事件
![](https://hackmd.io/_uploads/SkC5qAeBh.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>鍵盤事件</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2>歡迎來到{{name}}學習</h2>
        <input type="text" placeholder="按下回車提示輸入" @keyup="showInfo($event)">
    </div>

    <script type="text/javascript">
        // 自訂義
        // @keyup.13="showInfo" //放在上面的input
        // Vue.config.keyCodes.byMySelf = 13;
        new Vue({
            el:'#root',
            data:{
                name:'尚硅谷'
            },
            methods:{
                showInfo(e){
                    // 按enter 13
                    // if(e.keyCode != 13) return;
                    console.log(e.target.value);
                    console.log(e.keyCode);
                    
                }
            },
        });
    </script>
</body>
</html>
```
## 08-計算屬性
![](https://hackmd.io/_uploads/BJMeyg-rh.png)
### 08-1 姓名案例-插值
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>姓名案例-插值語法實現</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        姓: <input type="text" v-model:value="firstName">
        <br>
        名: <input type="text" v-model:value="lastName">
        <br>
        姓名: <span>{{firstName.slice(0,3)}}-{{lastName}}</span>
    </div>

    <script type="text/javascript">
        new Vue({
            el:'#root',
            data:{
                firstName:'張',
                lastName:'三'
            }
        })
    </script>
</body>
</html>
```
### 08-2 姓名案例-methods
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>姓名案例-methods實現</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<!-- 重點 當數據發生改變，模板會自動重新解析(div root模板)，所以{{fullName()}}才會自動更新 -->
<body>
    <div id="root">
        姓: <input type="text" v-model:value="firstName">
        <br>
        名: <input type="text" v-model:value="lastName">
        <br>
        姓名: <span>{{fullName()}}</span>
    </div>

    <script type="text/javascript">
        new Vue({
            el:'#root',
            data:{
                firstName:'張',
                lastName:'三'
            },
            methods:{
                fullName(){
                    console.log('查看是否調用fullName');
                    return this.firstName+'-'+this.lastName;
                }
            }
        })
    </script>
</body>
</html>
```
### 08-3 姓名案例-計算屬性
![](https://hackmd.io/_uploads/Byd4kbfrh.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>姓名案例-計算屬性實現</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        姓: <input type="text" v-model:value="firstName">
        <br>
        名: <input type="text" v-model:value="lastName">
        <br>
        <!-- 這個永遠不會影響到fullName，所以不會用到fullName的get -->
        測試: <input type="text" v-model:value="x">
        <br>
        姓名: <span>{{fullName}}</span>
        <br>
        <!-- 可以發現'調用get'只會印出一次，因為有緩存機制 -->
        <!-- 這樣會不會都取緩存?下面a1有解答 -->
        姓名: <span>{{fullName}}</span>
    </div>

    <script type="text/javascript">
        const vm = new Vue({
            el:'#root',
            data:{
                firstName:'張',
                lastName:'三',
                x:'您好'
            },
            computed:{
                // 計算後的屬性_data不會出現
                fullName:{
                    // get:當有人讀取fullName，get就會被調用，且返回值就作為fullName的值
                    // a1: get啥時調用? 1. 初次讀取 2. 所依賴的數據發生改變
                    get(){
                        console.log('調用get')
                        return this.firstName+'-'+this.lastName;
                    },
                    // set啥時調用? 當fullName被修改時
                    // 用f12 直接更改 vm.fullName=張-小名，即可看出輸入框更改
                    set(value){
                        const arr = value.split('-');
                        this.firstName = arr[0];
                        this.lastName = arr[1];
                    }
                }
            }
        })
    </script>
</body>
</html>
```
### 08-3 姓名案例-計算屬性實現_簡寫
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>姓名案例-計算屬性實現_簡寫</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<!-- 只讀不改，才可以用簡寫(ex:只有get，無set) -->
<body>
    <div id="root">
        姓: <input type="text" v-model:value="firstName">
        <br>
        名: <input type="text" v-model:value="lastName">
        <br>
        姓名: <span>{{fullName}}</span>
    </div>

    <script type="text/javascript">
        const vm = new Vue({
            el:'#root',
            data:{
                firstName:'張',
                lastName:'三'
            },
            
            computed:{
                // 完整寫法
                // fullName:{
                //     get(){
                //         console.log('調用get')
                //         return this.firstName+'-'+this.lastName;
                //     },
                //     set(value){
                //         const arr = value.split('-');
                //         this.firstName = arr[0];
                //         this.lastName = arr[1];
                //     }
                // }
                // 法1
                // fullName:function(){
                //     return this.firstName+'-'+this.lastName;
                // }
                // 法ˇ
                fullName(){
                    return this.firstName+'-'+this.lastName;
                }
            }
        })
    </script>
</body>
</html>
```
## 09 監聽屬性
![](https://hackmd.io/_uploads/Hy1B8rGBn.png)
### 09-1 天氣案例
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>天氣案例</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<!-- 只讀不改，才可以用簡寫(ex:只有get，無set) -->
<body>
    <div id="root">
        <h2>今天天氣很{{info}}</h2>
        <button @click="changeMeather">切換天氣</button>
        <!-- 下面如果一句業務邏輯可以用下面的 -->
        <!-- <button @click="isHot = !isHot">切換天氣</button> -->
    </div>
    <script type="text/javascript">
        const vm = new Vue({
            el:'#root',
            data:{
                isHot:true
            },
            computed: {
                info(){
                    return this.isHot ? '炎熱' : '涼爽';
                }
            },
            methods: {
                changeMeather(){
                    this.isHot = !this.isHot;
                }
            },
        })
    </script>
</body>
</html>
```
### 09-2 天氣案例_監視屬性_watch
![](https://hackmd.io/_uploads/S1RzX4mB2.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>天氣案例_監視屬性</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<!-- 只讀不改，才可以用簡寫(ex:只有get，無set) -->
<body>
    <div id="root">
        <h2>今天天氣很{{info}}</h2>
        <button @click="changeMeather">切換天氣</button>
        <!-- 下面如果一句業務邏輯可以用下面的 -->
        <!-- <button @click="isHot = !isHot">切換天氣</button> -->
    </div>
    <script type="text/javascript">
        // 法1 法2選擇用誰?  如果一開始就知道監視誰，就用法1，如果後續才知道用法2
        const vm = new Vue({
            el:'#root',
            data:{
                isHot:true
            },
            computed: {
                info(){
                    return this.isHot ? '炎熱' : '涼爽';
                }
            },
            methods: {
                changeMeather(){
                    this.isHot = !this.isHot;
                }
            },
            // 法1
            // watch: {
            //     isHot:{
            //         // 設定handler是否一開始就執行，還是發生改變才執行
            //         immediate:true,
            //         // 什麼時候調用呢? 當isHot發生改變時
            //         // 這樣就可以監聽每個屬性修改要做甚麼動作
            //         // ex: 新的溫度 和 舊的溫度 取差，差過幾度提醒...應用場景
            //         handler(newValue, oldValue){
            //             console.log('isHot發生改變', newValue, oldValue);
            //         }
            //     }
            // },
        })
        // 法2
        vm.$watch('isHot',{
            // 設定handler是否一開始就執行，還是發生改變才執行
            immediate:true,
            // 什麼時候調用呢? 當isHot發生改變時
            // 這樣就可以監聽每個屬性修改要做甚麼動作
            // ex: 新的溫度 和 舊的溫度 取差，差過幾度提醒...應用場景
            handler(newValue, oldValue){
                console.log('isHot發生改變', newValue, oldValue);
            }
        })
    </script>
</body>
</html>
```
### 09-3 天氣案例_深度監視屬性
![](https://hackmd.io/_uploads/SJeRvNXrn.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>天氣案例_深度監視屬性</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<!-- 只讀不改，才可以用簡寫(ex:只有get，無set) -->
<body>
    <div id="root">
        <h2>今天天氣很{{info}}</h2>
        <button @click="changeMeather">切換天氣</button>
        <hr>
        <h3>a的值是{{numbers.a}}</h3>
        <button @click="numbers.a++">點我a+1</button>
        <h3>b的值是{{numbers.b}}</h3>
        <button @click="numbers.b++">點我b+1</button>
        <!-- 下面如果一句業務邏輯可以用下面的 -->
        <!-- <button @click="isHot = !isHot">切換天氣</button> -->
    </div>
    <script type="text/javascript">
        const vm = new Vue({
            el:'#root',
            data:{
                isHot:true,
                numbers:{
                    a:1,
                    b:2
                }
            },
            computed: {
                info(){
                    return this.isHot ? '炎熱' : '涼爽';
                }
            },
            methods: {
                changeMeather(){
                    this.isHot = !this.isHot;
                }
            },
            // 法1
            watch: {
                isHot:{
                    // 設定handler是否一開始就執行，還是發生改變才執行
                    immediate:true,
                    // 什麼時候調用呢? 當isHot發生改變時
                    // 這樣就可以監聽每個屬性修改要做甚麼動作
                    // ex: 新的溫度 和 舊的溫度 取差，差過幾度提醒...應用場景
                    handler(newValue, oldValue){
                        console.log('isHot發生改變', newValue, oldValue);
                    }
                },
                // 監測多層屬性的特定屬性寫法
                'numbers.a':{
                    handler(){
                        console.log('a被改變');
                    }
                },
                //如果100個key只有一個改變呢?
                numbers:{
                    deep:true,// 要開啟才會監視內部所有屬性，不然只是監視numbers本身這個物件而已
                    handler(){
                        console.log('numbers改變')
                    }
                }
            },
        })
    </script>
</body>
</html>
```
## 09_08_計算屬性vs監視屬性 
* computed vs watch
![](https://hackmd.io/_uploads/BJMeyg-rh.png)
![](https://hackmd.io/_uploads/Sk8o8BXB3.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>姓名案例-watch實現</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<!-- 只讀不改，才可以用簡寫(ex:只有get，無set) -->
<body>
    <div id="root">
        姓: <input type="text" v-model:value="firstName">
        <br>
        名: <input type="text" v-model:value="lastName">
        <br>
        姓名: <span>{{fullName}}</span>

    </div>

    <script type="text/javascript">
        const vm = new Vue({
            el:'#root',
            data:{
                firstName:'張',
                lastName:'三',
                fullName:'張-三' //用watch屬性要自己先宣告
            },
            computed:{
                // 完整寫法
                // fullName:{
                //     get(){
                //         console.log('調用get')
                //         return this.firstName+'-'+this.lastName;
                //     },
                //     set(value){
                //         const arr = value.split('-');
                //         this.firstName = arr[0];
                //         this.lastName = arr[1];
                //     }
                // }
                // 法1
                // fullName:function(){
                //     return this.firstName+'-'+this.lastName;
                // }
                // 法2
                // fullName(){ 
                //     return this.firstName+'-'+this.lastName;
                // }
            },
            watch:{
                firstName:{
                    handler(newValue, oldVlaue){
                        // this.fullName = newValue+'-'+this.lastName;
                        // vue都用function(){}，才可調用this(vm)
                        // 這邊seTimeput，是原生js調用，this會調成window，
                        // 寫箭頭函式是因為這樣沒有this，就會自動去找上一層的當作this
                        setTimeout(()=>{
                            this.fullName = newValue+'-'+this.lastName;
                        },10000);   
                    }
                },
                lastName:{
                    handler(newValue, oldVlaue){
                        this.fullName = this.firstName+'-'+newValue;
                    }
                },
            }
            // 提出一個需求，如果fullName要三秒之後才出現呢?  只能用watch
            // computed 用的是return立即計算，
            // watch可以異步處理業務邏輯(同時間處理所有事物)
        })
    </script>
</body>
</html>
```
## 10 樣式綁定
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>綁定樣式</title>
    <script type="text/javascript" src="../js/vue.js"></script>
    <style>
        .basic{}
        .happy{}
        .sad{}
        .normal{}
        .atguigu1{}
        .atguigu2{}
        .atguigu3{}
    </style>
</head>
<body>
    <div id="root">
        <!-- class basic normal -->
        <!-- 綁定class樣式--字符串寫法，適用於: 樣式的類名不確定，需要動態指定 -->
        <div class="basic" :class="mood" @click="changeMood">{{name}}</div>
        <br>
        <br>
        <!-- 需求:綁定所有樣式，--數組寫法，操作arr等於操作要綁定的樣式 -->
        <div class="basic" :class="classArr">{{name}}</div>
        <br>
        <br>
        <!-- 需求:綁定特定樣式，--對象寫法，操作arr等於操作要綁定的樣式 -->
        <div class="basic" :class="classObj">{{name}}</div>
        <br>
        <br>
        <div class="basic" :style="styleObj">{{name}}</div>
    </div>
    <script type="text/javascript">
        const vm = new Vue({
            el:'#root',
            data:{
                name:'尚硅谷a',
                mood:'normal',
                classArr:['atguigu1','atguigu2','atguigu3'],
                classObj:{
                    atguigu1:false,
                    atguigu2:true
                },
                styleObj:{
                    fontSize: '40px',
                    color:'red'
                }
            },
            methods: {
                changeMood(){
                    const arr = ['happy','sad','normal'];
                    const index = Math.floor(Math.random()*3);
                    this.mood = arr[index];
                }
            },
        })
    </script>
</body>
</html>
```
## 11 條件渲染
* 當n===某值，顯示某div
![](https://hackmd.io/_uploads/rJoZ4t7rh.png)
![](https://hackmd.io/_uploads/BJdy4KXHh.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>條件渲染</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <!-- style="display: none;" -->
        <h2 v-show="false">歡迎來到{{name}}</h2>
        <!-- 整個不見 -->
        <h2 v-if="false">歡迎來到{{name}}</h2>
        <h2>當前的n值是:{{n}}</h2>
        <button @click="n++">點我n+1</button>
        <!-- 高頻率切換，使用v-show -->
        <!-- if elseif else 中間程式碼不能打斷 -->
        <div v-if="n === 1">Angular</div>
        <div v-else-if="n === 2">React</div>
        <div v-else-if="n === 3">Vue</div>
        <div v-else>哈哈</div>
        <!-- else撿剩的，根本不用條件式 -->
        <!-- 需求n是4時，同時顯，但只保留h2，法1為了實現外面多了一層div破壞原始結構 -->
        <!-- 法2:只能配合v-if，且不會破壞結構 -->
        <!-- <div v-if="n===4">
            <h2>你好</h2>
            <h2>台灣</h2>
        </div> -->
        <template v-if="n===4">
            <h2>你好</h2>
            <h2>台灣</h2>
        </template>
    </div>
    <script type="text/javascript">
        const vm = new Vue({
            el:'#root',
            data:{
                name:'尚硅谷',
                n:0
            }
        })
    </script>
</body>
</html>
```
## 12 列表渲染
### 12-1 基本列表
![](https://hackmd.io/_uploads/Hk4r9FQS3.png)
![](https://hackmd.io/_uploads/SJ7rQ9QS2.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>基本列表</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2>人員列表</h2>
        <!-- 遍歷數組 -->
        <ul>
            <!-- 下一小截會說明key，這邊先任知道他是唯一標示 -->
            <li v-for="p in persons" :key="p.id">
                {{p.id}}-{{p.name}}-{{p.age}}
            </li>
            <!-- p:{id:'001',name:'張三',age:18}, index:索引 -->
            <li v-for="(p,index) in persons" :key="index">
                {{p.name}}-{{p.age}}-{{index}}
            </li>
        </ul>
        <!-- 遍歷對象 -->
        <h2>汽車信息</h2>
        <ul>
            <li v-for="(val,key) of car" :key="key">
                {{key}}-{{val}}
            </li>
        </ul>
        <!-- 遍歷字符串 -->
        <h2>遍歷字符串信息</h2>
        <ul>
            <li v-for="(val,index) of str" :key="index">
                {{index}}-{{val}}
            </li>
        </ul>
        <!-- 遍歷指定次數 -->
        <h2>遍歷指定次數</h2>
        <ul>
            <li v-for="(number,index) of 5" :key="index">
                {{index}}-{{number}}
            </li>
        </ul>
    </div>
    <script type="text/javascript">
        const vm = new Vue({
            el:'#root',
            data:{
                persons:[
                    {id:'001',name:'張三',age:18},
                    {id:'002',name:'李四',age:19},
                    {id:'003',name:'王五',age:20}
                ],
                car:{
                    name:'奧迪',
                    price:'100萬'
                },
                str:'hello'
            }
        })
    </script>
</body>
</html>
```
### 12-2 key的原理
![](https://hackmd.io/_uploads/ryFsA5QSh.png)
* 1. index作為key，先比對key，一樣的東西續用，不一樣的東西新生成
![](https://hackmd.io/_uploads/rJVen9XH3.png)
* 如果堅持用index作為key，則新增數據一定要加在後面
![](https://hackmd.io/_uploads/Sy2s39XH3.png)
* 2. p.id作為key，先比對key，一樣的東西續用，不一樣的東西新生成
![](https://hackmd.io/_uploads/ByB_6cmr2.png)

### 12-3 列表過濾
![](https://hackmd.io/_uploads/Sy2S_BNHh.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>列表過濾</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2>人員列表</h2>
        <input type="text" placeholder="請輸入名字" v-model="keyWord">
        <!-- 遍歷數組 -->
        <ul>
            <!-- p:{id:'001',name:'張三',age:18}, index:索引 -->
            <li v-for="(p,index) in filPersons" :key="index">
                {{p.name}}-{{p.age}}-{{index}}
            </li>
        </ul>

    </div>
    <script type="text/javascript">
        // // 用watch實現
        // const vm = new Vue({
        //     el:'#root',
        //     data:{
        //         keyWord:'',
        //         persons:[
        //             {id:'001',name:'馬冬梅',age:18, sex:"女"},
        //             {id:'002',name:'周冬雨',age:20, sex:"女"},
        //             {id:'003',name:'周杰倫',age:21, sex:"男"},
        //             {id:'004',name:'溫亮倫',age:22, sex:"男"}
        //         ],
        //         filPersons:[]
        //     },
        //     watch:{
        //         keyWord:{
        //             immediate:true,// 一啟動就先做
        //             handler(newValue){
        //                 this.filPersons = this.persons.filter((p)=>{
        //                     // p.name.indexOf(newValue)是否包含(0,-1)
        //                     // indexOf比對到空字串('')，全部數據都符合
        //                     return p.name.indexOf(newValue) !== -1
        //                 })
        //                 // 笨蛋寫法
        //                 // if(newValue===''){
        //                 //     this.filPersons = this.persons;
        //                 // }
        //             }
        //         }
        //     }
        // })
        // 用computed實現
        const vm = new Vue({
            el:'#root',
            data:{
                keyWord:'',
                persons:[
                    {id:'001',name:'馬冬梅',age:18, sex:"女"},
                    {id:'002',name:'周冬雨',age:20, sex:"女"},
                    {id:'003',name:'周杰倫',age:21, sex:"男"},
                    {id:'004',name:'溫亮倫',age:22, sex:"男"}
                ]
            },
            computed:{
                filPersons:{
                    get(){
                        // get的return
                        return this.filPersons = this.persons.filter((p)=>{
                            // p.name.indexOf(newValue)是否包含(0,-1)
                            // indexOf比對到空字串('')，全部數據都符合
                            return p.name.indexOf(this.keyWord) !== -1
                        })
                    },
                    set(){}
                }
            }
        })
    </script>
</body>
</html>
```
### 12-4 列表排序
![](https://hackmd.io/_uploads/HynhrIEB3.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>列表排序</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2>人員列表</h2>
        <input type="text" placeholder="請輸入名字" v-model="keyWord">
        <button @click="sortType = 2">年齡升序</button>
        <button @click="sortType = 1">年齡降序</button>
        <button @click="sortType = 0">原順序</button>
        <!-- 遍歷數組 -->
        <ul>
            <!-- p:{id:'001',name:'張三',age:18}, index:索引 -->
            <li v-for="(p,index) in filPersons" :key="p.id">
                {{p.name}}-{{p.age}}-{{index}}
            </li>
        </ul>

    </div>
    <script type="text/javascript">
        // 用computed實現
        const vm = new Vue({
            el:'#root',
            data:{
                keyWord:'',
                sortType:0,// 0:原順序, 1:年齡升序, 2:年齡降序
                persons:[
                    {id:'001',name:'馬冬梅',age:30, sex:"女"},
                    {id:'002',name:'周冬雨',age:31, sex:"女"},
                    {id:'003',name:'周杰倫',age:18, sex:"男"},
                    {id:'004',name:'溫亮倫',age:19, sex:"男"}
                ]
            },
            computed:{
                filPersons:{
                    get(){
                        const arr = this.persons.filter((p)=>{
                            // p.name.indexOf(newValue)是否包含(0,-1)
                            // indexOf比對到空字串('')，全部數據都符合
                            return p.name.indexOf(this.keyWord) !== -1
                        })
                        if(this.sortType){
                            arr.sort((p1,p2)=>{
                                return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age;
                            })
                        }
                        return arr;
                        // sort說明
                        // let arr = [1,3,2,6,4,5];
                        // arr.sort((a,b)=>{
                        //     // 升序
                        //     // return a-b;
                        //     // 降序
                        //     return b-a;
                        // })
                    },
                    set(){}
                }
            }
        })
    </script>
</body>
</html>
```
### 12-5 觀察監測數據如何更新
* 可以發現下面程式有個方式更改data，但view不改變，因為vue沒監聽到
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>觀察監測數據如何更新</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2>人員列表</h2>
        <button @click="updateMei">更新馬冬梅的信息</button>
        <!-- 遍歷數組 -->
        <ul>
            <!-- p:{id:'001',name:'張三',age:18}, index:索引 -->
            <li v-for="(p,index) in persons" :key="p.id">
                {{p.name}}-{{p.age}}-{{p.sex}}-{{index}}
            </li>
        </ul>

    </div>
    <script type="text/javascript">
        // 用computed實現
        const vm = new Vue({
            el:'#root',
            data:{
                persons:[
                    {id:'001',name:'馬冬梅',age:30, sex:"女"},
                    {id:'002',name:'周冬雨',age:31, sex:"女"},
                    {id:'003',name:'周杰倫',age:18, sex:"男"},
                    {id:'004',name:'溫亮倫',age:19, sex:"男"}
                ]
            },
            methods: {
                updateMei(){
                    // this.persons[0].name='馬老師';// 奏效
                    // this.persons[0].age=50;// 奏效
                    // this.persons[0].sex='男';// 奏效
                    // vue的view不奏效，vm確實改了，但vue監測不到
                    // this.persons[0] = {id:'001',name:'馬老師',age:50, sex:"男"}; //不奏效
                    // 原因直接對persons數組直接改動，而不是對內容物
                    this.persons.splice(0,1,{id:'001',name:'馬老師',age:50, sex:"男"}); //奏效
                }
            },
        })
    </script>
</body>
</html>
```
### 12-6 vue監測數據原理_對象(重要)
1. data的name改變
2. vm._data改變數據(調用set)
3. 一調用set就重新解析模板
4. 解析模板就生成新的虛擬DOM
5. 新舊DOM對比
6. 更新頁面
![](https://hackmd.io/_uploads/BJVzEFEB2.png)
### 12-7 模擬一個數據監測(重要，_data如何實現)
* 只示範一層數據
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>模擬一個數據監測</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <script type="text/javascript">
        // 1. 創建obs，且傳入data 2. 創建vm,將vm._data及data指向obs物件，且綁定所有屬性的get、set 
        // 3. 當vm._data及data的屬性改變，則調用set

        let data = {
            name:'尚硅谷',
            address:'北京'
        }
        // 創建監測data的物件
        const obs = new Observer(data);
        // 模擬vm
        let vm = {}
        vm._data = obs;
        data = obs;

        // 監測物件
        function Observer(obj){
            // 匯總對象中所有的屬性形成一個數組
            const keys = Object.keys(obj)
            // 遍歷
            keys.forEach((k) => {
                Object.defineProperty(this,k,{
                    get(){
                        return obj[k];
                    },
                    set(val){
                        console.log(`${k}被改了，我要去解析模板，生成虛擬DOM...`)
                        obj[k]=val
                    }
                })
            });
        }
    </script>
</body>
</html>
```
### 12-8 Vue.set的使用
* 主要可以實現後添加數據在vm裡也有get set方法影響view改變
* 可以發現sex手動增上去，view沒改變，_data裡沒set方法
* 方法1 : vm.$set(vm.student,'sex','女')
* 方法2 : Vue.set(this.student,'sex','男')
* 這是有限制的，不能將添加數據直接加在vm或者data上，ex:vm.$set(vm._data,'leader','小張')
* 下面就是包在school裡作解決
![](https://hackmd.io/_uploads/BJZW1iErn.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Vue.set的使用</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2>學校名稱:{{school.name}}</h2>
        <h2>學校地址:{{school.address}}</h2>
        <hr>
        <h2>學生姓名:{{student.name}}</h2>
        <h2>學生年齡: 真實={{student.age.rAge}}, 對外:{{student.age.sAge}}</h2>
        <h2>朋友們</h2>
        <ul>
            <li v-for="(f, index) in student.friends" :key="index">
                {{f.name}}---{{f.age}}
            </li>
        </ul>
        <h2 v-if="student.sex">學生性別:{{student.sex}}</h2>
        <button @click="addSex">添加一個性別屬性，默認是男</button>
        <h2 v-if="school.leader">校長是:{{school.leader}}</h2>
        <button @click="addLeader">添加一個校長，默認是小張</button>
    </div>
    <script type="text/javascript">
        // 用computed實現
        const vm = new Vue({
            el:'#root',
            data:{
                school:{
                    name:'尚硅谷',
                    address:'北京'
                },
                student:{
                    name:'tom',
                    age:{
                        rAge:40,
                        sAge:29
                    },
                    friends:[
                        {name:'jerry',age:35},
                        {name:'tony',age:36}
                    ]
                }
            },
            methods: {
                addSex(){
                    // 重點
                    // Vue.set(this.student,'sex','男')
                    this.$set(this.student,'sex','男')
                },
                addLeader(){
                    // 重點
                    // Vue.set(this.student,'sex','男')
                    this.$set(this.school,'leader','小張')
                }
            },
        })
    </script>
</body>
</html>
```
### 12-9 Vue監測數據的原理_數組
* 針對hobby去觀察
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Vue監測數據的原理_數組</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2>愛好</h2>
        <ul>
            <li v-for="(h, index) in student.hobby" :key="index">
                {{h}}
            </li>
        </ul>
    </div>
    <script type="text/javascript">
        // 用computed實現
        const vm = new Vue({
            el:'#root',
            data:{
                school:{
                    name:'尚硅谷',
                    address:'北京'
                },
                student:{
                    name:'tom',
                    age:{
                        rAge:40,
                        sAge:29
                    },
                    hobby:['抽菸','喝酒','燙頭髮'],
                    friends:[
                        {name:'jerry',age:35},
                        {name:'tony',age:36}
                    ]
                }
            },
        })
    </script>
</body>
</html>
```
![](https://hackmd.io/_uploads/r107u34rn.png =20%x)
* 可以發現數組並沒有get set
![](https://hackmd.io/_uploads/rk-QuhEH3.png =60%x)
* 數組操作
![](https://hackmd.io/_uploads/H1WvK2VH2.png =10%x)
* hobby有get set所以可以對這個數組做改變，vue才監測的到
![](https://hackmd.io/_uploads/HkkRFhVSn.png =40%x)
* 1.)vue的push調用js原生push。 2.)vue再重新解析模板 
* 重點vue有重新封裝過數組操作
![](https://hackmd.io/_uploads/H1NS324rh.png =40%x)
* Vue.set(vm._data.student.hobby,0,'學習')，這也能改。
### 12-10 總結Vue監測數據
![](https://hackmd.io/_uploads/HJTiJ1rH3.png)
![](https://hackmd.io/_uploads/rJ1UJJrS3.png =20%x)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>總結Vue監測數據</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <button @click="addAge">年齡+1歲</button> <br>
        <button @click="addSex">添加性別屬性，默認值: 男</button> <br>
        <button @click="student.sex = '未知'">修改性別</button> <br> <!-- sex必須存在 -->
        <button @click="addFriend">在列表首位添加一個朋友</button> <br>
        <button @click="updateFirstFriendName">修改第一個朋友的名字為: 張三</button> <br>
        <button @click="addHobby">添加一個愛好</button> <br>
        <button @click="updateFirstHobby">修改第一個愛好為: 開車</button> <br>
        <button @click="removeSmoke">過濾掉愛好中的抽菸</button> <br>

        <h1>學生信息</h1>
        <h2>學生姓名:{{student.name}}</h2>
        <h2>學生年齡:{{student.age}}</h2>
        <h2 v-if="student.sex">學生性別:{{student.sex}}</h2>
        <h2>愛好</h2>
        <ul>
            <li v-for="(h, index) in student.hobby" :key="index">
                {{h}}
            </li>
        </ul>

        <h2>朋友們</h2>
        <ul>
            <li v-for="(f, index) in student.friends" :key="index">
                {{f.name}}---{{f.age}}
            </li>
        </ul>
    </div>
    <script type="text/javascript">
        // 用computed實現
        const vm = new Vue({
            el:'#root',
            data:{
                school:{
                    name:'尚硅谷',
                    address:'北京'
                },
                student:{
                    name:'tom',
                    age:18,
                    hobby:['抽菸','喝酒','燙頭髮'],
                    friends:[
                        {name:'jerry',age:35},
                        {name:'tony',age:36}
                    ]
                }
            },
            methods: {
                addAge(){
                    this.student.age++;
                },
                addSex(){
                    this.$set(this.student,'sex','男');
                },
                addFriend(){
                    this.student.friends.unshift({name:'jack',age:70});
                },
                updateFirstFriendName(){
                    this.student.friends[0].name = '張三';// 之前是數組直接附值會掛，因為數組本身沒有get set，這邊name有阿
                },
                addHobby(){
                    this.student.hobby.push('學習');
                },
                updateFirstHobby(){
                    // this.student.hobby.splice(0,1,'開車');// 第0個位置改成開車，1代表刪原本的第0個位置
                    this.$set(this.student.hobby,0,'開車');
                },
                removeSmoke(){
                    this.student.hobby = this.student.hobby.filter((h)=>{
                        return h != '抽菸';
                    })
                }
            },
        })
    </script>
</body>
</html>
```
## 13 收集表單數據
![](https://hackmd.io/_uploads/HkL_csSHh.png)
![](https://hackmd.io/_uploads/H1ksq6HB3.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>收集表單數據</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <!-- form預設會跳轉，prevent阻止默認行為，只執行自己綁定事件 -->
        <form>
            <label for="test1">帳號: </label>
            <input type="text" id="test1" v-model.trim="userInfo.account">
            <br>
            密碼: <input type="password" v-model="userInfo.password">
            <br>
            年齡: <input type="number" v-model.number="userInfo.age">
            <br>
            <!-- name綁定一組 -->
            性別:
            男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
            女<input type="radio" name="sex" v-model="userInfo.sex" value="female">
            <br>
            <!-- 要用數組去接 -->
            愛好:
            學習<input type="checkbox" v-model="userInfo.hobby" value="study">
            打遊戲<input type="checkbox" v-model="userInfo.hobby" value="game">
            吃飯<input type="checkbox" v-model="userInfo.hobby" value="eat">
            <br>
            所屬校區
            <select v-model="userInfo.city">
                <option value="">請選擇校區</option>
                <option value="beijing">北京</option>
                <option value="shanghai">上海</option>
                <option value="shenzhen">深圳</option>
                <option value="wuhan">武漢</option>
            </select>
            <br>
            <!-- lazy: 不會輸入一個字，就直接改vm裡的屬性，而是失去焦點再去更改屬性內容 -->
            其他信息
            <textarea v-mode.lazy="userInfo.other"></textarea>
            <br>
            <input type="checkbox" v-model="userInfo.agree"> 閱讀並接受<a href="https://tw.yahoo.com/"><用戶協議></a>
            <br>
            <button @click.prevent="demo">提交</button>
        </form>
    </div>
    <script type="text/javascript">
        const vm = new Vue({
            el:'#root',
            data:{
                userInfo:{
                    account:'',
                    password:'',
                    age:'',
                    sex:'',
                    hobby:[],
                    city:'beijing',
                    other:'',
                    agree:''
                }
            },
            methods: {
                demo(){
                    console.log(JSON.stringify(this.userInfo));
                }
            },
        })
    </script>
</body>
</html>
```
## 14 過濾器
![](https://hackmd.io/_uploads/ryeojTBH3.png)
![](https://hackmd.io/_uploads/SkIhOAHrn.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>收集表單數據</title>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript" src="../js/dayjs.min.js"></script>
</head>
<body>
    <div id="root">
        <h2>顯示格式化後的時間</h2>
        <!-- 計算屬性實現 -->
        <h3>{{fmtTime}}</h3>
        <br>
        <!-- methods實現 -->
        <h3>{{getFmtTime()}}</h3>
        <br>
        <!-- 過濾器 -->
        <h3>{{time | timeFormater}}</h3>
        <!-- 連鎖過濾器 -->
        <h3>{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3> <!--2021 -->
        <!-- 屬性使用過濾器 -->
        <h3 :x="msg | mySlice2">尚硅谷</h3>
        <!-- 錯的不能用在v-model -->
        <!-- <input type="text" v-model="msg | mySlice2"> -->
        
    </div>
    <!-- 全局過濾器使用 -->
    <div id="root2">
        <h2>{{msg | mySlice2}}</h2>
    </div>

    <script type="text/javascript">
        
        // 全局過濾器
        Vue.filter('mySlice2', function(value){
            return value.slice(0, 4);
        })

        const vm = new Vue({
            el:'#root',
            data:{
                time:1621561377603, // 時間戳
                msg:'您好，台灣'
            },
            computed: {
                fmtTime(){
                    return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
                }
            },
            methods: {
                getFmtTime(){
                    return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
                }
            },
            // 局部過濾器: 寫在Vue裡，只有該VM裡可以用，之後組件為一個VM，所以一個VUE實體可能包含多個VM
            filters:{
                timeFormater(value, var1='YYYY-MM-DD HH:mm:ss'){
                    console.log(var1)
                    return dayjs(value).format(var1);
                },
                mySlice(value){
                    return value.slice(0, 4);
                }
            }
        })
        const vm2 = new Vue({
            el:'#root2',
            data:{
                msg:'hello' // 時間戳
            }
        })
    </script>
</body>
</html>
```
## 15 內置指令
### 15-1 v-text
```htmlembedded=
<div id="root">
    <div>{{name}}</div>
    <div v-text="name"></div>
</div>
<script type="text/javascript">
    new Vue({
        el:'#root',
        data:{
            name:'尚硅谷'
        }
    })
</script>
```
### 15-2 v-html
![](https://hackmd.io/_uploads/rJ6e31UBh.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>收集表單數據</title>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript" src="../js/dayjs.min.js"></script>
</head>
<body>
    <div id="root">
        <div>{{name}}</div>
        <div v-html="str"></div>
    </div>
    <script type="text/javascript">
        new Vue({
            el:'#root',
            data:{
                name:'尚硅谷',
                str:'<h3>你好阿!</h3>'
            }
        })
    </script>
</body>
</html>
```
### 15-3 v-cloak
* 目的: v-cloak為隱藏屬性，當vue實例真的串建完成，該屬性則會自動消失。可看例子。
![](https://hackmd.io/_uploads/r1EHigLH3.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-cloak</title>
    <style>
        [v-cloak]{
            display: none;
        }
    </style>
</head>
<body>
    <div id="root">
        <div v-cloak>{{name}}</div>
    </div>
    <!-- 這邊我並沒有這伺服器隔五秒才拿到vue.js -->
    <!-- 頁面會先出現{{name}}，五秒後拿到js，頁面才會出現尚硅谷 -->
    <!-- 解決方法v-cloak，防止直接先出現{{name}} -->
    <!-- v-cloak運作方式:過一段時間(vue一介入)該屬性不見 -->
    <!-- 所以一開始先display:none，v-cloak消失，則display:none消失，就顯示了 -->
    <script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
</body>
<script type="text/javascript">
    new Vue({
        el:'#root',
        data:{
            name:'尚硅谷'
        }
    })
</script>
</html>
```
### 15-4 v-once
* 目的: 得到初始化數值，不受任何變動，依然可以顯示。
![](https://hackmd.io/_uploads/BkljNG4USn.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-once指令</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <!-- 只跑第一次 -->
        <h2 v-once>初始化n值是:{{n}}</h2>
        <h2>當前的n值是:{{n}}</h2>
        <button @click="n++">點我n+1</button>
    </div>
</body>
<script type="text/javascript">
    new Vue({
        el:'#root',
        data:{
            n:1
        }
    })
</script>
</html>
```
### 15-5 v-pre
* 目的: html元素不綁定vue解析。
![](https://hackmd.io/_uploads/Hkw444LB3.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-pre指令</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <!-- 不綁定vue解析 -->
        <h2 p-pre>Vue其實很簡單</h2>
        <h2>當前的n值是:{{n}}</h2>
        <button @click="n++">點我n+1</button>
    </div>
</body>
<script type="text/javascript">
    new Vue({
        el:'#root',
        data:{
            n:1
        }
    })
</script>
</html>
```
## 16 自訂義指令
* 需求1:定義一個v-big指令，和v-text功能類似，但會把綁定的數值放大10倍。
* 需求2:定義一個v-fbind指令，和v-bind功能類似，但可以讓其所綁定的input元素默認獲取焦點。
![](https://hackmd.io/_uploads/H1H1NlDSn.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>自訂義指令</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <!-- 
        需求1:定義一個v-big指令，和v-text功能類似，但會把綁定的數值放大10倍。
        需求2:定義一個v-fbind指令，和v-bind功能類似，但可以讓其所綁定的input元素默認獲取焦點。
     -->
    <div id="root">
        <!-- 需求1 -->
        <h2>當前的n值是: <span v-text="n"></span></h2>
        <h2>放大10倍後的n值是: <span v-big="n"></span></h2>
        <h2>放大10倍後的n值是: <span v-big-method2="n"></span></h2>
        <button @click="n++">點我n+1</button>
        <!-- 需求2 -->
        <input type="text" v-fbind:value="n">
    </div>
</body>
<script type="text/javascript">
    // 全局寫法 v-test
    Vue.directive('test', {
        bind(element,binding){
            element.value = binding.value;
        },
        inserted(element,binding){
            element.focus();
        },
        update(element,binding){
            element.value = binding.value;
        }
    })

    new Vue({
        el:'#root',
        data:{
            n:1
        },
        directives:{
            // element: span, binding裡面放著各種n的訊息之類的，this:window
            // big函數何時倍調用?
            // 1. 指令與元素成功綁定時
            // 2. 當點擊button時，數據改變時(也會造成模板重新解析時)，會調用。(因為數據改變=>模板就會重新解析)
            //    所以如果改變數據其他key也會調用big
            big(element,binding){
                // console.log(element, binding, this);
                element.innerText = binding.value * 10;
            },
            'big-method2'(element,binding){
                element.innerText = binding.value * 10;
            },
            // 失敗
            // fbind(element,binding){
            //     element.value = binding.value;
            //     element.focus(); // 沒用，雖然綁定內容，但是focus只在輸入框生成後執行，可發現點擊一次後可獲得焦點
            //     // 先討論fbind何時執行?上面有
            // }
            fbind:{
                // 可設定不同時間點，調用不同函數bind(){}、inserted(){}、update(){}
                // 如果用big寫法:代表只寫了bind、update都使用同個函式
                // 1. 指令與元素成功綁定時
                bind(element,binding){
                    element.value = binding.value;
                },
                // 2.指令元素被插入頁面時，被調用
                inserted(element,binding){
                    element.focus();
                },
                // 3.指令所在的模板重新解析頁面時，被調用
                update(element,binding){
                    element.value = binding.value;
                }
            }
            
        }
    })
</script>
</html>
```
## 17 Vue生命週期
### 17-1 引出生命週期 mounted
![](https://hackmd.io/_uploads/Bk8ZKxwH2.png)
* 這個標題越來越淡，淡到一定程度恢復全黑，再越來越淡。
![](https://hackmd.io/_uploads/rksBxZwSh.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>引出生命週期</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <!-- 一組一組的要寫對象{opacity: opacity} -->
        <h2 :style="{opacity: opacity}">歡迎學習Vue</h2>
    </div>
</body>
<script type="text/javascript">
    const vm = new Vue({
        el:'#root',
        data:{
            opacity:1
        },
        methods: {
            // 必須綁在按鈕上違反需求
            // change(){
            //     setInterval(()=>{
            //         this.opacity -= 0.01;
            //         if(this.opacity <= 0){
            //             this.opacity = 1;
            //         };
            //     },16)                
            // }
        },
        // 重點: 在生命週期中mounted是其中一個會調用的函數
        // Vue完成模板的解析並把初始的真實的DOM元素放入頁面後(掛載完畢)調用mounted
        // 只調用一次
        mounted() {
            // 寫箭頭函式是因為這樣沒有this，就會自動去找上一層的當作this
            setInterval(()=>{
                this.opacity -= 0.01;
                if(this.opacity <= 0){
                    this.opacity = 1;
                };
            },16)
        },
    })
    // 通過外部定時器實現(不推薦)
    // setInterval(()=>{
    //     vm.opacity -= 0.01;
    //     if(vm.opacity <= 0){
    //         vm.opacity = 1;
    //     };
    // },16)
</script>
</html>
```
### 17-2 分析生命週期(049)
* 黃色框框:Has 'el' option，outerHTML:是否包含root div整個為模板
![](https://hackmd.io/_uploads/H1wWZfPB2.png)
![](https://hackmd.io/_uploads/rysf4uPH3.png)

* 關注左邊
![](https://hackmd.io/_uploads/Bk6u5fPSh.png)
* 關注右邊
![](https://hackmd.io/_uploads/ryFAqGvH3.png)
![](https://hackmd.io/_uploads/Sky6JPwrh.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>分析生命週期</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2>當前的n值是:{{n}}</h2>
        <button @click="add">點我n+1</button>
        <button @click="bye">點我銷毀vm</button>
    </div>
</body>
<script type="text/javascript">
    const vm = new Vue({
        el:'#root',
        // template:`
        //     <div>
        //         <h2>當前的n值是:{{n}}</h2>
        //         <button @click="add">點我n+1</button>
        //     </div>
        // `,// 直接當模板，上面可以是空的html，上面div root還是要存在
        data:{
            n:1
        },
        methods: {
            add(){
                console.log('add');
                this.n++;
            },
            bye(){
                console.log('bye');
                this.$destroy();
            }
        },
        beforeCreate() {
            // console.log('beforeCreate');
            // console.log(this);// 看看是否有_data?這時候還沒處理到那邊
            // // 讓程式跑到這就先停
            // debugger;
        },
        created() {
            // console.log(this);// 有_data、methods
            // debugger;
        },
        beforeMount() {
            // console.log(this);
            // debugger;
        },
        mounted() {
            // console.log(this);
            // debugger;
        },
        beforeUpdate() {
            // 數據已更新，淡頁面還未重新解析前
            // console.log(this.n);
            // debugger;
        },
        updated() {
            console.log('updated');
        },
        beforeDestroy() {
            // 在vm銷毀前一刻執行
            // 可以調用方法那些，但是內部更新數據的指令已經不會跑了
        },
        destroyed() {
            
        },
    })

</script>
</html>
```
### 17-3 生命週期總結
![](https://hackmd.io/_uploads/Hkn0GOwHn.png)
![](https://hackmd.io/_uploads/BJ42F_wSn.png)
![](https://hackmd.io/_uploads/BJ21qdvrn.png)
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>引出生命週期</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2 :style="{opacity: opacity}">歡迎學習Vue</h2>
        <button @click="opacity = 1">點我透明度為1</button>
        <button @click="stop">點我停止變換</button>
    </div>
</body>
<script type="text/javascript">
    const vm = new Vue({
        el:'#root',
        data:{
            opacity:1
        },
        methods: {
            // 自殺，他殺連這個stop都沒有
            stop(){
                // clearInterval(this.timer);// vm還存在，所以可以透過f12或者任意方式都還可以被更改
                this.$destroy(); // 銷毀時定時器並不會消失阿，定時器是在window上阿，所以上面定時器可以在下面銷毀之前先取消訂時
            }
        },
        // 重點: 在生命週期中mounted是其中一個會調用的函數
        // Vue完成模板的解析並把初始的真實的DOM元素放入頁面後(掛載完畢)調用mounted
        // 只調用一次
        mounted() {
            // 寫箭頭函式是因為這樣沒有this，就會自動去找上一層的當作this
            // this=>vm
            this.timer = setInterval(()=>{
                console.log('setInterval');
                this.opacity -= 0.01;
                if(this.opacity <= 0){
                    this.opacity = 1;
                };
            },16)
        },
        // 善後動作
        beforeDestroy() {
            clearInterval(this.timer);
        },
    })
</script>
</html>
```
 






