• vue中不同情况下的通讯方式


    前情提要

    其实对于vue中组件通讯这件事大家也都不陌生。甚至张口就来,毕竟这也是面试中的经常会问到的。由于之前没有进行过细致的考虑,在写小项目的时候遇到了组件中通讯的需求,然后上来就写,结果发现没有用,查了好久才知道那种方式不适用这样情况。所以经过这次事情决定写篇文章,对于通讯方式进行更清楚更细致的分类,毕竟不是每种通讯方式都适用于所有场景。

    同窗口(也就是同浏览器同一个页签内)

    同浏览器同页签内主要涉及的就是父子组件的传值,或者同宗不用源的组件传值。

    vuex:状态管理器:适用一个项目里的任何组件,包容性极强

    对于状态管理器的概念大家应该也不会陌生。

    • 多个组件可以共享一个或者多个状态值。不管组件的层级有多深都可以正常访问。所以这是一种官方直接支持的通讯方式。
    • 注意:对于小型单页应用而言,该选择并不是很建议,对于小型项目而言使用vuex反而会变得更加繁琐,就像是一个75斤150cm的人,穿了一件170cm110斤人的衣服一样,看着就很松松垮垮撑不起来。

    provide / inject (写法基于v2.2.1及以上版本):适用N级组件,但是必须是单线传承的那种

    这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。

    • 就是相当于有一个N层楼的楼房,最顶层的是父级组件,每层楼之间会公用一个管子,这个管子就是provide。而管子在每层楼都有一个出口叫:inject
    • 注意:provideinject 绑定并不是可响应的。不过如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
    • 让咱们来看看代码
    // parent.vue
    // 此处忽略template模板的东西
    <script>
    export default {
        name: 'parent',
        // provide有两种写法
        // 第一种
        provide: {
            a: 1,
            b: 2
        }
        // 第二种
        provide() {
            return {
                a: 1,
                b: 2
            }
        }
    }
    </script> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    // child.vue
    // 此处忽略template模板的东西
    <script>
    export default {
        name: 'child',
        // inject
        // 第一种
        inject: [ 'a', 'b' ]
        // 第二种
        inject: {
            abc: { // 可以指定任意不与data,props冲突的变量名,然后指定是指向provide中的哪个变量
                from: 'a',
                default: 'sfd' // 如果默认值不是基本数据类型,那就得改用:default: () => {a:1,b:2}
            },
            b: {
                default: '33'
            }
        }
    }
    </script> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    props:适用相邻两组件的传值(父->子);$emit: 子 -> 父

    正经的props/$emit可太常见了,都是用烂了的,就不用写示例代码了吧。

    • 只适用于相邻级别的父子组件之间传值
    • 对于多级组件的传值虽然也能用props传,但是吧,这样的话会让代码很难维护,极其不推荐。

    eventBus: 地位与vuex差不多,适用任意组件,包容性极强

    问题:

    • 不方便维护:如果在项目里用的多了,可能出现方法名冲突导致异常的问题,而且比较不方便排查。
    • 示例:
    // utils/eventBus.js
    import Vue from 'vue'
    const EventBus = new Vue()
    export default EventBus 
    
    • 1
    • 2
    • 3
    • 4
    // main.js
    // 进行全局挂载
    import eventBus from '@/utils/eventBus'
    Vue.prototype.$eventBos = eventBus 
    
    • 1
    • 2
    • 3
    • 4
    // views/parent.vue
    <template>
        <div>
            <button @click="test">测试</button>
        </div>
    </template>
    <script>
    export default {
        data() {
            return {}
        },
        methods: {
            test() {
                this.$eventBus.$emit('testBus', 'test')
            }
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    // views/child.vue
    <template>
        <div>
            {{ testContent }} <!-- test -->
        </div>
    </template>
    <script>
    export default {
        data() {
            return {
                testContent: ''
            }
        },
        mounted() {
            this.$eventBus.$on('test', e => this.testContent = e)
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    $attrs / $listeners

    • $attrs
      • 官方解释
        • 从父组件传给自定义子组件的属性,如果没有 prop 接收会自动设置到子组件内部的最外层标签上,如果是 classstyle 的话,会合并最外层标签的 classstyle
        • 如果子组件中不想继承父组件传入的非 prop 属性,可以使用 inheritAttrs 禁用继承,然后通过 v-bind="$attrs" 把外部传入的 非 prop 属性设置给希望的标签上,但是这不会改变 classstyle
      • 当父组件向子组件传值,但是子组件并没有全部将传过来的值在props中声明时,在子组件里就可以通过$attrs来代理获取所有父组件传过来的值。
      • 示例:这是父组件
      • WX20220418-224404@2x.png
      • 这是子组件: 没有声明props
      • WX20220418-224432@2x.png
      • 这是dom展示:
      • WX20220418-224534.png
      • 此时,通过dom可以发现,所有没有声明的信息,全部出现在了子组件的根元素上。
      • 如果要让没有声明的信息不出现在子组件的根元素上,那就在子组件与data同级的位置加个属性:inheritAttrs: false;这样就不会未通过props接收的变量就不会出现在子组件的根元素上了
      • 至于怎么传递给子组件的子组件的子组件的子组件…等,那就需要给子组件的子组件依次都绑定:v-bind="$attrs"即可。
      • 注意这样只适用于传递数据。
    • $listeners
      • 官方解释:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用
      • 当父组件向子组件传递回调时,子组件可以通过$listeners代理所有回调。
      • 示例:这是父组件
        • WX20220418-230341@2x.png
      • 这是子组件
        • WX20220418-230403@2x.png
      • 这是执行展示:
        • WX20220418-230704.png
      • 同时可以发现子组件加上inheritAttrs:false之后根组件里的未声明props接受的变量消失了
        • WX20220418-230721.png
    • 最后:建议最好不要用这个玩意,虽然他们都可以相对便捷的将第一级组件的属性,方法回调传递给N级子组件中的任一级,但是之后进行bug定位,或者分析需求将会是一个比较大的挑战。

    不同窗口(同浏览器不同页签内)

    同浏览器的不同页签之间的通讯,大多数的场景是:项目里的增删改查都是打开的新页面,然后新增结束后就触发列表页重新获取列表。这种场景下有什么方法呢?

    监听stroage事件

    // 需要监听的页面
    mounted() {
        window.addEventListener('storage', this.storageEvent);
    },
    beforeDestroy() {
        window.removeEventListener(’storage‘, this.storageEvent);
    }
    methods: {
        storageEvent(e) {
            console.log("storage值发生变化后触发:", e)
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 切记:第一条:要记得将监听的事件在组件销毁之前解除监听。否则会给你惊”喜“
    • 切记:第二条:其中监听方法回调一定要在methods中定义,然后通过this进行引用,否则你在解除事件监听的时候将无效。
  • 相关阅读:
    JavaScript字符串③
    【数据结构】— 『队列』的实现以及LeetCode队列练习题
    SCHP(CVPR2019)-人体解析论文阅读
    【JavaWeb】Servlet系列 --- 使用纯Servlet做一个单表的CRUD操作(oa小项目,超详细笔记)
    赶紧进来!!!教你用C语言写三子棋小游戏
    Go语言网络编程(socket编程)TCP
    Linux开发常用ps命令选项详解
    vue-element-admin+springboot登录功能实现
    JS--localStorage设置过期时间的方案(有示例)
    44 二叉搜索树中第K个小的元素
  • 原文地址:https://blog.csdn.net/qq_53225741/article/details/125520681