MVVM模型
MVVM是Model-View-ViewModel的縮寫。
- Model:代表數(shù)據(jù)模型,也可以在Model中定義數(shù)據(jù)修改和操作的業(yè)務(wù)邏輯;
- View:代表UI 組件,它負責將數(shù)據(jù)模型轉(zhuǎn)化成UI 展現(xiàn)出來;
- ViewModel:是一個同步View 和 Model的對象。
在MVVM架構(gòu)下,View 和 Model 之間并沒有直接的聯(lián)系,而是通過ViewModel進行交互,Model 和 ViewModel 之間的交互是雙向的, 因此View 數(shù)據(jù)的變化會同步到Model中,而Model 數(shù)據(jù)的變化也會立即反應到View 上。
ViewModel 通過雙向數(shù)據(jù)綁定把 View 層和 Model 層連接了起來,而View 和 Model 之間的同步工作完全是自動的,無需人為干涉,因此開發(fā)者只需關(guān)注業(yè)務(wù)邏輯,不需要手動操作DOM, 不需要關(guān)注數(shù)據(jù)狀態(tài)的同步問題,復雜的數(shù)據(jù)狀態(tài)維護完全由 MVVM 來統(tǒng)一管理。
Vue.js的優(yōu)點
- 低耦合:視圖(View)可以獨立于Model變化和修改,一個ViewModel可以綁定到不同的"View"上,當View變化的時候Model可以不變,當Model變化的時候View也可以不變。
- 可重用性:可以把一些視圖邏輯放在一個ViewModel里面,讓很多view重用這段視圖邏輯。
- 獨立開發(fā):開發(fā)人員可以專注于業(yè)務(wù)邏輯和數(shù)據(jù)的開發(fā)(ViewModel),設(shè)計人員可以專注于頁面設(shè)計。
- 可測試性:界面素來是比較難于測試的,而現(xiàn)在測試可以針對ViewModel來寫。
指令和事件
指令(Directives)是Vue模板中最常用的一項功能,它帶有前綴v-,能幫我們快速完成DOM操作、循環(huán)渲染、顯示影藏。
-
v-text:解析文本,作用與
{{}}一樣 - v-html:解析html
-
v-bind:基本用途是動態(tài)更新HTML元素上的屬性,如id、class等,可用
:代替 -
v-on:用來綁定事件監(jiān)聽器,可用
@代替
v-on具體介紹
在普通元素中,v-on可以監(jiān)聽原生的DOM事件,除了click以外,還有dbclick、keyup、mousemove等。表達式可以是一個方法名,這些方法都寫在Vue實例的methods屬性內(nèi),并且是函數(shù)的形式,函數(shù)內(nèi)的this指向的是當前Vue實例本身,因此可以直接使用this.xxx的形式來訪問或修改數(shù)據(jù)。
代碼實戰(zhàn)
要求:
- 渲染文本到頁面
- 渲染HTML到頁面
- 動態(tài)綁定屬性(任意屬性均可)
- 綁定一個事件
- 必須使用到過濾器
Vue計算屬性
一、字符翻轉(zhuǎn)實例
創(chuàng)建以一個vue實例,并使用計算屬性和方法實現(xiàn)一個字符串翻轉(zhuǎn)的操作,寫出核心代碼:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<p>初始字符:{{number}}</p>
<p>方法反轉(zhuǎn):{{reverseNumber2()}}</p>
<p>計算屬性反轉(zhuǎn):{{reverseNumber}}</p>
<p>模板內(nèi)表達式反轉(zhuǎn):{{number.split(', ').reverse().join(', ')}}</p>
</div>
<script>
var app1 = new Vue({
el: '#app',
data: {
number: '123, 456, 789',
lastName: 'He',
fullName: 'Wang He'
},
computed: {
reverseNumber: function(){
return this.number.split(', ').reverse().join(', ')
}
},
methods: {
reverseNumber2: function(){
return this.number.split(', ').reverse().join(', ')
}
}
})
</script>
</body>
</html>
二、何時使用模板內(nèi)表達式,何時使用計算屬性?
- 模板內(nèi)表達式:只支持單行表達式,適用于簡單計算
- 計算屬性:適用于復雜邏輯計算
三、計算屬性與方法(methods)的區(qū)別
計算屬性與方法呈現(xiàn)的最終結(jié)果相同,然而不同的是,計算屬性是基于它們的依賴進行緩存的,只有相關(guān)依賴發(fā)生變化時它們才會重新執(zhí)行,否則返回已緩存的之前的計算結(jié)果;而方法只要觸發(fā)重新渲染就會再次執(zhí)行。
代碼實戰(zhàn)
實例化一個vue實例,在data中定義一個firstName和lastName,還有一個全稱叫fullName。
提示:fullName = firstName + lastName。
需求:分別使用watch監(jiān)聽器和計算屬性來實現(xiàn)以下功能實現(xiàn),只要firstName和lastName中的任意一個改變,全稱fullname就會改變
代碼預覽
v-bind以及class與style的綁定
v-bind通常用來綁定屬性,可以動態(tài)地綁定一些class類名或者style樣式:
-
變量語法:
v-bind:class="變量",這里變量的值通常是在CSS定義好的類名; -
數(shù)組語法:
v-bind:class="[變量1, 變量2]",形式與變量語法差不多,但可以同時綁定多個類名; -
對象語法:
v-bind:class="{classname1:boolean, classname2:boolean}",這里的classname就是樣式表的類名,boolean通常是一個變量,也可以是常量、計算屬性等,這種方法也是綁定class最常用的方式。
代碼實戰(zhàn)
Vue的內(nèi)置指令
一、v-model有什么作用
v-model可以在表單<input>、<textarea>及<select>元素上創(chuàng)建雙向數(shù)據(jù)綁定。它會根據(jù)控件類型自動選取正確的方法來更新元素。盡管有些神奇,但v-model的本質(zhì)不過是語法糖。它負責監(jiān)聽用戶的輸入事件以更新數(shù)據(jù),并對一些極端場景進行一些特殊處理。
<input type="text" v-model="msg"/> {{msg}}
二、各指令的作用
-
v-if:根據(jù)表達式的值的真假條件渲染元素。在切換時元素及其數(shù)據(jù)綁定/組件被銷毀并重建。如果元素是
<template>,將提出他的內(nèi)容作為條件塊。當條件變化時該指令觸發(fā)過渡效果。當和v-if一起使用時,v-for的優(yōu)先級更高,只渲染變化的元素。提供key值可以決定是否復用該元素。 - v-else:必須緊跟在v-if或v-else-if后,否則不能被識別。
-
v-show:根據(jù)條件顯示元素。與v-if不同的是,帶有v-show的元素始終會被渲染并保留在DOM中,v-show只是簡單地切換元素CSS的display屬性。
注意:v-show不支持<template>元素,也不支持v-else - v-once:只渲染元素和組件一次。元素和組件及其所有子節(jié)點會將隨后的重新渲染視為靜態(tài)內(nèi)容并跳過。這可以用于優(yōu)化更新性能。
-
v-cloak:解決初始化慢導致的頁面閃動。
[v-cloak]:{display:none}一起使用,可以隱藏未編譯的Mustache標簽指導失利準備完畢。
代碼實例
- 使用v-for循環(huán)寫出一個demo:預覽鏈接
- 使用v-bind,v-on寫出一個demo:預覽鏈接
- 在輸入框運用v-model:預覽鏈接
- 在復選框運用v-model:預覽鏈接
- 在單選框運用v-model:預覽鏈接
Vue組件
一、全局注冊和局部注冊組件
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<p>全局注冊組件:</p>
<my-component></my-component>
<hr>
<p>局部注冊組件:</p>
<app-component></app-component>
</div>
<script>
Vue.component('my-component',{
template:'<div class="quanju">我是全局注冊的組件</div>'
})
var app = new Vue({
el: '#app',
data: {
},
components: {
'app-component': {
template: '<div class="jubu">我是局部注冊的組件</div>'
}
}
})
</script>
</body>
</html>
二、父組件給子組件傳遞信息的代碼
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<a-component msg="我是父組件傳遞的數(shù)據(jù)"></a-component>
<hr>
<p>提示:通過輸入的數(shù)據(jù)改變正方形的邊長</p>
<input type="text" v-model="length">
<b-component :length="length"></b-component>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
length: '10'
},
components: {
'a-component': {
props: ['msg'],
template: '<div class="jubu">{{count}}</div>',
data: function(){
return {
count: this.msg
}
}
},
'b-component': {
props: ['length'],
template: '<div :style="style"></div>',
computed: {
style: function(){
return {
width: this.length + 'px',
height: this.length + 'px',
background: 'red',
marginTop: '10px'
}
}
},
data: function(){
return {}
}
}
}
})
</script>
</body>
</html>
三、子組件給父組件傳遞信息的代碼
1. 自定義事件:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<p>通過按鈕給父組件傳遞數(shù)據(jù)</p>
<div class="content">
<p class="btn-title">您現(xiàn)在的銀行卡余額是:{{total}}元</p>
<app-component @change="handleTotal"></app-component>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
total: 2000
},
components: {
'app-component': {
template: '<div class="btn">\
<button @click="increase">+100</button>\
<button @click="decrease">-100</button>\
</div>\
</div>',
methods: {
increase: function(){
this.count += 100
this.$emit('change', this.count)
},
decrease: function(){
this.count = this.count>0? this.count-100:0
this.$emit('change',this.count)
}
},
data: function(){
return {
count: 2000
}
}
}
},
methods: {
handleTotal: function(value){
this.total = value
}
}
})
</script>
</body>
</html>
預覽鏈接
2. v-model:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<p>通過按鈕給父組件傳遞數(shù)據(jù)</p>
<div class="content">
<p class="btn-title">您現(xiàn)在的銀行卡余額是:{{total}}元</p>
<app-component v-model="total"></app-component>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
total: 2000
},
components: {
'app-component': {
template: '<div class="btn">\
<button @click="increase">+100</button>\
<button @click="decrease">-100</button>\
</div>\
</div>',
methods: {
increase: function(){
this.count += 100
this.$emit('input', this.count)
},
decrease: function(){
this.count = this.count>0? this.count-100:0
this.$emit('input',this.count)
}
},
data: function(){
return {
count: 2000
}
}
}
}
})
</script>
</body>
</html>
五、非父子組件之間傳值
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<a-component></a-component>
<b-component></b-component>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
bus: new Vue()
},
components: {
'a-component': {
template: '<div class="a">a組件<br><button @click="handle">點擊向b組件傳值</button></div>',
data: function(){
return {
msg: '我是來自a組件的內(nèi)容'
}
},
methods: {
handle: function(){
this.$root.bus.$emit('hello', this.msg)
}
}
},
'b-component': {
template: '<div class="b">b組件</div>',
data: function(){
return {
msg2: '我是來自b組件的內(nèi)容'
}
},
created: function(){
this.$root.bus.$on('hello', function(value){
alert(value)
})
}
}
}
})
</script>
</body>
</html>