vue组件通信

父组件向子组件通信

方法一:通过 props 属性

  • 父组件通过在子组件上定义 props 属性并传递值,将数据传递给子组件。子组件可以通过 props 属性接收父组件传递的数据。

例:创建一个item的组件,在组件中使用props接收值并且给接收的值一些限制

 props: {
    isActive: {
      type: Boolean,//类型为布尔
      default: false,//默认值
      required: false,//是否为必传
    },
}

在使用item组件的地方传值:

<template>
  <div>
    <Item :isActive ='true'></Item>
  </div>
</template>
<script>
import Item from './Item.vue'
export default {
  components: { Item  },
}
</script>

方法二:通过引用(ref) 

  • 父组件可以通过 ref 获取子组件的实例,然后调用子组件的方法或直接访问子组件的属性来实现
//父组件
<el-button type="text" @click="addOrUpdateHandle>update</el-button>
<add-or-update ref="addOrUpdate"></add-or-update>
addOrUpdateHandle() {
      this.$nextTick(() => {
        this.$refs.addOrUpdate.init(this.id)
      })
},
//子组件
init(id) {console.log(id);}

子组件向父组件通信

方法一:通过事件传递数据,$emit()方法 

子组件通过 $emit 方法触发一个自定义事件,并且可以传递数据给父组件。父组件通过在子组件上监听这个事件来接收数据。

父组件:
<user-info-show @active="test"></user-info-show>
export default {
    methods:{
        test(e){
          console.log("子组件传过来的值 / 123", e);
        }
    }
}
子组件:
<view  @click="handleClick()"></view>
export default {
    methods:{
        handleClick(){
        	this.$emit('active',123);
        }
    }
}

方法二:使用回调函数,props

在父组件中通过 props 将一个函数传递给子组件,在子组件内部调用这个函数并传递数据,实现数据的回调到父组件

 在父组件中定义一个函数,并将它作为 props 传递给子组件 

//父组件
<template>
  <div>
    <ChildComponent :callback="handleCallback" />
  </div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
  components: {
    ChildComponent
  },
  methods: {
    handleCallback(data) {
      // 处理从子组件传递回来的数据
      console.log('Received data:', data);
    }
  }
}
</script>

在子组件中通过调用回调函数并传递数据,将数据返回给父组件:

//子组件
<template>
  <button @click="sendDataToParent">Send Data to Parent</button>
</template>
<script>
export default {
  props: ['callback'],
  methods: {
    sendDataToParent() {
      const data = 'Hello from child component';
      // 调用父组件传递的回调函数,将数据传递回去
      this.callback(data);
    }
  }
}
</script>

 方法三:使用 Vuex 状态管理

如果应用程序较为复杂,可以使用 Vuex 来管理应用的状态。子组件可以通过提交 mutation 或者触发 action 的方式改变状态,然后父组件可以监听状态的变化来获取数据。

  1. 在父组件中配置和使用 Vuex:

    • 安装 Vuex 并创建一个 Vuex store。
    • 在 store 中定义状态(state)和修改状态的方法(mutations)。
  2. 在子组件中使用 Vuex:

    • 在需要使用 Vuex 的子组件中导入 Vuex,并通过 mapState 和 mapMutations 等辅助函数,将 Vuex 的状态和方法映射到子组件的计算属性或方法中。
  3. 在子组件中触发 Vuex 的状态变更:

    • 子组件中通过调用映射得到的方法来触发 Vuex 的 mutations,从而修改 Vuex 的状态。

// 父组件 ParentComponent.vue
<template>
  <div>
    <span>{{ count }}</span>
    <ChildComponent />
  </div>
</template>

<script>
import { mapState } from 'vuex';
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  computed: {
    ...mapState(['count']) // 将 count 映射到父组件的计算属性中
  }
}
</script>

// 子组件 ChildComponent.vue
<template>
  <div>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
import { mapMutations } from 'vuex';

export default {
  methods: {
    ...mapMutations(['increment']) // 将 increment 映射到子组件的方法中
  }
}
</script>

