---
# System prepended metadata

title: Vue 技術
tags: [前端框架, javascript, vue]

---

# Vue 技術

###### tags: `vue` `javascript` `前端框架`

## 簡介

用於構建用戶介面的漸進式 Javascript 框架

### 特點

1. 採用組件化模式，提高代碼復用率，更好維護
   把每一個功能變成組件開發
2. 聲明式編碼，讓編碼無須直接操作 DOM，提高開發效率

### 003 創建 Vue 3 工程
＊官方新推出使用 Vite 去創建 Vue3 

* Vite: 構建工具，和 WebPack 等價
* 構建速度非常快，急速啟動服務
* 對 TS JSX CSS 開箱即時使用
* 真正的按需編譯，看啥準備啥，不用一開始全部都編譯

```
# 先安裝 Node JS
npm create vue@latest

Project name: vue project // 純小寫下滑線
Add TypeScript: Yes
Add JSX Support: No
Add Vue Router : No
Add Pinia : No
Add Vitest: No
Add End to End : No
Add ESLint: No // 語法檢查

# 比 vue-cli 構建還快
```

==npm i 安裝所有依賴==

文件: 
.vscode : vscode 插件
public : 根目錄，存放靜態圖片
src : 所有工作源代碼路徑
env.d.ts : 宣告 TS 認識的文件 (不可刪除)
index.html : 入口文件
package.json : 依賴包的文件
vite.config.ts : 安裝文件配置代理

起動項目： npm run dev

### 004 編寫 App 组件

src/main.ts : 入口文件

```javascript
import {createApp} from 'vue'
import App from './App.vue' // 一個組件，根組件

createApp(App).mount('#App') // 掛載在 id #App 的 div 裡面
```
scr/components : 組件，樹枝文件

```htmlembedded
<template>
    <div class="app">
        <h1>
            你好
        </h1>
    </div>
<template/>
    
<script lang="ts">
    // 寫 JS and TS
    export default{
        name: 'App'
    }
</script>
<style>
    // CSS    
</style>
```
### 008 數據綁定

##### 單、雙向數據綁定

```htmlmixed!
單向數據綁定 <input v-bind:value="name">
雙向數據綁定 <input v-model:value="name">

[縮寫方式]

單向數據綁定 <input :value="name">
雙向數據綁定 <input v-model="name">

new Vue({
    el:"root",
    data:{
        name="測試數據"
}
})
```

-   雙向數據綁定中，頁面的數據能夠直接修改 data 中的數據

*   v-model 只能綁訂在表單類(輸入類元素)的元素
*   輸入類元素的特點是都有 value 值，使用者跟他互動修改數值


