浅聊一下Vue3中的component组件

 更新时间:2023年08月31日 10:28:41   作者:BennuCTech  
开发过程中我们会经常遇到一些复杂的页面,而这些页面大部分由一个个小部分组合起来的,而且不同页面中可能有些部分是一样的,所以我们通常会将这些部分封装成组件,在Vue中,我们可以使用components组件(模板)来实现,本文就来详细的说一说Vue3中的component组件

实现一个组件

一个组件其实就是一个vue文件,简单示例(header.vue)如下:

<script setup></script>
<template>
  <div class="header"></div>
</template>
<style scoped lang="less">
.header {
  position: absolute;
  width: 100%;
  height: 80px;
  background: linear-gradient(
    180deg,
    rgba(0, 0, 0, 0.6) 0%,
    rgba(0, 0, 0, 0) 100%
  );
}
</style>

注册使用

基于script setup可以自动注册组件,只需要import即可使用,如下:

<script setup>
import Header from "./Header.vue";
</script>
<template>
  <div class="container">
    <Header></Header>
  </div>
</template>
<style scoped lang="less">
.container {
  width: 100%;
  height: 100%;
}
</style>

数据

可以通过Prop向组件传递数据,先在组件中定义,如下:

<script setup>
const props = defineProps({
  title: String,
  userInfo: Object
});
function doSomething(){
  if(props.userInfo.isVip){
    ...
  }
}
</script>
<template>
  <div class="header">{{props.title}}</div>
</template>
<style scoped lang="less">
...
</style>

这里定义了一个title属性,是一个字符串;一个userInfo属性,是一个对象,然后在组件中就可以通过props.xxx来使用这些属性。

那么如何将数据传递给这些属性呢,直接通过v-bind绑定数据即可,如下:

<script setup>
import Header from "./Header.vue";
let userInfo;
let title;
</script>
<template>
  <div class="container">
    <Header :title="title" :userInfo="userInfo"></Header>
  </div>
</template>
<style scoped lang="less">
...
</style>

这里使用的是v-bind的缩写。v-bind绑定后面双引号中是表达式,所以如果类型是:

  • 数值::count="3"
  • 布尔值::isVip="true"
  • 数组::array="[1,2,3]"
  • 对象::info="{name:'名字',isVip:true}" 其他就不一一列举了。

Props是支持类型如下:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol。

Props支持类型检查,同时支持默认值,如下:

  props: {
    // 基础的类型检查 (`null` 和 `undefined` 值会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组的默认值必须从一个工厂函数返回
      default() {
        return { message: 'hello' }
      }
    },
    // 具有默认值的函数
    propG: {
      type: Function,
      // 与对象或数组的默认值不同,这不是一个工厂函数——这是一个用作默认值的函数
      default() {
        return 'Default function'
      }
    }
  }

Props是单向数据流,这样可以防止子组件意外变更父组件的状态,每当父组件发生变更,子组件所有Props都会刷新到最新值。所以子组件中不能更改Props的属性,否则会在控制台警告。

非 Prop 的 Attribute

比如classstyle 和 id等attribute,默认是添加到组件的根节点上,如:

<script setup>
...
</script>
<template>
  <div class="header" >
    <input ...>
  </div>
</template>
<style scoped lang="less">
...
</style>

当我们使用这个组件时,为其添加attribute,如:

<Header id="my-header"></Header>

实际的渲染结果是:

<div class="header" id="my-header">
  <input ...>
</div>

禁用 Attribute 继承

如果不希望添加到根节点上,则可以设置inheritAttrs: false

<script setup>无法声明inheritAttrs,所以我们需要在添加普通的<script>,同时使用v-bind="$attrs"来绑定继承Attribute的节点,如

<script>
export default {
  inheritAttrs: false
};
</script>
<script setup>
...
</script>
<template>
  <div class="header" >
    <input ... v-bind="$attrs">
  </div>
</template>
<style scoped lang="less">
...
</style>

这样添加给组件的Attribute就会直接添加到input元素上,实际的渲染结果是:

<div class="header">
  <input ...  id="my-header">
</div>

多根节点

如果组件有多根节点,那么必须显式设置v-bind="$attrs",否则警告。

事件

数据传递清楚了,那么如何响应组件的一些自定义事件呢?通过emits来定义事件,如下:

<script setup>
const emit = defineEmits(["onSelected"]);
function selectItem(item){
  emit("onSelected", item);
}
</script>
<template>
  <div class="header" >
    ...
  </div>
</template>
<style scoped lang="less">
...
</style>

这里定义了一个onSelected事件,当组件中触发selectItem函数的时候就会执行这个事件。

注意:如果没有参数,则直接emit("onSelected")即可;如果有多个参数,则emit("onSelected", param1, param2, ...)

在父组件中则通过v-on来绑定事件,如下:

<script setup>
import Header from "./Header.vue";
function headerSelected(item){
}
</script>
<template>
  <div class="container">
    <Header @onSelected="headerSelected"></Header>
  </div>
