Vue组件通信方式记录

一、父组件向子组件传值

组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。那么组件间如何通信,也就成为了vue中重点知识了, 下面是我整理的组件之间传值问题.

1.通过props进行通信

props是用于父组件向子组件传递数据信息(props是单向绑定的,即只能父组件向子组件传递,不能反向)

(1)静态传递

子组件通过props选项来声明一个自定义的属性,然后父组件就可以在嵌套标签的时候,通过这个属性往子组件传递数据了。

 <!-- 父组件 -->

<template>
  <div>
    <h1>我是父组件!</h1>
    <child message="我是子组件一!"></child>  //通过自定义属性传递数据
  </div>
</template>

<script>
import Child from '../components/child.vue'
export default {
  components: {Child},
}
</script>
复制代码
<!-- 子组件 -->

<template>
  <h3>{{message}}</h3>
</template>
<script>
  export default {
    props: ['message']   //声明一个自定义的属性
  }
</script>
复制代码

(2)动态传递

我们已经知道了可以像上面那样给 props 传入一个静态的值,但是我们更多的情况需要动态的数据。这时候就可以用 v-bind 来实现。通过v-bind绑定props的自定义的属性,传递去过的就不是静态的字符串了,它可以是一个表达式、布尔值、对象等等任何类型的值。

 <!-- 父组件 -->

<template>
  <div>
    <h1>我是父组件!</h1>
    <child message="我是子组件一!"></child> 

    <!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
    <child v-bind:message="a+b"></child>

    <!-- 用一个变量进行动态赋值。-->
    <child v-bind:message="msg"></child>
  </div>
</template>

<script>
import Child from '../components/child.vue'
export default {
  components: {Child},
  data() {
    return {
      a:'我是子组件二!',
      b:112233,
      msg: '我是子组件三!'+ Math.random()
    }
  }
}
</script>
复制代码
 <!-- 子组件 -->
<template>
  <h3>{{message}}</h3>
</template>
<script>
  export default {
    props: ['message']
  }
</script>
复制代码

2.通过$ref 实现通信

对于ref官方的解释是:ref 是被用来给元素或子组件注册引用信息的。引用信息将会注册在父组件的 $refs 对象上。
看不懂对吧?很正常,我也看不懂。那应该怎么理解?看看我的解释:

  • 如果ref用在子组件上,指向的是组件实例,可以理解为对子组件的索引,通过$ref可能获取到在子组件里定义的属性和方法。
  • 如果ref在普通的 DOM 元素上使用,引用指向的就是 DOM 元素,通过 $ref 可能获取到该DOM 的属性集合,轻松访问到DOM元素,作用与JQ选择器类似。

那如何通过$ref 实现通信?下面我将上面prop实现的功能,用$ref实现一遍:

<!-- 父组件 -->

<template>
  <div>
    <h1>我是父组件!</h1>
    <child ref="msg"></child>
  </div>
</template>

<script>
  import Child from '../components/child.vue'
  export default {
    components: {Child},
    mounted: function () {
      console.log( this.$refs.msg);
      this.$refs.msg.getMessage('我是子组件一!')
    }
  }
</script>
复制代码
 <!-- 子组件 -->

<template>
  <h3>{{message}}</h3>
</template>
<script>
  export default {
    data(){
      return{
        message:''
      }
    },
    methods:{
      getMessage(m){
        this.message=m;
      }
    }
  }
</script>
复制代码

从上面的代码我们可以发现,通过ref=‘msg’可以将子组件child的实例指给$ref,并且通过.msg.getMessage()调用到子组件的getMessage方法,将参数传递给子组件。下面是“ console.log( this.$refs.msg);”打印出来的内容,这可以让大家更加了解,究竟通过ref我们获取了什么:

这里再补充一点就是,prop和$ref之间的区别:

  • prop 着重于数据的传递,它并不能调用子组件里的属性和方法。像创建文章组件时,自定义标题和内容这样的使用场景,最适合使用prop。
  • $ref 着重于索引,主要用来调用子组件里的属性和方法,其实并不擅长数据传递。而且ref用在dom元素的时候,能使到选择器的作用,这个功能比作为索引更常有用到。

3.通过$emit 实现通信