[008 尚硅谷Vue技术 数据绑定 - YouTube](https://www.youtube.com/watch?v=kv3w-v0_MNo&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=8)


###  009 el 與data 的兩種寫法

```javascript
const vm = new Vue({
    // el:"root", 方法一
    data:{
        name="測試數據"
}
})

vm.$mount('#root') // 方法二
```
* 第一種方式是在 Vue 實例對象中指定容器
* 或是使用 $mount 在事後指定容器
* 兩種方式皆可，差異在於 $mount 更靈活些 

```javascript

const vm = new Vue({
    el:"root"
    data:{
        name="測試數據" // data的第一種寫法
    }             
    data:function(){ // data的第二種寫法，函數式，未來腳手架會使用到
        retrun{
            name="測試數據"  
        }
    }
})

```
* data 有兩種寫法分為: 對象式 跟 函數式
-   學習到組件的時候，data必須使用函數式
* 由vue中管理的函數不可寫成箭頭函數，否則this會指向windows，錯誤

[009 尚硅谷Vue技术 el与data的两种写法 - YouTube](https://www.youtube.com/watch?v=xR25jMEzzDM&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=9)

### 010 MVVM 

1. Model(M): 對應data中的數據
2. View(V): 對應模板(模板經由解析形成的頁面)
4. ViewModel(VM): Vue的實例對象

![](https://hackmd.io/_uploads/BJBdpsRO3.png)


* Vue 的設計受到 MVVM模型的啟發
* DataBinding: 指將數據擺設到指定模板位置
* data中的數據都會出現在vm 實例對象中
* vm 身上所有的屬性，及vue原型上所有的屬性，都可以在模板之中直接使用

```htmlembedded!

<div id="root">
    {{name}} // View    
</div>

// ViewModel
// vm 用來代表實例對象
const vm = new Vue({
    el:"root"
    data:{
        name="測試數據" // Model
    }             
})

```
[010 尚硅谷Vue技术 理解MVVM - YouTube](https://www.youtube.com/watch?v=Kho_rOhFxq4&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=10)


### 011-013 數據代理 

#### 回顧 Object.defineproperty方法
```javascript!
// 建立一個對象
let person = {
    name: "王大明",
    sex: "boy"
}

// 對於person對象 object 配置一個新的屬性
Object.defineproperty(person,"age",{
    value:18 // 配置項
    enumerable: true // 控制屬性是否可以枚舉，默認 false
    writable: true // 控制屬性是否可以被修改，默認 false
    configurable: true // 控制屬性是否可以被刪除，默認 false
})

// console.log(Object.keys(person)
console.log(person)
delete person.age
```

**情形**: 是否 可以讓 person 中的 age屬性，可以跟number 進行綁定。number變了，person 中的age自動修改
```javascript
let number = 18
let person = {
    name: "王大明",
    sex: "boy",
    // age:number
}

Object.defineproperty(person,"age",{
    
    // 當有人讀取 person的 age 屬性的時候，get(getter) 函數就會被調用
    // 返回值是 age 的值
    get:function(){
        return number
    },
    // 當有人修改 person的 age 屬性的時，set(setter)函數就會被調用， 且會收到修改的具體值
    set(value){
        console.log(value)
        // 修改 person 的 age後，修改 number
        number = value
    }
})
person.age = 30
```

**數據代理**: 通過一個對象代理對於另一個對象中的屬性的操作(讀/寫)

```javascript
let obj = {x:100}
let obj2 = {y:100}

Object.defineproperty(obj2,"x",{
    
    get:function(){
        return obj.x
    },
    set(value){
        obj.x = value
    }
})
// 通過obj2 可以修改 obj 中的 x
```
Vue 中的數據代理
```javascript
<div id="root">
    地址: {{address}} 
    名稱: {{name}} 
</div>

// ViewModel
// vm 用來代表實例對象

let data = {
        name="測試數據",
        address="...."
}             
const vm = new Vue({
    el:"root"
    data
})

vm.name = "新數據" // 修改後 調用setter 影響 data中的數據


```
![](https://hackmd.io/_uploads/HJcHUnA_n.png)

* 驗證數據綁定中的 getter and setter
* vm.name // 可以取得 測試數據，代表 getter被調用
* vm.name = "測試新數據" // 修改後，setter調用 
* vm._data === options.data === data // true，vm下面的data 和下滑線的data 跟外部的data都是同一個人
* vm 把data中的數據，代理到 vm 底下。因此 vm.data.name === vm.name (這樣方便操作數據)

**總結**
1. Vue中的數據代理: 通過 vm來代理數據中的屬性操作(讀寫)
2. 運用數據代理可以更方便操作數據
3. 基本原理: 透過 Object.defineproperty，將data中的所有屬性添加到 vue上面，為每一個屬性都加上 getter/setter。在getter/setter操作data中的數據。

[011 尚硅谷Vue技术 Object defineProperty - YouTube](https://www.youtube.com/watch?v=foG5pm2h6Ho&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=11)
[012 尚硅谷Vue技术 理解数据代理 - YouTube](https://www.youtube.com/watch?v=JKaqOqO5TnM&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=12)
[013 尚硅谷Vue技术 Vue中的数据代理 - YouTube](https://www.youtube.com/watch?v=SdNQsohOdX8&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=13)


### 014 v-on 事件處理
網頁要跟用戶進行交互

```javascript!
<div id="root">
    // 當此元素被點擊後，執行某函數
    <button v-on:click="showInfo">點我提示訊息</button>
    <button v-on:click="showInfo2(66,$event)">點我提示訊息</button>
</div>

const vm = new Vue({
    el:"root"
    data:{
        name="測試數據" 
    }             
    methods:{
        showInfo(event){
            console.log(event.target) //不傳參數的話，自帶event
            console.log(this) // 此處的 this 是 vm
            alert("SHOW INFO!!")
        },
        showInfo2(number){
            console.log(number)
        }    
    }
})

```
* showInfo 可否接受參數? 其實內部自帶
* 如果 showInfo 改成 showInfo(event) =>{}, this 就是 window
* v-on:click，要傳參數才加 ()
* 如果參數需要有event，可以在 v-on:click後加入 $event
* 方法可以寫在data嗎? 可以，但是會做數據劫持跟代理，會導致效能浪費，因為函數只要調用，用完就釋放了。

**總結**
1. ==v-on:xxx== 或 ==@xxx== 用來綁定事件，xxx是事件名稱。
2. 事件回調配置在methods之中，最終會出現在vm 上面。
3. methods 中函數不可以使用箭頭函數，否則會影響this。
4. methods 中函數是vue實例中所管理的函數，this 一樣指向 vue 實例對象。
5. @click="demo" 和 @click="demo($event)" 效果一樣，後面可以傳參數。


[014 尚硅谷Vue技术 事件处理 - YouTube](https://www.youtube.com/watch?v=sIN4mTREaB8&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=14)

### 015事件修飾符號

```javascript!
<div id="root">   // prevent 是事件修飾符，可以阻止跳轉
    <a href="https://www.google.com" @click.prevent="showInfo">點我提示訊息</a>
</div>

// 事件冒泡案例
// 點擊後 button先作用，之後 div 作用 這就是事件冒泡
<div @click="showInfo">
    <button @click.stop="showInfo">事件冒泡<button> // 在裡面加就可以了  
</div>

// 事件捕獲模式案例
<div @click.capture="showMsg(1)">
    box1
    <div @click="showMsg(2)">
        box2  // 如果不加 capture，點 box 會 show 2,1 (冒泡階段)
    </div>
</div>

// 只有 event.target 是當前元素才觸發事件
<div @click.self="demo">
    <button @click="demo">事件冒泡<button> 
</div>

// 事件默認立即執行，無須等待事件執行完畢
<ul @wheel.passive="demo"> // 滾動事件 scroll ，wheel(鼠標滾動)
    <li></li>  // 滾動發動時候，調用demo，才向下移動一點 
    <li></li>  // 加 passive，移動一點 才調用 demo
    <li></li>
    <li></li>
</ul>

// 點擊後，會自動跳走到 href 的網址
const vm = new Vue({
    el:"root"
    data:{
        name="測試數據" 
    }             
    methods:{
        showInfo(event){
            // event.preventDefault()
            // 可以阻止跳轉
            // event.stopPropagation()
            // 阻止事件冒泡
            alert("SHOW INFO!!")
        },
        demo(){
            for(let i=0; i < 10000000; i++ ){
                console.log("#")
            }
        }
    }
})

```
* vue 準備了6個事件修飾符，修飾符寫在事件後面
* prevent: 阻止默認事件
* stop: 阻止事件冒泡
* once: 事件只會觸發一次 (事件發生完，下次點就沒作用了)
* capture: 使用事件捕獲模式
* self: 只有event.target 是當前元素時候才觸發事件
* passive: 事件默認立即執行，無須等待事件執行完畢 (不是所有事件會被影響，設計平板、手機程式才會用到)
* 事件修飾符可以連著寫，@click.stop.prevent="demo" 先停止冒泡，再阻止默認事件

[015 尚硅谷Vue技术 事件修饰符 - YouTube](https://www.youtube.com/watch?v=u5hsbvN1fUM&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=15)
[017 尚硅谷Vue技术 事件总结 - YouTube](https://www.youtube.com/watch?v=rWD2yN9P3L4&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=17)

### 016鍵盤事件
```javascript!
    <input @keyup="showInfo">
    <input @keyup.enter="showInfo"> // 設置按鍵別名，enter之後才觸發事件
const vm = new Vue({
    el:"root"           
    methods:{
        showInfo(event){
            console.log(event.key, event.keyCode) // 取的按鍵名稱
            // 如果不是 enter，就返回
            if(e.keyCode !==13) return
            console.log(event.target.value)
        },

    }
})

```

* @keydown: 按下去不用抬起來就觸發事件
* @keyup: 按下去抬起來才觸發事件
* 常用按鍵別名:enter、delete、esc、space、tab、up、down、left、right
* 按鍵名稱可以用 event.key取得，但是有些 key要當作鍵盤事件需要修改
* CapsLock 需要改成 caps-lock 才可以使用
* tab 比較特殊，tab會切換到其他元件焦點的功能
* shift ctrl meta alt 使用 @keyup.xxx 都會有問題，因為他們是系統修飾鍵
* @keyup.xxx 按下修飾鍵的時候，再按其他鍵，釋放其他鍵，才會觸發
* @keydown.xxx 沒有此問題 ex. ctrl +s s放開

**總結**
1. vue 有常用的常用按鍵別名可以使用，也夠用了。
2. 可以搭配keyCode去使用具體案件，不推薦，因為未來可能會停止支持，不同的鍵盤編碼不同
3. 自定義別名: Vue.config.keyCodes.CustomName = 13 (不推薦)
4. 案件也可以連著寫: @keyup.ctrl.y: ctrl + y 才會執行

[016 尚硅谷Vue技术 键盘事件 - YouTube](https://www.youtube.com/watch?v=C1Geqej3S5k&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=16)

### 018-020 計算屬性

==需求==
兩個 input 框，分別輸入姓氏，直接在頁面顯示 姓-名

1. 插值語法實踐

```javascript!
    姓<input type="text" v-model="lastName">
    名<input type="text" v-model="firstName"> 
    全名<span>{{lastName.splice(0,3)}}-{{firstName}}</span>

    // .splice(0,3) 這樣的風格不好，這樣會導致模板太過複雜，不推薦使用
    // 如果要切割完，反轉，首字大寫
    // 推薦計算屬性來操作內容，也苦用 methods
const vm = new Vue({
    el:"root"           
    data:{
        firstName: "三"
        lastName: "張"
    }
})
```

2. 方法實踐

```javascript!
    姓<input type="text" v-model="lastName">
    名<input type="text" v-model="firstName"> 
    全名<span>{{ fullName() }}</span> // 方法返回值放過來，要加 ()

    // .splice(0,3) 這樣的風格不好，這樣會導致模板太過複雜，不推薦使用
    // 如果要切割完，反轉，首字大寫
    // 推薦計算屬性來操作內容，也苦用 methods
const vm = new Vue({
    el:"root"           
    data:{
        firstName: "三"
        lastName: "張"
    },
    methods:{
        fullName(){
            return this.lastName + "-" + this.firstName
        }               
    }               
})
```
* 只要 data中的數據發生變化，就會重新解析模板

[018 尚硅谷Vue技术 姓名案例 - YouTube](https://www.youtube.com/watch?v=3Xb-tVtqTm0&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=18)

3. 計算屬性實踐

* data 中的配置項就是屬性

```javascript!
    姓<input type="text" v-model="lastName">
    名<input type="text" v-model="firstName"> 
    全名<span>{{ fullName }}</span>

const vm = new Vue({
    el:"root"           
    data:{
        firstName: "三"
        lastName: "張"
    },
    computed:{
        fullName:{
            // get 有啥作用?  
            // 當有人讀取 fullName時候，get 會被調用，返回值就作為
            // fullName 的值
            get(){
                // get 中的 this 指向 vm
                return this.lastName + "_" + this.firstName
            }
            set(value){
                // 如果沒有要修改fullName，可以不用寫 set 
                // 當 fullName被修改的時候，會被調用
                // 收到的 value 是 fullName的修改值
                const arr = value.split("_")
                this.lastName = arr[0]
                this.firstName = arr[1]
            }
        }
    }               
})
```

* vm._data 沒有計算後的屬性
* 可以藉由開發工具進行觀察
* get 何時調用? 初次讀取時候、所依賴的數據發生變化時 (姓名發生改變時，fullName重新調用)
* 計算屬性比起方法屬性，有緩存功能，不會被一直調用
* 如果修改fullName，他會先調用 set，然後修改 data中的屬性，這時候會重新渲染頁面，data中的屬性被修改後，會重新計算屬性。


==重點==
* 要用的屬性不存在，從已有的屬性而來
* 原理，底層使用 Object defineproperty 的 getter and setter
* get 何時被調用?
* 有緩存機制，效率更高
* 計算後的屬性最終會出現在 VM底下
* 如果計算屬性要被修改，要使用 set 

[019 尚硅谷Vue技术 计算属性 - YouTube](https://www.youtube.com/watch?v=ZodLl-G3HAg&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=19)

4. 計算屬性的==簡寫==
更多的情況，計算屬性不會被修改
set 會被省略
```javascript!
    姓<input type="text" v-model="lastName">
    名<input type="text" v-model="firstName"> 
    全名<span>{{ fullName }}</span>  //不用加誇號，他是執行完的函數

const vm = new Vue({
    el:"root"           
    data:{
        firstName: "三"
        lastName: "張"
    },
    computed:{
        fullName(){
                return this.lastName + "_" + this.firstName
            }
        }
    }               
})
```

[020 尚硅谷Vue技术 计算属性 简写 - YouTube](https://www.youtube.com/watch?v=rmoSGnyE8sY&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=20)

### 022-025 監視屬性、深度監視

原本學會的方法，但是這樣不夠精簡
```javascript!
<h2> 今天天氣很 {{info}}</h2>
<button @click="changeWether()">切換天氣</button>

const vm = new Vue({
    el:"root"           
    data:{
        isHot: true
    }, 
    computed:{
        info(){
            return isHot ? 'HOT': 'Cold'
        }          
    }
    methods:{
        changeWether(){
            this.isHot = !this.isHot
        }
    }
})
```

監視屬性: 監視某個屬性的變化

```javascript!
<h2> 今天天氣很 {{info}}</h2>
<button @click="changeWether()">切換天氣</button>

const vm = new Vue({
    el:"root"           
    data:{
        isHot: true
    },
    computed:{
        info(){
            return isHot ? 'HOT': 'Cold'
        }          
    }
    methods:{
        changeWether(){
            this.isHot = !this.isHot
        }
    }
    watch:{
        isHot:{
            // 何時調用? 當 isHot發生改變的時候調用
            // 修改前後的數值都會給
            handler(newValue,oldValue){
                
            },
            immediate: true // 一上來立刻使用，初始化時候，調用一下
            
        }
    }
})

// watch 的另外一種寫法
vm.$watch('isHot'){
    handler(newValue,oldValue){          
    },
}
```

1. 當監視屬性發生變化時，回調函數 handler 自動調用
2. 監視屬性必須存在，才能監視
3. 監視屬性的兩種寫法，new Vue時候傳入、通過 vm.$watch

深度監視

```javascript!
<h2> a: {{numbers.a}}</h2>
<button @click="numbers.a++">點我讓a+1</button>

<h2> b: {{numbers.b}}</h2>
<button @click="numbers.b++">點我讓b+1</button>

const vm = new Vue({
    el:"root"           
    data:{
        numbers:{
            a: 1,
            b: 2,
        }
    }, 
    
    watch:{
        // 監事多級結構中，某函數的變化
        // 如果有一百個 key values 怎麼辦?
        'numbers.a':{
            handler(newValue,oldValue){
                
            },        
        }
        number:{
            deep:true // 監測到多級結構中，所有屬性的變化
            handler(newValue,oldValue){
                
            },  
        }
    }
})

```
* VUE 是可以監測多級結構的，但是 watch默認不監測多級結構內部的值


監視屬性的簡寫

```javascript!
<h2> 今天天氣很 {{info}}</h2>
<button @click="changeWether()">切換天氣</button>

const vm = new Vue({
    el:"root"           
    data:{
        isHot: true
    }, 
    computed:{
        info(){
            return isHot ? 'HOT': 'Cold'
        }          
    }
    methods:{
        changeWether(){
            this.isHot = !this.isHot
        }
    }
    watch:{
        // 正常寫法
        isHot:{
            handler(newValue,oldValue){
                
            },
            immediate: true // 一上來立刻使用，初始化時候，調用一下
            
        },
        // 簡寫形式
        isHot(newValue,oldValue){
            
        }
    }
}) 

vm.$watch('isHot',function(newValue,oldValue){...})

```

對比計算屬性跟監視屬性

```javascript!
    姓<input type="text" v-model="lastName">
    名<input type="text" v-model="firstName"> 
    全名<span>{{ fullName }}</span>  //不用加誇號，他是執行完的函數

const vm = new Vue({
    el:"root"           
    data:{
        firstName: "三"
        lastName: "張"
        
    },
    computed:{
        fullName(){
                return this.lastName + "_" + this.firstName
            }
        }
    }  
    
    watch:{
        
        // 初始值必需先配好
        firstName(newValue,oldValue){
            setTimeout(() => {
            // setTimeout 寫成箭頭函數就沒有 this，就向外面找
            this.fullName =  this.lastName + "-" + newValue
            },1000)
        }
        lastName(newValue,oldValue){
            this.fullName = newValue + "-" + this.firstName
        }
    }
})

// 當姓寫了之後 晚了一秒鐘才反應，看起來只能用 watch

```
* computed 不能使用異步計算任務，此時只能使用 watch
* computed 能完成的 watch 也可以
* watch 能完成的 computed 不一定可以 (computed 不能使用異步計算任務)
* vue所管理的函數最好寫成普通函數，這樣 this才能指向 vm
* 所有不被 vue管理的函數 (定時器、ajax、Promise的回調)，最好寫成箭頭函數，這樣寫成箭頭函數，這樣this才會是vm

[022 尚硅谷Vue技术 监视属性 - YouTube](https://www.youtube.com/watch?v=L-gOeBU3Su0&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=22)
[023 尚硅谷Vue技术 深度监视 - YouTube](https://www.youtube.com/watch?v=qSpR5h-CRCU&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=23)
[024 尚硅谷Vue技术 监视的简写形式 - YouTube](https://www.youtube.com/watch?v=19APkdJp7nU&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=24)
[025 尚硅谷Vue技术 watch对比computed - YouTube](https://www.youtube.com/watch?v=DPAlM6qPLFM&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=25)


### 026-027 绑定class、style样式

```javascript!
    
    // 樣式類名不確定，需要動態指定
    <div class="basic" :class="myClass" @changeClass></div>  
    // 樣式類名數量、名字不確定，需要動態指定
    <div class="basic" :class="classArr" @changeClass></div> 
    // 樣式類名數量、名字確定，需要動態決定要不要用
    <div class="basic" :class="classObj" @changeClass></div>  
    
    // 綁定 style方式 
    <div class="basic" :style="{fontSize: fsize+'px'}"></div> 
    <div class="basic" :style="styleObj"></div> 
    <div class="basic" :style="[styleObj1,styleObj2]"></div> [不成用]

const vm = new Vue({
    el:"root"           
    data:{
        myClass: ""
            
        // 要綁定的class個數、樣數不一定
        classArr = ["cA","cB","cC"] // 這三個 class都會上去渲染
        classObj:{
            aaaa : true
            bbbb : false
        },
        fize = 50
        styleObj:{
            fontSize: '40px', // 兩個單字要寫成這樣
            color :'blue'
        }
         styleObj2:{
            backgroundColor: 'blue', // 兩個單字要寫成這樣
        }
    },
    methods:{
        changeClass(){
                const arr = ["classA","classB","classC"]
                const index = Math.floor(Math.random()*3) 
                this.myClass = arr[index]
            }
        }
    }  
    

})

// 當姓寫了之後 晚了一秒鐘才反應，看起來只能用 watch

```

[026 尚硅谷Vue技术 绑定class样式 - YouTube](https://www.youtube.com/watch?v=Y4n1q7HBHJM&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=26)
[027 尚硅谷Vue技术 绑定style样式 - YouTube](https://www.youtube.com/watch?v=GvKIY7VbVWo&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=27)

### 028 條件渲染

符合某些條件給你渲染某些東西

```javascript!
    
<div id="root">
    // v-show 可以引入條件判斷式
    // v-show 底層只是使用 display=none
    <h2 v-show="false">{{name}}<h2>
        
    // v-if 可以引入條件判斷式
    // 結構完全消失
    <h2 v-if="false">{{name}}<h2>
    <h2 v-else-if="false">{{name}}<h2>
    <h2 v-else>{{name}}<h2>   
    
    // 使用 template，結構不會顯示在網頁上
    <template v-if="false">
        <h2 v-if>A<h2>
        <h2 v-if>B<h2>
        <h2 v-if>C<h2>
    </template>
</dir>

```

* 如果有很高的切換頻率，建議使用 v-show
* v-if 會讓節點消失，如果在高切換頻率使用，會有效能問題
* 如果多個要判斷可以使用 v-else-if, v-else，結構不能被打斷
* 

[028 尚硅谷Vue技术 条件渲染](https://www.youtube.com/watch?v=VVEtJlfq_04&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=28)

### 029-30 列表渲染，KEY作用

```javascript!
    
<div id="root">
    <button @click.once="add">Add User<button>
    <ul> 
         // 動態綁定 id 
         // 使用 v-for 批量生成某結構時候，要用 key 給個標示
         // 可以用 of 也可以用 in persons
         // 遍歷數組
         // key 是 Vue 在使用的，頁面渲染不使用
        <li v-for="(person,index) of persons" :key="person.id">
            {{person.name}} - {{person.age}} - {{index}}
            <input> // key 用 index 當添加的時候，會有問題
        </li>
    </ul>
</dir>

const vm = new Vue({
    el:"root"           
    data:{
       persons: [
            {id:"001",name:"AA",age:18},
            {id:"002",name:"BB",age:19},
            {id:"003",name:"CC",age:17},       
        ]
    },
    methods:{
        add(){
            const p = {id:"004",name:"DD",age:40}
            this.persons.unshift(p)
        }
    }  
    
})
```

* 可以遍歷數組、對象、字符串、指定次數
* key 是給節點一個標示
* key: 如果有打亂數據的順序，不能使用 index 為 key
* diff 算法

==重點==
1. 當數據發生變化的時候，Vue會根據新數據生成新的虛擬 DOM，隨後發生 diff比較
2. 使用 index 作為 KEY可能會引發問題。破壞順序的時候會有問題
3. 開發中最好使用唯一值進行標示
4. 如果沒有增加數據刪除數據功能，只有展示，可以使用 index 為 key

對比規則: 

1. 新舊虛擬 DOM有相同 KEY。(1) DOM內容不變，使用之前的真實DOM (2) 虛擬 DOM改變，生成新的真實DOM，隨後替換頁面的真實DOM。
2. 新舊虛擬 DOM沒有相同 KEY。創建新的真實 DOM，渲染頁面。

[029 尚硅谷Vue技术 列表渲染](https://www.youtube.com/watch?v=zU7nfXWYskw&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=29)
[030 尚硅谷Vue技术 key作用与原理](https://www.youtube.com/watch?v=7xGu_7C168A&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=30)


### 031 列表過濾

```javascript!
    
<div id="root">
    // 用戶所輸入的東西發生變化，可以用watch偵測
    <input type="text" placeholder="請輸入用戶名" v-model="keyWord">
    <ul> 
        <li v-for="(person,index) of filpersons" :key="person.id">
            {{person.name}} - {{person.age}} - {{index}}
        </li>
    </ul>
</dir>

const vm = new Vue({
    el:"root"           
    data:{
       keyWord: "",
       persons: [
            {id:"001",name:"王杰二",age:18},
            {id:"002",name:"周杰三",age:19},
            {id:"003",name:"周杰倫",age:17},       
        ]
        filpersons: []
    },
    watch:{
            keyWord:{
                immediate:true // 利用字串包含 "空" 的特性
                 handler(val){
                   this.filpersons = this.person.filter((p)=>{
                    // 是否包含
                    return p.name.indexOf(newValue) !== -1
                }) 
                }  
            }   
        }
    }  
    
    // 計算屬性實現
    data:{
       keyWord: "",
       persons: [
            {id:"001",name:"王杰二",age:18},
            {id:"002",name:"周杰三",age:19},
            {id:"003",name:"周杰倫",age:17},       
        ]
    },
    computed:{
        filpersons(){
           return this.person.filter((p)=>{
                // 是否包含
                return p.name.indexOf(this.keyWord) !== -1
            }) 
        }
    }  
})
```
[031 尚硅谷Vue技术 列表过滤](https://www.youtube.com/watch?v=mBSb7TedDKw&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=31)

### 032 列表排序

```javascript!

<div id="root">
    // 用戶所輸入的東西發生變化，可以用watch偵測
    <input type="text" placeholder="請輸入用戶名" v-model="keyWord">
    <button @click="sortType=2">年齡升序</button>
    <button @click="sortType=1">年齡降序</button>
    <button @click="sortType=0">年齡原順序</button>
    <ul> 
        <li v-for="(person,index) of filpersons" :key="person.id">
            {{person.name}} - {{person.age}} - {{index}}
        </li>
    </ul>
</dir>

const vm = new Vue({
    el:"root"           
    data:{
       keyWord: "",
       sortType: 0 // 0 原順序 1 降序 2 升序
       persons: [
            {id:"001",name:"王杰二",age:18},
            {id:"002",name:"周杰三",age:19},
            {id:"003",name:"周杰倫",age:17},       
        ]
    },
    computed:{
        filpersons(){
           const arr =  this.person.filter((p)=>{
                return p.name.indexOf(this.keyWord) !== -1
            }) 
           // 判斷是否需要排序
           if (this.sortType){
               arr.sort(a,b)=>{
                  return this.sortType === 1 ? b.age-a.age: a.age-b.age
               }
           }
    
           return arr // 返回給 filperson
        }
    }  
   
})
```
[032 尚硅谷Vue技术 列表排序](https://www.youtube.com/watch?v=1Bn990zsABg&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=32)

### 033 Vue 監測數據改變的原理

vue 怎麼知道數據被修改了?

```javascript!

<div id="root">
    // 更新數據發生的問題
    // 按鈕更新周杰倫訊息
    <button @click="updateZou">更新周杰倫資料</button>
    <ul> 
        <li v-for="(person,index) of persons" :key="person.id">
            {{person.name}} - {{person.age}} - {{index}}
        </li>
    </ul>
</dir>

const vm = new Vue({
    el:"root"           
    data:{
       persons: [
            {id:"001",name:"王杰二",age:18},
            {id:"002",name:"周杰三",age:19},
            {id:"003",name:"周杰倫",age:17},       
        ]
    }
    methods:{
        updateZou(){
            this.persons[2].name = "周先生" // 可以奏效
            this.person[2] = {id:"003",name:"周杰倫",age:17} // 不奏效，VUE 沒有監測到
            this.persons.splice(0,1,{id:"003",name:"周杰倫",age:17}) // 可以奏效
        }               
    }
})
```

* data 的數據會先加工，加入 getter/setter
* 資料改變會影響 setter 調用
* VUE 會監測多層數據，直到不是對象

### 035 vue.set

1. 一開始沒有定義 data 某一個屬性
2. 事後新增: Vue.set(vm._data.student,'sex','boy') 這才會給 getter setter
3. vm.$set(vm._data.student,'sex','boy') // 和上面一樣
4. vm.$set(vm.student,'sex','boy') // 和上面一樣
5. set 不能給 data 附加對象，只能在屬性裡面附加

### 036 數組的數據監控

1. 數組沒有匹配getter/setter
2. 如果想對於數組修改會用 push、pop、shift、unshift、splice、sort、reverse。會影響數組的變化，Vue 監測到這些方法去監測數組的變化
3. vm 身上的 push等方法已經不是原型對象的 push
4. vue 重新包裹 數組的方法們

[033 尚硅谷Vue技术 更新时的一个问题](https://www.youtube.com/watch?v=abjCbh9xlRU&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=33)
[034 尚硅谷Vue技术 Vue监测数据的原理 对象](https://www.youtube.com/watch?v=9jyznItXP5Y&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=34)
[035 尚硅谷Vue技术 Vue set方法](https://www.youtube.com/watch?v=LGVu0_-BhKs&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=35)
[036 尚硅谷Vue技术 Vue监测数据的原理 数组](https://www.youtube.com/watch?v=S02BYZO2Hs4&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=36)

### 038 收集表單數據

v-model 配合不同的 input 框會有不同效果

```htmlembedded=
<!-- 實現頁面不跳轉，獲得數據 -->
<!-- 製作一個 form 表單 -->
<!-- 避免彈窗-->
<form @submit.prevent="demo"> 
    
    <label for="account_id">帳號:</label>
    <input type="text" id="account_id" v-model="userinfo.account">
    
    Password: <input type="password" v-model="userinfo.password">
    
    Age: <input type="number" v-model.number="userinfo.age">
    
    Sex: 
         男 <input type="text" name="sex" v-model="userinfo.sex" value="male">
         女 <input type="text" name="sex" v-model="userinfo.sex" value="female">
    
    hobby:
         學習 <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"> 
    學校
        <select v-model="userinfo.school">
            <option value="請選擇學校"></option>
            <option value="schoolA">school A</option>
            <option value="schoolB">school B</option>
        </select>
    其他訊息:
        <textarea v-model.lazy="userinfo.other"></textarea>
        <input type="checkbox" v-model="userinfo.accept"> 閱讀並接受 <a href="https:">用戶協議</a>
    
    <button>提交</button>
</form>
```
使用 Vue 收集表單數據
```javascript=

const vm = new Vue({
    el:"root"           
    data:{
       userinfo:{
        account:"",
           password:"",
           sex:"male",
           age:"",
           hobby:[], // 用 list 收集多組的check 框
           school:"",
           other:"",
           accept:"", // checkbox 不寫 value，預設用checked 取得 true false 
       }         
    }
   methods:{
       demo(){
           alter(1)
           // submit 後使用 JSON 傳遞數據
           console.log(JSON.stringify(this.userinfo))
       }
   }
})

```

* v-model.number 可以轉換成數值
* v-model.lazy 失去焦點的一瞬間，取得數據
* v-model.trim 去除前後空格
* v-model 收集 input 框的 value 
* check box 如果沒有配置 value，則收集 checked True False
* check box 可以收集成數組

[038 尚硅谷Vue技术 收集表单数据](https://www.youtube.com/watch?v=qjyjMIi61yI&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=38)

### 039 過濾器

```htmlmixed=
<script type="text/javascript" src="dayjs.min.js"></script>


<h2>顯示格式化後的時間</h2>
<!--  計算屬性實現 -->
<h3>現在的時間是{{fmtTime}}</h3>
<!--  方法實現  -->
<h3>現在的時間是{{getFmtTime()}}</h3>
<!--  過濾器實現  -->
<h3>現在的時間是{{time|timeFormater|sliceString}}</h3>
<h3 :x="msg|sliceString ">現在的時間是{{time|timeFormater('YYYY年MM月DD日')}}</h3>
                                    
// 配置全局過濾器
Vue.filter('sliceString' function(value){
    return ...
})

computed: {
    fmtTime(){
        return dayjs(this.time).format('YYYY年MM月DD日')                                            
    }                                       
}
methods:{
    getFmtTime(){
        return dayjs(this.time).format('YYYY年MM月DD日')
    }                                                
}
filters:{
    // 可以對數據進行加工
    // 過濾器可以傳參數
    // 過濾器可以疊加
    // 局部的過濾器
    timeFormater(value,str='YYYY年MM月DD日'){
        return dayjs(value).format(str)                     
    }
    sliceString(value){
        return value.slice(0,4)                                
    }
}


```
* bootCDN可以獲取開源 CDN 
* 過濾器對於顯示的數據進行格式化輸出
* 處理簡單的數據
* 可以用全局,局部註冊過濾器
* 過濾器可以接受而外的變數,過濾器可以串連
* 過濾器沒有改變原有的數據

[039 尚硅谷Vue技术 过滤器](https://www.youtube.com/watch?v=SuuEvy-7pVU&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=39)

### 040-043 內置指令

* 學過的指令 v-on v-bind... 
* v-text：向其所在的節點渲染文本內容
```htmlmixed!
// 取得name,修改內容
<div v-text="name"></div>

data:{
    name:"000"
}
```
* v-html: 支持結構解析
* 但是不要用這個方式, 危險,容易導致XSS攻擊
* 不要在用戶提交的地方使用
```htmlmixed!
// 取得name,修改內容
<div v-html="name"></div>

data:{
    name:"<h3>123456</h3>"
}
```

* cookie 登入驗證原理
* cookie 保存登入訊息
* document.cookie 可以獲取 cookie內容
![](https://hackmd.io/_uploads/B1oLn4s0n.png)
---
* v-cloak 延遲加載
* JS 阻塞 CDN慢 導致後面無法加載
* 怎麼讓頁面不先展示 {{...}}
* v-clock是一種特殊的標籤屬性
```htmlembedded!
<style>
    [v-cloak]{
        display:none;
    }
</style>
<h2 v-cloak>{{...}}</h2>
// vue 一旦接管後,移除 v-cloak
```
* v-once 所在的節點,初次動態渲染後,就視為靜態內容
* 以後數據的變動不引起 v-once結構的更新,優化效能
```htmlembedded
<p v-once>初始化的 N:{n}</p> //不會被變動
<p>當前的 N:{n}</p>

<button @click="n++"></button>
```

* v-pre 可以跳過所在節點的編譯
* vue 就不去解析了
* 可以利用他跳過不需要編譯的節點,加快編譯
```htmlembedded
<p v-pre>初始化的 N:{n}</p> //不會被變動
<p v-pre>當前的 N:{n}</p>
<p v-pre>當前的１２３４５６</p> //這邊加入,就跳過編譯

<button v-pre @click="n++"></button>
```

[040 尚硅谷Vue技术 v text指令](https://www.youtube.com/watch?v=vCDl0XkkZzQ&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=40)
[041 尚硅谷Vue技术 v html指令](https://www.youtube.com/watch?v=t0qRNzihLtM&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=41)
[042 尚硅谷Vue技术 v cloak指令](https://www.youtube.com/watch?v=MQv6nTLkabg&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=42)
[043 尚硅谷Vue技术 v once指令](https://www.youtube.com/watch?v=cm2x6lx--Cc&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=43)
[044 尚硅谷Vue技术 v pre指令](https://www.youtube.com/watch?v=ZpUFYNbsvOY&list=PLmOn9nNkQxJEARHuEpVayY6ppiNlkvrnb&index=44)

### 053 組件化編程

1. 傳統編程方式：代碼複用率不高、依賴關係混雜

```mermaid
graph TD;

HTML1--> JS1;
HTML1--> JS2;
HTML1--> JS3;
HTML1--> JS4;
HTML1--> JS5;

HTML1--> CSS1;
HTML1--> CSS2;
HTML1--> CSS3;
HTML2--> CSS1;
HTML2--> CSS2;
HTML2--> CSS3;

HTML2--> JS1;
HTML2--> JS2;
HTML2--> JS3;
HTML2--> JS4;
HTML2--> JS5;
```

2. 組件化編程方式：可以用組合的方式構成
3. 組件定義：實現應用中==局部==功能的代碼(html css js)和資源(mp3 tif zip..)的==集合==

```mermaid
graph TD;

HTML-->Header;
HTML-->Navbar;
HTML-->Footer;

Header-->Header.html
Header-->Header.css
Header-->Header.js

Navbar-->Navbar.html
Navbar-->Navbar.css
Navbar-->Navbar.js

Footer-->Footer.html
Footer-->Footer.css
Footer-->Footer.js
```