vuex是一个vue的状态管理工具,状态就是数据
大白话:vuex是一个插件,可以帮我们管理vue通用的数据(多组件共享的数据)
1. 某个状态在很多组件都要用(个人信息)
2. 多个组件共同维护一份数据(购物车)
1. 共同维护一份数据,数据集中化管理
2. 响应式变化
3. 操作简洁(vuex提供了一些辅助函数)
基于脚手架创建项目,构建vuex多组件数据共享环境
效果:
1. 三个组件,共享一份数据
2. 任意一个组件都可以修改数据
3. 三个组件的数据是同步的
安装vuex插件,初始化一个空仓库
//1. 安装vuex
yarn add vuex@3
//2. 新建vuex模块文件
store/index.js专门存放vuex
//3. 创建仓库
import Vue from 'vue'
import Vuex from 'vuex'
// 插件安装
Vue.use(Vuex)
// 创建仓库
const store = new Vuex.Store()
// 导出给main.js使用
export default store
//4. main.js导入挂载
import Vue from 'vue'
import App from './App.vue'
import store from '@/store/index'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store
}).$mount('#app')
如何给仓库提供数据
state提供唯一的公共数据源,所有共享的数据都要统一放到Store中的state中存储。
在state对象中可以添加我们要共享的数据。
const store = new Vuex.Store({
//state状态,即数据,类似于vue组件中的data
//区别:data是组件自己的数据,state是所有组件共享的数据
state: {
count: 101
}
})
如何使用仓库的数据
获取store:
1. this.$store
2. import 导入 store
模板中:{{ $store.state.xxx }}
组件逻辑中:this.$store.state.xxx
JS模块中:store.state.xxx
辅助函数mapState,映射状态
mapState是辅助函数,帮助我们把store中的数据,自动映射到组件的计算属性中
{{ count }}
//把state中的数据,定义在组件内的计算属性中
computed: {
count () {
return this.$store.state.count
}
}
// 上面的方法简化了以前的代码,但如果每次都要去写computed的话还是很麻烦,所有有了mapState
//1. 导入mapState
import { mapState } from 'vuex'
//2. 数组方式引入state
// 注意这样得到的结果是对象
mapState(['count', 'title'])
//3. 展开运算符映射
computed: {
...mapState(['count', 'title'])
}
// 此时页面中就可以直接写
{{ title }}
{{ count }}
state数据的修改只能通过mutations
vuex同样遵循单向数据流,组件中不能直接修改仓库的数据
// 通过strict: true可以开启严格模式
const store = new Vuex.Store({
// 开启严格模式(有利于初学者,检测不规范的代码=>上线时需要移除)
strict: true,
state: {
title: '仓库大标题',
count: 101
}
})
handleAdd (n) {
// 错误代码(vue默认不会监测,监测需要成本)
// this.$store.state.count++
// console.log(this.$store.state.count)
},
如何用mutation修改数据
mutations: {
// 所有mutation函数,第一个参数,都是state
addCount (state) {
// 修改数据
state.count += 1
}
}
//在页面中就可以提交调用mutation
handleAdd () {
// 应该通过 mutation 核心概念,进行修改数据
// 需要提交调用mutation
this.$store.commit('addCount')
}
mutations传参语法 this.$store,commit( 'xxx',参数 )
// 提供mutation函数(带参数-提交载荷payload)
mutations: {
...
addCount (state, n) {
// 修改数据
state.count += n
},
changeTitle (state, newTitle) {
state.title = newTitle
}
}
// 在页面中提交调用mutation
handleAdd (n) {
// 调用带参数的mutation函数
this.$store.commit('addCount', n)
},
changeFn () {
this.$store.commit('changeTitle', '传智教育')
}
辅助函数mapMutations,映射方法
把位于mutations中的方法提取出来,映射到组件methods中
// 这里是index.js文件
mutations:{
subCount(state,n) {
state.count -= n
},
}
//这里是别的.vue组件
import { mapMutions } from 'vuex'
methods: {
...mapMutations([ 'subCount' ])
}
// 等价于
methods: {
subCount (n) {
this.$store.commit( 'subCount',n )
}
}
// 调用
handleSub(n) {
this.subCount(n)
}
actions,处理异步操作
说明:mutations必须是同步的(便于检测数据变化,记录调试),如果有异步,就得走actions
// 需求:一秒钟之后,修改state的count成666
// 这里是index.js文件中
// 1. 提供action方法
actions: {
setAsyncCount (context, num) {
// 这里是setTimeout模拟异步,以后大部分场景是发请求
//一秒后,给一个数,去修改num
setTimeout(()=>{
context.commit('changeCount',num)
},1000)
}
}
// 这里是其他的.vue文件中
//2. 页面中dispatch调用
this.$store.dispatch('setAsyncCount', 666 )
辅助函数mapActions,映射方法
把位于actions中的方法提取出来,映射到组件methods中。
// 这里是index.js文件中
actions: {
setAsyncCount (context, num) {
// 这里是setTimeout模拟异步,以后大部分场景是发请求
//一秒后,给一个数,去修改num
setTimeout(()=>{
context.commit('changeCount',num)
},1000)
}
}
// 这里是其他的.vue文件中
import { mapActions } from 'vuex'
methods: {
...mapActions(['changeCountAction'])
}
//等价于
methods: {
changeCountAction (n) {
this.$store.dispatch('changeCountAction',n )
}
}
//调用
this.changeCountAction(666)
getters,类似于计算属性
除了state之外,有时我们还需要从state中派生出一些状态,这些状态是依赖state的,此时会用到getters。
// 这里是index.js文件
//1. 定义getters
getters: {
// 注意点:
// 1. 形参第一个参数,就是state
// 2. 必须有返回值,返回值就是getters的值
filterList (state) {
return state.list.filter(item => item > 5)
}
}
//这里是别的.vue组件
//2. 访问getters
//1. 通过store访问getters
{{ $store.getters.filterList }}
辅助函数mapGetters,映射属性
//2. 通过辅助函数mapGetters映射
computed: {
...mapGetters(['filterList'])
}
{{ filterList }}
模块module,进阶语法
由于vuex使用单一状态数,应用的所有状态会集中到一个比较大的对象。
当应用变得非常复杂时,store对象就有可能变得相当臃肿。(当项目变得越来越大的时候,Vuex会变得越来越难以维护)。
每一个.js文件就是一个模块
user模块:store/modules/user.js
setting模块:store/modules/setting.js
// user模块
const state = {
userInfo: {
name: 'zs',
age: 18
},
score: 80
}
const mutations = {
setUser (state, newUserInfo) {
state.userInfo = newUserInfo
}
}
const actions = {
setUserSecond (context, newUserInfo) {
// 将异步在action中进行封装
setTimeout(() => {
// 调用mutation context上下文,默认提交的就是自己模块的action和mutation
context.commit('setUser', newUserInfo)
}, 1000)
}
}
const getters = {
// 分模块后,state指代子模块的state
UpperCaseName (state) {
return state.userInfo.name.toUpperCase()
}
}
export default {
//开启命名空间
namespaced: true,
state,
mutations,
actions,
getters
}
// setting模块
const state = {
theme: 'light', // 主题色
desc: '测试demo'
}
const mutations = {
setTheme (state, newTheme) {
state.theme = newTheme
}
}
const actions = {}
const getters = {}
export default {
//开启命名空间
namespaced: true,
state,
mutations,
actions,
getters
}
在store下面的index.js里面进行导入和挂载
// 然后在index.js文件中,导入模块
import user from './modules/user'
import setting from './modules/setting'
const store = new Vuex.Store({
modules:{
user,
setting
}
})
//在vue调试工具中看到,说明挂载成功了
Root
user
setting
如何访问子模块中的state
尽管已经分模块了,但其实子模块的状态,还是会挂到根级别的state中,属性名就是模块名。
1. 直接通过模块名访问$store.state.模块名.xxx
{{ $store.state.user.userInfo.name }}
{{ $store.state.setting.theme }}
2. 通过mapState映射
默认根级别的映射mapState(['xxx])
...mapState(['user', 'setting'])
子模块的映射mapState('模块名', ['xxx'] ) -需要开启命名空间
...mapState('user', ['userInfo']),
...mapState('setting', ['theme', 'desc']),
如何访问子模块中的getters
1. 直接通过模块名访问$store.getters['模块名/xxx']
{{ $store.getters['user/UpperCaseName'] }}
2. 通过mapGetters映射
默认根级别的映射mapGetters(['xxx'])
...mapGetters(['user']),
子模块的映射mapGetters('模块名',['xxx'])-需要开启命名空间
...mapGetters('user', ['UpperCaseName'])
如何调用子模块的mutation
1. 直接通过store调用$store.commit('模块名/xxx',额外参数)
// 2. 定义两个click事件
//3. 在methods中调用方法
updateUser () {
// $store.commit('模块名/mutation名', 额外传参)
this.$store.commit('user/setUser', {
name: 'xiaowang',
age: 25
})
},
updateTheme () {
this.$store.commit('setting/setTheme', 'pink')
},
//1. 先在user.js和setting.js的mutations中定义mutation
const mutations = {
setUser (state, newUserInfo) {
state.userInfo = newUserInfo
}
}
const mutations = {
setTheme (state, newTheme) {
state.theme = newTheme
}
}
2. 通过mapMutations映射
默认根级别的映射mapMutations(['xxx'])
子模块的映射mapMutations('模块名',['xxx'])-需要开启命名空间
...mapMutations('setting', ['setTheme']),
...mapMutations('user', ['setUser']),
// 这时候就可以直接用了
如何调用子模块的action
1. 直接通过store调用$store.dispatch('模块名/xxx',额外参数)
// 2. 定义点击事件处理函数
//3. 在methods中方法
updateUser2 () {
// 调用action dispatch
this.$store.dispatch('user/setUserSecond', {
name: 'xiaohong',
age: 28
})
},
//1. 先在子模块user.js中提供一个action
const actions = {
setUserSecond (context, newUserInfo) {
// 将异步在action中进行封装
setTimeout(() => {
// 调用mutation context上下文,默认提交的就是自己模块的action和mutation
context.commit('setUser', newUserInfo)
}, 1000)
}
}
2. 通过mapActions映射
默认根级别的映射mapActions(['xxx'])
子模块的映射mapActions('模块名',['xxx'])-需要开启命名空间
...mapActions('user', ['setUserSecond'])
// 这个时候,页面中就可以直接调用了