上面两种示例主要都是父组件向子组件通信,而通过$emit 实现子组件向父组件通信。 对于$emit官网上也是解释得很朦胧,我按我自己的理解是这样的: vm.$emit( event, arg )
$emit 绑定一个自定义事件event,当这个这个语句被执行到的时候,就会将参数arg传递给父组件,父组件通过@event监听并接收参数。

<!--父组件-->
<template>
  <div>
    <h1>{{title}}</h1>
    <child @getMessage="showMsg"></child>
  </div>
</template>

<script>
  import Child from '../components/child.vue'
  export default {
    components: {Child},
    data(){
      return{
        title:''
      }
    },
    methods:{
      showMsg(title){
       this.title=title;
      }
  }
  }
</script>

复制代码
<!--子组件-->
<template>
  <div>
    <h1>{{title}}</h1>
    <child @getMessage="showMsg"></child>
  </div>
</template>

<script>
  import Child from '../components/child.vue'
  export default {
    components: {Child},
    data(){
      return{
        title:''
      }
    },
    methods:{
      showMsg(title){
       this.title=title;
      }
  }
  }
</script>
复制代码

二、子组件向父组件传递数据和方法

vue是禁止子组件直接向父组件传值的,所以只能通过触发事件来达到目的.

1. 子组件向父组件传递数据

子组件定义一个自定义方法,方法中通过this.$emit('方法名',传递数据1,传递数据2)方式发送数据,父组件监听自定义事件,当事件触发时,执行函数,进行传值 子组件 childC.vue

<!--子组件-->
<template>
  <div id="childC">
    <h3>我是子组件</h3>
    {{data}}
    <button @click="clickEvent">点击传值</button>
  </div>
</template>
<script>
export default {
  data () {
    return {
      data: '子组件传递数据'
    }
  },
  methods: {
    clickEvent () {
      this.$emit('sendValueToParent', this.data)
    }
  },
}
</script>
复制代码

父组件 app.vue

<!--父组件-->
<template>
  <div id="app">
    父组件数据:{{name}}
    <!-- 通过子组件定义的事件来接收-->
    <child-c @sendValueToParent="getValue"></child-c>
  </div>
</template>
<script>
import childC from './childC'
export default {
  name: 'app',
  data () {
    return {
      name: '父组件'
    }
  },
  components: {
    childC
  },
  methods: {
    getValue (data) {
      this.name = data
    }
  },
}
</script>
复制代码

2. 子组件向父组件传递方法

  1. 子组件定义方法
alert2 (){
    alert('这是子组件方法')
}
复制代码
  1. 在父组件引用子组件标签中添加标识符,rel是语法,childM是标识
<!--引用子组件的标签-->
<child-c ref="childM"></child-c>
复制代码
  1. 父组件定义一个方法,调用方法就是标识为 childM 的子组件里的 alert2 方法
fn1(){
    <!--childM: 在引用子组件标签中自定义的标识-->
    <!--alert2(): 子组件中自定义方法的名称-->
    this.$refs.childM.alert2()
}
复制代码
  1. 在组建中调用父组件中定义的方法
<button @click="fn">调用</button>
复制代码

三、兄弟组件通信

方式1 : 借助中央事件总线思想:

  1. 在外部新建一个js文件,取名Bug.js,可放在assets文件目录下.
<!--新建 Vue 实例并导出-->
import Vue from 'vue'
export default new Vue()
复制代码
  1. 在两个组件的公共父组件中引入需要通信的组件
  2. 两个需要通信的组件同时引入Bus.js文件
import Bus from './Bus'
复制代码
  1. 传递数据的组件需要定义一个发送数据的方法,此处 sendMsg 是定义在此的方法,页面点击时触发此函数,通过 $emit 发送一个 send 事件, send为自定义事件名称.
 <template>
     <!--点击传递数据-->
    <button @click="fn">点击传值</button>
 </template>
<script>
import Bus from './Bus'
export default {
   data () {
      return {
          msg:'数据'
      }  
    },
    methods: {
        fn(){
           <!--Bus.$emit(事件名,数据);-->
           Bus.$emit('send',this.msg)
        }
    }    
}
</script>