案例:列表菜单

                       单个使用                                      循环使用

 Item.vue

<template>
  <div :class="{ active: isActive }" @click="handleClick">
    <slot></slot>
  </div>
</template>
<script>
export default {
  props: {
    isActive: {
      default: false, //默认值
      type: Boolean, //类型
    },
  },
  methods: {
    handleClick() {
      this.$emit('active')//使用 $emit 方法触发active自定义事件,并通过自定义事件来实现组件之间的通信
    },
  },
}
</script>
<style scoped>
.active {
  background: rgb(204, 202, 202);
}
</style>

 TitleMenu.vue

<template>
  <div class="menu_box">
    <Item :isActive="isActive" @active="$emit('active')">//@active="$emit('active')"父组件接收到名为 active 的事件时,立即触发一个同名的自定义事件 active 并将其向上传播抛给父元素
      <div class="title_menu">
        <div>
          <slot name="title"></slot>//具名插槽,通过name指定名称来区分不同的插槽
        </div>
        <div>
          <slot name="icon"></slot>
        </div>
      </div>
    </Item>
  </div>
</template>
<script>
import Item from './Item.vue'
export default {
  props: {
    isActive: {
      default: false, //默认值
      type: Boolean, //类型
    },
  },
  components: {
    Item,
  },
}
</script>
<style scoped>
.menu_box {
  transition: 0.5s;
}
.menu_box:hover {
  background: rgb(219, 147, 147);
}
.title_menu {
  padding: 5px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
</style>

单个使用:

<template>
  <div style="width: 250px;">
    <TitleMenu :isActive="select" @active="select = true">
      <template v-slot:title>发现频道</template>
      <template v-slot:icon>></template>
    </TitleMenu>
  </div>
</template>
<script>
import TitleMenu from '../../components/common/TitleMenu.vue'
export default {
  components: { TitleMenu },
  data() {
    return {
      select: false,
    }
  },
}
</script>

循环使用:通过接口渲染数据,实现只有一个 select 状态为 true 的功能

<template>
  <div style="width: 250px;">
    <TitleMenu
      :isActive="item.select"
      @active="updateSelect(index)"
      v-for="(item, index) in titleData"
      :key="index"
    >
      <template v-slot:title>{{ item.uname }}</template>
      <template v-slot:icon>></template>
    </TitleMenu>
  </div>
</template>
<script>
import TitleMenu from '../../components/common/TitleMenu.vue'
export default {
  components: { TitleMenu },
  data() {
    return {
      select: false,
      titleData: [],
    }
  },
  methods: {
    updateSelect(index) {
      this.titleData.forEach((item, i) => {
        //this.$set(item, 'newProperty', 'value');给数组的每一项添加新属性
        // i === index当前循环的索引值 i 是否等于点击的菜单项的索引 index,结果为 true 或 false
        this.$set(item, 'select', i === index);
      })
    },
  },
}
</script>

案例:动态改变数据列数

                        columus="2"                                                      columus="3"

<template>
  <div class="channel_list">
    <div
      v-for="item in channelList"
      :key="item.id"
      class="item"
      :style="{ width: `${100 / columus}%` }"
    >...</div>
  </div>
</template>
<script>
export default {
  props: {
    columus: {
      type: Number,
      default: 2,//默认2列
    },
  },
  data() {
    return {
      channelList: [],
    }
  },
}
</script>
<style scoped>
.channel_list {
  overflow: hidden;
}
.item {
  float: left;
}
</style>
<ChanneList :columus='2'></ChanneList>

案例:封装搜索框

封装一个搜索框,宽度和高度由父元素控制 

封装图标icon:

<template>
  <div>
    <i :class="iconname"></i>
  </div>
</template>
<script>
export default {
  props: {
    iconname: {
      type: String,
      default: 'el-icon-edit',
      required: true,
    },
  },
}
</script>

封装Search:

<template>
  <form class="search_h" @submit.prevent="handleSearch">
     <input type="text" v-model="value" :placeholder="placeholder"></input>
     <div @click="handleSearch">
        <Icon class="icon" :iconname="'el-icon-search'"></Icon>
     </div>
  </form>
</template>
<script>
import Icon from './Icon.vue'
export default {
  components: {
    Icon,
  },
  props:{
     placeholder:{
        type:String,
        default:'请输入搜索内容'
    }
  },
  data() {
    return {
        value:''
    }
  },
  methods: {
    handleSearch() {
        if(this.value){//搜索框里有值的时候才触发事件
           this.$emit('search',this.value)
        }
    },
  }
}
</script>
<style scoped>
.search_h {
   border: 1px solid #cec9c9;
   width: 100%;
   height: 100%;
   box-sizing: border-box;
   border-right: 2px;
   position: relative;
}
.search_h input{
    border: none;
    outline: none;
    width: 100%;
    height: 100%;
    padding-left: 10px;
    padding-right: 40px;
    box-sizing: border-box;
}
.icon{
    background: #aaa5a5;
    color: white;
    text-align: center;
    width: 40px;
    height: 100%;
    position: absolute;
    top: 0;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
</style>

使用:

<div style="width: 100%; height: 30px;">
     <Search :placeholder="'请输入'" @search="handleSearch"></Search>
</div>
import Search from '../../components/common/search.vue'
components: { Search },
handleSearch(e) {
      console.log(e, '搜索框里的内容')
},

案例:封装一个侧边栏

//icon.vue
<template>
  <div>
    <i :class="iconname"></i>
  </div>
</template>
<script>
export default {
  props: {
    iconname: {
      type: String,
      default: 'el-icon-edit',
      required: true,
    },
  },
}
</script>
//item.vue
<template>
  <div :class="{ active: isActive }" @click="handleClick">
    <slot></slot>
  </div>
</template>
<script>
export default {
  props: {
    isActive: {
      default: false, //默认值
      type: Boolean, //类型
    },
  },
  methods: {
    handleClick() {
      this.$emit('active')
    },
  },
}
</script>
<style scoped>
.active {
  background: rgb(204, 202, 202);
}
</style>
//search.vue
<template>
  <form class="search_h" @submit.prevent="handleSearch">
     <input type="text" v-model="value" :placeholder="placeholder"></input>
     <div @click="handleSearch">
        <Icon class="icon" :iconname="'el-icon-search'"></Icon>
     </div>
  </form>
</template>
<script>
import Icon from './Icon.vue'
export default {
  components: {
    Icon,
  },
  props:{
     placeholder:{
        type:String,
        default:'请输入搜索内容'
    }
  },
  data() {
    return {
        value:''
    }
  },
  methods: {
    handleSearch() {
        if(this.value){//搜索框里有值的时候才触发事件
           this.$emit('search',this.value)
        }
    },
  }
}
</script>
<style scoped>
.search_h {
   border: 1px solid #cec9c9;
   width: 100%;
   height: 100%;
   box-sizing: border-box;
   border-right: 2px;
   position: relative;
}
.search_h input{
    border: none;
    outline: none;
    width: 100%;
    height: 100%;
    padding-left: 10px;
    padding-right: 40px;
    box-sizing: border-box;
}
.icon{
    background: #aaa5a5;
    color: white;
    text-align: center;
    width: 40px;
    height: 100%;
    position: absolute;
    top: 0;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
</style>
//TitleMenu.vue
<template>
  <div class="menu_box">
    <Item :isActive="isActive" @active="$emit('active')">
      <div class="title_menu">
        <div class="title">
          <slot name="title"></slot>
        </div>
        <div class="icon"> 
          <slot name="icon"></slot>
        </div>
      </div>
    </Item>
  </div>
</template>
<script>
import Item from './Item.vue'
export default {
  props: {
    isActive: {
      default: false, //默认值
      type: Boolean, //类型
    },
  },
  components: {
    Item,
  },
}
</script>
<style scoped>
.menu_box {
  transition: 0.5s;
}
.menu_box:hover {
  background: rgb(219, 147, 147);
}
.title_menu {
  padding: 5px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.title{
  font-weight: bolder;
}
.icon{
  color: #6d757a;
}
</style>
//channel.vue
<template>
  <div class="menu_box" v-if="data">
    <Item :isActive="isActive" @active="$emit('active')">
      <div class="title_menu">
        <span style="font-size: 14px;">
          {{ data.name }}
        </span>
        <span style="color: #6d757a; font-size: 12px;">
          {{ data.count }}
        </span>
      </div>
    </Item>
  </div>
</template>
<script>
import Item from './Item.vue'
export default {
  props: {
    isActive: {
      default: false, //默认值
      type: Boolean, //类型
    },
    data: {
      type: Object,
      default: function () {
        return {}
      },
    },
  },
  components: {
    Item,
  },
}
</script>
<style scoped>
.menu_box {
  transition: 0.5s;
}
.menu_box:hover {
  background: rgb(219, 217, 217);
}
.title_menu {
  padding: 5px;
}
</style>
//channelist.vue
<template>
  <div>
    <div class="channel_list" :style="{ height: `${height}px` }">
      <div
        v-for="item in channelList"
        :key="item.id"
        class="item"
        :style="{ width: `${100 / columus}%` }"
      >
        <Channe
          @active="$emit('active', item.id)"
          :isActive="item.id === activeId"
          :data="item"
        ></Channe>
      </div>
    </div>
    <div class="collapse" @click="isExpand = !isExpand">
      <span>{{ isExpand == true ? '折叠' : '展开' }}</span>
      <Icon
        :iconname="isExpand == true ? 'el-icon-arrow-down' : 'el-icon-arrow-up'"
      ></Icon>
    </div>
  </div>
</template>
<script>
import Channe from '../../components/common/channel.vue'
import Icon from '../../components/common/Icon.vue'
export default {
  props: {
    activeId: {
      type: Number,
      default: 0,
    },
    columus: {
      type: Number,
      default: 2,
    },
  },
  components: {
    Channe,
    Icon,
  },
  data() {
    return {
      channelList: [
        { id: 1, name: '动漫', count: '256' },
        { id: 2, name: '电视剧', count: '56' },
        { id: 3, name: '热门', count: '26' },
        ...
      ],
      isExpand: true, //是否是展开状态
    }
  },
  computed: {
    height() {
      var rows = 3
      if (this.isExpand) {
        rows = Math.ceil(this.channelList.length / this.columus)
      }
      return rows * 32
    },
  },
}
</script>
<style scoped>
.channel_list {
  overflow: hidden;
  transition: 1s;
}
.item {
  float: left;
}
.collapse {
  border: 1px solid #d3d0d0;
  display: flex;
  justify-content: center;
  align-items: center;
  clear: both;
  color: rgb(105, 102, 99);
  cursor: pointer;
  width: 100%;
  height: 40px;
  line-height: 40px;
  text-align: center;
}
</style>
//Aside.vue
<template>
  <div>
    <div>
      <div style="width: 100%; height: 30px;">
        <Search :placeholder="'请输入'" @search="handleSearch"></Search>
      </div>
      <TitleMenu :isActive="activeId === 3" @active="activeId = 3">
        <template v-slot:title>发现频道</template>
        <template v-slot:icon>></template>
      </TitleMenu>
    </div>
    <ChanneList
      :columus="2"
      @active="activeId = $event"
      :activeId="activeId"
    ></ChanneList>
  </div>
</template>
<script>
import TitleMenu from './TitleMenu.vue'
import ChanneList from './channelist.vue'
import Search from './search.vue'
export default {
  components: { TitleMenu, ChanneList, Search },
  data() {
    return {
      select: false,
      titleData: [],
      activeId: 1,
    }
  },
  methods: {
    handleSearch(e) {
      console.log(e, '搜索')
    },
    updateSelect(index) {
      this.titleData.forEach((item, i) => {
        this.$set(item, 'select', i === index)
      })
    },
  },
}
</script>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值