【前端 Vue】02 - 計(jì)算屬性 + 事件綁定 + v-for + v-if + v-show

1. 計(jì)算屬性

1.1 什么是計(jì)算屬性 ?

  1. 我們知道,在模板中可以直接通過插值語法顯示一些data中 的數(shù)據(jù)。但是在某些情況,我們可能需要對(duì)數(shù)據(jù)進(jìn)行一些轉(zhuǎn)化后再顯示,或者需要將多個(gè)數(shù)據(jù)結(jié)合起來進(jìn)行顯示。比如我們有firstNamelastName 兩個(gè)變量,我們需要顯示完整的名稱。但是如果多個(gè)地方都需要顯示完整的名稱,我們就需要寫多個(gè){{firstName}} {{lastName}}。

  2. 我們可以將上面的代碼換成計(jì)算屬性:我們發(fā)現(xiàn)計(jì)算屬性是寫在實(shí)例的 computed 選項(xiàng)中的。

<div id="app">

  <h2>{{firstName + ' ' + lastName }}</h2>
  <h2>{{firstName}} {{lastName }}</h2>
  <h2>{{getFullName()}}</h2>
  <!-- 計(jì)算屬性在使用的時(shí)候不需要加上括號(hào)  -->
  <h2>{{fullName}}</h2>

</div>

<script src="../../js/vue.js"></script>
const app = new Vue({
       el: '#app',
       data: {
           firstName: 'Lebron',
           lastName: 'James'
       },
       methods: {
           getFullName() {
               return this.firstName + '  ' + this.lastName;
           }
       },
       /* 計(jì)算屬性 */
       computed: {
           // 計(jì)算屬性一般不加動(dòng)詞 get 類似這些
     fullName() {
       return this.firstName + '  ' + this.lastName;
     }
   }
});
三種渲染方式

1.2 使用計(jì)算屬性計(jì)算圖書總價(jià)格

  1. 在計(jì)算圖書總價(jià)的時(shí)候盡量使用計(jì)算屬性的方式,計(jì)算屬性有緩存功能,在值不改變的時(shí)候他只會(huì)計(jì)算一次。

  2. 而使用方法返回值的方式,如果其中出現(xiàn)循環(huán)的時(shí)候就會(huì)降低計(jì)算效率。

<div id="app">
  <h2>總價(jià)格:{{totalPrice}}</h2>
</div>

<!-- 計(jì)算屬性在多次調(diào)用的時(shí)候只會(huì)調(diào)用一次 -->
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            books: [
                {id: '110', name: 'Unix編程藝術(shù)', price: 120},
                {id: '111', name: '代碼大全', price: 120},
                {id: '112', name: '深入理解計(jì)算機(jī)原理', price: 120},
                {id: '112', name: 'Unix編程藝術(shù)', price: 120},
                {id: '112', name: '現(xiàn)代操作系統(tǒng)', price: 120}
            ]
        },
        computed: {
            totalPrice() {
                let result = 0;
                for (let item of this.books) {
                    result += item.price;
                }
                return result;
            }
        }
    });

1.3 計(jì)算屬性的 setter 和 getter

  1. 每個(gè)計(jì)算屬性都包含一個(gè) getter 和一個(gè) setter ,在獲取值時(shí)都是使用計(jì)算屬性的getter方法讀取,在某些情況下,你可以提供一個(gè)setter方法(不常用)。
<div id="app">
  <h2>{{fullName}}</h2>
  <h2>{{fullNameSetAndGet}}</h2>
</div>

<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            firstName: 'Kobe',
            lastName: 'Bryant'
        },
        computed: {
            fullName() {
                return this.firstName + '  ' + this.lastName;
            },

            fullNameSetAndGet: {
                // set 方法一般是被刪除的
                /**
                 *
                 * @param newVal
                 */
                set(newVal) {
                    console.log('set方法');
                    console.log(newVal);
                    const names = newVal.split(' ');
                    this.firstName = names[0];
                    this.lastName = names[1];
                },
                get() {
                    return this.firstName + '  ' + this.lastName;
                }
            }

        }
    });

1.4 計(jì)算屬性的緩存

  1. 計(jì)算屬性會(huì)進(jìn)行緩存,如果多次使用時(shí),計(jì)算屬性只會(huì)調(diào)用一次。如果值發(fā)生改變才進(jìn)行重新計(jì)算。
