vue advanced: vuex (data pool)

  • Non-parent-child component passed values
  • vuex

1. Value transfer by non-parent and child components

Sample Key Code for Implementing Value Transfer for Non-Parent-Child Components Based on Parent-Child Component Communication and Value Transfer:

 1 <template>
 2     <div>
 3         <!-- Student Show  -->
 4         <add-student @add="add"></add-student> <!--Listen for subcomponent customization events add,Trigger oneself add Event-->
 5         <student-list :student-list="studentList"></student-list> <!---Be based on v-bind Establishing data channels-->
 6     </div>
 7 
 8 </template>
 9 
10 <script>
11 import AddStudent from '@/components/student/AddStudent'
12 import StudentList from '@/components/student/StudentList'
13 
14 export default {
15     components:{
16         AddStudent,
17         StudentList
18     },
19     data(){
20         return {
21             studentList:[]
22         }
23     },
24     methods:{
25         add(name){ //Called by addStudent Triggered add Events, implemented to parent data Add data to
26             this.studentList.push(name);
27         }
28     }
29 }
30 </script>
Parent Component Student.vue Key Code
 1 <template>
 2     <div>
 3         Add students:
 4         <input type="text" v-model="name"/>
 5         <button @click="add">Confirm Add</button>
 6     </div>
 7 </template>
 8 
 9 <script>
10 
11 export default {
12     data(){
13         return {
14             name:''
15         }
16     },
17     methods:{
18         add(){
19             this.$emit('add',this.name) //Trigger Parent Listening Event add
20         }
21     }
22 }
Subcomponent AddStudent.vue (Diagram Subcomponent A)
 1 <template>
 2     <div>
 3         Student List:
 4         <ul>
 5             <li v-for="(item,index) in studentList" 
 6                 :key="index+item">
 7                 {{item}}
 8             </li>
 9         </ul>
10     </div>
11 
12 </template>
13 
14 <script>
15 export default {
16     props:['student-list']
17 }
18 </script>
Subcomponent StudentList.vue (Diagram Subcomponent B)

This simple example shows that you can solve the non-parent-child component value-passing problem by the above method, so the problem arises. In the actual business requirements, if there are many components that depend on one component for value-passing?There may also be cases where the nesting hierarchy of components is more complex: (Dependency A in the diagram means that Dependency A passes values, not Dependency A)

When we encounter this complex dependency, we think that they are all based on parent $emit to listen for component A, and then render through the parent's data changes, is it possible to listen for component A in component A through a vue instance, and each component that depends on A can access that vue instance, and then in eachTrigger listening events in A-dependent components:

Event listener vueObje.$emit:

Bind instance vue.prototype.vueObje on Vue prototype chain;

Trigger listening events in each A-dependent component: this.vueObje.$on('listening events', data=>{data rendering})

So the example above can be modified as follows:

Vue.prototype.bus = new Vue(); // stay main.js From the entry file vue Add on prototype chain vue Example bus

Student.vue Component Template Modification:

<template>
    <div>
        <add-student></add-student> 
        <student-list></student-list>
    </div>
</template>

The listen event triggering bus in the AddStudent.vue component:

1 methods:{
2         add(){
3             this.bus.$emit('add',this.name) //Trigger Parent Listening Event add
4         }
5     }

In the StudentList.vue component, after instance creation is complete, use the hook function create() to add a bus listening event: (Note: props need to be removed)

1 created(){
2     this.bus.$on('add',name => {
3         this.studentList.push(name);
4     })
5 }

Passing values to multiple non-parent and child components based on a single component can be accomplished using event monitoring mechanisms of vue instances, but what if the business requirement is that multiple components have permission to modify data and affect data rendering of multiple components at the same time, what about this complex data relationship?

At this time, you can use vuex to solve this relatively complex data relationship, and vuex has a better data management mode, enter the second section to analyze the application of vuex in detail:

2. vuex

Official Manual: https://vuex.vuejs.org/zh/

 

Installation: vue add vuex