</template>
<style scoped lang="less">
...
</style>

这里同样是v-on的缩写形式,这样就绑定了事件。事件同样可以验证,这里就不细说了。

v-model

v-model是双向数据绑定,默认情况下,组件上的 v-model 使用 modelValue 作为 prop 和 update:modelValue 作为事件。比如有一个title属性:

<my-component v-model:title="bookTitle"></my-component>

那么在子组件中就可以这样做:

<script setup>
const props = defineProps({
  title: String
});
const emit = defineEmits(["update:title"]);
const setTitle = (newTitle) => {
  emit("update:title", newTitle);
}
</script>
<template>
  <div class="header" >
    ...
  </div>
</template>
<style scoped lang="less">
...
</style>

这样子组件中可以通过update:title来同步title数据。

插槽

如果子组件中部分区域是不定的,需要父组件来实现,那么怎么办?这就需要用到插槽slot,插槽使用很简单,如下:

<script setup>
...
</script>
<template>
  <div class="header" >
    <slot>default</slot>
  </div>
</template>
<style scoped lang="less">
...
</style>

这里定义了一个插槽,并且指定了一个默认内容,在父组件中:

<script setup>
import Header from "./Header.vue";
</script>
<template>
  <div class="container">
    <Header>newtitle</Header>
  </div>
</template>
<style scoped lang="less">
...
</style>

这样插槽就被“newtitle”这个字符串替代,如果这里没有任何内容<Header></Header>则显示默认内容,即"default"。

插槽中不仅是字符串,可以是html或其他组件,比如:

<Header>
 <button>submit</botton>
</Header>

当然默认内容也可以是html。

具名插槽

一个组件里可以有多个插槽,比如左边栏右边栏等,这样就需要具名插槽来区分,如下:

<script setup>
...
</script>
<template>
  <div class="header" >
    <div name="leftbar" >
      <slot>left</slot>
    </div>
    <div name="rightbar" >
      <slot>right</slot>
    </div>
  </div>
</template>
<style scoped lang="less">
...
</style>

使用的时候需要用v-slot指令,并且一定是<template>元素,因为v-slot只能添加在<template>上,如下:

<script setup>
import Header from "./Header.vue";
</script>
<template>
  <div class="container">
    <Header>
      <template v-slot:leftbar >
        <button>left</button>
      </template>
      <template v-slot:rightbar >
        <button>right</button>
      </template>
    </Header>
  </div>
</template>
<style scoped lang="less">
...
</style>

插槽名也可以是动态的<template v-slot:[dynamicSlotName] >。v-slot也可以缩写成“#”,如<template #leftbar >

作用域

如下代码:

<div v-for="item in list" >
  <slot></slot>
</div>

这时候当我们使用组件的时候,插槽中则无法item,如下

<my-component >
  <img :src="item.img"></img>
</my-component>

像上面这种代码就会报错,因为只能在组件中访问item,而插槽是在父组件上提供的,作用域不同。

当然我们可以为插槽添加一个attribute绑定,即插槽 prop,如下:

<div v-for="item in list" >
  <slot :item="item"></slot>
</div>

插槽可以添加多个attribute。

然后在父级中使用带值的v-slot来定义我们提供的插槽 prop 的名字,如:

<my-component >
  <template v-slot:default="slotProps">
    <img :src="slotProps.item.img"></img>
  </template>   
</my-component>

这样就不会报错了。如果只有一个插槽,那么v-slot可以直接添加组件上,如:

<my-component v-slot="slotProps">
  <img :src="slotProps.item.img"></img> 
</my-component>

就不需要template元素了;但是如果有多个插槽(具名)则必须添加在template元素上。

Provide / Inject

上面知道父组件向子组件传递数据用Props,但是如果组件层级很深,需要向一个底层的子组件传递数据,如果用Props就需要一层层的去传递。这时候就可以使用Provide / Inject。如: 父组件中通过provide(key, value)来设置数据

<script setup>
const userid = "123";
provide("userid", userid);
</script>
...

那么在子组件中通过inject(key, defaultValue)来获取数据

<script setup>
const userid = inject("userid", defaultId);
</script>
...

当然上面是一次性的数据,如果需要响应,则在父组件中使用ref或reactive,如:

<script setup>
const userid = ref("123");
provide("userid", userid);
</script>
...

获取DOM对象

在组件中,我们可以给元素设置id,并通过id的方式来获取它的DOM对象。但是在页面上有多个该组件的情况下,这样获取DOM对象就会有问题,因为id不唯一。

我们还可以使用 ref attribute 为子组件或 HTML 元素指定引用 ID。如:

<script setup>
import Header from "./Header.vue";
const headerRef = ref();
</script>
<template>
  <div class="container">
    <Header ref="headerRef">
      ...
    </Header>
  </div>
</template>
<style scoped lang="less">
...
</style>

