返回顶部
首页 > 资讯 > 前端开发 > JavaScript >vue嵌套组件传参实例分享
  • 197
分享到

vue嵌套组件传参实例分享

2024-04-02 19:04:59 197人浏览 八月长安
摘要

目录递归嵌套组件参数传递深层递归组件事件丢失EventBus什么事EventBus?前言: 假设我们已经了解Vue组件常见的有父子组件通信,兄弟组件通信。而父子组件通信很简单,父组件

前言:

假设我们已经了解Vue组件常见的有父子组件通信,兄弟组件通信。而父子组件通信很简单,父组件会通过 props 向下传数据给子组件,当子组件有事情要告诉父组件时会通过 $emit 事件告诉父组件。那么当两个组件之间不是父子关系,怎样传递数据呢?

先来看一下这个例子:

递归嵌套组件参数传递

我们封装了一个名为 NestedDir 的子组件(嵌套目录的意思),内容如下(用到了element ui组件):

<!-- NestedDir.vue -->
<template>
  <ul class="nest_wrapper">
    <li v-for="(el, index) in nested" :key="index">
      <div v-if="el.type ==='dir'" class="dir">
        <p>{{el.name}}</p>
        <div class="btn_group">
          <el-button type="warning" size="mini" @click="add({id: el.id, type: 'dir'})">新增目录</el-button>
          <el-button type="warning" size="mini" @click="add({id: el.id, type: 'file'})">新增文件</el-button>
        </div>
      </div>
      <div v-if="el.type ==='file'" class="file">
        <p>{{el.name}}</p>
      </div>
      <NestedDir v-if="el.children" :nested="el.children"/>
    </li>
  </ul>
</template>

<script>
export default {
  name: "NestedDir",
  props: {
    nested: {
      type: Array,
    }
  },
  methods: {
    add(el) {
      this.$emit('change', el)
    }
  }
}
</script>

可以看出这个 NestedDir 接收父级传来的 nested 数组类型的数据,并且它的内部点击 新增目录、新增文件,可以触发 父级 监听的 change 事件。比较特殊的是这个组件中调用了自己:

<NestedDir v-if="el.children" :nested="el.children"/>

不过要注意的是调用自己的时候我们并没有在它上面监听它内部传来的change事件,这也是导致二级目录点击新增按钮无效的原因。

我们传递给它的 nested 数据结构大概是下面的样子:

[{
    "id": 1,
    "name": "目录1",
    "type": "dir",
    "children": [{
        "id": 2,
        "name": "目录3",
        "type": "dir",
        "children": [],
        "pid": 1
    }, {
        "id": 3,
        "name": "文件2",
        "type": "file",
        "pid": 1
    }]
}, {
    "id": 4,
    "name": "目录2",
    "type": "dir",
    "children": []
}, {
    "id": 5,
    "name": "文件1",
    "type": "file",
    "children": []
}]

父组件中调用 NestedDir:

<!-- directory.vue -->
<template>
  <div style="width: 50%;box-shadow: 0 0 4px 2px rgba(0,0,0,.1);margin: 10px auto;padding-bottom: 10px;">
    <!-- 顶部按钮组 -->
    <div class="btn_group">
      <el-button type="warning" size="mini" @click="showDialog({type: 'dir'})">新增目录</el-button>
      <el-button type="warning" size="mini" @click="showDialog({type: 'file'})">新增文件</el-button>
    </div>
    <!-- 嵌套组件 -->
    <NestedDir :nested="catalog" @change="handleChange"/>
    <!-- 新增弹出框 -->
    <el-dialog :title="title" :visible.sync="dialogFORMVisible" width="300px">
      <el-form :model="form">
        <el-form-item label="名称">
          <el-input v-model="form.name" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="confirm">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import NestedDir from "./NestedDir";

