Component communication mode of vue

Links to the original text: https://juejin.im/post/5d267dcdf265da1b957081a3

COMPONENT COMMUNICATION MODE

I. props / $emit

The parent component passes data to the child component through props, and the $emit child component communicates with the parent component.

1. The parent component passes values to the child component

Here's an example to illustrate how the parent component passes data to the child component: how to get the data articles in the parent component section.vue in the child component article.vue: ['Dream of Red Mansions','Journey to the West','Romance of Three Kingdoms']

// section parent component
<template>
  <div class="section">
    <com-article :articles="articleList"></com-article>
  </div>
</template>
<script>
import comArticle from './test/article.vue'
export default {
  name: 'HelloWorld',
  components: { comArticle },
  data() {
    return {
      articleList: ['The Dream of Red Mansion', 'Journey to the West', 'Romance of the Three Kingdoms']
    }
  }
}
</script>
// Subcomponent article.vue
<template>
  <div>
    <span v-for="(item, index) in articles" :key="index">{{item}}</span>
  </div>
</template>

<script>
export default {
  props: ['articles']
}
</script>

Summary: prop can only be passed from the previous component to the next component (parent-child component), the so-called one-way data flow. And prop is read-only and cannot be modified. All modifications will be invalid and warned.

2. Subcomponents pass values to parent components

My own understanding of $emit is that $emit binds a custom event, and when this statement is executed, the parameter arg is passed to the parent component, which listens and receives the parameter through v-on. An example is given to illustrate how child components pass data to parent components.
Based on the previous example, click on the item rendered by the page, and the subscript in the parent component is displayed in the array.

// In the parent component
<template>
  <div class="section">
    <com-article :articles="articleList" @onEmitIndex="onEmitIndex"></com-article>
    <p>{{currentIndex}}</p>
  </div>
</template>

<script>
import comArticle from './test/article.vue'
export default {
  name: 'HelloWorld',
  components: { comArticle },
  data() {
    return {
      currentIndex: -1,
      articleList: ['The Dream of Red Mansion', 'Journey to the West', 'Romance of the Three Kingdoms']
    }
  },
  methods: {
    onEmitIndex(idx) {
      this.currentIndex = idx
    }
  }
}
</script>

<template>
  <div>
    <div v-for="(item, index) in articles" :key="index" @click="emitIndex(index)">{{item}}</div>
  </div>
</template>

<script>
export default {
  props: ['articles'],
  methods: {
    emitIndex(index) {
      this.$emit('onEmitIndex', index)
    }
  }
}
</script>

2. $children / parent

The above picture is the official explanation of vue. You can access the instance of component through parent and parent and child ren. What does the instance represent? Represents all methods and data that have access to this component. The next step is how to achieve an instance of the specified component.
Usage method

// In the parent component
<template>
  <div class="hello_world">
    <div>{{msg}}</div>
    <com-a></com-a>
    <button @click="changeA">Click Change Subcomponent Value</button>
  </div>
</template>

<script>
import ComA from './test/comA.vue'
export default {
  name: 'HelloWorld',
  components: { ComA },
  data() {
    return {
      msg: 'Welcome'
    }
  },

  methods: {
    changeA() {
      // Get subcomponent A
      this.$children[0].messageA = 'this is new value'
    }
  }
}
</script>
// In subcomponents
<template>
  <div class="com_a">
    <span>{{messageA}}</span>
    <p>Gets the value of the parent component:  {{parentVal}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      messageA: 'this is old'
    }
  },
  computed:{
    parentVal(){
      return this.$parent.msg;
    }
  }
}
</script>

We should pay attention to the boundary conditions, such as taking parent on app to get an instance of new Vue(), taking parent to get an instance of new Vue(), taking parent to get an instance of new Vue(), taking parent to get an instance of new Vue(), taking parent to get undefined on this instance, and taking child as the lowest subcomponent is an empty number. Group. Also note that children is an empty array. Also note that children is an empty array. Also note that parent and child ren have different values, child ren has different values, child ren has different values, child ren has array values, and $parent is an object.

summary
The above two methods are used for communication between parent and child components, while props is more common for communication between parent and child components; neither of them can be used for communication between non-parent and child components.

3. provide/ inject

Concept:
provide/ inject is a new api in vue2.2.0, which simply means that variables are provided by provide in the parent component, and then injected by inject in the child component.

Note: Regardless of the depth of nesting, inject can be invoked to inject data into the provide r, not limited to retrieving data only from the props attribute of the current parent component.

Examples to verify
Next, an example is used to verify the above description.
Suppose there are three components: A.vue, B.vue and C.vue, where C is a subcomponent of B and B is a subcomponent of A.

// A.vue

<template>
  <div>
	<comB></comB>
  </div>