上面Header的ref属性是“headerRef”,所以变量名也保持一致,即const headerRef = ref();,这样headerRef.value就是它的DOM对象,可以执行getElementsByTagName等方法。

这样即是页面上存在多个组件,但是因为作用域的存在,每个headerRef互不干扰。

列表

如果是一个列表中呢?比如:

<template>
  <div class="container">
    <item ref="itemRef" v-for="item in list">
      ...
    </item>
  </div>
</template>

这样通过const itemRef = ref();得到的是哪个?其实这样itemRef.value就变成了一个列表,itemRef.value[0]就是第一个item组件的对象。

元素位置受限

有些 HTML 元素如 <ul><ol><table> 和 <select>,对于其内部是有严格限制的。而有些元素如 <li><tr> 和 <option>,只能出现在某些特定的元素内部。

这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:

<table>
  <my-component></my-component>
</table>

自定义组件 <my-component> 会被作为无效的内容提升到外部,导致渲染出错。这时候需要使用 is attribute,如:

<table>
  <tr is="vue:my-component"></tr>
</table>

注意:is 的值必须以 vue: 开头,才可以被解释为 Vue 组件。这是避免和原生自定义元素混淆。

调用子组件方法

上面事件章节说的是父组件响应子组件的事件,也就是说是子组件调用父组件的方法。那么父组件如何调用子组件的方法?

Expose

首先子组件的方法需要暴露出去,如下:

<script setup>
defineExpose({
  onEvent
});
function onEvent(event) {
  console.log(`header: ${event}`);
}
</script>
<template>
  <div class="header" >
    ...
  </div>
</template>
<style scoped lang="less">
...
</style>

然后在父组件中为子组件添加ref属性,然后通过ref()函数获取对象执行方法即可,如下:

<script setup>
import Header from "./Header.vue";
const headerRef = ref();
function headerEvent(event){
  headerRef.value.onEvent(event);
}
</script>
<template>
  <div class="container">
    <Header ref="headerRef">
      ...
    </Header>
  </div>
</template>
<style scoped lang="less">
...
</style>

这样就可以通过headerRef.value执行暴露出来的方法了。

如果有多个子组件,都设置了ref属性,则定义多个变量即可,如下:

const headerRef = ref();
const footerRef = ref();

EventBus

更简单的方法是使用EventBus即可完成组件间通信,使用也非常简单,参考官方文档即可https://www.npmjs.com/package/events

我们可以简单封装一下以便使用,如下:

import EventEmitter from "events";
const eventEmitter = new EventEmitter();
export const WsEvent = {
  emit: data => {
    eventEmitter.emit("wsevent", data);
  },
  on: callback => {
    eventEmitter.on("wsevent", callback);
  }
};

以上就是详细说一说Vue3中的component组件的详细内容,更多关于Vue3 component组件的资料请关注脚本之家其它相关文章!

相关文章

  • vue3中的reactive函数声明数组方式

    vue3中的reactive函数声明数组方式

    这篇文章主要介绍了vue3中的reactive函数声明数组方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • vue如何在data中引入图片的正确路径

    vue如何在data中引入图片的正确路径

    这篇文章主要介绍了vue如何在data中引入图片的正确路径,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Vue3 中自定义插件的实现方法

    Vue3 中自定义插件的实现方法

    在 Vue 中,一些简单的功能,我们可以直接定义为全局方法,然后挂到 Vue 上就能使用了,这篇文章主要介绍了Vue3 中自定义插件的实现,需要的朋友可以参考下
    2022-08-08
  • 详解vue 项目白屏解决方案

    详解vue 项目白屏解决方案

    这篇文章主要介绍了详解vue 项目白屏解决方案,详细的介绍了白屏的解决方案,非常具有实用价值,需要的朋友可以参考下
    2018-10-10
  • Vue插件报错:Vue.js is detected on this page.问题解决

    Vue插件报错:Vue.js is detected on this page.问题解决

    这篇文章主要介绍了Vue插件报错:Vue.js is detected on this page.问题解决,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • Vue入门之数据绑定(小结)

    Vue入门之数据绑定(小结)

    本篇文章主要介绍了探索Vue高阶组件的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • 基于el-table封装的可拖拽行列、选择列组件的实现

    基于el-table封装的可拖拽行列、选择列组件的实现

    本文主要介绍了基于el-table封装的可拖拽行列、选择列组件的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • VUE页面中加载外部HTML的示例代码

    VUE页面中加载外部HTML的示例代码

    本篇文章主要介绍了VUE页面中加载外部HTML的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • require.js+vue开发微信上传图片组件

    require.js+vue开发微信上传图片组件

    这篇文章主要为大家详细介绍了require.js+vue+vue-router+vue-resource开发微信上传图片组件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • 浅谈vant组件Picker 选择器选单选问题

    浅谈vant组件Picker 选择器选单选问题

    这篇文章主要介绍了浅谈vant组件Picker 选择器选单选问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11

最新评论