<div id="app">
  <!-- 1. 直接拼接 -->
  <h2>{{firstName}}  {{lastName}}</h2>

  <!-- 2. 通過定義methods-->
  <!--<h2>{{getFullName()}}</h2>-->
  <!--<h2>{{getFullName()}}</h2>-->
  <!--<h2>{{getFullName()}}</h2>-->
  <!--<h2>{{getFullName()}}</h2>-->

  <!-- 3. 通過computed -->
  <h2>{{fullName}}</h2>
  <h2>{{fullName}}</h2>
  <h2>{{fullName}}</h2>
  <h2>{{fullName}}</h2>
</div>

<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            message: '您好啊!',
            firstName: 'Lebron',
            lastName: 'James'
        },
        methods: {
            getFullName() {
                console.log('getFullName');
                return this.firstName + this.lastName;
      }
    },
        /* 計(jì)算屬性 */
        computed: {
            fullName() {
                console.log('fullName');
                return this.firstName + this.lastName;
             }
    }
    });

2. ES6語法補(bǔ)充

  1. ES6中 ,新出現(xiàn) letconst 關(guān)鍵字,用于代替 var,使用let 聲明變量,使用const 聲明一個(gè)常量。

  2. let 關(guān)鍵字聲明的變量有塊級(jí)作用域,var聲明的變量沒有塊級(jí)作用域。

  3. 在ES6之前 if 和 for代碼塊是沒有塊級(jí)作用域的。在Es6 之前因?yàn)?code>if和for都沒有塊級(jí)作用域所以在很多時(shí)候,我們都必須借助與function的作用域來解決應(yīng)用外邊變量的問題。

2.1 let 和 var 的塊級(jí)作用域

  1. ES6中 使用 let 關(guān)鍵字替換var關(guān)鍵字聲明變量,var 關(guān)鍵字聲明變量沒有塊級(jí)作用域,容易引發(fā)一些問題。
// ==========================================================
    // 塊級(jí)作用域
    {

        var name = 'lyp';
        console.log(name);
    }
    console.log(name); // lyp

    {
        let age = 23;
        console.log(age);
    }
    // console.log(age); // Uncaught ReferenceError: age is not defined
    // ==========================================================
  
  

    // ==========================================================
    // 沒有塊級(jí)作用域引起的問題
    var func;
    if (true) {
        var firstName = 'andy';

        func = function () {
            console.log(firstName);
        }
    }
    // 在這里修改 firstName
    firstName = 'kobe';
    func(); // kobe
    // ==========================================================

2.2 塊級(jí)作用域?qū)?for 循環(huán)的影響

  1. 因?yàn)榛卣{(diào)函數(shù)是一個(gè)異步的操作,導(dǎo)致循環(huán)執(zhí)行完成之后才會(huì)值回調(diào)函數(shù),解決這個(gè)問題可以使用閉包。使用立即函數(shù) + 閉包,因?yàn)楹瘮?shù)擁有自己的作用域。
<button>按鈕1</button>
<button>按鈕2</button>
<button>按鈕3</button>
<button>按鈕4</button>
<button>按鈕5</button>
const btns = document.querySelectorAll('button');
/**
 * ES5中的var是沒有塊級(jí)作用域的
 *
 * 使用閉包 :
 * 為什么閉包可以解決問題 , 因?yàn)楹瘮?shù)是有作用域的
 * javascript 類似 if 和 for 都是沒有作用域的
 * 在Es6之前因?yàn)閕f和for都沒有塊級(jí)作用域所以在很多時(shí)候,我們都必須借助與function的作用域來解決應(yīng)用外邊變量1的問題
 *
 * 在ES6中加入的let是有塊級(jí)作用域的
 */
for (let i = 0; i < btns.length; i++) {
  // 這里面的i使用let聲明有自己的塊級(jí)作用域
  btns[i].addEventListener('click', () => {
    console.log('第' + (i + 1) + '個(gè)按鈕被點(diǎn)擊');
  });
}

2.3 使用let關(guān)鍵字改寫 2.1 的案例解決問題

  1. 因?yàn)?code>let有自己的塊級(jí)作用域。
