1. 計(jì)算屬性
1.1 什么是計(jì)算屬性 ?
我們知道,在模板中可以直接通過插值語法顯示一些
data中的數(shù)據(jù)。但是在某些情況,我們可能需要對(duì)數(shù)據(jù)進(jìn)行一些轉(zhuǎn)化后再顯示,或者需要將多個(gè)數(shù)據(jù)結(jié)合起來進(jìn)行顯示。比如我們有firstName和lastName兩個(gè)變量,我們需要顯示完整的名稱。但是如果多個(gè)地方都需要顯示完整的名稱,我們就需要寫多個(gè){{firstName}} {{lastName}}。我們可以將上面的代碼換成計(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à)格
在計(jì)算圖書總價(jià)的時(shí)候盡量使用計(jì)算屬性的方式,計(jì)算屬性有緩存功能,在值不改變的時(shí)候他只會(huì)計(jì)算一次。
而使用方法返回值的方式,如果其中出現(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
- 每個(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ì)算屬性的緩存
- 計(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ǔ)充
ES6中 ,新出現(xiàn)
let和const關(guān)鍵字,用于代替var,使用let聲明變量,使用const聲明一個(gè)常量。let關(guān)鍵字聲明的變量有塊級(jí)作用域,var聲明的變量沒有塊級(jí)作用域。在ES6之前
if 和 for代碼塊是沒有塊級(jí)作用域的。在Es6之前因?yàn)?code>if和for都沒有塊級(jí)作用域所以在很多時(shí)候,我們都必須借助與function的作用域來解決應(yīng)用外邊變量的問題。
2.1 let 和 var 的塊級(jí)作用域
- 在
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)的影響
- 因?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 的案例解決問題
- 因?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ì)比
- 定義多個(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)
建議 : 在開發(fā)中優(yōu)先使用
const只有需要在改變某個(gè)標(biāo)識(shí)符的時(shí)候才使用let。常量是不允許被修改的 一旦給
const修飾的標(biāo)識(shí)符被賦值之后不能進(jìn)行修改。在使用
const定義標(biāo)識(shí)符,必須進(jìn)行賦值。常量的含義是指向的對(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)聽
- 使用
v-on綁定事件監(jiān)聽器,可以縮寫成@。 是它的語法糖簡(jiǎn)寫形式。
3.1 v-on 的基本使用
- 下面的代碼中,我們使用了
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ù)問題:
- 情況一:如果該方法不需要額外參數(shù),那么方法后的()可以不添加。但是注意:如果方法本身中有一個(gè)參數(shù),那么會(huì)默認(rèn)將原生事件
event參數(shù)傳遞進(jìn)去。
- 情況二:如果需要同時(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 的修飾符
- 在某些情況下,我們拿到
event的目的可能是進(jìn)行一些事件處理。
Vue 提供了修飾符來幫助我們方便的處理一些事件:
.stop- 調(diào)用event.stopPropagation()。.prevent- 調(diào)用event.preventDefault()。.{keyCode | keyAlias}- 只當(dāng)事件是從特定鍵觸發(fā)時(shí)才觸發(fā)回調(diào)。.native- 監(jiān)聽組件根元素的原生事件。.once- 只觸發(fā)一次回調(diào)。
4. v-if、v-else-if、v-else
這三個(gè)指令與
JavaScript的條件語句if、else、else if類似。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 條件渲染案例
- 點(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
}
});
問題:如果我們?cè)谟休斎雰?nèi)容的情況下,切換了類型,我們會(huì)發(fā)現(xiàn)文字依然顯示之前的輸入內(nèi)容。
問題解答:這是因?yàn)?code>Vue在進(jìn)行
DOM渲染時(shí),出于性能考慮,會(huì)盡可能的復(fù)用已經(jīng)存在的元素,而不是重新創(chuàng)建新的元素。解決辦法:如果我們不希望
Vue出現(xiàn)類似重復(fù)利用的問題,可以給對(duì)應(yīng)的input添加key,并且我們需要保證key的不同。
5. v-for
- 當(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 屬性
- 官方推薦我們?cè)谑褂?code>v-for時(shí),給對(duì)應(yīng)的元素或組件添加上一個(gè)
:key屬性。這個(gè)key的值需要和遍歷的值是一一對(duì)應(yīng)的才能提高渲染效率。
為什么需要這個(gè)key屬性呢(了解)?
這個(gè)其實(shí)和
Vue的虛擬DOM的Diff算法有關(guān)系。這里我們借用
React’s diff algorithm中的一張圖來簡(jiǎn)單說明一下:




說明: 當(dāng)某一層有很多相同的節(jié)點(diǎn)時(shí),也就是列表節(jié)點(diǎn)時(shí),我們希望插入一個(gè)新的節(jié)點(diǎn)
我們希望可以在B和C之間加一個(gè)F,Diff算法默認(rèn)執(zhí)行起來是這樣的。
即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很沒有效率?
所以我們需要使用key 來給每個(gè)節(jié)點(diǎn)做一個(gè)唯一標(biāo)識(shí)
Diff算法就可以正確的識(shí)別此節(jié)點(diǎn);找到正確的位置區(qū)插入新的節(jié)點(diǎn)。