组件
什么是组件,就是标签,除了浏览器自带的组件外,Vue 还允许我们自定义组件,把一个功能的模板(template)封装在一个.vue 文件中,组件的开发由于要考虑代码的复用性,会比通常的业务开发要求更高,需要有更好的可维护性和稳定性的要求。其实之前的demo就是组件,只是缺少了一点复用与交互
简单的组件
所有demo建立在有vue基础上,没有基础建议先学习vue2
<template>
<div>
home
<Count :msg="msg"></Count>
</div>
</template>
<script setup>
import { ref } from "vue";
import Count from '../components/Count.vue'
const msg = ref('我是传递的信息内容')
</script>
父组件传递的格式较为固定,使用V-bind 绑定变量名传递响应变量即可
<template>
{{msg}}
</template>
<script>
export default {
props:['msg']
}
</script>
<style scoped>
</style>
传统的Options Api是像这样接收父组件的传值,不考虑PropsType的情况
之前介绍过setup中的存在两个参数,分别是props,context,那么此处的props你没想错,就是用来可以接收父组件的传值的,但是需要props声明
<template>
{{msg}}
</template>
<script>
export default {
//如果不声明 接收到undefined
props:['msg'],
setup(props){
console.log(props) //返回Proxy对象 Proxy:{msg:'。。。。值'}
}
}
</script>
<style scoped>
</style>
在 <script setup> 中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits ,它们具备完整的类型推断并且在 <script setup> 中是直接可用的
<template>{{ msg }}</template>
<script setup>
const props = defineProps({ msg: String });
</script>
<style scoped>
</style>
那么第一个参数props主要就是这样,第二个参数context简单使用一些场景,context是一个对象,里面主要有三个属性 attrs,slots,emit,因此setup函数也可以写成 setup(props,{attrs,slots,emit})
attrs
主要是子组件上显示的标签属性值,html标签都是可以自定义属性的嘛,emit
很熟悉哈,它是通信函数的一个非常熟悉的一个关键字
<template>
<div>
home
<Count :msg="msg" name="shengchao" age="99"></Count>
</div>
</template>
<script setup>
import { ref } from "vue";
import Count from '../components/Count.vue'
const msg = ref('我是传递的信息内容')
</script>
现在 我在父组件中自定义了俩个熟悉,使用attrs
可以在子组件中接收
<template>
{{msg}}
</template>
<script>
export default {
props:['msg'],
setup(props,{attrs,slots,emit}){
console.log(props) //返回Proxy对象 Proxy:{msg:'值'}
console.log(attrs) // 返回Proxy对象 Proxy:{name:'shengchao',age:''99}
}
}
</script>
<style scoped>
</style>
当然也可以使用context去接收,那么props大家知道可以直接使用插值,也就是{{}}在标签中使用,那么attrs 也是可以的
<template>
{{msg}}
<div>名字:{{$attrs.name}}</div>
<div>年龄:{{$attrs.age}}</div>
</template>
<script>
export default {
props:['msg'],
setup(props,context){
console.log(props) //返回Proxy对象 Proxy:{msg:'值'}
}
}
</script>
<style scoped>
</style>
在语法糖中,由于没有setup函数,需要引入useAttrs,useAttrs()返回attrs对象,也可以用来赋值
<template>
{{ msg }}
<div>名字:{{ $attrs.name }}</div>
<div>年龄:{{ $attrs.age }}</div>
</template>
<script setup>
import { useAttrs } from 'vue'
const props = defineProps({ msg: String });
useAttrs()
</script>
<style scoped>
</style>
emit
父组件
<template>
<div>
home
<Count :msg="msg" @getson="getson"></Count>
</div>
</template>
<script setup>
import { ref } from "vue";
import Count from '../components/Count.vue'
const msg = ref('我是传递的信息内容')
const getson = function(v){
console.log(v)
}
</script>
子组件
<template>
{{ msg }}
<button @click="backfather">click me</button>
</template>
<script>
export default {
props: ['msg'],
setup(props, context) {
function backfather() {
context.emit('getson', '子组件传递给父组件')
}
return { backfather }
}
}
</script>
<style scoped>
</style>
button点击会触发backfather函数,接着emit函数,第一个参数是父组件自定义的触发名,第二个参数是子组件传递给父组件的值,那么这时候控制台会保警告,vue3新增了emits
对象,用来更灵活的管理emit事件,用法与props类似,可以是简单的声明,也可以做一些拦截处理操作
// 与props相同,接收父组件的自定义事件名
emits:['getson']
这样警告就消失了
子组件
<template>
{{ msg }}
<button @click="backfather">click me</button>
<button @click="backfatherAgain">click me</button>
</template>
<script>
import {ref} from 'vue'
export default {
props: ['msg'],
emits:{
getson:null, //不做任何处理
getson2: val=>{
//val 传递的值
if(val){
//一些操作
}else{
console.error('报错了')
}
}
},
setup(props, context) {
var flag = ref(false)
function backfather() {
context.emit('getson', '子组件传递给父组件')
}
function backfatherAgain(){
context.emit('getson2', flag.value)
}
return { backfather,backfatherAgain,flag}
}
}
</script>
<style scoped>
</style>
父组件
<template>
<div>
home
<Count :msg="msg" @getson="getson" @getson2="getson2"></Count>
</div>
</template>
<script setup>
import { ref } from "vue";
import Count from '../components/Count.vue'
const msg = ref('我是传递的信息内容')
const getson = function(v){
console.log(v) //子组件传递给父组件
}
const getson2 = function(v){
console.log(v) //false
}
</script>
需要注意的是,验证只是做一些操作,不会改变传递的值 所以父组件接收到的还是false
在语法糖中需要使用defineEmits去声明
<template>
{{ msg }}
<button @click="backfather">click me</button>
<button @click="backfatherAgain">click me</button>
</template>
<script setup>
const emit = defineEmits(["getson","getson2"])
const props = defineProps({ msg: String });
const backfather = function(){
emit('getson','子组件传递给父组件')
}
const backfatherAgain = function(){
emit('getson2', false)
}
</script>
<style scoped>
</style>
实现一个评分星级组件
父组件
<template>
<div>
<Rate :score="score"></Rate>
</div>
</template>
<script setup>
import { ref } from "vue";
import Rate from '../components/Rate.vue'
const score = ref(3)
</script>
新建一个子组件,如果你用的是vscode,建议自己自定义代码片段,
点击左上角文件=>首选项=>用户片段
选择全局,这个就随便了,可以复制我的
"Print to console": {
"prefix": "vsetup",
"body": [
"<template>",
"$1",
"</template>",
"$2",
"<script setup>",
"$3",
"</script>",
"$4",
"<style scoped>",
"$5",
"</style>"
],
"description": "Log output to console"
}
输入vsetup就可以自动打出以上代码了,了解详情可以自行百度学习一下,
子组件
<template>
{{rate}}
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({ score: Number });
let rate = computed(()=>"★★★★★☆☆☆☆☆".slice(5 - props.score, 10 - props.score))
</script>
<style scoped>
</style>