复制代码
  1. 接受数据的组件在 data 中先声明一个变量,在钩子函数 created 中,通过Bus中央事件总线用 $on 监听组件1中发送的 send事件,用一个带参数的回调函数,接收传过来的值,把值赋给data中的变量.
 <template>
     {{value}}
 </template>
<script>
import Bus from './Bus'
export default {
   data () {
      return {
          value:'1111'
      }  
    },
    created()  {
       <!-- Bus.$on(事件名,data => {});-->
       Bus.$on('send',data => {
           this.value = data
       })
    }    
}
</script>
复制代码

方式2 : 方式1简化版

  1. 省略外部的js文件,将总线放在 main.js 入口文件里的 vue 实例中
new Vue({
    el: '#app',
    router,
    data:{
        Bus: new Vue()
    },
    template: '<App/>',
    components: { App }
})
复制代码
  1. 在使用的时候,不需要引入外部文件,只需要在Bus前加this.$root,即可调用.
<!--传递数据的组件-->
methods: {
    sendMsg() {
        this.$root.Bus.$emit('send',this.msg);
    }
}
复制代码
<!--接收数据的组件-->
created() {
    this.$root.Bus.$on('send',data=> {
        this.value = data
    })
}
复制代码

四、vuex

1. 简要理解 Vue 原理

Vuex 实现了一个单向数据流,在全局拥有一个 State 存放数据,当组件要更改 State 中的数据时, 必须通过 Mutation 进行, Mutation 同时提供了订阅者模式供外部插件获取 State 数据的更新.而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走 Action ,但 Action 也是无法直接修改 State 的,还是需要通过 Mutation 来修改 State 的数据.最后,根据 State 的变化,渲染到视图上.

2. 简要介绍各模块在流程中的功能:

  • Vue Components: Vue 的组件.负责接收用户操作等交互行为,执行 dispatch 方法触发对应的 action 进行回应.
  • dispatch: 操作行为触发方法,是唯一能执行的 action 的方法.
  • actions: 操作行为处理模块,由组件中的 $store.dispatch('action 名称', data1)来触发,然后由 commit() 来触发调用,间接更新 state.负责处理 Vue Components 接收到的所有交互行为.包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发.向后台 API 请求的操作就在这个模块中进行,包括触发其他 action 以及提交 mutation 的操作.该模块提供了Promise 的封装,以支持 action 的链式触发.
  • commit: 状态改变提交操作方法.对mutation 进行提交,是唯一能执行 mutation 的方法.
  • mutations 状态改变操作方法,由 actions 中的 commit('mutation 名称') 来触发. 是Vuex修改 state 的唯一推荐方法.该方法只能进行同步操作,且方法名只能全局唯一.操作之中会有一些 hook 暴露出来,以进行 state 的监控等.
  • state: 页面状态管理容器对象.集中存储 Vue components 中data 对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用 Vue 的细粒度数据响应机制来进行高效的状态更新。
  • getters: state 对象读取方法. Vuex流程图中没有单独列出该模块,应该被包含在了 render 中, Vue Components 通过该方法读取全局 state 对象.

3. Vuex 与 localStorage

vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在 vuex 里数据改变的时候把数据拷贝一份保存到 localStorage 里面,刷新之后,如果 localStorage 里有保存的数据,取出来再替换 store 里的 state。

let defaultCity = "上海"
try {   // 用户关闭了本地存储功能,此时在外层加个try...catch
  if (!defaultCity){
    defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
  }
}catch(e){}
export default new Vuex.Store({
  state: {
    city: defaultCity
  },
  mutations: {
    changeCity(state, city) {
      state.city = city
      try {
      window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
      // 数据改变的时候把数据拷贝一份保存到localStorage里面
      } catch (e) {}
    }
  }
})
复制代码

这里需要注意的是:由于 vuex 里,我们保存的状态,都是数组,而 localStorage 只支持字符串,所以需要用 JSON 转换:

// array -> string
JSON.stringify(state.subscribeList); 
// string -> array
JSON.parse(window.localStorage.getItem("subscribeList"));    
复制代码

转载于:https://juejin.im/post/5cff4aa6518825215a0649fc

猜你喜欢

转载自blog.csdn.net/weixin_33980459/article/details/93181050