export default {
  name: "directory",
  components: {
    NestedDir
  },
  created() {
    this.catalog = this.getTree()
  },
  computed: {
    maxId() {
      return this.arr.lastIndex + 2
    },
    topnodes() {
      this.arr.forEach(item => {
        if (item.children) item.children = []
      })
      return this.arr.filter(item => !item.pid)
    }
  },
  data() {
    return {
      arr: [
        {id: 1, name: '目录1', type: 'dir', children: []},
        {id: 2, name: '目录3', type: 'dir', children: [], pid: 1},
        {id: 3, name: '文件2', type: 'file', pid: 1},
        {id: 4, name: '目录2', type: 'dir', children: []},
        {id: 5, name: '文件1', type: 'file'},
      ],
      title: '',
      dialogFormVisible: false,
      form: {
        id: '',
        name: '',
        type: '',
        pid: ''
      },
      catalog: []
    }
  },
  methods: {
    handleChange(el) {
      this.showDialog(el)
    },
    confirm() {
      this.arr.push({...this.form})
      this.dialogFormVisible = false
      this.catalog = this.getTree()
      this.form = {
        id: '',
        name: '',
        type: '',
        pid: '' , // 父级的id
      }
    },
    showDialog(el) {
      if (el.type === 'dir') {
        this.title = '新增目录'
        this.form.children = []
        this.form.type = 'dir'
      } else {
        this.title = '新增文件'
        this.form.type = 'file'
      }
      if (el.id) {
        this.form.pid = el.id
        this.form.id = this.maxId
      } else {
        this.form.id = this.maxId
      }
      this.dialogFormVisible = true
    },
    getTree() {
      this.topNodes.forEach(node => {
        this.getChildren(this.arr, node.children, node.id)
      })
      return this.topNodes
    },
    getChildren(data, result, pid) {
      for (let item of data) {
        if (item.pid === pid) {
          const newItem = {...item, children: []}
          result.push(newItem)
          this.getChildren(data, newItem.children, item.id)
        }
      }
    }
  }
}
</script>

<style scoped>
.btn_group {
  padding: 20px 10px;
  background-color: rgba(87, 129, 189, 0.13);
}
</style>

渲染出的页面是下面的样子:

递归组件

深层递归组件事件丢失

我们构造出了一个理论上可以无限嵌套的目录结构,但是经过测试发现 在二级目录上的 新增按钮 点击是没有任何反应的,原因是我们在 NestedDir 中调用了它自己并没有监听内部的change事件(上边提到过),所以它无法触发 父级的-父级 的监听事件。

如何解决?

  • 在递归调用的时候也监听一下change事件,并间接传递到最外层组件(这个是最容易想到的方法,但是如果组件嵌套很深,简直就是个噩梦)
  • EventBus(事件总线)

EventBus

什么事EventBus?

它其实就是一个Vue实例,有$emit、$on、$off方法,允许从一个组件向另一组件传递数据,而不需要借助父组件。具体做法是在一个组件$emit,在另一个组件$on,可以像下面这样做:

// main.js
import Vue from 'vue'
import App from './App.vue'

export const eventBus = new Vue(); // creating an event bus.

new Vue({
  render: h => h(App),
}).$mount('#app')

这样我们来改造一下 directory.vue,只需要改动srcipt部分:

<script>
import NestedDir from "./NestedDir";
import { eventBus } from "../main";

