vuex
1. 是什么
vuex是一个vue的状态管理工具,状态就是数据
简单来说,vuex是一个插件,可以帮我们管理vue通用的数据(多组件共享数据)
2. 场景:
- 某个状态在很多个组件来使用(个人信息)
- 多个组件共同维护一份数据(购物车)
优势
- 共同维护一份数据,数据集中化管理
- 响应式变化
- 操作简洁(vuex提供了一些辅助函数)
- 任意一个组件都可以修改数据
- 三个组件的数据是同步的
vuex的核心概念:....
构建vuex多组件数据共享环境
- 创建项目
vue create vuex-demo
- 选自定义
- 选css、linter、babel
- vue2
- less
- standard
- lint on save
- In dedicated config files
- n
创建一个空仓库
目标:安装vuex插件,初始化一个空仓库
- yarn add vuex@3,装包
- 在src文件夹中新建store仓库文件夹,再在里面新建index.js文件
- 插件安装
Vue.use(Vuex)
- 创建空仓库
const store = new Vuex.Store()
- 导出给main.js使用
export default store
- 在main.js中导入挂载
import store from './store'
//再把store丢到实例中
new Vue({
store,
render: h => h(App)
}).$mount('#app')
仓库构建完之后,所有组件都可以去访问它
访问路由是
this.$router,所以访问仓库就是this.$store
1.🤯state状态(只能获取,使用,不能修改)
如何给仓库提供数据,并使用
- 提供数据
const store = new Vuex.Store({
state: {
title: '仓库大标题',
count: 100,
list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
})
State提供唯一的公共数据源,所有共享的数据都要统一 放到Store仓库中 的 State中 存储,在state对象中可以添加 我们要共享的数据
state状态,即数据,类似于vue组件中的data
与data区别
- data是组件自己的数据
- state是所有组件共享的数据
- 使用数据
- 通过store直接访问(也就是打点的方式)
- 通过辅助函数(更简化)
1. 访问使用里面数据要按层次顺序去打点
//获取store
1.store.$store
2.import导入store
//模板中
{{$store.state.xxx}}
//组件逻辑中
this.$store.state.xxx
//js模块中
store.state.xxx
2. 通过辅助函数mapState去简化
- 原本思路是:把这个state里的这个你需要的数据封装在计算属性之中,然后就可以在界面中直接
{{属性}}调用,无需打点 - 但是如果每一个属性都这样封装,反而不简便了,所以我们引入辅助函数来
- 比如
mapState,它可以帮助我们把store中的数据 自动 映射到组件的计算属性之中
2.🤯mutations(修改)
明确vuex同样遵循单项数据流,组件中不能直接修改仓库的数据
但是如果你比如写了
this.$store.state.count++,这样的错误代码(修改数据),系统是不会给你提示的。但是你如果希望系统提醒你,你就开启 严格模式
- 即把这个放在仓库里:
strict: true
state数据的修改只能通过mutation
- 在store仓库中定义mutations对象,对象中存放修改state的方法
//提供
const store = new Vuex.Store({
mutations: {
// 所有mutation函数,第一个参数,都是 state
//————————————也可以传参
// 注意点:mutation参数有且只能有一个
//如果需要多个参数,包装成一个对象
addCount (state, obj) {
console.log(obj)
// 修改数据
state.count += obj.count
},
changeTitle (state, newTitle) {
state.title = newTitle
}
}
})
//调用
//在之后调用的时候就是:用commit调用
methods: {
handleAdd () {
this.$store.commit('addCount')
}
}
TIP:通过e形参,然后e.target.value可以拿到输入框的值,+e.target.value可以转数字
辅助函数mapMutations
mapMutations和mapState很像,它是把位于mutations中的方法提取了出来,映射到组件methods中
流程图:
3.🤯actions(处理异步操作,不能修改)
需求1:一秒钟之后,修改state的count成666
说明:mutations必须是同步的(便于监测数据变化,记录调试)
- 在store仓库中提供action方法
- 页面中dispatch调用(在绑定的事件的methods里写)
- 注意:不能直接操作 state,操作 state,还是需要 commit mutation
- context上下文,相当于store
mutations: {
changeCount (state, newCount) {
state.count = newCount
}
}
actions: {
// context 上下文 (此处未分模块,可以当成store仓库)
// 有store就说明有commit
// context.commit('mutation名字', 额外参数)
changeCountAction (context, num) {
// 这里是setTimeout模拟异步,以后大部分场景是发请求
setTimeout(() => {
context.commit('changeCount', num)
}, 1000)
}
},
methods:{
handleChange () {
// 调用action
// this.$store.dispatch('action名字', 额外参数)
this.$store.dispatch('changeCountAction', 666)
}
}
辅助函数mapActions
mapActions是把位于actions中的方法提取了出来,映射到组件methods中
流程图:
4. 🤯getters(类似于计算属性,但是不能修改)
说明:除了state之外,有时我们还需要从state中派生处一些状态,这些状态是依赖state的,此时会用到getters
需求1:state中定义了list为一个1-10 的数组,现在在组件中需要显示所有大于5的数据
- 在store仓库中定义getters
getters: {
// 注意点:
// 1. 形参第一个参数,就是state
// 2. 必须有返回值return,返回值就是getters的值
filterList (state) {
return state.list.filter(item => item > 5)
}
},
- 访问getters
- 通过store访问getters(打点)
{{$store.getters.filterList}}
- 通过辅助函数mapGetters映射(在computed中)
computed: {
...mapFetters(['filterList'])
},
{{filterList}}
上面几种辅助函数分别映射的是什么
mapState 和 mapGetters 都是映射属性mapMutations 和 mapActions 都是映射方法
computed: {
// mapState 和 mapGetters 都是映射属性
...mapState(['count', 'user', 'setting']),
...mapGetters(['filterList']),
},
methods: {
// mapMutations 和 mapActions 都是映射方法
...mapMutations(['subCount', 'changeTitle']),
...mapActions(['changeCountAction'])
}
5.🤯模块model(进阶语法)
由于vuex使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相当臃肿。(当项目越来越大时,vuex会变得越来越难以维护)
创建子模块
- 模块拆分(在modules里再写其他js,每个js都是一个模块)
- 格式
const state = {}
const mutations = {}
const actions = {}
const getters = {}
export default {
state,
mutations,
actions,
getters
}
如何访问子模块中的state
问题:尽管已经分模块了,但其实子模块的状态,还是会挂到根级别的state中,属性名就是模块名
就像这样:
使用模块中的数据
- 直接通过模块名访问
$store.state.模块名.xxx - 通过mapState映射
- 默认根级别的映射:
mapState(['xxx']) - 子模块的映射:
mapState('模块名',['xxx']),需要开启命名空间namespaced: true,也就是给每个模块加上了自己的名字
- 默认根级别的映射:
export default {
namespaced: true,
state,
mutations,
actions,
getters
}
//默认根级别映射的访问
<div>{{ user.userInfo}}</div>
//子模块的映射的访问
<div>{{userInfo}}</div>
computed: {
// 默认根级别的映射
...mapState(['count', 'user', 'setting']),
//子模块的映射
...mapState('user', ['userInfo']),
},
如何访问使用子模块中的getters
- 直接通过模块名访问
$store.getters['模块名/xxx'] - 通过mapGetters映射
- 默认根级别的映射:
mapGetters(['xxx']) - 子模块的映射:
mapGetters('模块名',['xxx']),需要开启命名空间
- 默认根级别的映射:
如何访问使用子模块中的mutations
注意:默认模块中的mutation和actions会被挂载到全局,需要开启命名空间,才会挂载到子模块
调用子模块中的mutations:
- 直接通过store调用
$store.commit('模块名/xxx',额外参数) - 通过mapMutations映射
- 默认根级别的映射:
mapMutations(['xxx']) - 子模块的映射:
mapMutations('模块名',['xxx']),需要开启命名空间
- 默认根级别的映射:
如何访问使用子模块中的actions
注意:默认模块中的mutation和actions会被挂载到全局,需要开启命名空间,才会挂载到子模块
- 直接通过store调用
$store.dispatch('模块名/xxx',额外参数) - 通过mapActions映射
- 默认根级别的映射:
mapActions(['xxx']) - 子模块的映射:
mapActions('模块名',['xxx']),需要开启命名空间
- 默认根级别的映射: