Vue3中如何开发组件

重点学习:vue3.0之组件通信机制defineProps(组件接收外部传来的参数)、defineEmits(向组件外部传递参数)。

1. 评级组件第一版
简单的评级需求,只需要一行代码就可以实现:
"★★★★★☆☆☆☆☆".slice(5 - rate, 10 - rate)

只需要传入评分值 rate,就可以渲染出不同数量的星星。

src/components 目录,新建 Rate.vue,然后写出下面的代码:

<!-- 这是个评分组件 -->
<template>
    <div>
        { { rate }}
    </div>
</template>

<script setup>
import { defineProps, computed } from 'vue';

let props = defineProps({  // defineProps定义接受外部传来的参数
    value: Number  // 外部传来的value参数只能是数值
})

let rate = computed(() => "★★★★★☆☆☆☆☆".slice(5 - props.value, 10 - props.value))
</script>
 
使用Rate组件,通过:value 的方式,通过属性把 value 传递给 Rate 组件。

<template>
    <h1>这是首页</h1>
    <Counter />
    <Todos />
    <Rate :value="score"></Rate>
</template>

<script setup>
import {ref} from "vue"
import Counter from '../components/Counter.vue';
import Todos from '../components/Todos.vue';
import Rate from '../components/Rate.vue';
let score = ref(3);
</script>

2. 评级组件第二版
在组件中内置一些主题颜色,加入 CSS 的内容

<!-- 这是个评分组件,可以defineProps接收外部传进来的参数
    组件使用方式:
    <Rate :value=4 theme="red"></Rate>
    参数value表示几个星,theme表示星的颜色。
 -->
<template>
    <div :style="fontstyle">  
        { { rate }}
    </div>
</template>

<script setup>
import { defineProps, computed } from 'vue';

let props = defineProps({  // defineProps定义接受外部传来的参数
    value: Number,  // 外部传来的value参数只能是数值
    theme: { type: String, default: "orange" }  // 外部传进来theme只能是字符串
})

let rate = computed(() => "★★★★★☆☆☆☆☆".slice(5 - props.value, 10 - props.value))

const themeObj = { 'black': '#00', 'white': '#fff', 'red': '#f5222d', 'orange': '#fa541c', 'yellow': '#fadb14', 'green': '#73d13d', 'blue': '#40a9ff', }

const fontstyle = computed(()=>{
    return `color:${themeObj[[props.theme]]}`
})

</script>

在…pages/home.vue中,使用Rate组件,通过:value 的方式,通过属性把 value 传递给 Rate 组件,

<template>
    <h1>这是首页</h1>
    <Counter />
    <Todos />
    <Rate :value="score"></Rate>
    <!-- theme前不需加冒号 -->
    <Rate :value=4 theme="green"></Rate>
    <Rate :value=1 theme="yellow"></Rate>
</template>

<script setup>
import {ref} from "vue"
import Counter from '../components/Counter.vue';
import Todos from '../components/Todos.vue';
import Rate from '../components/Rate.vue';
let score = ref(3);
</script>

效果如下:


3. 组件事件
让组件的星星可点击,并且让点击后的评分值能够传递到父组件。

使用 defineEmits 来对外传递事件,这样父元素就可以监听 Rate 组件内部的变化。

1. 让组件的星星可点击

前面组件中,星星都是普通的文本,没有办法单独绑定 click 事件。要想让星星可以点击,需要每个星星都用 span 包裹,并且我用 width 属性控制宽度,支持小数的评分显示。

<!-- 这是个评分组件 -->
<!-- @绑定用户交互
    mouseout:当鼠标移出某元素,移入和移出其子元素时触发。鼠标在元素内移动,只要鼠标不断在其子元素间划来划去就会不断触发。
    mouseover:当鼠标移入某元素,移入和移出其子元素时触发。鼠标在元素内移动,只要鼠标不断在其子元素间划来划去就会不断触发。
-->
<template>
    <div :style="fontstyle">
        <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 { defineProps, computed, ref } from 'vue';

let props = defineProps({  // defineProps定义接受外部传来的参数
    value: Number,  // 外部传来的value参数只能是数值
    theme: { type: String, default: "orange" }  // 外部传进来theme只能是字符串
})

let rate = computed(() => "★★★★★☆☆☆☆☆".slice(5 - props.value, 10 - props.value))

const themeObj = { 'black': '#00', 'white': '#fff', 'red': '#f5222d', 'orange': '#fa541c', 'yellow': '#fadb14', 'green': '#73d13d', 'blue': '#40a9ff', }

const fontstyle = computed(() => {
    return `color:${themeObj[[props.theme]]}`
})

// 评分宽度
let width = ref(props.value)

// 鼠标移入span,修改评分宽度
function mouseOver(i) {
    width.value = i;
}
// 鼠标离开span,展示评分宽度
function mouseOut() {
    width.value = props.value;
}

// 展示宽度
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>

 
在pages/about.vue中的template中添加如下代码<RateV2 :value="3.5" />:

<template>
    <h1>这是关于页面</h1>
    <button @click="loadding">更换图标</button>
    <button @click="reset">重置图标</button>
    <RateV2 :value="3.5" />
</template>

http://localhost:5173/#/about 页面,鼠标滑动效果:

2. 使用 defineEmits 来对外传递事件

首先,子组件RateV2.vue中的变化主要有定义发射给父组件的事件,并将此事件emits给父组件:

// ①:子组件定义发射给父组件的方法 "update-rate"
let emits = defineEmits('update-rate');

// ②:子组件要触发的onRate方法中,调用emits并传入发射给父组件的方法'update-rate'以及参数num
function onRate(num) {
    emits('update-rate', num)
}

并为实心的星星绑定一个click事件。@click="onRate(num)"

其次,父组件about.vue的变化:

绑定子组件的@update-rate事件,收到事件后执行父组件的update方法。update方法中,接受子组件的num数值,并更新父组件的score变量。

<template>
    <h1>这是关于页面</h1>
    <button @click="loadding">更换图标</button>
    <button @click="reset">重置图标</button>
    你的评分是 { { score }}
    <RateV2 :value="3.5" @update-rate="update"/>
</template>

<script setup>
import { ref } from 'vue'
import RateV2 from '../components/RateV2.vue';
import useFavicon from '../utils/favicon';

let { favicon, reset } = useFavicon();
function loadding() {
    favicon.value = "/geek-favicon-32x32.webp"
}
// 父组件定义一个变量
let score = ref(3.5)
// 子组件的num赋值给父组件的变量
function update(num) { score.value = num }
</script>


4. 组件的 v-model
v-model 可以在组件上使用以实现双向绑定。

v-model 是传递属性和接收组件事件两个写法的简写。

默认情况下,v-model 在组件上都是使用 modelValue 作为 prop,并以 update:modelValue 作为对应的事件。

let props = defineProps({  // defineProps定义接受外部传来的参数
    modelValue: Number,  // 必须是这个变量名 modelValue
    theme: { type: String, default: "orange" }  // 外部传进来theme只能是字符串
})
省略代码。。。。
// ①子组件,定义发射给父组件的方法 
// let emits = defineEmits('update-rate');
let emits = defineEmits(['update:modelValue'])

// ②子组件要触发的onRate方法中,调用emits并传入发射给父组件的方法'update:modelValue'以及参数num
function onRate(num) {
    emits('update:modelValue', num)
}

在父组件中,v-model绑定这样使用:

<template>
    <h1>这是关于页面</h1>
    <button @click="loadding">更换图标</button>
    <button @click="reset">重置图标</button>
    你的评分是 { { score }}
    <!-- <RateV2 :value="3.5" @update-rate="update"/> -->
    <RateV2 v-model="score"></RateV2>
</template>

效果同## 3. 组件事件章节。

5. 插槽
在 Vue 中直接使用 slot 组件来显示组件的子元素,也就是所谓的插槽。

通过<slot></slot>在组件中创建插槽。插槽中,除了文本,也可以传递其他组件或者 html 标签 。

<template>
    <div :style="fontstyle">
        <!-- 这里创建一个插槽,用来放子元素 ,这里是放在了评分组件之前 -->
        <slot></slot>
        <div class='rate' @mouseout="mouseOut">
            <span @mouseover="mouseOver(num)" v-for='num in 5' :key="num">☆</span>
            <span class='hollow' :style="fontwidth">
                <span @click="onRate(num)" @mouseover="mouseOver(num)" v-for='num in 5' :key="num">★</span>
            </span>
        </div>
    </div>
</template>

父组件中使用方式如下,这里是放了一个html标签元素。

<template>
    <RateV2 v-model="score">
        <img width="14" src="/vite.svg">
    </RateV2>
</template>

Rate 组件前面显示一个图片。

6. 使用useVModel封装组件
这是一个给经常封装组件的小伙伴的大好利器。
在components/中新建Test.vue,代码如下:

<template>
    <div>
        <!--  v-model同步数据 -->
      name:
      <input v-model="_name"/> 
      age:
      <input v-model="_age"/>
      sex:
      <input v-model="_sex"/>
    </div>
  </template>

  <script lang="ts" setup>
  import { useVModel } from '@vueuse/core'

  // 通过defineProps 来定义接收 父组件的参数
  const props = defineProps({
    name: String,
    age: String,
    sex: String
  })
  // 向父组件发射事件update:开头
  const emit = defineEmits(['update:name', 'update:age', 'update:sex'])
  
  // useVModel返回的数据就是响应式数据
  const _name = useVModel(props, 'name', emit)
  const _age = useVModel(props, 'age', emit)
  const _sex = useVModel(props, 'sex', emit)
  </script>
  
接着,在usevue.vue中使用它:

<template>
    <Test
    v-model:name="formData.name"
    v-model:age="formData.age"
    v-model:sex="formData.sex"
    ></Test>
    { { formData }}
  </div>
</template>

<script setup>
import Test from '../components/Test.vue'
import {reactive} from 'vue';

const formData = reactive({
  name: 'lily',
  age: '8',
  sex: 'boy'
})
</script>
 

猜你喜欢

转载自blog.csdn.net/weixin_44821114/article/details/132762876