After installation, a new JS file was added to the src folder: store.js

 1 import Vue from 'vue' //Introduce vue
 2 import Vuex from 'vuex' //Introduce vuex
 3 
 4 Vue.use(Vuex) //Give Way vue Use vuex
 5 
 6 export default new Vuex.Store({ //adopt vuex.Store Establish vuex Example
 7   state: {
 8 
 9   },
10   mutations: {
11 
12   },
13   actions: {
14 
15   }
16 })

Then introduce this vuex instance into the entry file:

import store from './store' //main.js Introduction vuex Example
1.state - public data pool

Add a data field to the state of the store.js and use it in the component:

1 state: {
2     //Public Data Pool
3     name:'Snowfall in Other Countries'
4   },

Use data from a common data pool in component a:

 1 <template>
 2     <div>
 3        {{storeName}}
 4     </div>
 5 </template>
 6 //js data
 7 data(){
 8         return {
 9             storeName:this.$store.state.name
10         }
11     },

Use data from a common data pool in component b:

1 <template>
2     <div>
3         {{$store.state.name}}
4     </div>
5 </template>

Then modify the data of the public data pool by clicking events in Component c:

1 <button @click="ceshi">Confirm Add</button>
2 ...
3 methods:{
4     ceshi(){
5         this.$store.state.name += 1;
6     }
7 }

You can see the correct rendering on the page before triggering the click event, but after triggering the ceshi click event, you can only see that the data in component b has updated the data in component 1. This problem is not vuex, but the logic in component A has gone wrong because the word is assigned to the dataString, storeName's stack memory is "hiking snow in another place" rather than a reference dependency of $store.state.name, so when $store.state.name is modified, it is not possible to modify the storeName of component A. This is related to calculating attributes in template syntax: https://www.cnblogs.com/ZheOneAndOnly/p/11003014.html

So, in component a, you should use computed attributes to implement data binding:

1 computed:{
2         storeName(){
3             return this.$store.state.name
4         }
5     }

There's nothing wrong with getting the data this way directly using this.$store.state.name, but if there's a lot of data to get or write?Look at the following:

 1 //store.js
 2 state: {
 3     //Public Data Pool
 4     name:'Snowfall in Other Countries',
 5     age:18
 6   }
 7 //assembly a
 8 <script>
 9 import {mapState} from 'vuex'
10 
11 xport default {
12     data(){
13         name:''
14    },
15    computed:mapState(['name','age'])
16 }

Then you can see the rendering data of {{age}} correctly on the page, but you find that {{name}} does not render correctly because the data in the data is preferred in the template syntax and conflicts with the name obtained in the computed attribute. In this case, we can only consider aliasing the data in the computed attribute.After all, modifying data names in data is unlikely in practice, but vuex also provides a way to get data from a data pool by providing key-value pairs:

1 computed:mapState({
2     storeName:state => state.name,
3     storeAge: state => state.age
4 })

Obviously, there is another problem with the above writing. Using the direct assignment of mapState to the computed attribute field computed, what if you need to use the computed attribute to define the component's own data?The mapState returns an object as a whole, but each item inside the object is a function, just like the structure of computed, so it can be expanded using the ES6 notation: (Get the last correct notation in the data pool)

1 computed:{
2     ...mapState({
3         storeName:state => state.name,
4         storeAge: state => state.age
5     }),
6     a (){
7         return 'aaaa'
8     }
9 }
2.getters - store's computed properties

If a data derivation pattern occurs in an application and multiple components depend on the same derivation pattern, we must think about computing attributes. There are also computed-like attributes getters in vuex:

 1 state: {
 2   //Public Data Pool
 3   studentList:[]
 4 },
 5 getters:{
 6   // Equivalent to computational attributes
 7   newStudent(state){
 8     return state.studentList.map((item,index) => {
 9       if(index == 0){
10         return '**' + item
11       }else if(index == 1){
12         return item + '**'
13       }else{
14         return '++' + item + '++'
15       }
16     })
17   }
18 }

Then get the derived data for this getters in the component:

1 computed:{
2     newStudent(){
3         return this.$store.getters.newStudent //Obtain store In getters Derived data from
4     }
5 }

Of course, you can also use the map method to get the object schema of the data:

1 import { mapState, mapGetters } from 'vuex'; //adopt map How to get data
2 //
3 computed:{
4     ...mapGetters(['newStudent'])
5 }

The data alias for getters does not require a state parameter to obtain it, and the data in the state may not be used, but to distinguish the data in the state from that in the getters, there is, of course, a naming conflict issue:

...mapGetters({student:'newStudent'})

Finally, when building derived data, getters can pass in a second parameter, which is their own getters, which can then reference their own data when building derived data:

1 newA(state,getters){
2     return state.studentList.map(item => {
3       return item + getters.newB
4     })
5   },
6   newB(){
7     return 'B'
8   }
3. Modify data: Mutation, Action, and strict mode

The previous state s and getters solve the problem of uniform data acquisition, but what about data writing and modification?If multiple components have the same data write and modify operations, do you want to repeat the write in each component?This is obviously not what the program was designed to do, so vuex provides Mutation and Action to solve data writing operations:

In addition, there is a string field in the store instance of vuex, which, when assigned true, means that the store is in a strict mode and cannot write and modify data externally, or an error will be reported:

 

That is to say, a reasonable way to modify data should be defined in Automation:

 1 export default new Vuex.Store({
 2   strict:true,
 3   state: {
 4     //Public Data Pool
 5     studentList:[]
 6   },
 7   getters:{},
 8   mutations: {
 9     changeStudent(state,name){
10       state.studentList.push(name);
11     }
12   },
13   actions: {}
14 })

Then call the method again in the component: [Call the method in mutations through $store.commit()]

1 <button @click="add">Confirm Add</button>
2 ...
3 methods:{
4     add(){
5         this.$store.commit('changeStudent',this.name)
6     }
7 }

It is important to note that commit only receives two parameters, the first is to call the method name in mutations, the second is to execute the method, so if there are more than one parameter, you need to use the key-value pair of the object to pass the value (the code above can be modified to):

1 //mutations
2 changeStudent(state,{name}){ //Receive using key-value pairs
3   state.studentList.push(name);
4 }
5 //Methods in components pass values in the same way as key-value pairs
6 add(){
7     this.$store.commit('changeStudent',{name:this.name})
8 }

Then a new problem arises, writing and modifying can generally meet the requirements of strict mode. If there are asynchronous instructions, using mutations to manipulate data will also result in non-strict mode programming specifications because this points globally:

1 mutations: {
2   changeStudent(state,{name}){
3     setTimeout(() => {
4       state.studentList.push(name);
5     },1000)
6   }
7 }

Such a writing can also cause the same problems as before, but it can also cause some other helpful work to be difficult to do, such as using vue.js devtools to track the execution stack accurately:

Solving asynchronous data write operations in store s is using Actiony to solve:

 1 mutations: {
 2   changeStudent(state,{name}){
 3     state.studentList.push(name);
 4   }
 5 },
 6 actions: {
 7   changeStudent(ctx,{name}){ //ctx Represents the execution context of a data operation
 8     setTimeout(() => {
 9       ctx.commit('changeStudent',{name}) //In an asynchronous program ctx.commit call mutations Data manipulation methods in
10     },1000)
11   }
12 }
13 //Called in a component method actions Asynchronous data manipulation methods in:
14 methods:{
15     add(){
16         this.$store.dispatch('changeStudent',{name:this.name}) //call actions The method in dispatch Method calls, syntax and commit Agreement
17     }
18 },

Now that I've solved some of the practical problems, do I find it necessary to write a method call in the method instances of the component to call the method in actions with this.$store.dispatch()?Is it possible to use map directly into stores just like mapState and mapGetters? This is certainly possible, and the syntax is basically the same:

1 <button @click="changeStudent">Confirm Add</button>
2 ...
3 import {mapState, mapActions} from 'vuex'
4 ...
5 methods:{
6     ...mapActions(['changeStudent']) //Aliases are also implemented using key-value pairs
7 }
3.store Modularization

Unfinished:

Keywords: PHP Vue Attribute Programming

Added by Batosi on Fri, 26 Jul 2019 23:39:36 +0300