一誓斥、v-model
1只洒、v-model的含義
v-model就是vue的雙向綁定的指令,能將頁(yè)面上控件輸入的值同步更新到相關(guān)綁定的data屬性劳坑,也會(huì)在更新data綁定屬性時(shí)候毕谴,更新頁(yè)面上輸入控件的值。
2距芬、v-model的基礎(chǔ)用法
(1)v-model 指令在表單 <input>涝开、<textarea> 及 <select> 元素上創(chuàng)建雙向數(shù)據(jù)綁定。它會(huì)根據(jù)控件類(lèi)型自動(dòng)選取正確的方法來(lái)更新元素框仔。
(2)v-model 本質(zhì)上不過(guò)是語(yǔ)法糖舀武。它負(fù)責(zé)監(jiān)聽(tīng)用戶的輸入事件以更新數(shù)據(jù),并對(duì)一些極端場(chǎng)景進(jìn)行一些特殊處理离斩。
(3)v-model 在內(nèi)部為不同的輸入元素使用不同的 property 并拋出不同的事件:
text 和 textarea 元素使用 value property 和 input 事件银舱;
checkbox 和 radio 使用 checked property 和 change 事件;
select 字段將 value 作為 prop 并將 change 作為事件跛梗。
3寻馏、使用示例
當(dāng)你在input輸入框中輸入內(nèi)容的時(shí)候,上面p標(biāo)簽里面的數(shù)據(jù)實(shí)時(shí)發(fā)生變化(實(shí)際上是name和age兩個(gè)數(shù)據(jù)發(fā)生了響應(yīng)式變化)核偿。相反改變代碼中的name和age值诚欠,input輸入框中值也會(huì)實(shí)時(shí)變化。
其實(shí)現(xiàn)原理如下:
<div id="app">
<p>姓名:<input :value="name" @input="updateName">{{name}}</p>
<!-- v-model 其實(shí)就是v-bind: 和 v-on: 的語(yǔ)法糖 -->
<p>年齡:<input :value="age" @input="updateAge">{{age}}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
name: '張三',
age: 20,
},
methods: {
updateName(e) {
this.name = e.target.value;
},
updateAge(e){
this.age = e.target.value;
}
},
})
</script>
實(shí)現(xiàn)效果如下:
實(shí)現(xiàn)原理:
①首先input輸入框通過(guò)屬性綁定:value="name"&:value="age"得到響應(yīng)數(shù)據(jù)name&age.
②定義兩個(gè)函數(shù),通過(guò)e.target得到input框中的value值轰绵。
③最后通過(guò)input輸入框@input事件監(jiān)聽(tīng)粉寞,綁定兩個(gè)函數(shù)(updateName,updateAge)左腔,將input框中的value值傳給name&age唧垦。
vue中的v-model能夠?qū)崿F(xiàn)數(shù)據(jù)的雙向綁定,也是vue的最突出的優(yōu)勢(shì)翔悠。
v-model實(shí)際上是v-bind: 和 v-on:的語(yǔ)法糖业崖。它的實(shí)現(xiàn)原理主要包括屬性綁定和事件監(jiān)聽(tīng)兩部分野芒。
具體使用如下:
<div id="app">
<p>姓名:<input v-model="name">{{name}}</p>
<!-- v-model 其實(shí)就是v-bind: 和 v-on: 的語(yǔ)法糖 -->
<p>年齡:<input v-model="age">{{age}}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
name: '張三',
age: 20,
},
})
</script>
實(shí)現(xiàn)效果與v-bind: + v-on:相同:
二蓄愁、sync修飾符
在有些情況下,我們可能需要對(duì)一個(gè) prop 進(jìn)行“雙向綁定”狞悲。但真正的雙向綁定會(huì)帶來(lái)維護(hù)上的問(wèn)題撮抓,因?yàn)樽咏M件可以變更父組件,且在父組件和子組件兩側(cè)都沒(méi)有明顯的變更來(lái)源摇锋。
當(dāng)我們需要在某個(gè)標(biāo)簽中綁定多個(gè)屬性時(shí)丹拯,就選擇使用.sync修飾符。
.sync修飾符的約定:
① 屬性綁定必須是xx.sync
② 自定義事件必須是update:xx格式
③ 采用xx.sync修飾符荸恕,可以省略u(píng)pdate:xx對(duì)應(yīng)的事件綁定
使用方法如下:
<div id="app">
<div>
衣服:{{yf}},褲子:{{kz}},鞋子:{{xz}}
</div>
<hr>
<!-- 綁定屬性是乖酬,采用xx.sync修飾符,可以省略u(píng)pdate:xx對(duì)應(yīng)的事件綁定 -->
<!-- 約定1:屬性綁定必須是xx.sync -->
<b-counter :yf.sync="yf" :kz.sync="kz" :xz.sync="xz"></b-counter>
</div>
<script>
Vue.config.productionTip = false
Vue.component('b-counter', {
template: `
<div>
<div class='conter'>
<div class='label'>衣服</div>
<div class='btns'>
<button @click='yfCount--' class='btn'>-</button>
<input type='text' readonly class='txt' :value='yfCount'>
<button @click='yfCount++' class='btn'>+</button>
</div>
</div>
<div class='conter'>
<div class='label'>褲子</div>
<div class='btns'>
<button @click='kzCount--' class='btn'>-</button>
<input type='text' readonly class='txt' :value='kzCount'>
<button @click='kzCount++' class='btn'>+</button>
</div>
</div><div class='conter'>
<div class='label'>鞋子</div>
<div class='btns'>
<button @click='xzCount--' class='btn'>-</button>
<input type='text' readonly class='txt' :value='xzCount'>
<button @click='xzCount++' class='btn'>+</button>
</div>
</div>
</div>`,
props: ['yf', 'kz', 'xz'],
data() {
return {
yfCount: this.yf,
kzCount: this.kz,
xzCount: this.xz,
}
},
// 監(jiān)聽(tīng)器
watch:{
yfCount(val){
// 約定2:自定義事件必須是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
}
})
</script>
三融求、插槽
插槽的含義
插槽就是子組件中的提供給父組件使用的一個(gè)占位符咬像,用<slot></slot> 表示,父組件可以在這個(gè)占位符中填充任何模板代碼生宛,如 HTML县昂、組件等,填充的內(nèi)容會(huì)替換子組件的<slot></slot>標(biāo)簽陷舅。
1倒彰、匿名插槽
匿名插槽,我們又可以叫它單個(gè)插槽或者默認(rèn)插槽莱睁。與具名插槽相對(duì)待讳,它不需要設(shè)置name屬性。(它隱藏的name屬性為default仰剿。)
示例:
① 在vue子組件中定義一個(gè)匿名插槽
<script>
Vue.config.productionTip = false
Vue.component('b-box', {
template: `
<div class="box">
<div class="item">
<h2>插槽</h2>
<slot></slot>
</div>
</div>
`
})
new Vue({
el: '#app',
})
</script>
② 在頁(yè)面中使用子組件標(biāo)簽创淡,并寫(xiě)入內(nèi)容
<div id="app">
<b-box>
<!-- 如果沒(méi)有slot插槽,這里的內(nèi)容將不會(huì)顯示 -->
<div>我是匿名插槽</div>
</b-box>
</div>
效果如圖:
2酥馍、具名插槽
插槽有一個(gè)特殊的屬性:name辩昆,這個(gè)屬性可以用來(lái)定義多個(gè)插槽。
在向具名插槽提供內(nèi)容的時(shí)候旨袒,我們可以在一個(gè) <template> 元素上使用 v-slot 指令汁针,并以 v-slot 的參數(shù)的形式提供其名稱(chēng)术辐。
v-slot:的簡(jiǎn)寫(xiě)為#
使用方法如下:
<div id="app">
<b-box>
<template v-slot:slot1>
<div>我是插槽1</div>
</template>
<template v-slot:slot2>
<div>我是插槽2</div>
</template>
<template v-slot:slot3>
<div>我是插槽3</div>
</template>
</b-box>
</div>
<script>
Vue.config.productionTip = false
Vue.component('b-box', {
template: `
<div class="box">
<div class="item">
<h2>插槽1</h2>
<slot name="slot1"></slot>
</div>
<div class="item">
<h2>插槽2</h2>
<slot name="slot2"></slot>
</div>
<div class="item">
<h2>插槽3</h2>
<slot name="slot3"></slot>
</div>
</div>
`
})
new Vue({
el: '#app',
})
</script>
實(shí)現(xiàn)效果如下:3、作用域插槽
作用域插槽其實(shí)就是可以傳遞數(shù)據(jù)的插槽施无。子組件中的一些數(shù)據(jù)想在父組件中使用辉词,必須通過(guò)規(guī)定的方法來(lái)傳遞。
作用域插槽必須是具名插槽,在作用域插槽上可以通過(guò)v-bind:綁定屬性猾骡,綁定的屬性瑞躺,通過(guò)指定的作用域變量是接收 。
綁定在元素上的 屬性被稱(chēng)為插槽 prop⌒讼耄現(xiàn)在在父級(jí)作用域中幢哨,我們可以使用帶值的 v-slot 來(lái)定義我們提供的插槽 prop 的名字:
//default可以省略,簡(jiǎn)寫(xiě)為v-slot=" "嫂便,slotProps是自定義的名字捞镰,
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
具體使用如下:
<div id="app">
<!-- 作用域插槽必須是具名插槽,在作用域插槽上可以通過(guò)v-bind:綁定屬性,綁定的屬性毙替,通過(guò)指定的作用域變量是接收 -->
<b-box>
<template #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>
<script>
Vue.config.productionTip = false
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: '蘋(píng)果手機(jī)',
price: 6799
},
{
id: 1002,
name: '華為手機(jī)',
price: 5999
},
{
id: 1003,
name: '小米手機(jī)',
price: 1799
},
{
id: 1004,
name: '紅米手機(jī)',
price: 3499
},
]
}
},
})
new Vue({
el: '#app',
methods: {
priceDown(list,index){
list[index].price-=1000
},
priceUp(list,index){
list[index].price+=1000
},
},
})
</script>
四岸售、混入
混入 (mixin) 提供了一種非常靈活的方式,來(lái)分發(fā) Vue 組件中的可復(fù)用功能厂画。一個(gè)混入對(duì)象可以包含任意組件選項(xiàng)凸丸。當(dāng)組件使用混入對(duì)象時(shí),所有混入對(duì)象的選項(xiàng)將被“混合”進(jìn)入該組件本身的選項(xiàng)袱院。
1屎慢、選項(xiàng)合并
當(dāng)組件和混入對(duì)象含有同名選項(xiàng)時(shí),這些選項(xiàng)將以恰當(dāng)?shù)姆绞竭M(jìn)行“合并”坑填。
比如抛人,數(shù)據(jù)對(duì)象在內(nèi)部會(huì)進(jìn)行遞歸合并,并在發(fā)生沖突時(shí)以組件數(shù)據(jù)優(yōu)先脐瑰。
var mixin = {
data: function () {
return {
message: 'hello',
foo: 'abc'
}
}
}
new Vue({
mixins: [mixin],
data: function () {
return {
message: 'goodbye',
bar: 'def'
}
},
created: function () {
console.log(this.$data)
// => { message: "goodbye", foo: "abc", bar: "def" }
}
})
同名鉤子函數(shù)將合并為一個(gè)數(shù)組妖枚,因此都將被調(diào)用。另外苍在,混入對(duì)象的鉤子將在組件自身鉤子之前調(diào)用绝页。
var mixin = {
created: function () {
console.log('混入對(duì)象的鉤子被調(diào)用')
}
}
new Vue({
mixins: [mixin],
created: function () {
console.log('組件鉤子被調(diào)用')
}
})
// => "混入對(duì)象的鉤子被調(diào)用"
// => "組件鉤子被調(diào)用"
值為對(duì)象的選項(xiàng),例如 methods寂恬、components 和 directives续誉,將被合并為同一個(gè)對(duì)象。兩個(gè)對(duì)象鍵名沖突時(shí)初肉,取組件對(duì)象的鍵值對(duì)酷鸦。
var mixin = {
methods: {
foo: function () {
console.log('foo')
},
conflicting: function () {
console.log('from mixin')
}
}
}
var vm = new Vue({
mixins: [mixin],
methods: {
bar: function () {
console.log('bar')
},
conflicting: function () {
console.log('from self')
}
}
})
vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"
注意:Vue.extend() 也使用同樣的策略進(jìn)行合并
2、全局混入
當(dāng)我們存在多個(gè)組件中的數(shù)據(jù)或者功能很相近時(shí),我們就可以利用mixins將公共部分提取出來(lái)臼隔,在 mixin函數(shù)中混入統(tǒng)一的成員嘹裂。
注意:請(qǐng)謹(jǐn)慎使用全局混入,因?yàn)樗鼤?huì)影響每個(gè)單獨(dú)創(chuàng)建的 Vue 實(shí)例 (包括第三方組件)摔握。大多數(shù)情況下寄狼,只應(yīng)當(dāng)應(yīng)用于自定義選項(xiàng)。推薦將其作為插件發(fā)布氨淌,以避免重復(fù)應(yīng)用混入泊愧。
<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>
<p>汽車(chē)信息:{{car}}</p>
<button @click="sayHi">sayHi</button>
<button @click="getSubjects">請(qǐng)求課程數(shù)據(jù)</button>
<div>
{{subjects}}
</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>
<script>
Vue.config.productionTip = false
// 給所有的vue實(shí)例混入統(tǒng)一的成員
Vue.mixin({
data() {
return {
name:'',
age:0,
sex:'男',
salary:1000
}
},
methods: {
sayHi(){
alert(`大家好!我叫${this.name},性別是${this.sex}盛正,今年${this.age}歲`)
},
async $get(url,params){
let {data} = await axios.get(url,{params})
return data
},
async $post(url,params){
let {data} = await axios.post(url,params)
return data
}
},
computed:{
salary2(){
return this.salary*0.8
}
},
watch:{
age(val){
if (this.age>100) {
alert('年齡不能超過(guò)100')
this.age = 100
}
}
},
mounted() {
console.log('mixin:掛載完成')
},
})
new Vue({
el:'#app1',
data:{
car:{
name:'奔馳',
price:'100W'
},
subjects:[]
},
methods: {
async getSubjects(){
let {data} = await this.$get('http://bingjs.com:81/Subject/GetSubjectsConditionPages',)
this.subjects = data
}
},
mounted() {
console.log('app1:掛載完成')
},
})
new Vue({
el:'#app2',
mounted() {
console.log('app2:掛載完成')
},
})
</script>
效果如圖: