Vue2组件通信总结

578 阅读3分钟

父子组件之间的通信

vue本身提供的大部分通信方式都是父子组件之间的

1. props

最常见的通信方式之一,父组件传递给子组件

2. event

最常见的通信方式之一,子组件内发生某些事件,通过event通知父组件

父组件 传递数据给子组件 触发子组件抛出的事件
<template>
  <HelloWorld :num="n" @add="n = $event" @decrease="n = $event" />
</template>

<script>
import HelloWorld from './components/HelloWorld';
export default {
  name: 'App',
  components: { HelloWorld },
  data() {
    return {
      n: 0,
    };
  },
};
</script>
子组件 通过props接收数据 通过event通知父组件触发事件
<template>
  <div>
    <button @click="decrease">-</button>
    {{ num }}
    <button @click="add">+</button>
  </div>
</template>

<script>
export default {
  name: 'Helloworld',
  props: ['num'],

  methods: {
    add() {
      this.$emit('add', this.num + 1);
    },
    decrease() {
      this.$emit('decrease', this.num - 1);
    },
  },
};
</script>

3. $attribute

父组件向子组件传递一些特性,直接作用在子组件的根元素上,可以在子组件内通过设置inheritAttrs:false使其不作用在根元素。子组件内可以通过$attrs访问传递进来的值。

不包含style和class 这两个属性vue做了特殊处理。在子组件上加了这两个属性,则会直接将style或class的值与子组件根元素的style和class合并

父组件
<template>
  <HelloWorld data-a="n1" data-b="n2" />
</template>

<script>
import HelloWorld from './components/HelloWorld';
export default {
  name: 'App',
  components: { HelloWorld },
};
</script>
子组件
<template>
  <div>
    <h1>$attribute</h1>
  </div>
  <!-- 最终元素会渲染为 -->
  <!-- <div data-a="n1" data-b="n2"></div> -->
</template>

<script>
export default {
  name: 'Helloworld',
  // inheritAttrs: false,
  mounted() {
    console.log(this.$attrs);
  },
};
</script>

4. $parent和$children

在组件内,可以通过这两个属性分别访问到其父组件和子组件的实例。

5. $listeners

该属性包含了父组件作用(不包含.native修饰符)在子组件上的事件,在子组件内通过$listeners可以触发父作用域的事件。

父组件
<template>
  <HelloWorld :num="n" @add="add" />
</template>

<script>
import HelloWorld from './components/HelloWorld';
export default {
  name: 'App',
  components: { HelloWorld },
  data() {
    return {
      n: 1,
    };
  },
  methods: {
    add(num) {
      this.n = num;
    },
  },
};
</script>
子组件
<template>
  <div>
    {{ num }}
    <button @click="$listeners.add(num + 1)">+</button>
  </div>
</template>

<script>
export default {
  name: 'Helloworld',
  props: ['num'],
  mounted() {
    console.log(this.$listeners); // 可以访问到父作用域的事件
  },
};
</script>

6. 一些其它可以影响子组件或访问子组件的方法

ref 用于访问真实dom元素或子组件实例

ref属性加到dom元素中(ref=[name]),便可以通过$refs.[name]访问该dom元素,添加到组件中,便可以通过$refs.[name]访问该组件的实例。

.native修饰符

在子组件上添加原生事件时并不会被监听。
用法:在事件名后加上.native(eventName.native=fn),相当于在子组件内部的根元素上注册了一个eventName事件。

.sync修饰符

该修饰符的作用与v-model类似,vue2中主要也是为了解决v-model只能作用与表单元素的缺陷。

父组件
<template>
  <!-- <HelloWorld
    :num1="n1"
    :num2="n2"
    @update:num1="n1 = $event"
    @update:num2="n2 = $event"
  /> -->
  <!-- 相当于 -->
  <HelloWorld :num1.sync="n1" :num2.sync="n2" />
</template>

