# 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的實例對象

* 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中的數據
```

* 驗證數據綁定中的 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內容

---
* 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>當前的123456</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
```