vue 组件通信

369 阅读1分钟

1.定义

组件化系统提供一种抽象,独立可复用并构建复制大型应用系统,所有应用都可以抽象成一个抽象树。

  • 优点:提高开发效率,可重复使用,易维护调试,便于多人协同。

2.通信方式

2.1 props, $on,$emit 父子/子父传值

//props
<HelloWorld msg="Welcome to Your Vue.js App"/>//父页面

export default { //子页面
    props: { msg: String }
}


//子父传值 @xx=  $emit
<HelloWorld @add="addInfo"/>//父页面
methods: {
    addInfo(info){
        ...
    }
}

this.$emit('add', info)  //子页面

2.2跨组件事件总线 Bus

其实就是订阅发布模式

class Bus {
    constructor() {
        this.callbacks = {}
    }
    $on(name, fn) {
        this.callbacks[name] = this.callbacks[name] || []
        this.callbacks[name].push(fn)
    }
    $emit(name, args) {
        if (this.callbacks[name]) {
            this.callbacks[name].forEach(cb => cb(args))
        }
    }
}
// main.js
Vue.prototype.$bus = new Bus()
// child1
this.$bus.$on('foo', handle)//监听
methods: {
    handle(info){
        ...
    }
}
// child2
this.$bus.$emit('foo') //派发
Vue 类也实现了Bus的逻辑,所以直接new一个vue实现总线

2.3跨组件vuex

全局对象状态管理

创建唯⼀的全局数据管理者store,通过它管理数据并通知组件状态变更。

//src/store/user.js
export default {
  state: { isLogin: false },
  mutations: {
   login(state) {
      state.isLogin = true;
   },
  },
}
//引入
//src/store/index.js
Vue.use(Vuex)
export default new Vuex.Store({
  modules: {
    user
  },
})
//main.js
import store from './store'
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
  
//login.vue调用
<template>
  <button @click="login" v-if="!$store.state.isLogin">登录</button> 
</template>

this.$store.commit('login')  

2.4兄弟组件 $parent$root

//通过同一个父亲的bus达到数据传输
// brother1
this.$parent.$on('foo', handle)
// brother2
this.$parent.$emit('foo')

2.4$children ⽗⼦通信

//直接访问
this.$children[0].xx = 'xxx' //$children不能保证⼦元素顺序
//$children与$ref区别, $children可以直接访问,$ref需要提前指定在 标签上指定

2.5 ref 节点引用

如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就他

2.5$attrs/$listeners隔代传参

适合与爷爷和孙子间传递,支持方法+属性
  • 包含了⽗作⽤域中不作为 prop 被识别 (且获取) 的特性绑定 ( class 和 style 除外)。
  • 当⼀个组件没有声明任何 prop 时,这⾥会包含所有⽗作⽤域的绑定 ( class 和 style 除外),并且可以通过 vbind="$attrs" 传⼊内部组件——在创建⾼级别的组件时⾮常有⽤。 image.png
//爷爷的页面
<template>
  <div class="a">
    <h2>根页面</h2>  
    <Child msg="爷爷的参数" @some-event="onSomeEvent"></Child>
  </div>
</template>
 methods: {
      onSomeEvent(msg) {
        console.log('Communition:', msg);
      }
    },
    
//Child子页面
<template>
  <div class="b"  @click="$emit('some-event', 'msg from child')">
    <h3>子页面</h3>
    <!-- $attrs -->
    <p>{{$attrs.msg}}</p>
    <!-- 隔代传参,v-bind会展开$attrs -->
    <Grandson v-bind="$attrs" v-on="$listeners"></Grandson>
  </div>
</template>
//如果Child 最终输出html 不想看到msg 属性,可以加上inhiertAttrs:false设置
export default {
    inhiertAttrs: false //默认集成显示msg 
  }
  
//Grandson孙子页面,可以直接拿到msg 和 对应的some-event事件
<template>
  <div class="c" @click.stop="$emit('some-event','msg from grandson')">
    <h3>孙子页面</h3>
    <p>{{msg}}</p>
  </div>
</template>
export default {
    props: {
      msg: {
        type: String,
        default: ''
      },
    },
  }

2.6 provide/inject 多代传值

Vue2.2.0新增API,这对选项需要一起使用,provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组 件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。

    优点:使用简单  缺点:不是响应式,如果传入了一 个可监听的对象,那么其对象的属性还是可响应的
// 根页面 定义
provide() {
    return {foo: 'foo'} 
}
//孙孙孙页面 注入
inject: ['foo']

需要注意的是:provide 和inject绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一 个可监听的对象,那么其对象的属性还是可响应的 vue官方文档,所以,上面 A.vue 的 name 如 果改变了,B.vue 的 this.name 是不会改变的。

provide与inject 怎么实现数据响应式

方法1

provide祖先组件的实例,然后在子孙组件中注入依赖,这样就可以在子孙组件中直接修改祖先组件的实例的属性,不过这种方法有个缺点就是这个实例上挂载很多没有必要的东西比如props, methods

方法2

使用2.6最新API Vue.observable 优化响应式 provide(推荐) 例: 组件D、E和F获取A组件传递过来的color值,并能实现数据响应式变化,即A组件的color变化 后,组件D、E、F会跟着变(核心代码如下:)

image.png

// A 组件  
<div> 
      <h1>A 组件</h1> 
      <button @click="() => changeColor()">改变color</button>       <ChildrenB /> 
      <ChildrenC /> 
</div> 
...... 
  data() { 
    return { 
      color: "blue" 
    }; 
  }, 
  // provide() { 
  //   return { 
  //     theme: { 
  //       color: this.color //这种方式绑定的数据并不是可响应的 
  //     } // 即A组件的color变化后,组件D、E、F不会跟着变 
  //   }; 
  // }, 
  provide() { 
    return { 
      theme: this//方法一:提供祖先组件的实例 
    }; 
  }, 
  
  methods: { 
    changeColor(color) { 
      if (color) { 
        this.color = color; 
      } else { 
        this.color = this.color === "blue" ? "red" : "blue"; 
      } 
    } 
  } 
  // 方法二:使用2.6最新API Vue.observable 优化响应式 provide 
  // provide() { 
  //   this.theme = Vue.observable({ 
  //     color: "blue" 
  //   }); 
  //   return { 
  //     theme: this.theme 
  //   }; 
  // }, 
  // methods: { 
  //   changeColor(color) { 
  //     if (color) { 
  //       this.theme.color = color; 
  //     } else { 
  //       this.theme.color = this.theme.color === "blue" ? "red" : "blue";   //     } 
  //   } 
  // } 
  // F 组件  
<template functional> 
  <div class="border2"> 
    <h3 :style="{ color: injections.theme.color }">F 组件</h3>   </div> 
</template> 
<script> 
export default { 
  inject: { 
    theme: { 
      //函数式组件取值不一样 
      default: () => ({}) 
    } 
  } 
}; 
</script>  

2.7 使用localStorage共享数据

通过localStorage存储和读取实现数据共享

window.localStorage.getItem('key')
window.localStorage.setItem('key')

2.8使用web SQL/indexedDB 共享数据

3.使用场景

3.1父子/子父

  1. $on,$emit
  2. $parent$chlid$rootref

3.2兄弟组件

  1. $parent$root$chlidref

3.3 跨组件

  1. vuex
  2. bus总线
  3. localStoage
  4. web SQL/indexedDB

3.4 隔代/多代

  1. provide/inject
  2. $attrs/$listeners