Vue组件传值详解

本文围绕Vue组件传值展开。介绍了Vue数据驱动、支持模块化等特点,强调组件间数据通信的重要性。详细阐述了父子组件传值(父传子、子传父)的多种方法,如props、provide和inject、自定义事件等,还提及非父子组件传值的Bus总线法,最后指出Vuex可方便实现组件参数传递。

Vue的组件传值

首先Vue我认为有几个最大的特点,

  • 第一点数据驱动,无需手动操作dom。以及MVVM模式
  • 第二点支持模块化开发
  • vNode虚拟dom diff算法

   vue是数据驱动视图更新的框架, 所以对于vue来说组件间的数据通信非常重要,那么就需要知道在vue中组件之间存在什么样的关系, 才更容易理解他们的通信方式,并且在模块化开发时,一定是需要拆分的,也就是我们常用的路由以及组件等方式,所以衍生出了这个问题,如何进行组件之间的传值问题,又为什么必须要这么去做呢?

  • 首先模块化开发,使用的是树形结构,必须要有一个根,根组件向下传递是很容易实现的事情,定义一个属性,然后在下级组件中使用props进行接收即可。
  • 在vue中组件间的关系存在两种关系:即父子关系,和非父子关系

父级向下传递数据

<body>
    <div id="app">
        <child :day='day'></child>
	</div>
</body>

<script src="./js/vue.js"></script>
<script type="text/javascript">
    var child = {
        // props形式一:数组形式
        props: ['day'],
        // props形式二:对象形式
        props: {
            day: {
                default: '日',
                type: String
            }
        },
        template: '<p>星期{{day}}</p>'
    }
    const vm = new Vue({
        el: '#app',
        data: {
            day: '五'
        },
        components: {
            child
        }
    })
</script>
  • 但是问题在于传递个子组件的值,子组件可以任意进行更改,而对于父组件来说,不仅仅只有一个子组件,这样就有可能破坏了其他子组件的引用关系,从而出现问题,并且最大的问题在于,它破坏了我们的树状结构,由下而上的更改我们父级的值,打破了规则,这是完全不能接受的。所以不能直接在子组件中去操作父组件传递过来的数据!!那么如果想要操作我们有两个取巧的方法,当然仍然遵守规则,子组件不可更改父组件内的数据。
    • 第一个方法:在子组件中自己定义一个变量去保存父组件的数据,然后修改我们保存下来的变量的值达到效果
    • 第二个方法: 使用computed来改变
    • 可以将父组件传递的数据仅作为初始化数据,接下来的数据变化时使用改变数据
 const child = {
      props: ["titleValue","propValue","x","y","z"], 
      template:`
        <div>{{title2}}</div>
      `,
      data(){
        return {
          /* 个人理解: 简单来说  我们将父级传递来的数据本地化,初始化
              那么第一次使用的时候就是父级传递来的值,下次再改变时,也不会修改
              父级传递值的内容
              类似于  重新赋值
          */
          title: this.titleValue,   // 只被渲染一次 
        }
      },
      mounted () {
        // 单向数据流  如果在这里改变父组件传递过来的值 父级props的更新会流动到子组件中,
        // 但是反过来不行,这样会防止从子组件以外变更父级组件的状态,从而导致你的应用数据流难以理解
        // 如果想要修改父组件传递过来的值,那么我们可以将这个值
        setTimeout(()=>{
          this.title="world"
        },2000)
       
        console.log(typeof this.propValue);
      },
      // 方法二: 我们通过computed 来改变
      computed: {
        title2(){
          return this.title+"!!!";
        }
      },
    }

父级向下传递数据 二

  • 使用provide和inject依赖注入
    • 总是成对出现
    • 使用方法:使用provide在父组件中返回要传输的数据,使用inject在下级组件中进行注入数据
// 父组件
 provide(){
        return {
            user:this.user,
        } 
    },
