第六部分 状态管理-Vuex

目录

1. 状态管理

1.1 vue组件共享数据

1.2 vuex状态管理

1.3 安装和使用vuex

1.3.1 安装

1.3.2使用

2. state状态管理

2.1 this.$store.state.xxx形式获取状态数据

2.2 mapState辅助函数

3. getters计算属性

3.1 $store.getters.xxx形式获取计算属性

3.2 mapGetters 辅助函数

4. mutations更新数据

4.1 $store.commit( ) 提交mutation

4.2 mapMutations 辅助函数

4.3 提交mutation时附加参数

5. actions异步更新数据

5.1 $store.dispatch( ) 分发action

5.2 mapActions 辅助函

6. vuex多模块机制和插件

6.1 vuex的模块化

6.2 vuex插件

7. 组织vuex程序的目录结构


1. 状态管理

 

1.1 vue组件共享数据

 

在单页面应用程序中,路由和组件关联,采用单文件组件组织 vue程序。

➢优点:

✓提高模块的独立性,便于代码重用和维护。

➢缺点:

✓组件与组件(即不同路由页面)之间必然需要进行数据通信,vue中实现组件间共 享数据比较繁琐。

 

组件间共享数据方式

父子组件间:

父组件向子组件传递props数据,子组件向父组件$emit自定义事件。

➢缺点

✓只能对于父子组件有效

✓状态数据更新位置不统一,不利于代码维护

 

非父子组件间:

使用全局事件总线(全局的vue实例)

➢缺点

✓操作繁琐(需要在每个组件的生命周期回调方 法中监听事件)

✓状态数据更新零散,不利于代码维护

 

借助本地存储(如localStorage或sessionStorage)实现数据共享

➢缺点

✓操作繁琐(需要在每个组件的生命周期 回调方法中获取数据)

✓不能实现数据响应式更新

✓状态数据管理零散,不利于代码维护

 

1.2 vuex状态管理

 

vue官方推荐使用vuex进行状态管理,实现组件间数据共享

➢vuex把组件间的所有状态数据集中到一处进行统一管理,并提供统一的 操作接口进行状态更新。

✓使用 state 存储状态数据

✓使用 mutations 更新数据

 

vue官方推荐使用vuex进行状态管理,实现组件间数据共享。

➢vuex也集成到 Vue 的官方调试工具 vue-devtools 中,提供了诸如零配 置的 time-travel 调试、状态快照导入导出等高级调试功能

1.3 安装和使用vuex

1.3.1 安装

vuex安装和使用

➢安装 vuex : vue add vuex

✓自动在 vue-cli 程序中创建 store.js 文件

✓Vuex.Store对象:状态数据管理对象

➢Store对象默认包含三部分内容

✓state:状态数据

✓mutations:更新状态数据的操作方法

✓actions:异步操作方法

 

安装示例:

vue add vuex 

 

自动生成如下代码:

1.3.2使用

➢使用

✓Store对象定义状态数据:在 state 部分定义状态数据

✓组件中使用状态数据:使用 this.$store 获取状态数据管理对象

• 每个组件都会获取到状态数据

• this.$store.state 获取状态数据信息

 

 

使用示例:

在store的state中定义数据

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    message: 'this is state message.'
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

在页面组件App.vue中使用数据

<template>
  <div id="app">
   <p>
     message = {{$store.state.message}}
   </p>
  </div>
</template>

运行后,使用devtoos查看状态数据:

 

2. state状态管理

2.1 this.$store.state.xxx形式获取状态数据

在Vuex.Store对象中,使用 state属性存储状态数据

➢在组件中获取state信息时,可以使用 this.$store.state.*** 形式获取状态数据

➢state数据建议在组件的 computed 属性中使用,以实现响应式更新

示例:

定义状态数据

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    message: 'this is state message.',
    user: {
      name: 'test',
      age: 18
    },
    bookList: [
      {bookName: 'java讲义', price: 108.5},
      {bookName: '安卓讲义', price: 88.5},
      {bookName: 'IOS讲义', price: 77.9}
    ]
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

 

App.vue组件和HelloWorld.vue组件的计算属性中使用状态数据

<template>
  <div id="app">
   <HelloWorld/>
   <p>
     message = {{$store.state.message}}
   </p>
  </div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld'

export default {
  name: 'App',
  components: {HelloWorld},
  computed: {
    message(){
      return  this.$store.state.message
    }
  }
}
</script>

 

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>

  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  computed: {
    msg(){
       return  this.$store.state.message
    }
  }
}
</script>

使用devtoos查看状态数据:

2.2 mapState辅助函数

可以使用 mapState 辅助函数,以简写形式在组件内获取state数据

✓使用之前,要首先引入 mapState 辅助函数

 

✓在计算属性中处理状态数据

✓若直接使用状态数据原始名称,可以直接使用数组形式

 

示例:

<template>
  <div id="app">

    <p>
     message = {{message}}
    </p>
    <p>
     user = {{user}}
   </p>
   <p>
     bookList = {{bookList}}
   </p>
  </div>
</template>
<script>

import {mapState} from 'vuex'

export default {
  name: 'App',

  computed: mapState({
     message: function(state){
       return state.message
     },
     user: state => state.user,
     bookList: 'bookList'
  })
}
</script>

 

 

 

 

若直接使用状态数据原始名称,可以直接使用数组形式:

<template>
  <div id="app">
    <p>
     message = {{message}}
    </p>
    <p>
     user = {{user}}
   </p>
   <p>
     bookList = {{bookList}}
   </p>
  </div>
</template>
<script>

import {mapState} from 'vuex'

export default {
  name: 'App',
  computed: mapState(['message', 'user', 'bookList'])
}
</script>

 

computed中既有状态数据又可以有自已的计算数据:

<template>
  <div id="app">
    <p>
     message = {{message}}
    </p>
    <p>
     user = {{user}}
   </p>
   <p>
     bookList = {{bookList}}
   </p>
   <p>
     localComputed = {{localComputed}}
   </p>
  </div>
</template>
<script>

import {mapState} from 'vuex'

export default {
  name: 'App',
  data() {
    return {info: ' this is App'}
  },
  computed: {
    // 当前组件自身计算属性
    localComputed(){
      return 'hello' + this.info
    },
    // 使用对象展开运算符引入状态数据
    ...mapState(['message', 'user', 'bookList'])
  }
}
</script>

3. getters计算属性

 

问题:如果在HelloWorld.vue组件中也需要以上功能,怎么办?是复制代码?

解决办法:

使用getters计算属性

3.1 $store.getters.xxx形式获取计算属性

 

在vuex中使用 getters 定义状态数据的计算属性,方便为多个组 件提供统一的过虑数据

➢定义getters

➢ 在组件中使用$store.getters.xxx形式获取gettes数据

示例:

import Vue from 'vue'
import Vuex from 'vuex'


Vue.use(Vuex)


export default new Vuex.Store({
  state: {
    bookList: [
      {bookName: 'java讲义', price: 108.5},
      {bookName: '安卓讲义', price: 88.5},
      {bookName: 'IOS讲义', price: 77.9}
    ]
  },
  getters: {
    bookNameList: state => {
      return state.bookList.filter(book => {
        return book.price > 80
      })
    }
  },
  mutations: {

  },
  actions: {

  }
})
<template>
  <div id="app">
   <p>
      bookNameList = {{$store.getters.bookNameList}}
   </p>
  
  </div>
</template>
<script>


export default {
  name: 'App',
  
  data() {
    return {info: ' this is App'}
  },
  
}
</script>

3.2 mapGetters 辅助函数

组件中使用getters

➢使用 mapGetters 引入 getters 数据

✓首先引入 mapGetters 辅助函数

✓在组件的计算属性中,引入getters数据

 

 

在vuex中使用 getters 定义状态数据的计算属性示例:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    message: 'this is state message.',
    user: {
      name: 'test',
      age: 18
    },
    bookList: [
      {bookName: 'java讲义', price: 108.5},
      {bookName: '安卓讲义', price: 88.5},
      {bookName: 'IOS讲义', price: 77.9}
    ]
  },
  getters: {
    bookNameList: state => {
      return state.bookList.filter(book => {
        return book.price > 80
      })
    }
  }
})

 

组件中使用getters示例:

<template>
  <div id="app">
   <p>
     bookNameList = {{bookNameList}}
   </p>
   <HelloWorld></HelloWorld>
  </div>
</template>
<script>

import HelloWorld from '@/components/HelloWorld'
import {mapGetters} from 'vuex'
export default {
  name: 'App',
  components: {HelloWorld},
  data() {
    return {info: ' this is App'}
  },
  computed: {
    // 使用对象展开运算将getter混入computed对象中
    ...mapGetters([
        'bookNameList'
      ])
  }
}
</script>

使用devtools查看组件的计算属性:

使用devtools的状态面板可以看到状态数据中state和getters数据:

同样在HelloWorld.vue视图组件中也可以使用getters计算数据:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>bookNameList= {{bookNameList}}</p>
  </div>
</template>

<script>
import {mapGetters} from 'vuex'

export default {
  name: 'HelloWorld',
  computed: {
    msg(){
       return  this.$store.state.message
    },
        // 使用对象展开运算将getter混入computed对象中
    ...mapGetters([
        'bookNameList'
      ])
  }
}
</script>

参考文档

https://vuex.vuejs.org/zh/guide/state.html

https://vuex.vuejs.org/zh/guide/getters.html

4. mutations更新数据

• 严格模式下,vuex不允许在组件 内部直接修改 $store.state 中的 数据,也不允许直接修改 computed 数据

• strict模式下更改 Vuex.Store 中 的状态的唯一方法是提交 mutation

开启严格模式:

更改 Vuex.Store 中的状态的唯一方法是提交 mutation

➢在 Store 对象中定义 mutations

✓每一个mutation 是函数形式,表示状态数据对外提供的操作接口

✓函数接收回调参数 state ,表示要处理的状态数据集合

示例:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  // 开启严格模式
  strict: true,
  state: {
    message: 'this is state message.',
    user: {
      name: 'test',
      age: 18
    },
    bookList: [
      {bookName: 'java讲义', price: 108.5},
      {bookName: '安卓讲义', price: 88.5},
      {bookName: 'IOS讲义', price: 77.9}
    ]
  },
  getters: {
    bookNameList: state => {
      return state.bookList.filter(book => {
        return book.price > 80
      })
    }
  },
  // 严格模式下,修改状态数据
  mutations: {
    // 修改状态数据
    mutationMessage(state){
      // state表示当前的状态数据
      state.message = 'mutation updated messge.'
    }
  }
})

4.1 $store.commit( ) 提交mutation

更改 Vuex.Store 中的状态的唯一方法是提交 mutation

➢在组件中使用 $store.commit( ) 提交mutation更新

直接更新state状态数据则报错:

<template>
  <div id="app">
   <p>
     bookNameList = {{bookNameList}}
   </p>
   <HelloWorld></HelloWorld>
   <button @click="updateMsg">更新</button>
  </div>
</template>
<script>

import HelloWorld from '@/components/HelloWorld'
import {mapGetters,} from 'vuex'
export default {
  name: 'App',
  components: {HelloWorld},
  data() {
    return {info: ' this is App'}
  },
  methods: {
      updateMsg(){
        // 不允许直接更新状态数据
        this.$store.state.message = 'hello world'
      }
  },
  computed: {
    // 使用对象展开运算将getter混入computed对象中
    ...mapGetters([
        'bookNameList'
      ])
  }
}
</script>

运行后点击更新发现出错:

示例:

使用提交mutations的方法更新state数据

<template>
  <div id="app">
   <p>
     bookNameList = {{bookNameList}}
   </p>
   <HelloWorld></HelloWorld>
   <button @click="updateMsg">更新</button>
  </div>
</template>
<script>

import HelloWorld from '@/components/HelloWorld'
import {mapGetters,} from 'vuex'
export default {
  name: 'App',
  components: {HelloWorld},
  data() {
    return {info: ' this is App'}
  },
  methods: {
      updateMsg(){
        // 提交mutation更新状态数据
        this.$store.commit('mutationMessage')
      }
  },
  computed: {
    ...mapGetters([
        'bookNameList'
      ])
  }
}
</script>

在devtools中查看state数据的改变:

4.2 mapMutations 辅助函数

➢在组件中使用 mapMutations 辅助函数快速引入 mutation操作到当前 组件的方法中

✓组件中直接调用方法即可

示例:在组件中使用 mapMutations 辅助函数快速引入 mutation操作到当前 组件的方法中

<template>
  <div id="app">
   <p>
     bookNameList = {{bookNameList}}
   </p>
   <HelloWorld></HelloWorld>
   <button @click="updateMsg">更新</button>
  </div>
</template>
<script>

import HelloWorld from '@/components/HelloWorld'
import {mapGetters, mapMutations} from 'vuex'
export default {
  name: 'App',
  components: {HelloWorld},
  data() {
    return {info: ' this is App'}
  },
  methods: {
    // 快速引入mutation方法  
    ...mapMutations(['mutationMessage']),
      updateMsg(){
        // 直接调用mapMutations引入的方法
        this.mutationMessage()
      }
  },
  computed: {
    ...mapGetters([
        'bookNameList'
      ])
  }
}
</script>

4.3 提交mutation时附加参数

更改 Vuex.Store 中的状态的唯一方法是提交 mutation

➢ 定义mutation时接收附加参数

➢在组件中传递参数

示例:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  // 开启严格模式
  strict: true,
  state: {
    message: 'this is state message.',
    user: {
      name: 'test',
      age: 18
    },
    bookList: [
      {bookName: 'java讲义', price: 108.5},
      {bookName: '安卓讲义', price: 88.5},
      {bookName: 'IOS讲义', price: 77.9}
    ]
  },
  getters: {
    bookNameList: state => {
      return state.bookList.filter(book => {
        return book.price > 80
      })
    }
  },
  // 严格模式下,修改状态数据
  mutations: {
    // 修改状态数据
    mutationMessage(state, payload){
      // state表示当前的状态数据
      // payload表示传入的附加参数,建议使用对象形式
      state.message = ('mutation updated messge.' + payload.message)
    }
  }
})
<template>
  <div id="app">
   <p>
     bookNameList = {{bookNameList}}
   </p>
   <HelloWorld></HelloWorld>
   <button @click="updateMsg">更新</button>
  </div>
</template>
<script>

import HelloWorld from '@/components/HelloWorld'
import {mapGetters, mapMutations} from 'vuex'
export default {
  name: 'App',
  components: {HelloWorld},
  data() {
    return {info: ' this is App'}
  },
  methods: {
    ...mapMutations(['mutationMessage']),
      updateMsg(){
        // 直接调用mapMutations引入的方法,并传入附加参数
        this.mutationMessage({message: 'this is message mutated from App component'})
      }
  },
  computed: {
    ...mapGetters([
        'bookNameList'
      ])
  }
}
</script>

 

5. actions异步更新数据

mutations只允许实现同步操作,异步操作方法(如ajax获取数 据)应该交给 actions 处理

使用actions异步更新

➢Store中定义actions

✓每一个 action 是函数形式,表示异步更新数据操作接口

✓函数接收回调参数 context ,表示要状态数据管理对象 Vuex.Store

✓action中不允许直接修改 state中的数据,而要使用 mutation 更新数据

示例:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  strict: true,
  state: {
    message: 'this is state message.',
    user: {
      name: 'test',
      age: 18
    },
    bookList: [
      {bookName: 'java讲义', price: 108.5},
      {bookName: '安卓讲义', price: 88.5},
      {bookName: 'IOS讲义', price: 77.9}
    ]
  },
  getters: {
    bookNameList: state => {
      return state.bookList.filter(book => {
        return book.price > 80
      })
    }
  },
 
  mutations: {
    mutationMessage(state, payload){
      state.message = ('mutation updated messge.' + payload.message)
    }
  },
  // 更新更新数据(主要使用ajax加载数据)
  actions: {
    actionMessage(context){
      window.setTimeout(function(){
        // 更新状态数据
        context.commit('mutationMessage', {message: 'this message is from action'})
      }, 1000)
    }
  }
})

5.1 $store.dispatch( ) 分发action

使用actions异步更新

➢组件methods中使用actions

✓直接通过 $store.dispatch( ) 方法使用

示例:在method中分发action:

<template>
  <div id="app">
   <p>
     bookNameList = {{bookNameList}}
   </p>
   <HelloWorld></HelloWorld>
   <button @click="updateMsg">异步更新</button>
  </div>
</template>
<script>

import HelloWorld from '@/components/HelloWorld'
import {mapGetters} from 'vuex'
export default {
  name: 'App',
  components: {HelloWorld},
  data() {
    return {info: ' this is App'}
  },
  methods: {
    updateMsg(){
      // 分发一个action异步更新数据
      this.$store.dispatch('actionMessage')
    }
  },
  computed: {
    ...mapGetters([
        'bookNameList'
      ])
  }
}
</script>

在devtools中查看state改变:

 

5.2 mapActions 辅助函

✓使用 mapActions 辅助函数引入到当前组件的methods中

示例:

<template>
  <div id="app">
   <p>
     bookNameList = {{bookNameList}}
   </p>
   <HelloWorld></HelloWorld>
   <button @click="updateMsg">异步更新</button>
  </div>
</template>
<script>

import HelloWorld from '@/components/HelloWorld'
import {mapGetters, mapActions} from 'vuex'
export default {
  name: 'App',
  components: {HelloWorld},
  data() {
    return {info: ' this is App'}
  },
  methods: {
    ...mapActions(['actionMessage']),
    updateMsg(){
      // 直接调用mapActions引入的方法
      this.actionMessage();
    }
  },
  computed: {
    ...mapGetters([
        'bookNameList'
      ])
  }
}
</script>

参考文档

https://vuex.vuejs.org/zh/guide/mutations.html

https://vuex.vuejs.org/zh/guide/actions.html

6. vuex多模块机制和插件

6.1 vuex的模块化

在大型vuex应用程序中,可以使用 modules 定义多模块的状态数据

➢定义模块对象:本质就是 Vuex.Store 对象,包含自己的 state、mutations、 actions 等

 

➢在Vuex.Store中注册多模块

✓可以为模块修改模块名称

 

➢在组件中使用多模块

✓使用模块状态数据(state):使用 $store.state.模块名.*** 形式

• 注意:不能直接使用 mapState( ) 辅助函数获取模块内的状态数据

示例:

<template>
  <div id="app">
   <p>
      moduleA name = {{$store.state.ma.name}}
   </p>
   <p>
     moduleB name = {{$store.state.moduleB.name}}
     </p>
   <HelloWorld/>
  </div>
</template>
<script>

import HelloWorld from '@/components/HelloWorld'
export default {
  name: 'App',
  components: {HelloWorld},
  data() {
    return {info: ' this is App'}
  },
  methods: {

  },
  computed: {
 
  }
}
</script>

在devtools中查看state数据:

 

 

或:

<template>
  <div id="app">
   <p>
      moduleA name = {{ma.name}}
   </p>
   <p>
     moduleB name = {{moduleB.name}}
     </p>
   <HelloWorld/>
  </div>
</template>
<script>

import HelloWorld from '@/components/HelloWorld'
import { mapState } from 'vuex'
export default {
  name: 'App',
  components: {HelloWorld},
  data() {
    return {info: ' this is App'}
  },
  methods: {

  },
  computed: {
    ...mapState({
      // 模块的状态数据
      ma: state=>state.ma,
      moduleB: state=>state.moduleB,
      // 全局状态数据
      message: state=>state.message
    })
  }
}
</script>

✓使用getters、mutations和actions:直接和全局模块使用方法相同

• 可以使用 mapGetters、mapMutations和mapActions 辅助函数,获取模块内的指定 类型操作方法

示例:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

// 定义子模块
const moduleA  = {
  state: {
    name: 'moduleA-name'
  },
  mutations: {
    mutationModuleAName(state){
      state.name = 'update A'
    }
  }
}
const moduleB  = {
  state: {
    name: 'moduleB-name'
  },
  mutations: {
    mutationModuleBName(state){
      state.name = 'update B'
    }
  }
}
export default new Vuex.Store({
  strict: true,
  // 注册模块
  modules: {
    ma:moduleA,
    moduleB
  },
  state: {
    message: 'this is state message.',
  },
  getters: {
   
  },

  mutations: {
    mutationMessage(state, payload){
      state.message = payload.message
    }
  },
  actions: {
  
  }
})
<template>
  <div id="app">
   <p>
      moduleA name = {{$store.state.ma.name}}
   </p>
   <p>
     moduleB name = {{$store.state.moduleB.name}}
     </p>

     <button @click="updateMsg">更新</button>
   <HelloWorld/>
  </div>
</template>
<script>

import HelloWorld from '@/components/HelloWorld'
import { mapState, mapMutations } from 'vuex'
export default {
  name: 'App',
  components: {HelloWorld},
  data() {
    return {info: ' this is App'}
  },
  methods: {
    ...mapMutations(['mutationMessage']),
    updateMsg(){
        // 直接调用mapMutations引入的方法
        this.mutationMessage({message: 'this is messsage from App component '})
        // 更新模块A的name值
        this.$store.commit('mutationModuleAName', {name: 'this is module A name from App componet'})
    }
  },
  computed: {
    ...mapState({
      ma: state=>state.ma,
      moduleB: state=>state.moduleB,
      message: state=>state.message
    })
  }
}
</script>

6.2 vuex插件

vuex中允许定义和使用插件,增强vuex的功能

➢vuex状态数据一般要和本地存储结合起来使用

✓vuex状态数据只对当前SPA程序有效,当手动刷新页面时,状态数据会丢失

✓本地存储不能响应式修改状态数据

当页面刷新时state数据又恢复原来了的数据了:

解决办法 :使用vuex插件实现 vuex状态数据 和 本地存储的结合使用

➢安装方法:使用 npm 或 yarn 安装 vuex-persistedstate

➢使用方法

✓引入 vuex-persistedstate ✓在 Store 对象中 使用 plugins

参考文档:

https://vuex.vuejs.org/zh/guide/modules.html 

https://vuex.vuejs.org/zh/guide/plugins.html 

https://www.npmjs.com/package/vuex-persistedstate

7. 组织vuex程序的目录结构

在大型项目中store非常庞大,这时需要对他进行模块化组织

1. 创建一个mutations.js ,放全局的mutation

2. 创建一个actions.js放全局的action

3. 在index.js中导入mutation.js和action.js

4. 创建module子目录,把不同模块的store放到单独的js中:

在module中创建一个index.js

5. 在store的index.js中,创建Vuex.Store时引入模块的index.js

建议只对action与mutation进入模块分离,state与getters不做分离

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值