export default {
  name: "directory",
  components: {
    NestedDir
  },
  created() {
    this.catalog = this.getTree()

    eventBus.$on('change', function (data) {
      // todo 向之前一样处理即可
    })
  },
  destroyed() {
    eventBus.$off('change')
  },
  computed: {
    maxId() {
      return this.arr.lastIndex + 2
    }
  },
  data() {
    return {
      arr: [
        {id: 1, name: '目录1', type: 'dir', children: []},
        {id: 2, name: '目录3', type: 'dir', children: [], pid: 1},
        {id: 3, name: '文件2', type: 'file', pid: 1},
        {id: 4, name: '目录2', type: 'dir', children: []},
        {id: 5, name: '文件1', type: 'file'},
      ],
      title: '',
      dialogFormVisible: false,
      form: {
        id: '',
        name: '',
        type: '',
        pid: ''
      },
      catalog: []
    }
  },
  methods: {
    handleChange(el) {
      this.showDialog(el)
    },
    confirm() {
      this.arr.push({...this.form})
      this.dialogFormVisible = false
      this.catalog = this.getTree()
      this.form = {
        id: '',
        name: '',
        type: '',
        pid: '' , // 父级的id
      }
    },
    showDialog(el) {
      if (el.type === 'dir') {
        this.title = '新增目录'
        this.form.children = []
        this.form.type = 'dir'
      } else {
        this.title = '新增文件'
        this.form.type = 'file'
      }
      if (el.id) {
        this.form.pid = el.id
        this.form.id = this.maxId
      } else {
        this.form.id = this.maxId
      }
      this.dialogFormVisible = true
    },
    getTree() {
      this.topNodes.forEach(node => {
        this.getChildren(this.arr, node.children, node.id)
      })
      return this.topNodes
    },
    getChildren(data, result, pid) {
      for (let item of data) {
        if (item.pid === pid) {
          const newItem = {...item, children: []}
          result.push(newItem)
          this.getChildren(data, newItem.children, item.id)
        }
      }
    }
  }
}
</script>

引入了import { eventBus } from "../main";

在页面创建时加入事件监听,销毁时移除事件监听:

created() {
  eventBus.$on('change', function (data) {
    this.handleChange(data)
  })
},
destroyed() {
  eventBus.$off('change')
}

NestedDir.vue 中也需要做相应改动,只需要修改methods中的add方法:

import { eventBus } from "../main";

//...略

methods: {
  add(el) {
    // this.$emit('change', el)
    eventBus.$emit('change', el)
  }
}

这样点击二级目录的新增按钮,就可以正常触发弹出框了。

上面的eventBus只在Vue2中有效,vue3中已经移除了$on, $off 这些方法,所以下一篇文章打算自己做一个Vue的插件来处理这种类似于Pub/Sub的情况。

到此这篇关于vue嵌套组件传参实例分享的文章就介绍到这了,更多相关vue嵌套组件传参内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: vue嵌套组件传参实例分享

