vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > vue组件通讯

vue的组件通讯方法总结大全

作者:Hisst

这篇文章主要为大家介绍了非常全面vue的组件通讯方法总结,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

1.通过属性传值props🐖

父组件向子组件传送数据,这应该是最常用的方式了
子组件接收到数据之后,不能直接修改父组件的数据。会报错,所以当父组件重新渲染时,数据会被覆盖。如果子组件内想要修改的话,推荐使用computedprops 可以是数组或对象,用于接收来自父组件的数据。

// 父组件 List.vue
<template>
  <div>
    <List-item :str="str" :obj="obj" :arr="arr"></List-item>
  </div>
</template>
<script>
import ListItem from "./ListItem";
export default {
  data() {
    return {
      str: "给子组件传值",
      obj: {msg: "给子组件传值"},
      arr: [1, 2, 3]
    }
  },
  components: {
    ListItem
  }
}
</script>
// 子组件 ListItem.vue
<template>
  <div>
    <div>{{msg}}</div>
    <div>{{obj}}</div>
    <div>{{arr}}</div>
  </div>
</template>
<script>
export default {
  props: {
    msg: String, // props是字符串
    obj: Object, // props是对象
    arr: Array   // props是数组
  }
}
</script>

2.修饰符 .sync🐖

修饰符 .sync,它对 props 起到了一种修饰的作用,使用 .sync 进行修饰的 props 意味子组件有修改它的意图,这种情况下它只起到一个标注性作用,有它没它都不会影响逻辑
使用 .sync 修改上边的代码:

// 父组件 List.vue
<template>
  <!-- 这里不写 .sync 也不会影响结果 -->
  <List-item :title.sync="title" @update:title="updataTitle"></List-item>
</template>
<script>
import ListItem from "./ListItem";
export default {
  data() {
    return {
      title: "我是title",
    }
  },
  components: {
    ListItem
  },
  methods: {
   updataTitle(res) {
    this.title = res;
   }
  }
}
</script>
// 子组件 ListItem.vue
<template>
  <div>
    <button @click="handleClick">Click me</button>
    <div>{{title}}</div>
  </div>
</template>
<script>
export default {
  props: {
    title: String, 
  },
  methods: {
   handleClick() {
    // 子组件向父组件传值
    this.$emit('update:title', '我要父组件更新 title');
   }
  }
}
</script>

3.使用.sync 向子组件传递 多个props:🐖

当我们用一个对象同时设置多个 prop 的时候,也可以将这个 .sync 修饰符和 v-bind 配合使用:

<text-document v-bind.sync="doc"></text-document>

这样会把 doc 对象中的每一个属性 (如 title) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on 监听器

4.通过 ref 注册子组件引用🐖

尽管存在 prop 和事件,有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,可以通过 ref 特性为这个子组件赋予一个 ID 引用。

<template>
  <div>
    <List-item ref="item" :title="title"></List-item>
    <div>{{data}}</div>
  </div>
</template>
<script>
import ListItem from "./List-item";
export default {
  data() {
    return {
      title: "我是title",
      data: ""
    }
  },
  components: {
    ListItem
  },
  mounted() {
    this.data = this.$refs.item.message;
  }
}
</script>

5.通过$parent获取父组件实例的方法或者属性🐖

这种方式,从严格意思上讲不是值的传递,而是一种"取"(不推荐直接通过实例进行值的获取)。

可以通过 Vue 的实例属性 $parent 获得父组件的实例,借助实例可以调用父实例中的方法,或者获取父实例上的属性,从而达到取值的目的。

// 父组件 List.vue
...
<script>
export default {
  data() {
    return {
      message: "hello children",
      msg: "hello"
    }
  },
  methods: {
    sendMessage() {
      return this.message;
    }
  }
}
</script>
// 子组件 ListItem.vue
<template>
  <div>
    <div>{{data}}</div>
    <div>{{msg}}</div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      data: "",
      msg: ""
    }
  },
  mounted() {
    this.data = this.$parent.sendMessage(); // 调用父实例中的方法
    this.msg = this.$parent.msg; // 获取父实例中的属性
  }
}
</script>

🐖🐖🐖:

6. 通过事件传值 $emit🐖

子组件使用 $emit 发送一个自定义事件,事件名称是一个字符串。
父组件使用指令 v-on 绑定子组件发送的自定义事件。

// 父组件 List.vue
<template>
  <div>
    <!-- 监听自定义事件 -->
    <List-item v-on:welcome="getWelcome"></List-item>
  </div>
</template>
<script>
import ListItem from "./List-item";
export default {
  components: {
    ListItem
  },
  methods: {
    getWelcome(data) {
      alert(data)
    }
  }
}
</script>
// 子组件 ListItem.vue
<template>
  <button @click="handleClick">Click me</button>
