一、深入理解v-model
v-model 其實(shí)就是 v-bind: 和 v-on: 的語法糖。
將自定義組件,綁定數(shù)據(jù)的屬性改成value,監(jiān)聽事件的名稱改成input,也可以使用v-model簡寫形式。
<div id="app">
<ul class="list">
<li>{{yf.label}}--{{yf.count}}</li>
<li>{{kz.label}}--{{kz.count}}</li>
</ul>
<b-counter :label="yf.label" :value="yf.count" @input="yf.count=$event"></b-counter>
<b-counter :label="kz.label" v-model="kz.count"></b-counter>
</div>
Vue.component('b-counter', {
template: `
<div class="counter">
<div class="label">{{label}}</div>
<div class="btns">
<button @click="myCount--" :disabled="myCount===minCount">-</button>
<input readonly class="text" type="text" :value="myCount">
<button @click="myCount++" :disabled="myCount===maxCount">+</button>
</div>
</div>
`,
props: {
//文本
label: {
type: String,
required: false,
},
//數(shù)量
value: {
type: Number,
required: true
},
//最大值
maxCount: {
type: Number,
default: 999
},
//最小值
minCount: {
type: Number,
default: 1
}
},
//定義數(shù)據(jù)
data() {
return {
myCount: this.value
}
},
//監(jiān)聽器
watch: {
myCount(val) {
// 觸發(fā)一個(gè)自定義事件,事件名稱是input
this.$emit('input', val)
}
}
})
let vm = new Vue({
el: '#app',
data: {
// 衣服
yf: {
label: '衣服',
count: 5
},
// 褲子
kz: {
label: '褲子',
count: 5
}
}
})
效果:

二、.sync修飾符
綁定屬性采用:xx.sync修飾符,可以省略update:xx對應(yīng)的事件綁定。約定①:屬性綁定必須是xx.sync;約定②:自定義事件必須是update:xx。
如果觸發(fā)的事件名稱是update:屬性名,那么就可以使用.sync修飾符簡化調(diào)用的過程。
總結(jié):如果組件只回傳一份數(shù)據(jù),用v-model。如果組件回傳多份數(shù)據(jù),用.sync修飾符。
<div id="app">
<div>衣服:{{yf}},褲子:{{kz}},鞋子:{{xz}}</div>
<!-- 屬性綁定必須是:xx.sync -->
<!-- :yf.sync="yf" @update:yf="yf=$event"可以簡寫成 :yf.sync="yf" -->
<b-counter :yf.sync="yf" @update:yf="yf=$event" :kz.sync="kz" :xz.sync="xz"></b-counter>
</div>
Vue.component('b-counter', {
template: `
<div>
<div class="counter">
<div class="label">衣服:</div>
<div class="btns">
<button @click="yfCount--">-</button>
<input readonly class="text" type="text" :value="yfCount">
<button @click="yfCount++">+</button>
</div>
</div>
<div class="counter">
<div class="label">褲子:</div>
<div class="btns">
<button @click="kzCount--">-</button>
<input readonly class="text" type="text" :value="kzCount">
<button @click="kzCount++">+</button>
</div>
</div>
<div class="counter">
<div class="label">鞋子:</div>
<div class="btns">
<button @click="xzCount--">-</button>
<input readonly class="text" type="text" :value="xzCount">
<button @click="xzCount++">+</button>
</div>
</div>
</div>
`,
props: ['yf', 'kz', 'xz'],
data() {
return {
yfCount: this.yf,
kzCount: this.kz,
xzCount: this.xz
}
},
watch:{
yfCount(val){
// 自定義事件必須是update:xx
this.$emit('update:yf',val)
},
kzCount(val){
this.$emit('update:kz',val)
},
xzCount(val){
this.$emit('update:xz',val)
}
}
})
new Vue({
el: '#app',
data: {
//衣服數(shù)量
yf: 5,
//褲子數(shù)量
kz: 5,
//鞋子數(shù)量
xz: 5
}
})
三、插槽
1、具名插槽
具名插槽:在組件內(nèi)部通過slot標(biāo)簽定義插槽,再通過name屬性給插槽定義名字,這樣的插槽稱之為具名插槽。
插槽的默認(rèn)名稱是:default。如果有多個(gè)插槽,允許其中一個(gè)插槽不定義名稱。
在template標(biāo)簽中采用v-slot:插槽名稱的方式,指定內(nèi)容在哪一個(gè)具體的插槽中呈現(xiàn)。#是v-slot:的簡寫。
<div id="app">
<b-box>
<!-- 在template組件中采用v-slot:插槽名稱的方式,指定使用哪個(gè)插槽 -->
<template v-slot:house>
<div>有5套房子</div>
</template>
<!-- #是v-slot:的簡寫 -->
<template #car>
<div>有8輛汽車</div>
</template>
<template v-slot:money>
<div>有3千萬存款</div>
</template>
</b-box>
</div>
Vue.component('b-box', {
template: `
<div class="box">
<div class="item">
<h2>房產(chǎn)信息</h2>
<slot name="house"></slot>
</div>
<div class="item">
<h2>車輛信息</h2>
<slot name="car"></slot>
</div>
<div class="item">
<h2>存款信息</h2>
<slot name="money"></slot>
</div>
</div>
`
})
new Vue({
el: '#app',
})
2、作用域插槽
作用域插槽必須是具名插槽;在作用域插槽上,可以通過v-bind:綁定屬性,綁定的屬性通過指定的作用域變量(通常會定義scope變量)去接收。
<div id="app">
<b-box>
<template v-slot:list="scope">
<button @click="priceDown(scope.list,scope.index)">降價(jià)</button>
<button @click="priceUp(scope.list,scope.index)">加價(jià)</button>
<button @click="scope.list.splice(scope.index,1)">刪除</button>
</template>
</b-box>
</div>
Vue.component('b-box', {
template:`
<div>
<ul>
<li v-for="(item,index) in list" :key="index">
<span>{{item.id}}-{{item.name}}-{{item.price}}</span>
<slot name="list" v-bind:index="index" v-bind:list="list"></slot>
</li>
</ul>
</div>
`,
data() {
return {
list:[
{
id:1001,
name:'蘋果手機(jī)',
price:5999
},
{
id:1002,
name:'華為手機(jī)',
price:6999
},
{
id:1003,
name:'小米手機(jī)',
price:7999
},
{
id:1004,
name:'三星手機(jī)',
price:8999
}
]
}
}
})
new Vue({
el:'#app',
methods: {
priceDown(list,index){
list[index].price-=1000
},
priceUp(list,index){
list[index].price+=1000
}
},
})
四、mixin混入
混入 (mixin) 提供了一種非常靈活的方式,來分發(fā) Vue 組件中的可復(fù)用功能。mixin()方法的參數(shù)是配置對象,Vue實(shí)例可以配置的東西,它都可以配置。比如:數(shù)據(jù),方法,生命周期鉤子函數(shù),計(jì)算屬性,偵聽器,過濾器,等等...
全局混入的內(nèi)容,之后創(chuàng)建的所有Vue實(shí)例包括組件實(shí)例都將擁有。在創(chuàng)建Vue實(shí)例時(shí),會將mixin里面的成員跟Vue實(shí)例自身的成員進(jìn)行合并,如果沖突了,最終采用Vue實(shí)例身上的成員。
特別注意:生命周期鉤子不是合并,是疊加執(zhí)行,是先執(zhí)行mixin里面的生命周期鉤子,再執(zhí)行Vue實(shí)例里面的生命周期鉤子。
<div id="app1">
<p>姓名:<input type="text" v-model="name"></p>
<p>年齡:<input type="text" v-model.number="age"><button @click="age++">++</button></p>
<p>性別:<input type="text" v-model="sex"></p>
<p>稅前薪資:<input type="text" v-model="salary">稅后薪資:<input type="text" :value="salary2"></p>
<button @click='sayHi'>sayHi</button>
<div>汽車信息:{{car}}</div>
</div>
<hr>
<div id="app2">
<p>姓名:<input type="text" v-model="name"></p>
<p>年齡:<input type="text" v-model.number="age"><button @click="age++">++</button></p>
<p>性別:<input type="text" v-model="sex"></p>
<p>稅前薪資:<input type="text" v-model="salary">稅后薪資:<input type="text" :value="salary2"></p>
<button @click='sayHi'>sayHi</button>
<div>飛機(jī)信息:{{plane}}</div>
</div>
//全局混入,給所有的Vue實(shí)例混入統(tǒng)一的成員 -- 注意:必須要先執(zhí)行
Vue.mixin({
data() {
return {
name: '',
age: 0,
sex: '男',
salary: 1000
}
},
computed: {
salary2() {
return this.salary * 0.88
}
},
methods: {
sayHi() {
alert(`大家好!我叫${this.name},性別是${this.sex},今年${this.age}歲`)
}
},
watch: {
age(val) {
if (val > 100) {
alert('年齡不能超過100歲')
this.age = 100
}
}
},
mounted() {
console.log('mixin:組件掛載完成');
},
})
// 創(chuàng)建第一個(gè)Vue的實(shí)例--操作的容器是#app1
new Vue({
el: '#app1',
data() {
return {
car: {
name: '奔馳',
price: '100W'
}
}
},
// 注意:先執(zhí)行混入的生命周期,再執(zhí)行自己的生命周期
mounted() {
console.log('app1:組件掛載完成');
},
})
// 創(chuàng)建第二個(gè)Vue的實(shí)例--操作的容器是#app2
new Vue({
el: '#app2',
data() {
return {
plane: {
name: '波音747',
price: '100Y'
}
}
},
})
效果:

五、混入ajax的基本操作
ajax請求相關(guān)的操作,也可以通過mixin混入給Vue。
<div id="app1">
<button @click="getSubjects">請求課程數(shù)據(jù)</button>
<div>
{{subjects}}
</div>
</div>
Vue.mixin({
methods: {
//get請求
async $get(url, params) {
let { data } = await axios.get(url, { params })
return data
},
//post請求
async $post(url, params) {
let { data } = await axios.post(url, params)
return data
}
}
})
new Vue({
el: '#app1',
data() {
return {
//課程數(shù)組
subjects: []
}
},
methods: {
async getSubjects() {
let { data } = await this.$get('http://www.bingjs.com:81/Subject/GetSubjectsConditionPages')
this.subjects = data
}
}
})