本文链接: https://lsjlt.com/news/145860.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • vue嵌套组件传参实例分享
    目录递归嵌套组件参数传递深层递归组件事件丢失EventBus什么事EventBus?前言: 假设我们已经了解vue组件常见的有父子组件通信,兄弟组件通信。而父子组件通信很简单,父组件...
    99+
    2024-04-02
  • vue嵌套路由-query传递参数的示例分析
    这篇文章主要介绍了vue嵌套路由-query传递参数的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在嵌套路由中我们经常会遇到父路由...
    99+
    2024-04-02
  • vue嵌套路由中params传递参数的示例分析
    这篇文章主要介绍vue嵌套路由中params传递参数的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!在嵌套路由中,父路由向子路由传值除了query外,还有params,par...
    99+
    2024-04-02
  • Vue实现嵌套菜单组件
    本案例为大家分享了Vue实现嵌套菜单组件的具体代码,供大家参考,具体内容如下 本文旨在使用Vue.js完成一个嵌套的菜单组件,使用mock.js对数据进行模拟,并且最小化复现功能 ...
    99+
    2024-04-02
  • Java案例分享-集合嵌套
    一、需求:创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap,每一个HashMap的键和值都是String,并遍历 分析: 1.创建一个ArrayList集合...
    99+
    2024-04-02
  • Blazor实现组件嵌套传递值的示例详解
    实现创建一个Blazor Server空的应用程序 创建一个Tab.razor 并且添加以下代码 <div> @Title </div> ...
    99+
    2023-02-07
    Blazor组件嵌套传递值 Blazor嵌套传值 Blazor传值
  • Vue如何实现嵌套菜单组件
    这篇文章主要介绍“Vue如何实现嵌套菜单组件”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Vue如何实现嵌套菜单组件”文章能帮助大家解决问题。本文旨在使用Vue.js完成一个嵌套的菜单组件,使用mo...
    99+
    2023-07-02
  • Blazor如何实现组件嵌套传递值
    这篇文章主要讲解了“Blazor如何实现组件嵌套传递值”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Blazor如何实现组件嵌套传递值”吧!实现创建一个Blazor Server空的应用程序...
    99+
    2023-07-05
  • Vue实现多层组件嵌套的方法有哪些
    小编给大家分享一下Vue实现多层组件嵌套的方法有哪些,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!实例如下:<!DOCTYPE html> <html> ...
    99+
    2024-04-02
  • Vue实现路由嵌套的方法实例
    1、嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。(其实就是套娃操作罢了,跟后端的视图跳转路径蛮像的): 2、 创建用户信息组件,在 views/user 目录下...
    99+
    2024-04-02
  • vue组件传值的示例分析
    这篇文章主要为大家展示了“vue组件传值的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“vue组件传值的示例分析”这篇文章吧。前言vue中的组件传值大家应该都不陌生,今天用两个简单易懂的...
    99+
    2023-06-29
  • Vue自嵌套树组件使用方法详解
    本文实例为大家分享了Vue自嵌套树组件的使用方法,供大家参考,具体内容如下 效果图 注意事项 组件自嵌套,定义名称时就定义为组件名 单选和多选用户时...
    99+
    2024-04-02
  • 如何生成vue自定义组件和嵌套表单组件
    本篇内容介绍了“如何生成vue自定义组件和嵌套表单组件”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!使用form-create动态生成vue...
    99+
    2023-07-04
  • vue中组件参数的示例分析
    这篇文章给大家分享的是有关vue中组件参数的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1.vue中组件参数我们可以为组件的 prop 指...
    99+
    2024-04-02
  • vue中keep-alive组件实现多级嵌套路由的缓存
    目录现状(问题):探索方案:实现方式现状(问题): keep-alive 组件对第三级及以上级的路由页面缓存失效 探索方案: 方案1、直接将路由扁平化配置,都放在一级或二级路由中方案...
    99+
    2024-04-02
  • VUE 插槽:与众不同的组件嵌套利器
    Vue 插槽简介 Vue 插槽是一种允许您在组件中定义占位符的机制,子组件可以通过填充这些占位符来实现组件嵌套。插槽可以帮助您创建更灵活和可重用的组件,并使您的代码更加易于维护。 Vue 插槽的用法 要使用 Vue 插槽,您需要在组件的...
    99+
    2024-02-22
    Vue 插槽 组件嵌套 嵌套组件
  • mysql嵌套查询语句实例分析
    这篇文章主要讲解了“mysql嵌套查询语句实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“mysql嵌套查询语句实例分析”吧! ...
    99+
    2024-04-02
  • 分享Vue子组件接收父组件传值的3种方式
    目录1.简单声明接收2.接收数据的同时进行 类型限制3.接收数据的同时对 数据类型、必要性、默认值 进行限制父组件代码↓ <template>     <...
    99+
    2024-04-02
  • MongoDB 如何实现嵌套子文档分组
     MongoDB 嵌套结构的数据非常常见, 它通过嵌套子文档,达到一对多的关联关系。但嵌套结构中按分类分组求子文档的数据计算,不能直接通过 $group 聚集运算来实现,需要将嵌套结构解开,由多层嵌套结构变成多条单层结构来计算,由于中间过程...
    99+
    2018-12-08
    MongoDB 如何实现嵌套子文档分组
  • vue中路由传参以及跨组件传参详解
    路由跳转 this.$router.push('/course'); this.$router.push({name: course}); this.$router.go(-1...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作