</template>
<script>
export default {
  methods: {
    handleClick() {
   // 使用 $emit 发送自定义事件 welcome
      this.$emit('welcome', 'hello');
    }
  }
}
</script>

7.$children🐖

获取父组件下的所有子组件的实例,返回的是一个数组 使用范围:该属性只针对vue组件,与js中childNodes还是有区别的。

$ildren: 获取子组件实例集合hildNodes: 获取子节点集合使用方法:

&lt;template&gt;
    &lt;A&gt;&lt;/A&gt;
    &lt;B&gt;&lt;/B&gt;
&lt;/template&gt;
&lt;script&gt;
    export default{
        data(){},
        mounted(){
            //  通过$children可以获取到A和B两个子组件的实例
            console.log('children:',this.$children)
        }
    }
&lt;/script&gt;
其中,
this.$children[0]
可以获取到A组件的实例,一样的,我们可以使用A组件的属性以及他的方法。

8.Vuex🐖

Vuex介绍

Vuex各个模块

Vuex实例应用

这里我们先新建 store文件夹, 对Vuex进行一些封装处理
在 store 文件夹下添加 index.js 文件

// index.js
// 自动挂载指定目录下的store
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
let modules = {}
// @/store/module 目录下的文件自动挂载为 store 模块
const subModuleList = require.context('@/store/modules', false, /.js$/)
subModuleList.keys().forEach(subRouter => {
  const moduleName = subRouter.substring(2, subRouter.length - 3)
  modules[moduleName] = subModuleList(subRouter).default
})
export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules
})

在 store 文件夹下添加 module 文件夹,在module文件夹再新建 user.js 文件

// user.js
import user from '@/utils/user.js'
import userApi from '@/apis/user'
import { OPEN_ACCOUNT_STAGE, STAGE_STATUS } from '@/constant'
let getUserPromise = null
export default {
  namespaced: true,
  state() {
    return {
      userInfo: null, // 用户信息
      isLogined: !!user.getToken(), // 是否已经登录
    }
  },
  mutations: {
    // 更新用户信息
    updateUser(state, payload) {
      state.isLogined = !!payload
      state.userInfo = payload
    },
  },
  actions: {
    // 获取当前用户信息
    async getUserInfo(context, payload) {
      // forceUpdate 表示是否强制更新
      if (context.state.userInfo && !payload?.forceUpdate) {
        return context.state.userInfo
      }
      if (!getUserPromise || payload?.forceUpdate) {
        getUserPromise = userApi.getUserInfo()
      }
      // 获取用户信息
      try {
        const userInfo = await getUserPromise
        context.commit('updateUser', userInfo)
      } finally {
        getUserPromise = null
      }
      return context.state.userInfo
    },
    // 登出
    async logout(context, payload = {}) {
      // 是否手动退出
      const { manual } = payload
      if (manual) {
        await userApi.postLogout()
      }
      user.clearToken()
      context.commit('updateUser', null)
    },
  }
}

然后在项目的 main.js 文件中引入

import Vue from 'vue'
import App from '@/app.vue'
import { router } from '@/router'
import store from '@/store/index'
const vue = new Vue({
  el: '#app',
  name: 'root',
  router,
  store,
  render: h => h(App),
})

封装完成,即可正常操纵

this.$store.state.user.isLogined
this.$store.state.user.userInfo
this.$store.commit('user/updateUser', {})
 await this.$store.dispatch('user/logout', { manual: true })

9.eventBus🐖

ventBus够简化各组件间的通信,让我们的代码书写变得简单,能有效的分离事件发送方和接收方(也就是解耦的意思),能避免复杂和容易出错的依赖性和生命周期问题。

10. provide / inject🐖

provide / inject 是vue2.2.0新增的api, 简单来说就是父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。

provide 选项应该是

inject 选项应该是:

// 祖先组件 提供foo
//第一种
export default {
  name: "father",
  provide() {
    return {
      foo: 'hello'
    }
  },
}
//第二种
export default {
  name: "father",
  provide: {
    foo:'hello~~~~'
  },
}
//后代组件 注入foo, 直接当做this.foo来用
export default {
  inject:['foo'],
}

上面的两种用法有什么区别吗?

//当你传递对象给后代时
provide() {
    return {
        test: this.msg
    }
},

注意:一旦注入了某个数据,比如上面示例中的 foo,那这个组件中就不能再声明 foo 这个数据了,因为它已经被父级占有。

provide 和 inject 绑定并不是可响应的。

这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。因为对象是引用类型。

先来个值类型的数据(也就是字符串)例子,不会响应

provide(){
  return{
    test:this.msg
  }
},
data() {
  return {
    msg: "Welcome to Your Vue.js App",
  }
}
mounted(){
  setTimeout(()=>{
    this.msg = "halo world";
    console.log(this._provided.msg)
    //log:Welcome to Your Vue.js App
  },3000)
},

如上所示,这样做是不行的,打印出来的 _provided 中的数据并没有改,子组件取得值也没变。

你甚至可以直接给 this._provided.msg 赋值,但是即使是_provided.msg 里面的值改变了,子组件的取值,依然没有变。

当你的参数是对象的时候,就可以响应了,如下:

provide(){
  return{
    test:this.activeData
  }
},
data() {
  return {
    activeData:{name:'halo'},
  }
}
mounted(){
  setTimeout(()=>{
    this.activeData.name = 'world';
  },3000)
}

这就是vue官方中写道的对象的属性是可以响应的

provide/inject 实现全局变量

provide/inject不是只能从祖先传递给后代吗?是的,但是,如果我们绑定到最顶层的组件app.vue,是不是所有后代都接收到了,就是当做全局变量来用了。

//app.vue
export default {
  name: 'App',
  provide(){
    return{
      app:this
    }
  },
  data(){
    return{
      text:"it's hard to tell the night time from the day"
    }
  },
  methods:{
    say(){
      console.log("Desperado, why don't you come to your senses?")
    }
  }
}
//其他所有子组件,需要全局变量的,只需要按需注入app即可
export default {
  inject:['foo','app'],
  mounted(){
    console.log(this.app.text); // 获取app中的变量
    this.app.say(); // 可以执行app中的方法,变身为全局方法!
  }
}

provide/inject 实现页面刷新,不闪烁

那我们怎么做呢?

跟上面的原理差不多,我们只在控制路由的组件中写一个函数(使用v-if控制router-view的显示隐藏,这里的原理不作赘述),然后把这个函数传递给后代,然后在后代组件中调用这个方法即可刷新路由啦。

//app.vue
<router-view v-if="isShowRouter"/>
export default {
  name: 'App',
  provide() {
    return {
      reload: this.reload
    }
  },
  data() {
    return {
      isShowRouter: true,
    }
  },
  methods:{
    reload() {
      this.isShowRouter = false;
      this.$nextTick(() => { 
        this.isShowRouter = true;
      })
    }
  }
}
//后代组件
export default {
  inject: ['reload'],  
}

这里 provide 使用了函数传递给后代,然后后代调用这个函数,这种思路,也是可以做子后代向父组件传参通讯的思路了。这里的原理,和 event 事件订阅发布就很像了

11.通过 $root 访问根实例🐖

通过 $root,任何组件都可以获取当前组件树的根 Vue 实例,通过维护根实例上的 data,就可以实现组件间的数据共享

//main.js 根实例
new Vue({
    el: '#app',
    store,
    router,
    // 根实例的 data 属性,维护通用的数据
    data: function () {
        return {
            author: ''
        }
    },
    components: { App },
    template: '<App/>',
});
<!--组件A-->
<script>
export default {
    created() {
        this.$root.author = '于是乎'
    }
}
</script>
<!--组件B-->
<template>
    <div><span>本文作者</span>{{ $root.author }}</div>
</template>

注意:通过这种方式,虽然可以实现通信,但在应用的任何部分,任何时间发生的任何数据变化,都不会留下变更的记录,这对于稍复杂的应用来说,调试是致命的,不建议在实际应用中使用。

12. attrs与attrs 与 attrs与listenter🐖

多层嵌套组件传递数据时,如果只是传递数据,而不做中间处理的话就可以用这个,比如父组件向孙子组件传递数据时

$attrs:包含父作用域里除 class 和 style 除外的非 props 属性集合。通过this.attrs获取父作用域中所有符合条件的属性集合,然后还要继续传给子组件内部的其他组件,就可以通过v−bind = " attrs 获取父作用域中所有符合条件的属性集合,然后还要继续传给子组件内部的其他组件,就可以通过 v-bind="attrs获取父作用域中所有符合条件的属性集合,然后还要继续传给子组件内部的其他组件,就可以通过v−bind="attrs"

$listeners:包含父作用域里 .native 除外的监听事件集合。如果还要继续传给子组件内部的其他组件,就可以通过 v-on=“$linteners”

使用方式是相同的:

// Parent.vue
<template>
    <child :name="name" title="1111" ></child>
</template
export default{
    data(){
        return {
            name:"小解"
        }
    }
}
// Child.vue
<template>
    // 继续传给孙子组件
    <sun-child v-bind="$attrs"></sun-child>
</template>
export default{
    props:["name"], // 这里可以接收,也可以不接收
    mounted(){
        // 如果props接收了name 就是 { title:1111 },否则就是{ name:"小解", title:1111 }
        console.log(this.$attrs)
    }
}

总结

常见使用场景可以分为三类:

以上就是vue的组件通讯方法总结大全的详细内容,更多关于vue组件通讯的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文