<button>按鈕1</button>
<button>按鈕2</button>
<button>按鈕3</button>
<button>按鈕4</button>
<button>按鈕5</button>
for (let i = 0; i < btns.length; i++) {
  // 這里面的i使用let聲明有自己的塊級(jí)作用域
  btns[i].addEventListener('click', () => {
    console.log('第' + (i + 1) + '個(gè)按鈕被點(diǎn)擊');
  });
}

2.4 三種方案對(duì)比

  1. 定義多個(gè)按鈕的點(diǎn)擊事件。由于var 沒有塊級(jí)作用域而引發(fā)的問題。有兩種解決方案:
<button>按鈕1</button>
<button>按鈕2</button>
<button>按鈕3</button>
<button>按鈕4</button>
<button>按鈕5</button>
var btns = document.querySelectorAll('button');
    console.log(btns);

    // 1. 不使用閉包的方式
    /*for (var i = 0; i < btns.length; i++) {
        btns[i].addEventListener('click', () => {
            console.log('第' + (i + 1) + '個(gè)按鈕被點(diǎn)擊了!');
        });
    }*/

    // 2. 使用閉包處理
    /*for (var i = 0; i < btns.length; i++) {
        (function (i) {
            btns[i].addEventListener('click', () => {
                console.log('第' + (i + 1) + '個(gè)按鈕被點(diǎn)擊了!');
            });
        })(i)
    }*/


    // 3. 使用let關(guān)鍵字
    for (let i = 0; i < btns.length; i++) {
        btns[i].addEventListener('click', () => {
            console.log('第' + (i + 1) + '個(gè)按鈕被點(diǎn)擊了!');
        });
    }

2.5 const的使用和注意點(diǎn)

  1. 建議 : 在開發(fā)中優(yōu)先使用const只有需要在改變某個(gè)標(biāo)識(shí)符的時(shí)候才使用let。

  2. 常量是不允許被修改的 一旦給const修飾的標(biāo)識(shí)符被賦值之后不能進(jìn)行修改。

  3. 在使用const 定義標(biāo)識(shí)符,必須進(jìn)行賦值。

  4. 常量的含義是指向的對(duì)象不能修改,但是可以改變對(duì)象內(nèi)部的屬性。


    // 建議 : 在開發(fā)中優(yōu)先使用const只有需要在改變某個(gè)標(biāo)識(shí)符的時(shí)候才使用let
    const name = 'sa easy';

    // 1. 常量是不允許被修改的 一旦給const修飾的標(biāo)識(shí)符被賦值之后不能進(jìn)行修改。
    name = 'change';

    // 2. 在使用const定義標(biāo)識(shí)符,必須進(jìn)行賦值
    const name;

    // 3. 常量的含義是指向的對(duì)象不能修改,但是可以改變對(duì)象內(nèi)部的屬性
    const obj = {
        name: 'lyp',
        age: 23,
        height: 181
    };

    obj.height = 192;

    // 不能改變其指向
    obj = 123;

2.6 對(duì)象字面量的增強(qiáng)寫法

/**
     * 對(duì)象字面量寫法
     * @type {{}}
     */
    const objA = {
        name: '張三',
        age: 23,
        run: function () {
            console.log('您在干嘛');
        }
    };


    const name = '王五';
    const age = 25;
    const height = 181;

    /**
     * ES5寫法
     * */
    const objC = {
        name: name,
        age: age,
        height: height
    };

    /**
     * Es6 增強(qiáng)寫法
     * @type {{}}
     */
    const objB = {
        name,
        age,
        height
    }

    // 方法增強(qiáng)寫法
    /**
     * ES5 寫法
     * @type {{run: objD.run}}
     */
    const objD = {
        run: function () {

        }
    }

    /**
     * ES6寫法
     * @type {{}}
     */
    const objE = {
        run() {

        }
    }

3. 事件監(jiān)聽

  1. 使用 v-on 綁定事件監(jiān)聽器,可以縮寫成@ 。 是它的語法糖簡(jiǎn)寫形式。

3.1 v-on 的基本使用

  1. 下面的代碼中,我們使用了 v-on:click="counter++"。另外,我們可以將事件指向一個(gè)在methods中定義的函數(shù)。
<div id="app">
  <h2>{{counter}}</h2>
  <!--<button v-on:click="increment">+</button>-->
  <!--<button v-on:click="decrement">-</button>-->
  <!-- 語法糖簡(jiǎn)寫形式 -->
  <button @click="increment">+</button>
  <button @click="decrement">-</button>
</div>

