组件上的v-model(数据传递),props验证,自定义事件,计算属性
一.props验证
在封装组件时对外界传递过来的props数据进行合法性校验,从而防止数据不合法问题。
1.基础类型检查
String,Number,Boolean,Array,Object,Date,Function,Symbol
2.多个可能的类型
3.必须项校验
4.属性默认值
5.自定义验证函数
<template>
<div>父组件</div>
<DemoSon :msg="msg" :title="title" type="abc"></DemoSon>
</template>
<script>
import DemoSon from './son.vue'
export default {
name:'demo',
components:{DemoSon},
data(){
return{
msg:'111',
title:'abc'
}
}
}
</script>
<template>
<div>子组件</div>
<p>{{ msg }}{{ title }}</p>
</template>
<script>
export default {
name:'demo-son',
props:{
msg:{
type:[Number,String],
required:true,
default:10
},
title:String,
type:{
validator(value){
// true成功,false失败
return ['success','warning','danger'].indexOf(value)!==-1
}
}
}
}
</script>
二.计算属性computed
计算属性本质上是一个function函数,可以监听data中数据的变化,并return一个计算后的值,供组件渲染dom时使用。
计算属性会缓存计算的结果,只有在计算属性的依赖项发生变化时,才会重新计算。
计算属性只能当作普通数据项使用,不能当作方法调用(不能使用括号调用)。
<template>
<div>
<input type="text" v-model.number="num">
</div>
<div>{{ plus }}</div>
</template>
<script>
export default {
name:'demo',
data(){
return{
num:111
}
},
computed:{
plus(){
return this.num*2
}
}
}
</script>
<template>
<div>
<table border>
<tr><td class="goodState">状态</td>
<td class="goodId">#</td>
<td class="goodName">名称</td>
<td class="goodPrice">单价</td>
<td class="goodNum">数量</td>
<td class="goodTime">价格</td>
<td class="goodAction">操作</td>
</tr>
<tr v-for="(item,index) in goods.data" :key="item.id">
<td class="goodState">
<input type="checkbox" v-model="item.state" :id="item.id">
<label :for="item.id">{{item.state===true?"上架":"下架"}}</label>
</td>
<td class="goodId">{{index+1}}</td>
<td class="goodName">{{item.name}}</td>
<td class="goodNum">{{item.price}}</td>
<td class="goodNum">{{item.num}}</td>
<td class="goodTime">{{item.num*item.price}}</td>
<td class="goodAction"><b @click="addFn(item.id,item.num)">+</b><b @click="minusFn(item.id,item.num)">-</b></td>
</tr>
<tr>
<td>总数:{{ number }}</td>
<td>总计:{{ total }}</td>
</tr>
</table>
</div>
</template>
<script setup>
import { ref,reactive, computed } from "vue"
const goods=reactive({data:[{
id:'1',
name:'苹果',
state:true,
price:2,
num:0,
time:'2020-11-03 11:00:00'
},
{
id:'2',
name:'梨',
state:true,
price:2,
num:0,
time:'2020-11-03 11:00:00'
},
{
id:'3',
name:'香蕉',
state:true,
price:2,
num:0,
time:'2020-11-03 11:00:00'
}
]
})
const minusFn=(id,num)=>{
if(num>0){
for(let i=0;i<goods.data.length;i++){
if(goods.data[i].id===id){
goods.data[i].num= num-1
}
}
}
}
const addFn=(id,num)=>{
for(let i=0;i<goods.data.length;i++){
if(goods.data[i].id===id){
goods.data[i].num= num+1
}
}
}
const number = computed(()=>{
let n=0;
goods.data.forEach((item)=>{
if(item.state){
n+=item.num
}
})
return n;
})
const total = computed(()=>{
let sum=0;
goods.data.forEach((item)=>{
if(item.state){
sum+=item.num*item.price
}
})
return sum;
})
</script>
<style scoped lang="scss">
td{
width: 200px;
height: 40px;
}
b{
margin-right: 20px;
}
</style>
三.自定义事件
1.声明自定义事件
2.触发自定义事件
3.监听自定义事件
<template>
<div class="card">
{{ count }}
<Son @changeCount="changeCount"></Son>
</div>
</template>
<script >
import Son from './son.vue'
export default{
name:'index',
components: {
Son
},
data(){
return{
count:0
}
},
methods:{
changeCount(str){
this.count = str
}
}
}
</script>
<template>
<button type="button" @click="clickFn">按钮</button>
</template>
<script>
export default{
name:'son',
emits:['changeCount'],
methods:{
clickFn(){
this.$emit('changeCount',1)
}
}
}
</script>
<template>
<div class="card">
{{ count }}
<Son @changeCount="changeCount"></Son>
</div>
</template>
<script setup>
import { ref } from 'vue'
import Son from './son.vue'
const count = ref(0)
const changeCount = (str)=>{
count.value = str
}
</script>
<template>
<button type="button" @click="clickFn">按钮</button>
</template>
<script setup>
const emit = defineEmits('changeCount')
const clickFn = () => {
emit('changeCount',1)
}
</script>
四.组件上v-model
1.父传子
a.父组件通过v-bind属性绑定的形式,把数据传递给子组件
b.子组件中,通过props接受子组件传递过来的数据
<template>
<div>父组件</div>
<DemoSon :msg="msg"></DemoSon>
</template>
<script>
import DemoSon from './son.vue'
export default {
name:'demo',
components:{DemoSon},
data(){
return{
msg:'111'
}
}
}
</script>
<template>
<div>子组件</div>
<p>{{ msg }}</p>
</template>
<script>
export default {
name:'demo-son',
props:{
msg:{
type:[Number,String],
required:true,
default:10
}
}
}
</script>
2.子传父
a.在v-bind指令前添加v-model指令
b.在子组件中声明emits自定义事件,格式为update:xxx
c.调用$emit()触发自定义事件,更新父组件中的数据
<template>
<div class="card">
{{ count }}
<Son v-model:number="count"></Son>
</div>
</template>
<script >
import Son from './son.vue'
export default{
name:'index',
components: {
Son
},
data(){
return{
count:0
}
}
}
</script>
<template>
{{ number }}
<button type="button" @click="clickFn">按钮</button>
</template>
<script>
export default{
name:'son',
props:['number'],
emits:['update:number'],
methods:{
clickFn(){
this.$emit('update:number',this.number+1)
}
}
}
</script>