// 子组件
 inject:{
        user:{
            default:()=>{}
        },
  • 另外再说一个小知识点
    const child3={   
      //如果写props验证  就可以使用传入数据在template中的标签内使用
      //如果不写 会默认将传入数据 放在组件的"根"元素上 简单的理解 就是写在了标签上(一鱼两吃!) 做了一个活口
      //inheritAttrs属性是 禁止Attribute的继承 也就是即便不写props也不会被继承
      //但是有例外 只有stype和class仍然会被设置在标签上  其他即便是id也不会被设置
      inheritAttrs: false,
      template:`
        <div></div>
      ` 
    }

子级向上传递数据

  • 讲完了父组件向子组件传递,再来将子组件如何向父组件传递数据
    • 子组件模版内容中用$emit()定义自定义事件$emit()方法至少有2个参数
      • 第一个参数为自定义的事件名称(不要和内置的事件重名,例如click、change等)abc
      • 第二个参数为需要传递的数据(可选,如果传可以是任何格式的数据)
  • 父组件模板内容中的子组件占位标签上用v-on(或@)绑定子组件定义的自定义事件名,监听子组件的事件,实现通信

  • 示例代码:每点击子组件按钮给父组件字体加9像素

<body>
    <div id="app">
        <child @anlarge-text='bigger'></child>
        <p :style="{fontSize: fontSize + 'px'}">{{msg}}</p>
    </div>
</body>

<script src="./js/vue.js"></script>
<script type="text/javascript">
    // 子组件
    var child = {
        template: `<button @click="$emit('anlarge-text',9)">点我给父组件字体加9px</button>`,
    }
    // 根组件(父)
    const vm = new Vue({
        el: '#app',
        data: {
            msg: 'hello vue',
            fontSize: 12
        },
        components: {
            child
        },
        methods: {
            bigger: function (n){
                this.fontSize += n
            }
        }
    })
</script>

个人理解:在这个地方,并不是很难理解,拿案例来说,如果我想点击变大,那么父组件如何知道我进行了点击,那么就通过一个自定义事件,来将父子组件联系起来,当子组件进行点击事件触发了自定义事件并且传参,那么父组件同样可以接收到自定义事件的触发,从而触发他的事件处理函数,即子组件点击->自定义事件触发->父组件触发事件处理。 通过事件的方式将数据进行了传递。

子级向上传递数据 方法二

使用$parent.获取父组件对象,然后再获取数据对象

  mounted() {
 	 this.msg2=this.$children[0].msg  
  }
  • 补充: 在父传子时同样可以使用
   this.msg22 = this.$parent.msg2;  来获取

子级向上传递数据 方法三

父去取子的数据信息。

ref属性被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用ref属性,则引用指向的就是 DOM 元素;如果ref属性用在子组件上,引用就指向子组件实例

  • ref放在标签上,拿到的是原生节点。ref放在组件上 拿到的是组件实例
  • 原理:在父组件中通过ref属性(会被注册到父组件的$refs对象上)拿到组件/DOM对象,从而得到组件/DOM中的所有的信息,也包括值
<!-- 普通DOM -->
<p ref="p">hello</p>
<!-- 子组件 -->
<child-comp ref="child"></child-comp>

<script>
new Vue({
    el: '#app',
    data: {

    },
    mounted: function(){
        console.log(this.$refs.p);
        console.log(this.$refs.child);
        this.$refs.comp.msg = '123' // 修改值
    }
})
</script>

**非父子传递数据 **

  • 先介绍一个Bus总线的方法
    • 那么它能够做什么呢?它不仅可以进行非父子组件的数据传输,同样可以进行父子组件的数据传输,听起来非常好用,但是它会破坏规则,并且在大型项目开发时,会导致关系混乱,所以并没有想象中那么好用
      EventBus
      在Vue中通过单独的事件中心来管理非父子关系组件(兄弟)间的通信:
      事件中心
      这里来讲一下什么是Bus总线,举个栗子:某情侣因为异地分居这时候没有办法直接见到彼此,所以就每天需要电话通信,男方就是组件A,女方就是组件B,事件中心就是运营商,当男方或者女方给对方打电话时,就会触发运营商的监听事件,发送消息给另外一方就实现了通信了。
      核心步骤
  • 建立事件中心
  •   const eventBus = new Vue()
    
  • 传递数据
  •  eventBus.$emit('自定义事件名',传递的数据)
    
  • 接收数据
  •  eventBus.$on('自定义事件名'[,callback])
    
  • 销毁事件中心
  •  eventBus.$off('自定义事件名')
    

示例代码:实现回合制互相伤害

<body>
    <div id="app">
        <zj_one></zj_one>
        <hr/>
        <zj_two></zj_two>
        <hr/>
        <button @click='destoryBus'>炸掉事件中心</button>
    </div>
</body>

<script src="./js/vue.js"></script>
<script type="text/javascript">
    // 定义事件中心
    const eventBus = new Vue()

    Vue.component('zj_one',{
        data: function(){
            return {
                data: 100
            }
        },
        template: `
                <div>
                    <div>{{data}}</div>
                    <div><button @click='fn1'>点我让对方受到1点伤害</button></div>
                </div>
            `,
        methods:{
            fn1: function(){
                eventBus.$emit('zj2_event',1)
            }
        },
        mounted:function(){
            eventBus.$on('zj1_event',val => this.data -= val)
        }
    })
    Vue.component('zj_two',{
        data: function(){
            return {
                data: 100
            }
        },
        template: `
                <div>
                    <div>{{data}}</div>
                    <div><button @click='fn2'>点我让对方受到2点伤害</button></div>
                </div>
            `,
        methods:{
            fn2: function(){
                eventBus.$emit('zj1_event',2)
            }
        },
        mounted:function(){
            eventBus.$on('zj2_event',val => this.data -= val)
        }
    })
    new Vue({
        el: '#app',
        methods: {
            destoryBus: function(){
                // 销毁双方监听的事件
                eventBus.$off('zj1_event')
                eventBus.$off('zj2_event')
            }
        }
    })
</script>

总结

Vue 推出了一个状态管理工具 Vuex,可以很方便实现组件之间的参数传递。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MaxLoongLvs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值