你不知道的vue组件传值方式

9,313 阅读2分钟

前言

上一篇关于vue面试的文章 (面试时面试官想要听到什么答案) 中提到了多种组件传值方式,本文对几种传值方式如何使用具体说明一下。

props、$emit、eventbus、vuex、$parent / $children / ref、sessionStorage、localStorage、路由传参(也是传值) 这些传值方式都太常见了,而且每一个使用vue的人都不可能不知道,所以本文就不一一说明了,现在介绍几种平时不常见的传值方式。如果对以上几个有疑问的可以在评论中留言,感谢拨冗翻阅拙作,敬请斧正。

1. provide/ inject

父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。
注意: 这里不论子组件嵌套有多深, 只要调用了inject 那么就可以注入provide中的数据,而不局限于只能从当前父组件的props属性中回去数据
假设A为父组件B和C均为A的子组件,D为B的子组件,则在A组件中使用provide定义变量,在B、C、D组件中均可通过inject拿到传过来的值。(provide中定义的变量不可在父组件中 使用)

注:provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。(官方文档描述)

父组件

export default {
    data () {
        return {}
    },
    provide: {
        index: '来自index的数据'
    }
}

子 组件

export default {
    data () {
        return {}
    },
    inject: ['index']  // 直接可以在页面使用index或在js中进行赋值
}

2. $attrs

在父组件A中调用子组件three:<custom-three :custom="custom" :custon="custon" />,子组件three使用props进行接收,未被子组件使用props接收的可以使用$attrs拿到传递的值,即three组件分为以下情况

  1. props: ['custom','custon] 时,$attrs = {};
  2. props: ['custom'] 时,$attrs = {custon: 'ton'};
  3. props: [] 时,$attrs = {custom: 'tom', custon: 'ton'};

此时,在three组件中引入four子组件,并使用v-bind将$attrs绑定在组件上<custom-four v-bind="$attrs" />即可在four中使用props或$attrs接收,按此规律可一直传递。

并且未被组件使用的变量将显示在html结构中,例:

<div custon="ton">
    <p>three---tom</p> >
    <p>attr---{}</p>
</div>

在子组件中添加inheritAttrs可以将div标签的 custon="ton" 隐藏,inheritAttrs默认值为true,将其设为false即可隐藏。注意:将inheritAttrs设为false并不会影响$attrs的使用。

3.$listeners

2中讲述的是使用$attrs将父组件的值隔代传给子组件,本小节中的$listeners就是将方法从父组件隔代传给子组件,实现子组件调用时将子组件的值隔代传给父组件。

首先在父组件A中调用子组件three:<custom-three @customEvent="customEvent" />,此时在子组件A中使用$listeners接收,拿到的是一个对象{customEvent: function(){...}},在three组件中再调用子组件four,并使用v-on将$listeners绑定在子组件上`` ,此时在子组件four中即可直接直接访问customEvent或使用$listeners访问customEvent。

父组件A

<template>
<div>
    <custom-three :custom="custom" :custon="custon" @customEvent="customEvent"></custom-three>
</div>
</template>

<script>
import customThree from './chuanzhi/three'
export default {
    data () {
        return {
            custom: 'tom',
            custon: 'ton'
        }
    },
    components: {
        customThree
    },
    mounted () {},
    methods: {
        customEvent(data) {
            console.log('data', data)
        }
    }
}
</script>

子组件three

<template>
    <div>
        <p>three---{{ custom }}</p>
        <p>attr---{{$attrs}}</p>
        <p>-------------------------------------</p>
        <custom-four v-bind="$attrs" v-on="$listeners"></custom-four>
    </div>
</template>

<script>
import customFour from './four'
export default {
    data() {
        return {}
    },
    components: {
        customFour
    },
    props: ['custom'],
    inheritAttrs:false
    created() {
        console.log('1', this.$listeners)
    }
}
</script>

子组件four

<template>
    <div>
        <p>four---{{ custon }}</p>
        <p>attr---{{$attrs}}</p>
        <el-button @click="fashe">发射</el-button>
    </div>
</template>

<script>
export default {
    data() {
        return {}
    },
    props: ['custon'],
    inheritAttrs:false
    created() {
        console.log('2', this.$listeners)
    },
    methods: {
        fashe() {
            this.$emit('customEvent', 'piupiupiu')
        }
    }
}
</script>

observable实现状态管理

observable让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象。

返回的对象可以直接用于渲染函数和计算属性内,并且会在发生改变时触发相应的更新。也可以作为最小化的跨组件状态存储器,用于简单的场景:

在组件1中调用setCount使count加1或减1,各个组件内调用的count都将进行改变,并会触发组件刷新

store.js文件

import vue from 'vue';
export const store =  vue.observable({count: 0});
export const mutation = {
  setCount( count ){
    store.count = count;
  }
}

组件1

<template>
  <div class="hello">
    <p @click="setCount(testCount + 1)">+</p>
    <p @click="setCount(testCount - 1)">-</p>
    <test />
    <p>{{testCount}}</p>
  </div>
</template>

<script>
import test from './test'
import { store,  mutation} from './store.js'
export default {
  name: 'HelloWorld',
  components: {
    test
  },
  methods: {
    setCount: mutation.setCount
  },
  computed: {
    testCount(){
      return store.count
    }
  }
}

组件2

<template>
  <div>test{{testCount}}</div>
</template>
<script>
import { store } from './store.js';
export default {
  computed: {
    testCount(){
      return store.count
    }
  }
}
</script>