# 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>當前的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 ```