Vue全套視頻教程vite版,Vue2+Vue3快速上手,一套通透

老杜Vue學(xué)習(xí)筆記
第二章 Vue 核心技術(shù)
2.1 事件處理
2.1.1 事件處理的核心語法
(1) 指令的語法格式:<標(biāo)簽 v-指令名:參數(shù)=”表達(dá)式”></標(biāo)簽>
(2) 事件綁定的語法格式:v-on:事件名。例如鼠標(biāo)單擊事件的綁定使用 v-on:click。
(3) 綁定的回調(diào)函數(shù)需要在 Vue 實例中使用 methods 進(jìn)行注冊。methods 可以配置多個回調(diào)函數(shù),采用逗號隔開。
(4) 綁定回調(diào)函數(shù)時,如果回調(diào)函數(shù)沒有參數(shù),( )可以省略。
(5) 每一個回調(diào)函數(shù)都可以接收一個事件對象 event。
(6) 如果回調(diào)函數(shù)有參數(shù),并且還需要獲取事件對象,可以使用$event 進(jìn)行占位。
(7) v-on:click 可以簡寫為@click。簡寫的語法格式:@事件名
(8) 回調(diào)函數(shù)中的 this 是 vm。如果回調(diào)函數(shù)是箭頭函數(shù)的話,this 是 window 對象,因為箭頭函數(shù)沒有自己的 this,它的 this 是繼承過來的,默認(rèn)這個 this 是箭頭函數(shù)所在的宿主對象。這個宿主對象其實就是它的父級作用域。
而對象又不能構(gòu)成單獨的作用域,所以這個父級作用域是全局作用域,也就是 window。
(9) 回調(diào)函數(shù)并沒有在 vm 對象上,為什么通過 vm 可以直接調(diào)用函數(shù)呢?嘗試手寫 Vue 框架。
(10) 可以在函數(shù)中改變 data 中的數(shù)據(jù),例如:this.counter++,這樣會聯(lián)動頁面上產(chǎn)生動態(tài)效果。
2.1.2 事件修飾符
(1) .stop - 調(diào)用 event.stopPropagation()。
1. <div @click="san">
2.<div @click.stop="er">
3.<button @click="yi">{{name}}</button>
4.</div>
5. </div>
(2) .prevent - 調(diào)用 event.preventDefault()。
1. <a href="http://www.bjpowernode.com" @click.prevent="yi">
2. {{name}}
3. </a>
(3) .capture - 添加事件偵聽器時使用 capture 模式。
1. <div @click.capture="san">
2.<div @click.capture="er">
3.<button @click="yi">{{name}}</button>
4.</div>
5. </div>
注意:只有添加了 capture 修飾符的元素才會采用捕獲模式。(或者說帶有 capture 修飾符的優(yōu)先觸發(fā))
(4) .self - 只當(dāng)事件是從偵聽器綁定的元素本身觸發(fā)時才觸發(fā)回調(diào)。
1. <div @click="san">
2.<div @click.self="er">
3.<button @click="yi">{{name}}</button>
4.</div>
5. </div>
(5) .once - 只觸發(fā)一次回調(diào)。
1. <button @click.once="yi">
2.{{name}}
3. </button>
(6) .passive - (2.3.0) 以 { passive: true } 模式添加偵聽器
①無需等待,直接繼續(xù)(立即)執(zhí)行事件默認(rèn)行為。(對 wheel 事件有效果)
②.passive 和 .prevent 修飾符不能共存。
2.1.3 按鍵修飾符
1. 常用的按鍵修飾符包括:
(1) .enter
(2) .tab (只能配合 keydown 使用)
(3) .delete (捕獲“刪除”和“退格”鍵)
(4) .esc
(5) .space
(6) .up
(7) .down
(8) .left
(9) .right
2. 可以直接將 KeyboardEvent.key 暴露的任意有效按鍵名轉(zhuǎn)換為 kebab-case 來作為修飾符。
<input type=”text” @keyup.page-down=”getInfo”>
3. 可以通過全局 config.keyCodes 對象自定義按鍵修飾符別名
Vue.config.keyCodes.huiche = 13
2.1.4 系統(tǒng)修飾鍵
1. 系統(tǒng)修飾鍵包括 4 個
(1) .ctrl
(2) .alt
(3) .shift
(4) .meta
2. 系統(tǒng)修飾鍵在使用時應(yīng)注意:
(1) 只有當(dāng)系統(tǒng)修飾鍵和其他鍵組合使用,并且組合鍵釋放時,才會觸發(fā) keyup 事件。
(2) 只要按下系統(tǒng)修飾鍵,就會觸發(fā) keydown 事件。
3. 小技巧
(1) <input type=”text” @keyup.ctrl.c=”getInfo”/>
2.2 計算屬性
1. 案例:用戶輸入信息,然后翻轉(zhuǎn)用戶輸入的字符串。
(1) 插值語法可以實現(xiàn),但是有三個問題
1、代碼可讀性差
2、代碼不可復(fù)用
3、代碼難以維護(hù)
(2) 可以使用 methods 方式實現(xiàn),存在 1 個問題
1、效率低,即使數(shù)據(jù)沒有發(fā)生變化,但每一次仍然會調(diào)用 method。
(3) 使用計算屬性可以解決以上問題。
2. 什么是計算屬性?
data 中的是屬性。用 data 的屬性經(jīng)過計算得出的全新的屬性就是計算屬性。
3. 計算屬性的使用
1. <div id="app">
2.<h1>{{msg}}</h1>
3.輸入的信息:<input type="text" v-model="info"><br>
4.反轉(zhuǎn)的信息:{{reversedInfo}} <br>
5.反轉(zhuǎn)的信息:{{reversedInfo}} <br>
6.反轉(zhuǎn)的信息:{{reversedInfo}} <br>
7.反轉(zhuǎn)的信息:{{reversedInfo}} <br>
8. </div>
9. <script>
10.const vm = new Vue({
11.el : '#app',
12.data : {
13.msg : '計算屬性-反轉(zhuǎn)字符串案例',
14.info : ''
15.},
16.computed : {
17.reversedInfo:{
18.get(){
19.console.log('getter 被調(diào)用了');
20.return this.info.split('').reverse().join('')
21.},
22.set(val){
23.//this.reversedInfo = val // 不能這樣做,這樣會導(dǎo)致無限遞歸
24.this.info = val.split('').reverse().join('')
25.}
26.}
27.}
28.})
29. </script>
(1) 計算屬性需要使用:computed
(2) 計算屬性通過 vm.$data 是無法訪問的。計算屬性不能通過 vm.$data 訪問。
(3) 計算屬性的 getter/setter 方法中的 this 是 vm。
(4) 計算屬性的 getter 方法的調(diào)用時機(jī):
①第一個時機(jī):初次訪問該屬性。
②第二個時機(jī):計算屬性所依賴的數(shù)據(jù)發(fā)生變化時。
(5) 計算屬性的 setter 方法的調(diào)用時機(jī):
①當(dāng)計算屬性被修改時。(在 setter 方法中通常是修改屬性,因為只有當(dāng)屬性值變化時,計算屬性的值就會聯(lián)動更新。注意:計算屬性的值被修改并不會聯(lián)動更新屬性的值。)
(6) 計算屬性沒有真正的值,每一次都是依賴 data 屬性計算出來的。
(7) 計算屬性的 getter 和 setter 方法不能使用箭頭函數(shù),因為箭頭函數(shù)的 this 不是 vm。而是 window。
4. 計算屬性的簡寫形式
只考慮讀取,不考慮修改時,可以啟用計算屬性的簡寫形式。
1. computed : {
2.reversedInfo(){
3.console.log('getter 被調(diào)用了');
4.return this.info.split('').reverse().join('')
5.}
6. }
2.3 偵聽屬性的變化
1. 偵聽屬性的變化其實就是監(jiān)視某個屬性的變化。當(dāng)被監(jiān)視的屬性一旦發(fā)生改變時,執(zhí)行某段代碼。
2. 監(jiān)視屬性變化時需要使用 watch 配置項。
使用 watch 實現(xiàn):比較數(shù)字大小的案例
1. <div id="app">
2.<h1>{{msg}}</h1>
3.數(shù)值 1:<input type="text" v-model="number1"><br>
4.數(shù)值 2:<input type="text" v-model="number2"><br>
5.比較大小:{{compareResult}}
6. </div>
7. <script>
8.const vm = new Vue({
9.el : '#app',
10.data : {
11.msg : '偵聽屬性的變化',
12.number1 : 1,
13.number2 : 1,
14.compareResult : ''
15.},
16.watch : {
17.number1 : {
18.immediate : true,
19.handler(newVal, oldVal){
20.let result = newVal - this.number2
21.if(result > 0){
22.this.compareResult = newVal + '>' + this.number2
23.}else if(result < 0){
24.this.compareResult = newVal + '<' + this.number2
25.}else{
26.this.compareResult = newVal + '=' + this.number2
27.}
28.}
29.},
30.number2 : {
31.immediate : true,
32.handler(newVal, oldVal){
33.let result = this.number1 - newVal
34.if(result > 0){
35.this.compareResult = this.number1 + '>' + newVal
36.}else if(result < 0){
37.this.compareResult = this.number1 + '<' + newVal
38.}else{
39.this.compareResult = this.number1 + '=' + newVal
40.}
41.}
42.}
43.}
44.})
45. </script>
運行效果:



3. 如何深度監(jiān)視:
(1) 監(jiān)視多級結(jié)構(gòu)中某個屬性的變化,寫法是:’a.b.c’ : {}。注意單引號哦。
(2) 監(jiān)視多級結(jié)構(gòu)中所有屬性的變化,可以通過添加深度監(jiān)視來完成:deep : true
4. 如何后期添加監(jiān)視:
(1) 調(diào)用 API:vm.$watch(‘number1’, {})
5. watch 的簡寫:
(1) 簡寫的前提:當(dāng)不需要配置 immediate 和 deep 時,可以簡寫。
(2) 如何簡寫?
①watch:{ number1(newVal,oldVal){}, number2(newVal, oldVal){} }
(3) 后期添加的監(jiān)視如何簡寫?
①vm.$watch(‘number1’, function(newVal, oldVal){})
6. computed 和 watch 如何選擇?
(1) 以上比較大小的案例可以用 computed 完成,并且比 watch 還要簡單。所以要遵守一個原則:computed
和 watch 都能夠完成的,優(yōu)先選擇 computed。
(2) 如果要開啟異步任務(wù),只能選擇 watch。因為 computed 依靠 return。watch 不需要依賴 return。
7. 關(guān)于函數(shù)的寫法,寫普通函數(shù)還是箭頭函數(shù)?
(1) 不管寫普通函數(shù)還是箭頭函數(shù),目標(biāo)是一致的,都是為了讓 this 和 vm 相等。
(2) 所有 Vue 管理的函數(shù),建議寫成普通函數(shù)。
(3) 所有不屬于 Vue 管理的函數(shù),例如 setTimeout 的回調(diào)函數(shù)、Promise 的回調(diào)函數(shù)、AJAX 的回調(diào)函數(shù),
建議使用箭頭函數(shù)。
2.4 class 與 style 綁定
數(shù)據(jù)綁定的一個常見需求場景是操縱元素的 CSS class 列表和內(nèi)聯(lián)樣式。因為 class 和 style 都是 attribute,我們
可以和其他 attribute 一樣使用 v-bind 將它們和動態(tài)的字符串綁定。但是,在處理比較復(fù)雜的綁定時,通過拼接
生成字符串是麻煩且易出錯的。因此,Vue 專門為 class 和 style 的 v-bind 用法提供了特殊的功能增強(qiáng)。除了字
符串外,表達(dá)式的值也可以是對象或數(shù)組。
2.4.1 class 綁定
2.4.1.1 綁定字符串
適用于樣式的名字不確定,需要動態(tài)指定。
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.<meta charset="UTF-8">
5.<title>class 綁定字符串形式</title>
6.<script src="../js/vue.js"></script>
7.<style>
8..static{
9.border: 1px solid black;
10.background-color: beige;
11.}
12..big{
13.width: 200px;
14.height: 200px;
15.}
16..small{
17.width: 100px;
18.height: 100px;
19.}
20.</style>
21.</head>
22.<body>
23.<div id="app">
24.<h1>{{msg}}</h1>
25.<div class="static" :class="c1"></div>
26.</div>
27.<script>
28.const vm = new Vue({
29.el : '#app',
30.data : {
31.msg : 'class 綁定字符串形式',
32.c1 : 'small'
33.}
34.})
35.</script>
36.</body>
37.</html>
運行效果:

使用 vue 開發(fā)者工具修改 c1 的 small 為 big:

通過測試可以看到樣式完成了動態(tài)的切換。
2.4.1.2 綁定數(shù)組
適用于綁定的樣式名字不確定,并且個數(shù)也不確定。
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.<meta charset="UTF-8">
5.<title>class 綁定數(shù)組形式</title>
6.<script src="../js/vue.js"></script>
7.<style>
8..static{
9.border: 1px solid black;
10.}
11..active{
12.background-color: green;
13.}
14..text-danger{
15.color: red;
16.}
17.</style>
18.</head>
19.<body>
20.<div id="app">
21.<h1>{{msg}}</h1>
22.<div class="static" :class="['active','text-danger']">
數(shù)組形式</div>
23.<br><br>
24.<div class="static" :class="[activeClass,errorClass]">
數(shù)組形式</div>
25.<br><br>
26.<div class="static" :class="classArray">數(shù)組形式</div>
27.</div>
28.<script>
29.const vm = new Vue({
30.el : '#app',
31.data : {
32.msg : 'class 綁定數(shù)組形式',
33.activeClass : 'active',
34.errorClass : 'text-danger',
35.classArray : ['active', 'text-danger']
36.}
37.})
38.</script>
39.</body>
40.</html>
運行效果:

使用 vue 開發(fā)者工具刪除數(shù)組中的一個樣式:

2.4.1.3 綁定對象
適用于樣式名字和個數(shù)都確定,但是要動態(tài)決定用或者不用。
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.<meta charset="UTF-8">
5.<title>class 綁定對象形式</title>
6.<script src="../js/vue.js"></script>
7.<style>
8..static{
9.border: 1px solid black;
10.}
11..active{
12.background-color: green;
13.}
14..text-danger{
15.color: red;
16.}
17.</style>
18.</head>
19.<body>
20.<div id="app">
21.<h1>{{msg}}</h1>
22.<div class="static" :class="{active : true, 'text-dang
er' : true}">對象形式</div>
23.<br><br>
24.<div class="static" :class="classObject">對象形式
</div>
25.</div>
26.<script>
27.const vm = new Vue({
28.el : '#app',
29.data : {
30.msg : 'class 綁定對象形式',
31.classObject : {
32.active : true,
33.text-danger' : false
34.}
35.}
36.})
37.</script>
38.</body>
39.</html>
運行效果:

使用 vue 開發(fā)者工具修改 text-danger 為 true:

2.4.2 style 綁定
2.4.2.1 綁定對象
1. <div id="app">
2.<h1>{{msg}}</h1>
3.<!-- 靜態(tài)寫法 -->
4.<div class="static" style="font-size: 20px;">對象形式
</div><br><br>
5.<!-- 動態(tài)寫法 1 -->
6.<div class="static" :style="{fontSize: 40 + 'px'}">對象形式
</div><br><br>
7.<!-- 動態(tài)寫法 2 -->
8.<div class="static" :style="styleObject">對象形式
</div><br><br>
9. </div>
10.<script>
11.const vm = new Vue({
12.el : '#app',
13.data : {
14.msg : 'style 綁定對象形式',
15.styleObject : {
16.fontSize : '40px'
17.}
18.}
19.})
20.</script>
2.4.2.2 綁定數(shù)組
1. <div id="app">
2.<h1>{{msg}}</h1>
3.<!-- 靜態(tài)寫法 -->
4.<div class="static" style="font-size: 40px; color: red;">數(shù)
組形式</div><br><br>
5.<!-- 動態(tài)寫法 1 -->
6.<div class="static" :style="[{fontSize:'40px'},{color:'red
'}]">數(shù)組形式</div><br><br>
7.<!-- 動態(tài)寫法 2 -->
8.<div class="static" :style="styleArray">對象形式
</div><br><br>
9. </div>
10.<script>
11.const vm = new Vue({
12.el : '#app',
13.data : {
14.msg : 'style 綁定對象形式',
15.styleArray : [
16.{fontSize:'40px'},
17.{color:'red'}
18.]
19.}
20.})
21.</script>
2.5 條件渲染
2.5.1 v-if
指令用于條件性地渲染一塊內(nèi)容。這塊內(nèi)容只會在指令的表達(dá)式返回 true 時才被渲染
1. <div id="app">
2.<h1>{{msg}}</h1>
3.溫度:<input type="number" v-model="temprature"><br>
4.天氣:
5.<span v-if="temprature <= 10">寒冷</span>
6.<span v-if="temprature > 10 && temprature <= 25">涼爽
</span>
7.<span v-if="temprature > 25">炎熱</span>
8. </div>
9. <script>
10.const vm = new Vue({
11.el : '#app',
12.data : {
13.msg : '條件渲染',
14.temprature : 10
15.}
16.})
17.</script>
運行效果:


2.5.2 v-else-if、v-else
顧名思義,v-else-if 提供的是相應(yīng)于 v-if 的“else if 區(qū)塊”。它可以連續(xù)多次重復(fù)使用。
一個使用 v-else-if 的元素必須緊跟在一個 v-if 或一個 v-else-if 元素后面。
你也可以使用 v-else 為 v-if 添加一個“else 區(qū)塊”,當(dāng)然,v-else 元素也是必須緊跟在一個 v-if 或一個 v-else-if 元素
后面。
1. <div id="app">
2.<h1>{{msg}}</h1>
3.溫度:<input type="number" v-model="temprature"><br>
4.天氣:
5.<span v-if="temprature <= 10">寒冷</span>
6.<span v-else-if="temprature <= 25">涼爽</span>
7.<span v-else>炎熱</span>
8. </div>
9. <script>
10.const vm = new Vue({
11.el : '#app',
12.data : {
13.msg : '條件渲染',
14.temprature : 10
15.}
16.})
17.</script>
2.5.3 <template>與 v-if
因為 v-if 是一個指令,他必須依附于某個元素。但如果我們想要切換不止一個元素呢?在這種情況下我們可以在一個 <template> 元素上使用 v-if,這只是一個不可見的包裝器元素,最后渲染的結(jié)果并不會包含個 <template> 元素。v-else 和 v-else-if 也可以在 <template> 上使用。
1. <div id="app">
2.<h1>{{msg}}</h1>
3.溫度:<input type="number" v-model="temprature"><br>
4.天氣:
5.<template v-if="temprature <= 10">
6.<span>寒冷</span>
7.</template>
8.<template v-else-if="temprature <= 25">
9.<span>涼爽</span>
10.</template>
11.<template v-else>
12.<span>炎熱</span>
13.</template>
14.</div>
15.<script>
16.const vm = new Vue({
17.el : '#app',
18.data : {
19.msg : '條件渲染',
20.temprature : 10
21.}
22.})
23.</script>
2.5.4 v-show
另一個可以用來按條件顯示一個元素的指令是 v-show。其用法基本一樣:
1. <div id="app">
2.<h1>{{msg}}</h1>
3.溫度:<input type="number" v-model="temprature"><br>
4.天氣:
5.<span v-show="temprature <= 10">寒冷</span>
6.<span v-show="temprature > 10 && temprature <= 25">涼爽
</span>
7.<span v-show="temprature > 25">炎熱</span>
8. </div>
9. <script>
10.const vm = new Vue({
11.el : '#app',
12.data : {
13.msg : '條件渲染',
14.temprature : 10
15.}
16.})
17.</script>
不同之處在于 v-show 會在 DOM 渲染中保留該元素;v-show 僅切換了該元素上名為 display 的 CSS 屬性。
v-show 不支持在 <template> 元素上使用,也不能和 v-else 搭配使用。
2.5.5 v-if VS v-show
v-if 是“真實的”按條件渲染,因為它確保了在切換時,條件區(qū)塊內(nèi)的事件監(jiān)聽器和子組件都會被銷毀與重建
v-if 也是惰性的:如果在初次渲染時條件值為 false,則不會做任何事。條件區(qū)塊只有當(dāng)條件首次變?yōu)?true 時才被渲染。
相比之下,v-show 簡單許多,元素?zé)o論初始條件如何,始終會被渲染,只有 CSS display 屬性會被切換。
總的來說,v-if 有更高的切換開銷,而 v-show 有更高的初始渲染開銷。因此,如果需要頻繁切換,則使用 v-show 較好;如果在運行時綁定條件很少改變,則 v-if 會更合適。
2.6 列表渲染
語法格式:v-for 指令。該指令用在被遍歷的標(biāo)簽上。
1. v-for="(element, index) in elements" :key="element.id"
或者
1.v-for="(element, index) of elements" :key="element.id"
2.6.1 遍歷數(shù)組、對象、字符串、指定次數(shù)
1. 遍歷數(shù)組
1. <div id="app">
2.<h1>{{msg}}</h1>
3.<h2>遍歷數(shù)組</h2>
4.<ul>
5.<li v-for="(product, index) in products" :key="product.
id">
6.商品名稱:{{product.name}},單價:{{product.price}}元/
千克,下標(biāo):{{index}}
7.</li>
8.</ul>
9. </div>
10.<script>
11.const vm = new Vue({
12.el : '#app',
13.data : {
14.msg : '列表渲染',
15.products : [
16.{id:'111',name:'西瓜',price:20},
17.{id:'222',name:'蘋果',price:10},
18.{id:'333',name:'香蕉',price:30}
19.]
20.}
21.})
22.</script>
運行效果:

2. 遍歷對象
1. <div id="app">
2.<h1>{{msg}}</h1>
3.<h2>遍歷對象</h2>
4.<ul>
5.<li v-for="(propertyValue, propertyName) of dog" :key=
"propertyName">
6.{{propertyName}}:{{propertyValue}}
7.</li>
8.</ul>
9. </div>
10.<script>
11.const vm = new Vue({
12.el : '#app',
13.data : {
14.msg : '列表渲染',
15.dog : {
16.name : '拉布拉多',
17.age : 3,
18.gender : '雄性'
19.}
20.}
21.})
22.</script>
運行結(jié)果:

3. 遍歷字符串
1. <div id="app">
2.<h1>{{msg}}</h1>
3.<h2>遍歷字符串</h2>
4.<ul>
5.<li v-for="char,index of str" :key="index">
6.{{index}}:{{char}}
7.</li>
8.</ul>
9. </div>
10.<script>
11.const vm = new Vue({
12.el : '#app',
13.data : {
14.msg : '列表渲染',
15.str : '動力節(jié)點'
16.}
17.})
18.</script>
運行結(jié)果:

4. 遍歷指定次數(shù)
1. <div id="app">
2<h1>{{msg}}</h1>
3.<h2>遍歷指定次數(shù)</h2>
4.<ul>
5.<li v-for="number,index of 10" :key="index">
6.下標(biāo):{{index}},數(shù)字:{{number}}
7.</li>
8.</ul>
9. </div>
10.<script>
11.const vm = new Vue({
12.el : '#app',
13.data : {
14.msg : '列表渲染'
15.}
16.})
17.</script>
運行結(jié)果:

2.6.2 虛擬 dom 和 diff 算法
所謂的虛擬 dom 就是內(nèi)存當(dāng)中的 dom 對象。vue 為了提高渲染的效率,只有真正改變的 dom 元素才會重新渲染。

2.6.3 v-for 的 key 的作用以及實現(xiàn)原理
1. 用 index 作為 key
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.<meta charset="UTF-8">
5.<title>key 的原理</title>
6.<script src="../js/vue.js"></script>
7. </head>
8. <body>
9.<div id="app">
10.<h1>{{msg}}</h1>
11.<button @click="addFirst">在數(shù)組第一個位置添加 tom</button>
12.<button @click="addLast">在數(shù)組最后位置添加 vue</button>
13.<table>
14.<tr>
15.<th>序號</th>
16.<th>姓名</th>
17.<th>郵箱</th>
18.<th>選擇</th>
19.</tr>
20.<tr v-for="(vip,index) of vips" :key="index">
21.<td>{{index + 1}}</td>
22.<td>{{vip.name}}</td>
23.<td>{{vip.email}}</td>
24.<td><input type="checkbox"></td>
25.</tr>
26.</table>
27.</div>
28.<script>
29.const vm = new Vue({
30.el : '#app',
31.data : {
32.msg : 'key 原理(虛擬 dom 與 diff 算法)',
33.vips : [
34.{id:'100',name:'jack',email:'jack@123.com'},
35.{id:'200',name:'lucy',email:'lucy@123.com'},
36.{id:'300',name:'james',email:'james@123.com'}
37.]
38.},
39.methods : {
40.addFirst(){
41.this.vips.unshift({id:'400',name:'tom',email:'tom@123.com'})
42.},
43.addLast(){
44.this.vips.push({id:'500',name:'vue',email:'vue@123.com'})
45.}
46.}
47.})
48.</script>
49. </body>
50. </html>
運行效果:

全部選中:

添加 tom:

可以看到錯亂了。思考這是為什么?
2. 用 vip.id 作為 key
運行和測試結(jié)果正常,沒有出現(xiàn)錯亂。為什么?

3. key 的作用
key 存在于虛擬 dom 元素中,代表該虛擬 dom 元素的唯一標(biāo)識(身份證號)。
4. diff 算法是如何比較的?
新的虛擬 dom 和舊的虛擬 dom 比較時,先拿 key 進(jìn)行比較:
(1) 如果 key 相同:則繼續(xù)比較子元素:
①子元素不同:直接將新的虛擬 dom 元素渲染到頁面生成新的真實 dom 元素。
②子元素相同:直接復(fù)用之前的真實 dom。
(2) 如果 key 不同:直接將新的虛擬 dom 元素渲染到頁面生成新的真實 dom 元素。
5. index 作為 key 存在兩個問題
(1) 效率較低。
(2) 對數(shù)組的非末尾元素進(jìn)行增刪時,容易錯亂。
6. index 作為 key 和 vip.id 作為 key 對比
當(dāng) index 作為 key 時:

當(dāng) vip.id 作為 key 時:

2.7 列表過濾
使用 watch 和 computed 分別進(jìn)行實現(xiàn):
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.<meta charset="UTF-8">
5.<title>列表過濾</title>
6.<script src="../js/vue.js"></script>
7.<style>
8.table,tr,th,td{
9.border: 1px solid blue;
10.}
11.</style>
12.</head>
13.<body>
14.<div id="app">
15.<h1>{{msg}}</h1>
16.<input type="text" placeholder="請輸入搜索關(guān)鍵詞
" v-model="keyword">
17.<table>
18.<tr>
19.<th>序號</th>
20.<th>姓名</th>
21.<th>郵箱</th>
22.</tr>
23.<tr v-for="(vip,index) of filterVips" :key="vip.id
">
24.<td>{{index+1}}</td>
25.<td>{{vip.name}}</td>
26.<td>{{vip.email}}</td>
27.</tr>
28.</table>
29.</div>
30.<script>
31.const vm = new Vue({
32.el : '#app',
33.data : {
34.keyword : '',
35.msg : '列表過濾',
36.vips : [
37.{id:'100',name:'jack',email:'jack@123.com'}
,
38.{id:'200',name:'lucy',email:'lucy@123.com'}
,
39.{id:'300',name:'james',email:'james@123.co
m'}
40.],
41.//filterVips : []
42.},
43./* watch : {
44.keyword : {
45.immediate : true,
46.handler(newValue, oldValue){
47.this.filterVips = this.vips.filter((v)
=> {
48.return v.name.indexOf(newValue) >=
0
49.})
50.}
51.}
52.}, */
53.computed : {
54.filterVips(){
55.return this.vips.filter((v) => {
56.return v.name.indexOf(this.keyword) >=
0
57.})
58.}
59.}
60.})
61.</script>
62.</body>
63.</html>
2.8 列表排序
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.<meta charset="UTF-8">
5.<meta http-equiv="X-UA-Compatible" content="IE=edge">
6.<meta name="viewport" content="width=device-width, initial-scale=1.0">
7.<title>列表排序</title>
8.<script src="../js/vue.js"></script>
9.<style>
10.table,tr,td,th{
11.border:1px solid black;
12.}
13.</style>
14. </head>
15. <body>
16.<div id="app">
17.<h1>{{msg}}</h1>
18.<input type="text" placeholder="輸入關(guān)鍵字搜索" v-model="keyword"><br>
19.<button @click="type = 1">按照名字升序</button><br>
20.<button @click="type = 2">按照名字降序</button><br>
21.<button @click="type = 0">按照名字原始順序</button><br>
22.<table>
23.<tr>
24.<th>序號</th>
25.<th>姓名</th>
26.<th>郵箱</th>
27.<th>操作</th>
28.</tr>
29.<tr v-for="(vip, index) in filterVips" :key="vip.id">
30.<td>{{index+1}}</td>
31.<td>{{vip.name}}</td>
32.<td>{{vip.email}}</td>
33.<td><input type="checkbox"></td>
34.</tr>
35.</table>
36.</div>
37.<script>
38.const vm = new Vue({
39.el : '#app',
40.data : {
41.msg : '列表排序',
42.vips : [
43.{id:'100',name:'jack',email:'jack@123.com'},
44.{id:'200',name:'lucy',email:'lucy@123.com'},
45.{id:'300',name:'james',email:'james@123.com'},
46.{id:'400',name:'lilei',email:'lilei@123.com'},
47.],
48.keyword : '',
49.type : 0
50.},
51.computed : {
52.filterVips(){
53.// 篩選
54.let arr = this.vips.filter((vip) => {
55.return vip.name.indexOf(this.keyword) >= 0
56.})
57.// 根據(jù)排序類型進(jìn)行排序
58.if(this.type){
59.arr.sort((v1, v2) => {
60.console.log('@')
61.return this.type == 1 ? v1.name.localeCompare(v2.name) : v2.nam
e.localeCompare(v1.name)
62.})
63.}
64.// 返回
65.return arr
66.}
67.}
68.})
69.</script>
70. </body>
71.</html>