Vite创建项目,准备vuex仓库
https://www.yuque.com/u158868/ng9lzm/gw0v9foop1we9gdm?singleDoc# 《day 05-21 Vuex》 Gitee地址:https://gitee.com/vrfe/vue-28.git
javascript
// 1. 导入Vue / Vuex
import Vue from 'vue'
import Vuex from 'vuex'
// 2. Vue.use注册vuex
Vue.use(Vuex)
// 3. 创建一个仓库store
const store = new Vuex.Store()
// 4. 导出store,挂载到vue根实例上
export default store
main.js
javascript
import Vue from 'vue'
import App from './App.vue'
// 1. 导入
import store from '@/store' // 可以省略index.js
Vue.config.productionTip = false
new Vue({
render: (h) => h(App),
store // 所有组件都能访问到store这个仓库
}).$mount('#app')
核心-state
通过$store访问
javascript
// 1. 导入Vue / Vuex
import Vue from 'vue'
import Vuex from 'vuex'
// 2. Vue.use注册vuex
Vue.use(Vuex)
// 3. 创建一个仓库store
const store = new Vuex.Store({
// state状态 => 数据, 所有组件都可以访问的公共数据
state: {
title: 'vuex so easy',
count: 100
}
})
// 4. 导出store,挂载到vue根实例上
export default store
app.vue
javascript
<template>
<div id="app">
<h1>根组件</h1>
// template中访问
<h2>state - {{ $store.state.title }}</h2>
<h2>state - {{ $store.state.count }}</h2>
<input type="text" />
<Son1></Son1>
<hr />
<Son2></Son2>
</div>
</template>
<script>
import Son1 from '@/components/Son1.vue'
import Son2 from '@/components/Son2.vue'
export default {
name: 'app',
data: function () {
return {}
},
created() {
console.log(this.$store.state.title)
},
components: {
Son1,
Son2
}
}
</script>
son1 / son2
javascript
从vuex中获取的值:<label>{{ $store.state.count }}</label>
mapState-辅助函数
javascript
<template>
<div id="app">
<h1>根组件</h1>
<h2>state - {{ title }}</h2>
<h2>state - {{ count }}</h2>
<input type="text" />
<Son1></Son1>
<hr />
<Son2></Son2>
</div>
</template>
<script>
import Son1 from '@/components/Son1.vue'
import Son2 from '@/components/Son2.vue'
// 把store中的数据,自动映射到computed中
import { mapState } from 'vuex'
export default {
name: 'app',
data: function () {
return {}
},
// computed: mapState(['count', 'title']),
computed: {
// count() {
// return this.$store.state.count
// }
// ...扩展运算符,展开对象
...mapState(['count', 'title'])
// 还可以加一些自己的计算属性
},
created() {
console.log(mapState(['count', 'title']))
// mapState(['count', 'title'])方法,返回一个对象
// console.log(this.$store.state.title)
},
components: {
Son1,
Son2
}
}
</script>
核心-mutations
- 修改store中的数据,唯一的方式就是提交mutation!!
- mutations的方法最多接收两个参数
- mutation必须是同步的,方法中不能有异步的操作(不能写定时器,不能发请求) ,保证数据的可跟踪性。
Vuex单向数据流
javascript
<template>
<div class="box">
<h2>Son1 子组件</h2>
从vuex中获取的值: <label>{{ count }}</label>
<button @click="handleAdd">值 + 1</button>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'Son1Com',
computed: {
// count() {
// return this.$store.state.count
// }
...mapState(['count'])
},
methods: {
handleAdd() {
// mapState, 这种形式修改也不行
this.count++
// 这句话,不符合单向数据流的规范
// 子组件不应该操作父组件中的数据,同理,子组件中也不应该操作store中的数据
// this.$store.state.count++
}
}
}
</script>
store/index.js
javascript
const store = new Vuex.Store({
// 严格模式,如果操作了state数据,报错Error
strict: true,
// state状态 => 数据, 所有组件都可以访问的公共数据
state: {
title: 'vuex so easy',
count: 100
}
})
mutations基本
- store/index.jx 中 定义mutation方法
javascript
const store = new Vuex.Store({
// 严格模式,如果操作了state数据,报错Error
strict: true,
//1. state状态 => 数据, 所有组件都可以访问的公共数据
state: {
title: 'vuex so easy',
count: 100
},
// 2. mutations : 存放操作数据的方法
// => 更改Vuex store状态的唯一方法是 提交mutation!!!
mutations: {
// 所有的mutation函数,第一个参数都是当前store的state状态(数据)
addCount(state) {
state.count += 1
}
}
})
- 组件中提交mutation
javascript
handleAdd() {
// 要提交一个mutation方法
this.$store.commit('addCount')
}
练习
javascript
<button @click="handleAdd">值 + 1</button>
<button @click="handleAddFive">值 + 5</button>
<button @click="changeTitle">改标题</button>
mutation-传参
javascript
handleAdd(n) {
// 要提交一个mutation方法 => 参数如果要传,这里只能写一个 => str, num, [] {}等
// this.$store.commit('addCount', n)
// 多个参数,可以封装成对象传给store的mutation
this.$store.commit('addCount', {
count: n,
msg: 'hello'
})
},
changeTitle() {
this.$store.commit('changeTitle', 'vuex-mutation')
}
store/index.js => mutations
javascript
mutations: {
// 所有的mutation函数, 只有两个参数,多了接收不了
// 1. 第一个参数都是当前store的state状态(数据)
// 2. 第二个参数,payload 载荷, payload可以是任意类型
// addCount(state, n) {
// state.count += n
// },
addCount(state, payload) {
console.log(payload)
state.count += payload.count
},
changeTitle(state, newTitle) {
state.title = newTitle
}
}
mapMutations-辅助函数
javascript
<template>
<div class="box">
<h2>Son2 子组件</h2>
从vuex中获取的值:<label>{{ $store.state.count }}</label>
<button @click="subCount(1)">值 - 1</button>
<button @click="subCount(5)">值 - 5</button>
<button @click="subCount(10)">值 - 10</button>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
name: 'Son2Com',
created() {
// {subCount: ƒ, changeTitle: ƒ} => 调用mapMutations,返回一个对象
console.log(mapMutations(['subCount', 'changeTitle']))
},
methods: {
...mapMutations(['subCount', 'changeTitle']),
// subCount(n){
// this.$store.commit('subCount', n)
// },
// changeTitle(title){
// this.$store.commit('changeTitle', title)
// },
// 可以直接在模板上调用方法
handleSub(n) {
// this.$store.commit('subCount', n)
this.subCount(n)
}
}
}
</script>
核心-actions
基本使用dispatch
mutations必须是同步的,便于检测数据变化,保证数据的可追踪性,方便vue-devtool记录调试。 异步操作,放到actions中处理!actions处理异步逻辑,处理完了,提交mutation,mutation修改数据。
javascript
// 3. actions 如果是异步操作,放到actions中处理
// 1. action可以包含任意异步操作
// 2. action中不能直接修改state数据,还是得提交mutation commit
actions: {
// 1. context 上下文对象,这里指的是store, 一般可以简写 ctx
// 2. payload 传来的参数
awaitToAdd(ctx, payload) {
setTimeout(() => {
ctx.commit('changeCount', payload)
}, 1000)
}
}
son1.vue
vue
<template>
<div class="box">
...
<!-- 异步操作 -->
<button @click="handleWait">一秒后修改为666</button>
<!-- end -->
<button @click="changeTitle">改标题</button>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
export default {
name: 'Son1Com',
computed: {
...mapState(['count'])
},
methods: {
handleWait() {
// action中的方法,需要用dispatch调用
this.$store.dispatch('awaitToAdd', 666)
},
}
}
</script>
mapActions
vue
<template>
<div class="box">
<h2>Son2 子组件</h2>
从vuex中获取的值:<label>{{ $store.state.count }}</label>
<button @click="subCount(1)">值 - 1</button>
<button @click="subCount(5)">值 - 5</button>
<button @click="subCount(10)">值 - 10</button>
<button @click="awaitToAdd(888)">一秒后修改为888</button>
<button @click="changeTitle('前端666')">改标题</button>
</div>
</template>
<script>
import { mapMutations, mapActions } from 'vuex'
export default {
name: 'Son2Com',
created() {
console.log(mapMutations(['subCount', 'changeTitle']))
},
methods: {
...mapMutations(['subCount', 'changeTitle']),
...mapActions(['awaitToAdd']),
// subCount(n){
// this.$store.commit('subCount', n)
// },
// changeTitle(title){
// this.$store.commit('changeTitle', title)
// },
// 可以直接在模板上调用方法
handleSub(n) {
// this.$store.commit('subCount', n)
this.subCount(n)
},
// 也可以直接在模板上使用
handleWait() {
this.awaitToAdd(888)
}
}
}
</script>
核心-getters
$store+mapGetters
相当于是store的computed
javascript
<template>
<div class="box">
<h2>Son2 子组件</h2>
从vuex中获取的值:<label>{{ $store.state.count }}</label>
<button @click="subCount(1)">值 - 1</button>
<button @click="subCount(5)">值 - 5</button>
<button @click="subCount(10)">值 - 10</button>
<button @click="awaitToAdd(888)">一秒后修改为888</button>
<button @click="changeTitle('前端666')">改标题</button>
<hr />
<div>{{ $store.getters.filterList }}</div>
<div>{{ filterList }}</div>
</div>
</template>
<script>
import { mapMutations, mapActions, mapGetters } from 'vuex'
export default {
name: 'Son2Com',
created() {
console.log(mapMutations(['subCount', 'changeTitle']))
},
computed: {
...mapGetters(['filterList'])
},
}
</script>
javascript
// getters类似于计算属性,
// 1. getters函数第一个参数state
// 2. 必须要有返回值,return
getters: {
filterList(state) {
return state.list.filter((el) => el > 5)
},
// 箭头函数的形式
filterList: state => state.list.filter((el) => el > 5)
},
回顾
mutation同步操作
为什么mutation是同步的?Vuex官网上有一个解释,大概是说保证数据的可跟踪性,可以vue-devtool监听变化。
actions异步操作
module模块
需要新建modules文件夹,然后导入到index.js中
javascript
export default {
// user用户模块
state: () => ({
username: '练练',
age: 18,
hobby: '老二次元了'
}),
getters: {},
mutations: {},
actions: {}
}
javascript
export default {
// settings用户模块
state: () => ({
theme: 'black',
desc: '练练的blog'
}),
getters: {},
mutations: {},
actions: {}
}
store/index.js
javascript
import user from './modules/user'
import settings from './modules/settings'
const store = new Vuex.Store({
// xxx
// 5. modules 模块
modules: {
user,
settings
}
})
// 导入模块之后,查看vue-devtool Vuex 中 state是否有模块中的数据
module-state
javascript
export default {
// user用户模块
namespaced: true, // 需要开启命名空间
state: () => ({
username: '练练',
age: 18,
hobby: '老二次元了'
}),
getters: {},
mutations: {},
actions: {}
}
son1.vue $store.state.模块名.xxx
html
<!-- 我们让Son1 通过store方式访问state -->
<hr />
<h3>通过store方式 访问模块中的state</h3>
<div>{{ $store.state.user.username }}</div>
<div>{{ $store.state.user.age }}</div>
<div>{{ $store.state.user.hobby }}</div>
Son2.vue
- ...mapState(['xxx'])
- ...mapState('模块名', ['username', 'age', 'hobby'])
vue
<hr />
<h3>通过mapState的方式 访问模块中的state</h3>
<div>{{ user.username }}</div>
<div>{{ user.age }}</div>
<div>{{ user.hobby }}</div>
<hr />
<div>{{ username }}</div>
<div>{{ hobby }}</div>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
computed: {
// 1. 默认根级别的映射 ...mapState(['xxx'])
...mapState(['user']),
// 2. 直接映射到子模块 这种形式 需要开启命名空间 namespaced:true , 建议开启!
...mapState('user', ['username', 'age', 'hobby'])
},
module-getters
user.js
javascript
export default {
// user用户模块
namespaced: true, // 需要开启命名空间
state: () => ({
username: 'lianlian',
age: 18,
hobby: '老二次元了'
}),
getters: {
upperCaseName(state) {
// state指的是当前模块中的state
return state.username.toUpperCase()
}
},
mutations: {},
actions: {}
}
Son1.vue
html
<hr />
<h3>通过store方式 访问模块中的getters</h3>
<!-- Error 不能这么访问 -->
<!-- <div>{{ $store.getters.user.upperCaseName }}</div> -->
<div>{{ $store.getters['user/upperCaseName'] }}</div>
javascript
import { mapGetters } from 'vuex'
computed: {
// 1. 默认根级别的映射 ...mapState(['xxx'])
...mapState(['user']),
// 2. 子模块的映射 ...mapState('模块名', ['xxx'])
...mapState('user', ['username', 'age', 'hobby']),
// getters
// 1. 默认根级别的getters,找的是index.js中getters
...mapGetters(['filterList']),
// 2. 子模块的映射,mapGetters('模块名', ['xxx'])
...mapGetters('user', ['upperCaseName'])
},
module-mutations
javascript
export default {
// user用户模块
namespaced: true, // 需要开启命名空间
state: () => ({
username: 'lianlian',
age: 18,
hobby: '老二次元了'
}),
getters: {
upperCaseName(state) {
// state指的是当前模块中的state
return state.username.toUpperCase()
}
},
mutations: {
// updateXXX()
updateUser(state, payload) {
state.username = payload
}
},
actions: {}
}
Son1.vue
javascript
<!-- mutation 修改用户名 -->
<button @click="$store.commit('user/updateUser', '果果')">
修改用户名
</button>
Son2.vue
javascript
<!-- mutation 修改用户名 -->
<button @click="updateUser('周杰伦')">修改用户名</button>
import { mapMutations } from 'vuex'
methods: {
...mapMutations('user', ['updateUser']),
}
module-actions
javascript
export default {
// settings用户模块
namespaced: true, // 需要开启命名空间
state: () => ({
theme: 'black',
desc: '练练的blog'
}),
getters: {},
mutations: {
updateTheme(state, payload) {
state.theme = payload
}
},
actions: {
// ctx ==> 模块中,代表的是当前模块的store
updateTheme(ctx, payload) {
console.log(ctx)
setTimeout(() => {
// 要提交mutation
ctx.commit('updateTheme', payload)
}, 1000)
}
// 可以做解构
// updateTheme({commit}, payload) {
// setTimeout(() => {
// // 要提交mutation
// commit('updateTheme', payload)
// }, 1000)
// }
}
}
Son1.vue
vue
<!-- actions 一秒之后,修改settings,theme主题 -->
<div>{{ theme }}</div>
<button @click="$store.dispatch('settings/updateTheme', 'skyblue')">
一秒后修改Theme
</button>
Son2.vue
vue
<!-- actions 一秒之后,修改settings,theme主题 -->
<button @click="updateTheme('orange')">一秒后修改Theme</button>
import { mapActions } from 'vuex'
methods: {
...mapActions('settings', ['updateTheme']),
}