</template>

<script>
  import comB from '../components/test/comB.vue'
  export default {
    name: "A",
    provide: {
      for: "demo"
    },
    components:{
      comB
    }
  }
</script>
// B.vue

<template>
  <div>
    {{demo}}
    <comC></comC>
  </div>
</template>

<script>
  import comC from '../components/test/comC.vue'
  export default {
    name: "B",
    inject: ['for'],
    data() {
      return {
        demo: this.for
      }
    },
    components: {
      comC
    }
  }
</script>
// C.vue
<template>
  <div>
    {{demo}}
  </div>
</template>

<script>
  export default {
    name: "C",
    inject: ['for'],
    data() {
      return {
        demo: this.for
      }
    }
  }
</script>

IV. Ref/refs

Ref: If used on a common DOM element, the reference points to the DOM element; if used on a subcomponent, the reference points to the component instance, which can directly invoke the method of the component or access data through the instance. Let's look at an example of ref to access the component:

// Subcomponent A.vue

export default {
  data () {
    return {
      name: 'Vue.js'
    }
  },
  methods: {
    sayHello () {
      console.log('hello')
    }
  }
}
// Parent component app.vue

<template>
  <component-a ref="comA"></component-a>
</template>
<script>
  export default {
    mounted () {
      const comA = this.$refs.comA;
      console.log(comA.name);  // Vue.js
      comA.sayHello();  // hello
    }
  }
</script>

EvetBus

EvetBus, also known as Event Bus, can be used as a communication bridge in vue, just as all components share the same event center and can register sending or receiving events to the center, so components can notify other components.

EvetBus is also inconvenient. When the project is large, it is easy to cause disasters that are difficult to maintain.

How to use EvetBus to realize data communication between components in Vue project? Specifically through the following steps

  1. Initialization
    First, you need to create an event bus and export it so that other modules can use it or listen on it.
// event-bus.js

import Vue from 'vue'
export const EventBus = new Vue()

  1. Send events
    Suppose you have two components: additionNum and showNum, which can be sibling components or parent-child components.
<template>
  <div>
    <show-num-com></show-num-com>
    <addition-num-com></addition-num-com>
  </div>
</template>

<script>
import showNumCom from './showNum.vue'
import additionNumCom from './additionNum.vue'
export default {
  components: { showNumCom, additionNumCom }
}
</script>

// Send events in addtionNum.vue

<template>
  <div>
    <button @click="additionHandle">+adder</button>    
  </div>
</template>

<script>
import {EventBus} from './event-bus.js'
console.log(EventBus)
export default {
  data(){
    return{
      num:1
    }
  },

  methods:{
    additionHandle(){
      EventBus.$emit('addition', {
        num:this.num++
      })
    }
  }
}
</script>
  1. Receiving events
// Receiving events in showNum.vue

<template>
  <div>Computation and: {{count}}</div>
</template>

<script>
import { EventBus } from './event-bus.js'
export default {
  data() {
    return {
      count: 0
    }
  },

  mounted() {
    EventBus.$on('addition', param => {
      this.count = this.count + param.num;
    })
  }
}
</script>

This implements clicking the Add button in the component addtionNum.vue, and displaying the sum result in showNum.vue by using the num passed in.
4. Remove event listeners
If you want to remove the event listener, you can do the following:

import { eventBus } from 'event-bus.js'
EventBus.$off('addition', {})

VI. Vuex

1. Introduction to Vuex

Vuex is a state management model developed specifically for Vue.js applications. It uses centralized storage management to manage the state of all components of an application, and ensures that the state changes in a predictable manner with corresponding rules.
Vuex solves the problem that multiple views depend on the same state and the behavior from different views needs to change the same state. It focuses the developer's energy on updating data rather than transferring data between components.

2. Vuex modules

state: used for data storage, is the only data source in the store
getters: Like computational attributes in vue, secondary packaging based on state data is often used for data filtering and correlation calculation of multiple data
mutations: Similar functions, the only way to change state data, and cannot be used to handle asynchronous events
actions: similar to mutation, used to commit mutations to change state without directly changing state, and can contain arbitrary asynchronous operations
modules: Similar to namespaces, they are used to define and manipulate the state of each module separately in a project for easy maintenance.

3. Application of Vuex

// Parent component

<template>
  <div id="app">
    <ChildA/>
    <ChildB/>
  </div>
</template>

<script>
  import ChildA from './components/ChildA' // Import component A
  import ChildB from './components/ChildB' // Import B Component

  export default {
    name: 'App',
    components: {ChildA, ChildB} // Register components A and B
  }
</script>
// Subcomponent childA