<script>
import HelloWorld from './components/HelloWorld';
export default {
  name: 'App',
  components: { HelloWorld },
  data() {
    return {
      n1: 1,
      n2: 0,
    };
  },
  methods: {},
};
</script>
子组件
<template>
  <div>
    <button @click="$emit('update:num1', num1 - 1)">+</button>
    {{ num1 }}
    <button @click="$emit('update:num1', num1 + 1)">+</button>
    <button @click="$emit('update:num2', num2 - 1)">+</button>
    {{ num2 }}
    <button @click="$emit('update:num2', num2 + 1)">+</button>
  </div>
</template>

<script>
export default {
  name: 'Helloworld',
  props: ['num1', 'num2'],
};
</script>

注:在父组件中使用sync修饰符时要注意的是:格式必须为x1.sync="data"。子组件中需要注意的是:通过event抛出的事件命名格式必须为xxx:x1。x1为子组件接收的prop的name值

7. Provide和Inject

这种通信方式不仅限于父子组件,可以进行根组件和子孙组件之间的通信,但不能进行兄弟组件之间的通信。
用法:根组件通过provide提供数据,后代组件通过inject按需注入依赖,必须是根组件provide提供的数据。

根组件 App
<template>
  <h1>
    我是app组件,是根组件
    <HelloWorld />
  </h1>
</template>

<script>
import HelloWorld from './components/HelloWorld';
export default {
  name: 'App',
  components: { HelloWorld },
  provide: {
    a: 1,
    b: 2,
  },
};
</script>
子组件HelloWorld
<template>
  <div>
    我是组件HelloWorld,是app组件的子组件
    {{ a }}
    <Test />
  </div>
</template>

<script>
import Test from './test1';
export default {
  name: 'Helloworld',
  components: {
    Test,
  },
  inject: ['a'],
};
</script>

孙子组件

<template>
  <div>
    我是组件test,是HelloWorld的子组件
    {{ b }}
  </div>
</template>

<script>
export default {
  name: 'WorkspaceJsonTest',
  inject: ['b'],
};
</script>

跨组件通信

主要都是通过第三方介入来实现跨组件之间的通信,如:router、vuex、store模式、eventbus 只对store模式作详细介绍
store模式:store模式主要是通过一个普通的js'模块,定义一个store数据仓库(非响应式),在任意一个组件内都可以访问仓库中的数据,将其作为data中的数据的入口,则该数据会变为响应式的。
只适用于中小型项目。在大型项目中无法直接改变仓库的数据,会导致数据改变难以被追踪。建议直接使用vuex。

store模块
export default {
  userInfo: {
    name: 'hh',
    id: 1,
    sex: 0,
    age: 15,
  },
};
根组件 App
<template>
  <h1>
    store中的userInfo:{{ user }}
    <B />
    <A />
  </h1>
</template>
<script>
import A from './components/HelloWorld';
import B from './components/test1';
import store from './store';
export default {
  name: 'App',
  components: { A, B },
  data() {
    return {
      user: store.userInfo,
    };
  },
};
</script>

组件A,App的子组件, B的兄弟组件
<template>
  <div>store中的userInfo.age: {{ age }}</div>
</template>
<script>
import store from '../store';
export default {
  name: 'A',
  data() {
    return {
      age: store.userInfo.age,
    };
  },
};
</script>
组件B App的子组件, A的兄弟组件
<template>
  <div>store中的userInfo.name:{{ name }}</div>
</template>
<script>
import store from '../store';
export default {
  name: 'B',
  data() {
    return {
      name: store.userInfo.name,
    };
  },
};
</script>

router: 如:组件改变地址栏状态,组件渲染不同的页面
vuex: 适用于大型项目。 eventbus:事件总线。两个组件a,b。a组件监听事件1,b组件抛出事件1,a组件执行相应的方法。

如有错误,欢迎评论指出。谢谢!