上篇:VUE从入门到入坑—06. 父子组件通信 / 几种常用的第三方组件库
一、深入理解v-model
1.v-model,可以实现数据双向的绑定,其实就是一个由属性和事件组成的语法糖。
元素类型 | 属性 | 事件 |
---|---|---|
input[type=text]、textarea | value | input |
input[checkbox]、input[radio] | checked | change |
select | value | change |
2.将自定义组件,绑定数据的属性改成value,监听事件的名称改成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,
},
//数量
value: {
type: Number,
required: true
},
//最大值
maxCount: {
type: Number,
default: 999
},
//最小值
minCount: {
type: Number,
default: 1
}
},
//定义数据
data() {
return {
myCount: this.value
}
},
//监听器
watch: {
myCount(val) {
// 触发一个自定义事件,事件名称是input
this.$emit('input', val)
}
}
})
let vm = new Vue({
el: '#app',
data: {
// 衣服
yf: {
label: '衣服',
count: 5
},
// 裤子
kz: {
label: '裤子',
count: 5
}
}
})
效果:
二、插槽
1.组件中的插槽slot,用于在组件的内部定义插槽,组件标签之间的所有html内容,会在插槽所在位置呈现。
<div id="app">
<b-tab :list="list" :active="activeIndex">
<h2>全国著名小吃</h2>
</b-tab>
</div>
Vue.component('b-tab', {
template: `
<div class="tab">
<slot></slot>
<ul class="titles">
<li @click="activeIndex=index" :class="{active:activeIndex===index}" v-for="(item,index) in list" :key="index">{{item.title}}</li>
</ul>
<ul class="contents">
<li v-show="activeIndex===index" v-for="(item,index) in list" :key="index">{{item.content}}</li>
</ul>
</div>
`,
props: ['list', 'active'],
data() {
return {
activeIndex: this.active
}
}
})
new Vue({
el: '#app',
data: {
// 高亮索引
activeIndex: 0,
list: [
{
title: '北京',
content: '北京的糖葫芦真好吃'
},
{
title: '南京',
content: '南京的盐水鸭真好吃'
},
{
title: '武汉',
content: '武汉的热干面真好吃'
},
{
title: '长沙',
content: '长沙的臭豆腐真好吃'
}
]
}
})
效果:
2.具名插槽,在组件内部通过slot标签定义插槽,再通过name属性给插槽定义名字,这样的插槽称之为具名插槽。插槽的默认名称是:default。如果有多个插槽,允许其中一个插槽不定义名称。在template标签中采用v-slot:插槽名称的方式,指定内容在哪一个具体的插槽中呈现。#是v-slot:的简写。
<div id="app">
<b-box>
<!-- 在template组件中采用v-slot:插槽名称的方式,指定使用哪个插槽 -->
<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>房产信息</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',
})
3.作用域插槽,必须是具名插槽;在作用域插槽上,可以通过v-bind:绑定属性,绑定的属性通过指定的作用域变量(通常会定义scope变量,变量名随意取)去接收。
<div id="app">
<b-box>
<template v-slot:list="scope">
<button @click="priceDown(scope.list,scope.index)">降价</button>
<button @click="priceUp(scope.list,scope.index)">加价</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:'苹果手机',
price:5999
},
{
id:1002,
name:'华为手机',
price:6999
},
{
id:1003,
name:'小米手机',
price:7999
},
{
id:1004,
name:'三星手机',
price:8999
}
]
}
}
})
new Vue({
el:'#app',
methods: {
priceDown(list,index){
list[index].price-=1000
},
priceUp(list,index){
list[index].price+=1000
}
},
})
三、.sync修饰符
1.绑定属性采用:xx.sync修饰符,可以省略update:xx对应的事件绑定。
2.必要条件①:属性绑定必须是xx.sync;必要条件②:自定义事件必须是update:xx。
3.如果触发的事件名称是update:属性名,那么就可以使用.sync修饰符简化调用的过程。
4.总结:如果组件只回传一份数据,用v-model。如果组件回传多份数据,用.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: {
//衣服数量
yf: 5,
//裤子数量
kz: 5,
//鞋子数量
xz: 5
}
})
四、mixin混入
1.混入 (mixin),提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。mixin()方法的参数是配置对象,Vue实例可以配置的东西,它都可以配置。比如:数据,方法,生命周期钩子函数,计算属性,侦听器,过滤器,等等。
2.全局混入的内容,之后创建的所有Vue实例包括组件实例都将拥有。在创建Vue实例时,会将mixin里面的成员跟Vue实例自身的成员进行合并,如果冲突了,最终采用Vue实例身上的成员。
特别注意:生命周期钩子不是合并,是叠加执行,是先执行mixin里面的生命周期钩子,再执行Vue实例里面的生命周期钩子。
<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>飞机信息:{{plane}}</div>
</div>
//全局混入,给所有的Vue实例混入统一的成员 -- 注意:必须要先执行
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:组件挂载完成');
},
})
// 创建第一个Vue的实例--操作的容器是#app1
new Vue({
el: '#app1',
data() {
return {
car: {
name: '奔驰',
price: '100W'
}
}
},
// 注意:先执行混入的生命周期,再执行自己的生命周期
mounted() {
console.log('app1:组件挂载完成');
},
})
// 创建第二个Vue的实例--操作的容器是#app2
new Vue({
el: '#app2',
data() {
return {
plane: {
name: '波音747',
price: '100Y'
}
}
},
})
效果:
五、混入ajax的基本操作
1.请求ajax相关的操作的时候也可以通过mixin混入给Vue。
<div id="app1">
<button @click="getSubjects">请求课程数据</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 {
//课程数组
subjects: []
}
},
methods: {
async getSubjects() {
let { data } = await this.$get('http://www.bingjs.com:81/Subject/GetSubjectsConditionPages')
this.subjects = data
}
}
})