<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            counter: 0
        },
        methods: {
            increment() {
                this.counter++;
            },
            decrement() {
                this.counter--;
            }
        }
    });

3.2 v-on 的參數(shù)

當(dāng)通過methods 中定義方法,以供@click調(diào)用時(shí),需要注意參數(shù)問題:
  1. 情況一:如果該方法不需要額外參數(shù),那么方法后的()可以不添加。但是注意:如果方法本身中有一個(gè)參數(shù),那么會(huì)默認(rèn)將原生事件event參數(shù)傳遞進(jìn)去。
  1. 情況二:如果需要同時(shí)傳入某個(gè)參數(shù),同時(shí)需要event時(shí),可以通過$event傳入事件。
<!-- 1. 函數(shù)中的默認(rèn)參數(shù)是事件對(duì)象 event -->
  <button @click="btnClick">按鈕1</button>
  <!-- 2. 如果明確的寫出則第一個(gè)參數(shù)就是 -->
  <button @click="btnClick1('abc' , $event)">按鈕2</button>
  <!-- 3. 傳遞的變量不存在而且未使用引號(hào)標(biāo)明 -->
  <button @click="btnClick2(abc , $event)">按鈕3</button>
const app = new Vue({
        el: '#app',
        data: {},
        methods: {
            btnClick(event) {
                console.log(event);// 事件對(duì)象 MouseEvent {isTrusted: true, screenX: 88, screenY: 116, clientX: 88, clientY: 13, …}
            },
            btnClick1(abc, event) {
                console.log(abc, event); // abc MouseEvent {isTrusted: true, screenX: 88, screenY: 116, clientX: 88, clientY: 13, …}
            },
      btnClick2(abc,event) {
                console.log(abc, event); // Property or method "abc" is not defined on the instance
        // undefined MouseEvent {isTrusted: true, screenX: 152, screenY: 119, clientX: 152, clientY: 16, …}
      }
        }
    });

3.3 v-on 的修飾符

  1. 在某些情況下,我們拿到 event 的目的可能是進(jìn)行一些事件處理。
Vue 提供了修飾符來幫助我們方便的處理一些事件:
  1. .stop - 調(diào)用 event.stopPropagation()。

  2. .prevent - 調(diào)用 event.preventDefault()

  3. .{keyCode | keyAlias} - 只當(dāng)事件是從特定鍵觸發(fā)時(shí)才觸發(fā)回調(diào)。

  4. .native - 監(jiān)聽組件根元素的原生事件。

  5. .once - 只觸發(fā)一次回調(diào)。

4. v-if、v-else-if、v-else

  1. 這三個(gè)指令與JavaScript 的條件語句if、else、else if 類似。

  2. Vue的條件指令可以根據(jù)表達(dá)式的值在DOM中渲染或銷毀元素或組件。

v-if 的基本使用
<div id="app">
  <div v-if="isShow">
    <h1>abc</h1>
    <h1>abc</h1>
    <h1>abc</h1>
    <h1>abc</h1>
    <h1>abc</h1>
  </div>
  <div v-else>
    <h1>當(dāng)isShow為false的時(shí)候顯示</h1>
  </div>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            isShow: true
        }
    });
v-if不同分?jǐn)?shù)顯示不同的詞
<div id="app">
  <div>
    <h2 v-if="score>=90">優(yōu)秀</h2>
    <h2 v-else-if="score>=80">良好</h2>
    <h2 v-else-if="score>=60">及格</h2>
    <h2 v-else>不及格</h2>
  </div>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
    el:'#app',
    data:{
        score:99
    }
  });
當(dāng)出現(xiàn)復(fù)雜的邏輯的時(shí)候不建議在標(biāo)簽中使用多個(gè)v-if標(biāo)簽。
<div id="app">
  <div>
    <h2>{{result}}</h2>
  </div>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            score: 99
        },
        computed: {
            result() {
                let showMessage = '';
                if (this.score >= 90) {
                    showMessage = '優(yōu)秀';
                } else if (this.score >= 80) {
                    showMessage = '良好';
                } else if (this.score >= 60) {
                    showMessage = '及格';
                } else {
                    showMessage = '不及格';
                }
                return showMessage;
            }
        }
    });
v-if 的原理:v-if 后面的條件為false時(shí),對(duì)應(yīng)的元素以及其子元素不會(huì)渲染。也就是根本沒有不會(huì)有對(duì)應(yīng)的標(biāo)簽出現(xiàn)在DOM中。

4.1 條件渲染案例

  1. 點(diǎn)擊切換登錄方式按鈕,切換不同的登錄方式。
<div id="app">
  <span v-if="isUser">
    <label for="username">用戶名:</label>
    <input type="text" name="username" id="username" placeholder="輸入用戶名">
  </span>
  <span v-else="isUser">
    <label for="email">郵箱名:</label>
    <input type="text" name="email" id="email" placeholder="輸入郵箱地址" >
  </span>
  <button @click="isUser = !isUser">切換類型</button>
</div>

<script src="../../js/vue.js"></script>
    const app = new Vue({
        el: '#app',
        data: {
            isUser: true
        }
    });
  1. 問題:如果我們?cè)谟休斎雰?nèi)容的情況下,切換了類型,我們會(huì)發(fā)現(xiàn)文字依然顯示之前的輸入內(nèi)容。

  2. 問題解答:這是因?yàn)?code>Vue在進(jìn)行DOM渲染時(shí),出于性能考慮,會(huì)盡可能的復(fù)用已經(jīng)存在的元素,而不是重新創(chuàng)建新的元素。

  3. 解決辦法:如果我們不希望Vue出現(xiàn)類似重復(fù)利用的問題,可以給對(duì)應(yīng)的input添加key,并且我們需要保證key的不同。

5. v-for

  1. 當(dāng)我們有一組數(shù)據(jù)需要進(jìn)行渲染時(shí),我們就可以使用 v-for 來完成。v-for 的語法類似于JavaScript中的for循環(huán)。格式如下:item in items 的形式。

5.1 v-for 遍歷數(shù)組

<div id="app">
  <ul>
    <!-- 1. 獲取數(shù)組的元素 -->
    <li v-for="item in arr">{{item}}</li>
  </ul>

  <ul>
    <!-- 2. 獲取數(shù)組元素和 index -->
    <li v-for="(item,index) in arr">{{index + 1}} . {{item}}</li>
  </ul>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            arr: ['盲僧', '雷霆咆哮', '腕豪', '石頭人', '瑟提', '寡婦', '男槍']
        }
  });

5.2 v-for 遍歷對(duì)象

<div id="app">
  <!-- 1. 獲取對(duì)象的值 -->
  <ul>
    <li v-for="value in obj">{{value}}</li>
  </ul>

  <!-- 2. 獲取對(duì)象的 鍵 和 值 -->
  <ul>
    <li v-for="(value , key) in obj">{{ key }} => {{value}}</li>
  </ul>

  <!-- 3. 獲取對(duì)象的 value 和 key 和 index -->
  <ul>
    <li v-for="(value,key ,index) in obj">{{index + 1}} . {{key}} => {{value}}</li>
  </ul>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            obj: {
                name: '張三',
                age: 23,
                height: 188
            }
        }
    });

5.3 組件的 key 屬性

  1. 官方推薦我們?cè)谑褂?code>v-for時(shí),給對(duì)應(yīng)的元素或組件添加上一個(gè):key 屬性。這個(gè)key的值需要和遍歷的值是一一對(duì)應(yīng)的才能提高渲染效率。
為什么需要這個(gè)key屬性呢(了解)?
  1. 這個(gè)其實(shí)和Vue的虛擬DOMDiff算法有關(guān)系。

  2. 這里我們借用React’s diff algorithm 中的一張圖來簡(jiǎn)單說明一下:

diff
diff
節(jié)點(diǎn)移動(dòng)流程
有key和沒有key
說明: 當(dāng)某一層有很多相同的節(jié)點(diǎn)時(shí),也就是列表節(jié)點(diǎn)時(shí),我們希望插入一個(gè)新的節(jié)點(diǎn)
  1. 我們希望可以在B和C之間加一個(gè)F,Diff算法默認(rèn)執(zhí)行起來是這樣的。

  2. 即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很沒有效率?

所以我們需要使用key 來給每個(gè)節(jié)點(diǎn)做一個(gè)唯一標(biāo)識(shí)
  1. Diff 算法就可以正確的識(shí)別此節(jié)點(diǎn);

  2. 找到正確的位置區(qū)插入新的節(jié)點(diǎn)。

所以一句話,key 的作用主要是為了高效的更新虛擬DOM
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容