我们加点颜色上去,但是一个个加是不是太憨了,我们可以在父组件中,使用属性去加自己喜欢的颜色,当然也可以使用传值,这个无所谓,灵活使用一下我们学习的东西
<template>
<div>
<Rate :score="score"></Rate>
<Rate :score="1" color="red"></Rate>
<Rate :score="5" color="green"></Rate>
<Rate :score="4" color="orange"></Rate>
</div>
</template>
<script setup>
import { ref } from "vue";
import Rate from '../components/Rate.vue'
const score = ref(3)
</script>
子组件
<template>
<div :style="fontstyle">
{{ rate }}
</div>
</template>
<script setup>
import { computed,useAttrs } from 'vue'
const props = defineProps({ score: Number });
let rate = computed(() => "★★★★★☆☆☆☆☆".slice(5 - props.score, 10 - props.score))
//自定义一些颜色
const colorObj = {
'black': '#000',
'white': '#fff',
'red': '#f5222d',
'orange': '#fa541c',
'yellow': '#fadb14',
'green': '#73d13d',
'blue': '#40a9ff',
}
let attrs = useAttrs()
let fontstyle = computed(()=>{
return `color:${colorObj[attrs.color]}`
})
</script>
<style scoped>
</style>

是不是有那味了!!
再创建一个组件,做一个交互组件
<template>
<div>
<div class="rate" @mouseout="mouseOut">
<span @mouseover="mouseOver(num)" v-for="num in 5" :key="num">☆</span>
<span class="hollow" :style="fontwidth">
<span @mouseover="mouseOver(num)" v-for="num in 5" :key="num">★</span>
</span>
</div>
</div>
</template>
<script setup>
import { ref,computed } from 'vue'
const props = defineProps({ score: Number });
let width = ref(props.score)
function mouseOver(i) {
width.value = i
}
function mouseOut(num) {
width.value = num
}
const fontwidth = computed(() => `width:${width.value}em;`)
</script>
<style scoped>
.rate {
position: relative;
display: inline-block;
}
.rate > span.hollow {
position: absolute;
display: inline-block;
top: 0;
left: 0;
width: 0;
overflow: hidden;
}
</style>
这是一个简易的鼠标hover评分效果,当然没那么完美,凑合看,复制代码看到效果后发现移开鼠标值没有变,因为改变的值没有返回给父组件
import { ref,computed } from 'vue'
const props = defineProps({ score: Number });
const emits = defineEmits(['getscore'])
let width = ref(props.score)
function mouseOver(i) {
width.value = i
}
function mouseOut(num) {
emit('getscore',num)
}
const fontwidth = computed(() => `width:${width.value}em;`)
引入emits 返回一下就好了,当然父组件需要设置接收事件
网友评论