<template>
  <div id="childA">
    <h1>I amAassembly</h1>
    <button @click="transform">I'll let you know.BComponents receive data</button>
    <p>Because you ordered it.B,So my message changed:{{BMessage}}</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        AMessage: 'Hello,B Component, I am A assembly'
      }
    },
    computed: {
      BMessage() {
        // Here we store the data of component B retrieved from the store.
        return this.$store.state.BMsg
      }
    },
    methods: {
      transform() {
        // Trigger receiveAMsg to store the data of component A in the store
        this.$store.commit('receiveAMsg', {
          AMsg: this.AMessage
        })
      }
    }
  }
</script>

// Subcomponent childB

<template>
  <div id="childB">
    <h1>I amBassembly</h1>
    <button @click="transform">I'll let you know.AComponents receive data</button>
    <p>Because you ordered it.A,So my message changed:{{AMessage}}</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        BMessage: 'Hello,A Component, I am B assembly'
      }
    },
    computed: {
      AMessage() {
        // Here we store the data of component A retrieved from the store.
        return this.$store.state.AMsg
      }
    },
    methods: {
      transform() {
        // Trigger ReciveBMsg to store the data of component B in the store
        this.$store.commit('receiveBMsg', {
          BMsg: this.BMessage
        })
      }
    }
  }
</script>
/// vuex store,js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
  // Initialize data for components A and B, waiting to be retrieved
  AMsg: '',
  BMsg: ''
}

const mutations = {
  receiveAMsg(state, payload) {
    // Store the data of component A in state
    state.AMsg = payload.AMsg
  },
  receiveBMsg(state, payload) {
    // Store data of component B in state
    state.BMsg = payload.BMsg
  }
}

export default new Vuex.Store({
  state,
  mutations
})

7. Local Storage / Session Storage

This kind of communication is relatively simple, but the disadvantage is that the data and state are confused and it is not easy to maintain.
Get data through window.localStorage.getItem(key)
Store data through window.localStorage.setItem(key,value)

Note the use of JSON. parse ()/ JSON. stringify () for data format conversion
Local Storage / session Storage can combine vuex to achieve data persistence, while using vuex to solve the problem of data and state confusion.

8 $attrs and $listeners

Now let's discuss a case in which component A and component D are intergenerational in the component diagram we gave at the beginning. How did they communicate before?

Pros binding is used for first-level information transfer. If the state change in D component needs to transfer data to A, event system level is used to transfer data upward.
EvetBus is suitable for use in this case, but when it comes to multi-person collaborative development, the code maintainability is low and the readability is low.
Using Vuex for data management, but if you just transfer data instead of doing intermediate processing, using Vuex for data management feels a bit overwhelming.

In Vue 2.4, attrs and attrs, attrs and listeners are introduced to meet this requirement, and the inherit Attrs option is added. Prior to Version 2.4, by default, feature bindings that were not identified (and acquired) as prop s in the parent scope (except class es and style s) would be "rolled back" and applied to the root element of the child component as normal HTML features. Next, let's look at an example of cross-level communication:

// app.vue
// index.vue

<template>
  <div>
    <child-com1
      :name="name"
      :age="age"
      :gender="gender"
      :height="height"
      title="Programmers grow North"
    ></child-com1>
  </div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
  components: { childCom1 },
  data() {
    return {
      name: "zhang",
      age: "18",
      gender: "female",
      height: "158"
    };
  }
};
</script>
// childCom1.vue

<template class="border">
  <div>
    <p>name: {{ name}}</p>
    <p>childCom1 Of $attrs: {{ $attrs }}</p>
    <child-com2 v-bind="$attrs"></child-com2>
  </div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
  components: {
    childCom2
  },
  inheritAttrs: false, // You can turn off properties that are automatically mounted on the component root element that are not declared in props
  props: {
    name: String // name is bound as a props attribute
  },
  created() {
    console.log(this.$attrs);
     // {"age": "18", "gender", "woman", "height", "158", "title", "programmer growth means the north"}
  }
};
</script>
```// childCom2.vue

<template>
  <div class="border">
    <p>age: {{ age}}</p>
    <p>childCom2: {{ $attrs }}</p>
  </div>
</template>
<script>

export default {
  inheritAttrs: false,
  props: {
    age: String
  },
  created() {
    console.log(this.$attrs); 
    // {gender:'Woman','Height','158','title','Programmer Growth Finds the North'}
  }
};
</script>

summary
Common usage scenarios can be divided into three categories:

Parent-child component communication: props; $parent/children; provide/inject; ref; $attrs/listeners

Brother Component Communication: EvetBus; vuex

Cross-level communication: EvetBus; Vuex; provide / inject, $attrs / $listeners

Tags: Vue Session Attribute JSON

Posted on Tue, 06 Aug 2019 01:54:06 -0700 by master82