本章內(nèi)容:認識 Vue.js、數(shù)據(jù)綁定芥颈、計算屬性、內(nèi)置指令
一赚抡、初始 Vue.js
1.1 Vue.js 是什么
簡單小巧的核心爬坑,漸進式技術(shù)棧,足以應(yīng)付任何規(guī)模的應(yīng)用涂臣。
簡單小巧 指Vue.js 壓縮后大小僅 17KB
所謂漸進式(Progressive)就是你可以一步一步盾计、有階段性地來使用 Vue.js
Vue.js 提供了現(xiàn)代 Web 開發(fā)中常見的高級功能,比如:
- 解耦視圖與數(shù)據(jù)
- 可復(fù)用的組件
- 前端路由
- 狀態(tài)管理
- 虛擬 DOM(Virtual DOM)
1.1.1 MVVM 模式
Vue.js使用 MVVM (Model-View-ViewModel)模式赁遗。MVVM 模式是由經(jīng)典的軟件架構(gòu)MVC 衍生而來的署辉。當 View(視圖層)變化時,會自動更新到 ViewModel(視圖模型)吼和,反之亦然
1.1.2涨薪、Vue.js 有什么不同
與傳統(tǒng)開發(fā)模式相比,Vue.js 通過 MVVM 的模式拆分為 視圖 與 數(shù)據(jù) 兩部分炫乓,并將其分離刚夺,因此,你只需要關(guān)系數(shù)據(jù)即可末捣,DOM的 事情 Vue 會自動幫你搞定侠姑。
1.2、如何使用 Vue.js
1.2.1箩做、傳統(tǒng)的前端開發(fā)模式
前些年稱之為“萬金油” 的一套技術(shù)棧:
jQuery + RequireJS(SeaJS) + artTemplate(doT) + Gullp(Grunt)
這套技術(shù)棧 以 jQuery 為核心莽红,RequireJS 或 SeaJS 進行模塊開發(fā);加上輕量級的前端模板artTemplate 或 doT邦邦;最后使用自動構(gòu)建工具(如 Gulp)壓縮代碼
1.2.2安吁、Vue.js 的開發(fā)模式
Vue.js 是一個漸進式框架,根據(jù)項目需求的不同燃辖,你可以選擇從不同的維度來使用它鬼店。
如果只是想體驗 Vue.js 或 開發(fā)簡單的 HTML5頁面 或 小應(yīng)用,可以直接通過 <script> 加載 CDN 文件黔龟。
對于一些業(yè)務(wù)邏輯復(fù)雜妇智,對前端工程有要求的項目,可以使用 Vue 單文件的形式配合 webpack 使用氏身,必要時還會用到 vuex 狀態(tài)管理巍棱,vue-router 管理路由。
二蛋欣、數(shù)據(jù)綁定 和 第一個Vue應(yīng)用
學(xué)習任何一種框架航徙,都應(yīng)該從 寫 Hello World 應(yīng)用 開始
<body>
<div id="app">
<input type="text" v-model="user">
<h1>Hello {{user}}</h1>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
user: ''
}
})
</script>
</body>
在輸入框輸入的內(nèi)容會實時展示在 頁面的 h1 標簽內(nèi),雖然這是一段簡單的代碼陷虎,卻展示了 Vue.js 最核心的功能:數(shù)據(jù)的雙向綁定
2.1捉偏、Vue 實例 與 數(shù)據(jù)綁定
2.1.1倒得、實例 與 數(shù)據(jù)
Vue.js 應(yīng)用的創(chuàng)建很簡單,通過 構(gòu)造函數(shù) Vue 就可以 創(chuàng)建一個 Vue 實例夭禽,并啟動 Vue 應(yīng)用霞掺。
const app = new Vue({
// options
})
app 就代表了這個 Vue 實例
實例選項
首先,必不可少的一個選項就是 el讹躯。el 用于指定一個頁面中已經(jīng)存在的 DOM
元素來掛載 Vue 實例菩彬,他可以是 HTMLElement,也可以是 CSS 選擇器潮梯。
const app1 = new Vue({
el: document.getElementById('app')
})
const app2 = new Vue({
el: '#app'
})
掛載成功后骗灶,我們可以通過 app.$el 來訪問該元素
data 選項,可以聲明應(yīng)用中需要雙向綁定的數(shù)據(jù)秉馏,建議所有會用到的數(shù)據(jù)都預(yù)先在 data 內(nèi)聲明耙旦,這樣不至于將數(shù)據(jù)散落在業(yè)務(wù)邏輯中,難以維護萝究。
Vue 實例本身也代理了 data 對象里的所有屬性免都,所以可以這樣訪問:
var app = new Vue({
el: '#app',
data: {
a: 1
}
})
console.log(app.a) // 1
處理顯式地聲明數(shù)據(jù)外,也可以指定一個已有的變量帆竹,并且他們之間默認建立了雙向數(shù)據(jù)綁定绕娘,當修改其中任意一個時,另一個也會一起改變栽连。
2.1.2险领、生命周期
每個 Vue 實例創(chuàng)建時,都會經(jīng)歷一系列的初始化過程秒紧,同時也會調(diào)用相應(yīng)的生命周期鉤子绢陌,我們可以利用這些鉤子,在合適的時機執(zhí)行我們的業(yè)務(wù)邏輯熔恢。
比較常用的有:
- created:實例創(chuàng)建完成后調(diào)用脐湾,此階段完成了數(shù)據(jù)的觀測等,但尚未掛載绩聘,$el 還不可用沥割。需要初始化處理一些數(shù)據(jù)時比較有用
- mounted:el 掛載到實例上后調(diào)用耗啦,一般我們的第一個業(yè)務(wù)邏輯在這里開始
- beforeDestroy:實例銷毀之前調(diào)用凿菩。主要解綁一些使用 addEventListener 監(jiān)聽的事件等。
詳細的聲明周期函數(shù)帜讲,會在后面章節(jié)講解
這些鉤子 與 el 和 data 類似衅谷,也是作為選項寫入 Vue 實例內(nèi),并且 鉤子的 this 指向的是調(diào)用它的 Vue 實例:
var app = new Vue({
el: '#app',
data: {
a: 2
},
created: function() {
console.log(this.a) // 2
},
mouted: function() {
console.log(this.$el) // <div id="app"></div>
}
})
2.1.3似将、差值與表達式
使用 雙大括號(Mustache 語法)“{{}}” 是最基本的文本插值方法获黔,它會自動將我們雙向綁定的數(shù)據(jù)實時顯示出來蚀苛。
<body>
<div id="app">
<h1>{{ book }}</h1>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
book: '《云邊有個小賣部》'
}
})
</script>
</body>
如果有時候想要輸出 HTML,而不是將數(shù)據(jù)解釋后的存文本玷氏,可以使用 v-html:
<body>
<div id="app">
<span v-html="bookLink"></span>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
bookLink: '<a href="#">《云邊有個小賣部》</a>'
}
})
</script>
</body>
這里要注意堵未,如果將用戶產(chǎn)生的內(nèi)容 使用 v-html 輸出后,有可能會導(dǎo)致 XSS 攻擊盏触,所以要在服務(wù)端對用戶提交的內(nèi)容進行處理渗蟹,一般可將 尖括號 "<>" 轉(zhuǎn)義
如果想顯示 {{}} 標簽而不進行替換,可以使用 v-pre赞辩,即可跳過這個元素和它的子元素的編譯過程雌芽。
<span v-pre> {{ 這里的內(nèi)容不會被編譯 }} <span>
在 {{}} 中,處理簡單的綁定屬性外辨嗽,還可以使用 JavaScript 表單是進行簡單的運算世落、三元運算等,例如:
<div id="app">
{{num / 10}}
{{ isOK ? '確定' : '取消' }}
{{ text.split(',').reverse().join(',') }}
</div>
<script>
var app = new Vue({
el: '#app',
data: {
number: 100,
isOK: false,
text: '123,456'
}
})
</script>
Vue.js 只支持單個表達式糟需,不支持語句和流程控制屉佳。另外,在表達式中篮灼,不能使用用戶自定義的全局變量忘古,只能使用 Vue 白名單內(nèi)的全局變量,例如 Math 和 Date
2.1.4诅诱、過濾器
Vue.js 支持在 {{}} 插值的尾部添加一個 管道符 “ | ” 來對數(shù)據(jù)進行過濾髓堪,經(jīng)曾用于格式化文本,比如字母全部大寫娘荡,貨幣千位使用逗號分割等干旁。過濾的規(guī)則是自定義的,通過給 Vue 實例添加 filters 來實現(xiàn)
時間案例
<body>
<div id="app">
{{ date | formatDate}}
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
date: new Date()
},
mounted() { // 實例掛載后初始化義時器
this.timer = setInterval(() => this.date = new Date(), 1000)
},
beforeDestroy () { // 實例銷毀時 情況定時器
if (this.timer) clearInterval(this.timer)
},
filters: { // 定義過濾器
formatDate(value) { // value 指需要進行過濾的數(shù)據(jù), 管道符前面的數(shù)據(jù)
const date = new Date(value)
const year = date.getFullYear()
const month = (date.getMonth() + 1).toString().padStart(2, 0)
const day = date.getDate().toString().padStart(2, 0)
const hours = date.getDate().toString().padStart(2, 0)
const min = date.getMinutes().toString().padStart(2, 0)
const sec = date.getSeconds().toString().padStart(2, 0)
return `${year}-${month}-${day} ${hours}:${min}:${sec}`
},
}
})
</script>
</body>
過濾器也可以串聯(lián)炮沐,而且可以接收參數(shù)
<!-- 串聯(lián) -->
{{ message | filterA | filterB }}
<!-- 接收參數(shù) -->
{{ message | filter('arg1', 'arg2') }}
這里的 arg1 和 arg2 將分別傳給過濾器的第二個 和 第三個參數(shù)争群,因為第一個是數(shù)據(jù)本身。
2.2大年、指令
指令(Directives)是 Vue.js 模板中最常用的 一項功能换薄,它帶有前綴的 v-,Vue,js內(nèi)置了很多指令翔试,后面會詳細講到轻要,在此之前,你需要先知道 v-bind 和 v-on
v-bind 的基本用途是動態(tài)更新 HTML 元素上的屬性垦缅,比如 id冲泥、class 等
例如下面示例:
<body>
<div id="app">
<a v-bind:href="url">鏈接</a>
<img v-bind:src="imgUrl" alt="">
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
url: 'https://www.baidu.com',
imgUrl: 'https://wallhaven.cc/w/8396gk'
}
})
</script>
</body>
v-on,用來綁定事件監(jiān)聽,這樣我們就可以實現(xiàn)一些交互
<body>
<div id="app">
<p v-if="isShow">The End of the F***ing World</p>
<button v-on:click="clickHandle">toggle</button>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isShow: true
},
methods: {
clickHandle() {
this.isShow = !this.isShow
}
}
})
</script>
</body>
表達式可以是一個方法名凡恍,這些方法都寫在 Vue 實例的 methods 屬性內(nèi)志秃,并且是函數(shù)的形式,函數(shù)內(nèi)的this 指向的是當前 Vue 實例本身嚼酝,因此可以直接使用 this.xxx 的形式來訪問或修改數(shù)據(jù)
表達式除了方法名浮还,也可以直接是一個內(nèi)聯(lián)語句。
<div id="app">
<p v-if="isShow">The End of the F***ing World</p>
<button v-on:click="this.isShow = !this.isShow">toggle</button>
</div>
如果綁定的事件要處理復(fù)雜的業(yè)務(wù)邏輯闽巩,建議還是在 methods 里聲明一個方法碑定,這樣可讀性更強也好維護。
Vue.js 將 methods 里的方法也代理了又官,所以也可以像 訪問 Vue 數(shù)據(jù)那樣來調(diào)用方法延刘。在外部 也可以通過 “實例.方法名” 調(diào)用內(nèi)部的方法
2.3、語法糖
語法糖是指在不影響功能的情況下六敬,添加某種方法實現(xiàn)同樣的效果碘赖,從而方便程序開發(fā)。
Vue.js 的 v-bind 和 v-on 指令都提供了語法糖外构,
v-bind普泡,可以省略 v-bind,直接寫一個冒號 “:”
<a :href="url"> 連接 </a>
<img :src="imgUrl" />
v-on 可以直接用 “@” 來縮寫
<button @click="handleClick">toggle</button>
三、計算屬性
3.1、什么是計算屬性
在模板中雙向綁定一些數(shù)據(jù)或表達式蔗候,如果過長,或邏輯更為復(fù)雜時砰嘁,就會變得臃腫甚至難以閱讀和維護,比如:
{{ text.split(',').reverse().join(',') }}
當遇到復(fù)雜的邏輯是應(yīng)該使用 計算屬性勘究。
<body>
<div id="app">
{{ reversedText}}
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
text: '123,456'
},
computed: {
reversedText() {
// 這里的this 指向的是當前啊的 vue 實例
return this.text.split(',').reverse().join(',')
}
}
})
</script>
</body>
3.2矮湘、計算屬性用法
計算屬性只要其中任意數(shù)據(jù)變化,計算屬性就會重新執(zhí)行口糕,視圖也會更新缅阳。
<body>
<div id="app">
總價:{{ prices }}
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
package: [
{
name: '桔梗',
price: 10,
count: 2
},
{
name: '荼蘼',
price: 11,
count: 10,
},
{
name: '滿天星',
price: 20,
count: 15
}
]
},
computed: {
prices() {
var prices = 0
for (var i = 0, len = this.package.length; i < len; i++) {
var item = this.package[i]
prices += item.price * item.count
}
return prices
}
}
})
</script>
</body>
如上 當 package 中的商品有任何變化時,計算屬性 prices 就會自動更新
每一個計算屬性都包含一個 getter 和 一個 setter景描。在你需要時十办,也可以提供一個 setter 函數(shù),當手動修改計算屬性的值就像修改一個普通數(shù)據(jù)那樣時超棺,就會觸發(fā) setter 函數(shù)向族,執(zhí)行一些自定義操作。
<body>
<div id="app">
{{ fullName }}
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'Don',
lastName: 'Shirley'
},
computed: {
fullName: {
// getter 用于讀取
get() {
return this.firstName + ' ' + this.lastName
},
// setter 寫入時觸發(fā)
set(newVal) {
var names = newVal.split(' ')
this.firstName = names[0]
this.lastName = name[names.length -1]
}
}
}
})
</script>
</body>
絕大多數(shù)情況下说搅,我們只會用默認的 getter 方法來讀取一個計算屬性炸枣,在業(yè)務(wù)中很少用到 setter虏等,所以在聲明一個計算屬性時弄唧,可以直接使用 默認的寫法适肠,不必將 getter 和 setter 都聲明。
計算屬性還有兩個很實用的小技巧容易被忽略:
- 一是計算屬性可以依賴其他計算屬性候引;
- 而是計算屬性不僅可以依賴當前 Vue 實例的數(shù)據(jù)侯养,還可以依賴其他實例的數(shù)據(jù)。
3.3澄干、計算屬性緩存
調(diào)用 methods 里的方法也可以 與 計算屬性起到同樣的作用逛揩。
本小節(jié)的第一個例子,使用 methods改寫:
<body>
<div id="app">
<!-- 注意麸俘,這里的 reversedText 是方法辩稽,所以要帶 () -->
{{ reversedText() }}
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
text: '123,456'
},
methods: {
reversedText() {
// 這里的this 指向的是當前啊的 vue 實例
return this.text.split(',').reverse().join(',')
}
}
})
</script>
</body>
使用方法還可以接受參數(shù),使用起來更靈活从媚,那么為什么還需要計算屬性呢逞泄?原因就是 計算屬性是基于它的依賴緩存的。一個計算屬性所依賴的數(shù)據(jù)發(fā)生變化時拜效,它才會重新取值喷众。所以 text 只要不改變,計算屬性也就不更新紧憾。
使用 計算屬性 還是 methods 取決于你是否需要緩存到千,當遍歷大數(shù)組和做大量計算時,應(yīng)該使用計算屬性赴穗,除非你不希望得到緩存憔四。
四、v-bind 及 class 與 style 綁定
4.1般眉、了解 v-bind 指令
前面已經(jīng)介紹了 v-bind 指令的基本用法及語法糖加矛,它的主要用處是動態(tài)更新 HTML 元素上的屬性。在數(shù)據(jù)綁定中煤篙,最常見的兩個需求就是 元素的樣式名稱 class 和 內(nèi)聯(lián)樣式 style 的動態(tài)綁定
4.2斟览、綁定 class 的幾種方式
4.2.1、對象語法
給 v-bind:class 設(shè)置 一個對象辑奈,可以動態(tài)地切換 class
例如
<body>
<div id="app">
<div :class="{'active': isActive}"></div>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isActive: false
}
})
</script>
</body>
對象中也可以傳入多個屬性苛茂,來動態(tài)切換 class。:class 與 普通 class 共存
<body>
<div id="app">
<div :class="{'active': isActive, 'error': isError}"></div>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isActive: false,
isError: true
}
})
</script>
</body>
:class 內(nèi)的表達式每項為真時鸠窗,對應(yīng)的類名就會加載妓羊。
當 :class 的表達式過長 或 邏輯復(fù)雜時,還可以綁定一個計算屬性稍计,這是一種很友好和常見的用法躁绸,一般當條件多于兩個時,都可以使用 data 或 computed。
例如使用 計算屬性:
<body>
<div id="app">
<div :class="classes"></div>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isActive: true,
isError: null
},
computed: {
classes() {
return {
active: this.isActive && !this.isError,
'text-fail': this.isError && this.error.type === 'fail'
}
}
}
})
</script>
</body>
除了計算屬性净刮,你也可以直接綁定一個 Object 類型的數(shù)據(jù)剥哑,或者使用 雷士計算屬性的 methods
4.2.2、數(shù)組語法
當需要應(yīng)用多個 class 時淹父,可以使用數(shù)組語法株婴。
<body>
<div id="app">
<div :class="[activeCls, errorCls]"></div>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
activeCls: 'active',
errorCls: null
}
})
</script>
</body>
也可以在數(shù)組中使用 三元表達式。
<div :class="[activeCls ? 'active-success' : '', errorCls]"></div>
當 class 有多個條件是暑认,這樣寫比較煩瑣困介,可以在數(shù)組語法中使用對象語法:
<body>
<div id="app">
<div :class="[{'activeCls': isActive}, errorCls]"></div>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isActive: true,
errorCls: null
}
})
</script>
</body>
與對象語法一樣,也可以使用 data蘸际、computed座哩、methods 三種方法。
4.2.3粮彤、在組件上使用
如果直接在 自定義組件上使用 class 或 :class八回,樣式規(guī)則會直接應(yīng)用到這個組件的根元素上,例如聲明一個簡單的組件:
<body>
<div id="app">
<my-component :class="{'active': isActive}">
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
Vue.component('my-component', {
template: '<p class="article"> some text </p>'
})
const app = new Vue({
el: '#app',
data: {
isActive: true,
errorCls: null
}
})
</script>
</body>
最終組件渲染的結(jié)果為:
<p class="article active"> some text </p>
4.3驾诈、綁定內(nèi)聯(lián)樣式
使用 v-bind:style(:style) 可以給元素綁定內(nèi)聯(lián)樣式缠诅,方法與 :class 類似,也有對象語法和數(shù)組語法乍迄,看起來很像直接在元素上寫 CSS:
<body>
<div id="app">
<div :style="{'color': color, 'fontSize': fontSize + 'px'}">The End of the F***ing World</div>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
color: 'cyan',
fontSize: 40
}
})
</script>
</body>
大多數(shù)情況下管引,直接寫一長串的樣式不便于閱讀和維護,所以一般 寫在 data 或 computed 里闯两。
<body>
<div id="app">
<div :style="styles">The End of the F***ing World</div>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
styles: {
color: 'cyan',
fontSize: '40px'
}
}
})
</script>
</body>
在實際業(yè)務(wù)中褥伴,:style 的數(shù)組語法并不常用,因為往往可以寫在一個對象里面漾狼;而較為常用的應(yīng)當時計算屬性重慢。
在使用 :style 時, Vue.js 會自動給特殊的 CSS 屬性 名稱添加前綴(weikit逊躁、ms...)似踱。
五、內(nèi)置指令
5.1稽煤、基本指令
5.1.1核芽、v-cloak
v-cloak 不需要表達式,它會在 Vue 實例結(jié)束編譯是從綁定的 HTML 元素上移除酵熙,經(jīng)常和 CSS 的 display: none; 配合使用轧简。
<head>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<span v-cloak>{{ message }}</span>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'some message'
}
})
</script>
</body>
在一般情況下,v-cloak 是一個解決初始化慢導(dǎo)致頁面閃動的最佳實踐匾二,對于簡單的項目很實用哮独。
5.1.2拳芙、v-once
v-once 也是一個不需要表達式的指令,作用是定義它的元素或組件只渲染一次皮璧,包括元素或組件的所有子節(jié)點舟扎。首次渲染后,不再隨數(shù)據(jù)的變化重新渲染恶导,將被視為靜態(tài)內(nèi)容。
<body>
<div id="app">
<span v-once>{{ message }}</span>
<div v-once>
<span>
{{ message }}
</span>
</div>
<span>{{ message }}</span>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'some message'
}
})
app.message = 'no message'
</script>
</body>
5.2浸须、條件渲染指令
5.2.1惨寿、v-if 、v-else-if删窒、v-else
與 JavaScript 的條件語句 if裂垦、else、else if 類似肌索,Vue.js 的條件指令可以根據(jù)表達式的值在 DOM 中渲染或銷毀元素/組件蕉拢,例如:
<body>
<div id="app">
<p v-if="status === 1">當 status 為1時顯示 該行</p>
<p v-else-if="status === 2">當 status 為2時顯示 該行</p>
<p v-else>否則顯示該行</p>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
status: 3
}
})
</script>
</body>
v-else-if 要緊跟 v-if, v-else 要跟緊 v-else-if 或 v-if诚亚。
表達式的值為真時晕换,當前元素/組件及所有子節(jié)點被渲染,為假時被移除站宗。
如果一次判斷多個元素闸准,可以在 Vue.js 內(nèi)置的 <template> 元素上使用條件指令,最終渲染的結(jié)果不會包含該元素梢灭。
<body>
<div id="app">
<template v-if="status === 1">
<p>message</p>
<p>message</p>
<p>message</p>
</template>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
status: 1
}
})
</script>
</body>
Vue 在渲染元素是夷家,出于效率考慮,會盡可能地復(fù)用已有的元素而非重新渲染敏释。
如下示例:
<body>
<div id="app">
<template v-if="type === 'name'">
<label for="username">用戶名:</label>
<input type="text" id="username" placeholder="用戶名" />
</template>
<template v-else>
<label for="email">郵箱:</label>
<input type="email" id="email" placeholder="郵箱" />
</template>
<button @click="handleToggle">切換輸入類型</button>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
type: 'name'
},
methods: {
handleToggle() {
this.type = this.type === 'name' ? 'email' : 'name'
}
}
})
</script>
</body>
如上代碼库快,輸入內(nèi)容后,點擊切換按鈕钥顽,DOM會發(fā)生改變义屏,但是之前在輸入框鍵入的內(nèi)容并沒有改變,只是替換了 placeholder 的內(nèi)容蜂大,說明 <input> 元素被復(fù)用了湿蛔。
如果你不希望這樣做,可以使用 Vue.js 提供的 key 屬性县爬,它可以讓你自己決定是否要復(fù)用元素阳啥,key 的值必須是唯一的,例如:
<!-- ...省略其他代碼 -->
<input type="text" id="username" placeholder="用戶名" key="username-input" />
<!-- ...省略其他代碼 -->
<input type="email" id="email" placeholder="郵箱" key="username-email"/>
<!-- ...省略其他代碼 -->
給兩個 <input> 元素都增加 key 后财喳,就不會復(fù)用了察迟,切換類型時鍵入的內(nèi)容也會被刪除斩狱,不過 <label> 元素仍然是被復(fù)用的,因為沒有添加 key 屬性
5.2.2扎瓶、v-show
v-show 的用法 和 v-if 基本一致所踊。只不過 v-show 指令是通過修改元素的 CSS屬性 display。當 v-show 表達式的值為 false 時概荷,元素會隱藏秕岛,查看 DOM 結(jié)構(gòu)會看到元素上加載了 內(nèi)聯(lián)樣式 display: none;
<body>
<div id="app">
<span v-show="status === 1"> 嗶哩嗶哩 (゜-゜)つロ 干杯~-bilibil</span>
<span v-show="status === 2"> xxx </span>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
status: 1
}
})
</script>
</body>
渲染的結(jié)果為
<span> 嗶哩嗶哩 (゜-゜)つロ 干杯~-bilibil</span>
<span style="display: none;"> xxx </span></div>
v-show 不能再 <template> 上使用
5.2.3、v-if 與 v-show 的選擇
v-if 和 v-show 具有類似的功能误证。
- v-if 真正的條件渲染继薛。若表達式初始值為 false,則一開始元素/組件比不會渲染愈捅,只有當條件第一次變?yōu)?true 時才開始渲染
- v-show 只是簡單的 CSS 屬性切換遏考,無論條件真與否,都會被編譯蓝谨。
相比之下灌具,v-if 更適合條件不經(jīng)常改變的常見,因為他切換開銷相對較大譬巫,而 v-show 適合頻繁切換條件咖楣。
5.3、列表渲染指令 v-for
5.3.1芦昔、基本用法
當需要將一個數(shù)組遍歷或枚舉一個對象顯示循環(huán)時截歉,就會用到列表渲染指令 v-for
<body>
<div id="app">
<ul>
<li v-for="item in books"> {{ item.name }}</li>
</ul>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
books: [
{name: 'Vue.js 實戰(zhàn)'},
{name: 'JavaScript 高級程序設(shè)計'},
{name: 'jQuery 基礎(chǔ)入門'}
]
}
})
</script>
</body>
列表渲染也支持用 of 來代替 in 作為分隔符,它更接近 JavaScript 迭代器的語法
v-for 的表達式 支持一個 可選參數(shù) 作為當前項的索引烟零。
例如:
<ul>
<li v-for="(item, index) in books"> {{ index }} ---- {{ item.name }}</li>
</ul>
與 v-if 一樣瘪松,v-for 也可以使用 內(nèi)置標簽 <template> 上,將多個元素進行渲染:
<body>
<div id="app">
<template v-for="(item, index) in books">
<p>
<span>{{ item.name }}</span> ---
<span>{{ item.price }}</span> ---
<span>{{ item.level }}</span>
</p>
</template>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
books: [
{
name: 'Vue.js 實戰(zhàn)',
price: 50,
level: '1'
},
{
name: 'ES 6 標準入門',
price: 70,
leevl: '2'
},
{
name: 'JavaScript 高級程序設(shè)計',
price: 100,
level: '3'
}
]
}
})
</script>
</body>
除了數(shù)組外锨阿,對象的屬性也是可以遍歷的宵睦。遍歷對象時,有兩個可選參數(shù)墅诡,分別是鍵名和索引:
<body>
<div id="app">
<p v-for="(value, key, index) in user">value: {{value}} ----- key: {{key}} ------ index: {{index}}</p>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
user: {
name: '了凡',
age: 1,
male: true
}
}
})
</script>
</body>
v-for 還可以迭代整數(shù)
<span v-for="n in 10"> {{ n }}</span>
5.3.2壳嚎、數(shù)組更新
Vue 的核心是數(shù)據(jù)與視圖的雙向綁定,當我們修改數(shù)組時末早,Vue 會檢測到數(shù)據(jù)變化烟馅,所以用 v-for 渲染的視圖也會立即更新。Vue包含了 一組觀察數(shù)組變異的方法然磷,使用他們改變數(shù)組會觸發(fā)視圖更新:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
使用以上方法會改變被這些方法調(diào)用的原數(shù)組郑趁。
有些方法不會改變原數(shù)組,例如:
- filter()
- concat()
- slice()
它們返回的是一個新數(shù)組姿搜,在使用這些非變異方法時寡润,可以用新數(shù)組來替換原數(shù)組
<body>
<div id="app">
<template v-for="book in books">
<p>
<span>書名:{{ book.name }}</span>
<span>作者:{{ book.author }}</span>
</p>
</template>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
books: [
{
name: '《Vue.js 實戰(zhàn)》',
author: '梁灝'
}, {
name: 'Professional JavaScript for Web Developers',
author: 'Nicholas C.Zakas'
}
]
}
})
app.books = app.books.filter(function(item) {
return item.name.match(/JavaScript/) // 返回 書名中 含有 JavaScript 的書
})
</script>
</body>
Vue 在檢測到數(shù)組變化時捆憎,并不是直接重新渲染整個列表,而是最大化地復(fù)用 DOM 元素梭纹。替換的數(shù)組中躲惰,含有相同元素的項不會被重寫渲染,因此可以大膽地用新數(shù)組來替換舊數(shù)組变抽,不用擔心性能問題础拨。
需要注意的是,以下變動的數(shù)組中绍载,Vue 是不能檢測的诡宗,也不會觸發(fā)視圖更新
- 通過 索引直接設(shè)置項,比如 app.books[3] = {...}
- 修改數(shù)組長度逛钻,比如 app.books.length = 1僚焦。
解決第一個問題可以用兩種方法實現(xiàn)同樣的效果锰提,第一種是使用 Vue 內(nèi)置的 set方法曙痘。
Vue.set(app.books, 3, {
name: '《CSS 揭秘》',
author: 'Lea Verou'
})
如果是在 webpack 中使用 組件化的方式(后面會介紹到),默認是沒有Vue的立肘,這是可以使用$set边坤。
this.$set(app.books, 3, {
name: '《CSS 揭秘》',
author: 'Lea Verou'
})
這里的this 指向的就是當前組件實例,即 app谅年。在非 webpack 模式下也可以用 $set 方法茧痒,例如 app.$set(...)
另一種方法,使用 splice() 方法也可以達到同樣的效果:
app.books.aplice(3, 1, {
name: '《CSS 揭秘》',
author: 'Lea Verou'
})
第二個問題也可以直接使用 splice() 來解決
app.books.splice(1)
5.3.3融蹂、過濾與排序
但你不想改變原數(shù)組旺订,想通過一個數(shù)組的副本來做過濾或排序的顯示時,可以使用 計算屬性來返回過濾或排序后的數(shù)組超燃。
<body>
<div id="app">
<template v-for="book in filterBooks">
<p>
<span>書名:{{ book.name }}</span>
<span>作者:{{ book.author }}</span>
</p>
</template>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
books: [
{
name: '《Vue.js 實戰(zhàn)》',
author: '梁灝'
}, {
name: 'Professional JavaScript for Web Developers',
author: 'Nicholas C.Zakas'
}
]
},
computed: {
filterBooks: function() {
return this.books.filter(function(book) {
return book.name.match(/JavaScript/) // 返回 書名中 含有 JavaScript 的書
})
}
}
})
</script>
</body>
類似的 實現(xiàn)排序
sortedBooks() {
return this.books.sort(function(a, b) {
return a.name.length - b.name.length
})
}
}
5.4区拳、方法和事件
5.4.1、基本用法
事件綁定的表達式 可以直接使用 JavaScript 語句意乓,也可以是一個在 Vue 實例中 methods 選項內(nèi)的函數(shù)名樱调。
調(diào)用的方法名后可以不跟括號“()”。此時届良,如果該方法有參數(shù)笆凌,默認會將原生對象事件 event 傳入,在大部分業(yè)務(wù)場景中士葫,如果方法不需要傳入?yún)?shù)乞而,為了簡便可以不寫括號
Vue提供一個特殊變量 $event,用于訪問原生 DOM 事件慢显。
如下示例:
<body>
<div id="app">
<a @click="handleClick('禁止打開', $event)">百度</a>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
methods: {
handleClick(msg, event) {
console.log(msg)
event.preventDefault() // 阻止跳轉(zhuǎn)
}
}
})
</script>
</body>
5.4.2晦闰、修飾符
在 @ 綁定的事件后面加 小圓點“.”放祟,后跟一個后綴來使用修飾符。
Vue 支持以下修飾符:
- .stop —— 阻止事件冒泡
- .prevent —— 阻止默認行為
- .capture —— 使用事件捕獲模式
- .self —— 只當在 event.target 是當前元素自身時觸發(fā)處理函數(shù)
- .once —— 事件將只會觸發(fā)一次
- .passive —— 不會調(diào)用 preventDefault()呻右,仍然調(diào)用了這個函數(shù)跪妥,客戶端將會忽略它并拋出一個控制臺警告。
在表單元素上 監(jiān)聽鍵盤事件時声滥,還可以使用按鍵修飾符眉撵。
比如按下具體某個鍵時才調(diào)用方法
<!-- 按下 keyCode 是 13 時 調(diào)用 vm.submit() -->
<input @keyup.13="submit" />
也可以自定義具體按鍵
Vue.config.keyCode.f1 = 112
這樣就可以通過 f1 來訪問 keyCode=112 的鍵了。
除了具體的某個 keyCode 外落塑,Vue 還提供了一些快捷鍵名稱纽疟。
以下是全部的別名
- .enter
- .tab
- .delete
- .esc
- .space
- .up
- .down
- .left
- .left
-
.right
下列按鍵修飾也可以組合使用,或者和鼠標一起配合使用
- .ctrl
- .alt
- .shift
- .meta
<!-- shift + s -->
<input @keyup.shift.83="handleSave" />
<!-- Ctrl + click -->
<div @click.ctrl="doSomething" >Do Something</div>
5.5憾赁、 發(fā)開購物車
基本功能:商品數(shù)量增減污朽、移除操作、計算總價
以下是 實現(xiàn)代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
[v-cloak] {
display: none;
}
table {
border: 1px solid #e9e9e9;
border-collapse: collapse;
border-spacing: 0;
empty-cells: show;
}
td, th {
padding: 8px 16px;
border: 1px solid #e9e9e9;
text-align: left
}
th {
background: #f7f7f7;
color: #5c6b77;
font-weight: 600;
white-space: nowrap
}
</style>
</head>
<body>
<div id="app" v-cloak>
<template v-if="list.length">
<table>
<thead>
<tr>
<th></th>
<th>商品名稱</th>
<th>商品單價</th>
<th>購買數(shù)量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in list">
<td>{{ index + 1 }}</td>
<td>{{ item.name }}</td>
<td>{{ item.price }}</td>
<td>
<button @click="handleReduce(index)" :disbaled="item.count === 1">-</button> <!-- 減少按鈕 -->
{{ item.count }}
<button @click="handleAdd(index)">+</button> <!-- 增加按鈕 -->
</td>
<td>
<button @click="handleRemove(index)">移除</button> <!-- 移除按鈕 -->
</td>
</tr>
</tbody>
</table>
<div>總價:¥ {{ totalPrice }}</div>
</template>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
list: [
{
id: 1,
name: 'iPhone 7',
price: 6188,
count: 1
}, {
id: 2,
name: 'iPad Pro',
price: 5888,
count: 1
}, {
id: 3,
name: 'MacBook Pro',
price: 21488,
count: 1
}
]
},
methods: {
handleReduce(index) { // 減少
if (this.list[index].count === 1) return
this.list[index].count--
},
handleAdd(index) { // 增加
this.list[index].count++
},
handleRemove(index) { // 移除
this.list.splice(index, 1)
}
},
computed: {
totalPrice() { // 計算總價
var total = 0;
for (var i = 0, len = this.list.length; i < len; i++) {
var item = this.list[i]
total += item.price * item.count
}
/*
\B:匹配非單詞邊界
(?=):正向零寬斷言
\d{3}:匹配三個數(shù)字字符
+:與前面的\d{3}結(jié)合表示匹配3的整數(shù)倍個數(shù)字字符
$:字符串結(jié)尾
所以合起來的意思就是:匹配單詞中的某個位置龙考,這個位置之后的字符全部為數(shù)字蟆肆,且出現(xiàn)次數(shù)是3的整數(shù)倍。
*/
return total.toString().replace(/\B(?=(\d{3})+$)/g, ',') // 千元分隔符
}
}
})
</script>
</body>
</html>