老项目还在用 vuex,来复习一波~
管理不断变化的state本身是非常困难的
将组件的内部状态抽离出来,以一个全局单例的方式来管理
Vuex 背后的基本思想,它借鉴了 Flux、Redux、Elm(纯函数语言,redux有借鉴它的思想)
使用
vue2:
模板中使用:
- 数据不长:{{ $store.state.counter }}
- 数据很长,可使用 computed:
computed: {
storeCounter() {
return this.$store.state.counter;
},
}使用: {{ storeCounter}}
vue3:
- 修改 state: commit 提交:store.commit(“changeCounter”)
- 获取数据:const { counter } = toRefs(store.state)
npm i vuex
store
=> index.js
import { createStore } from "vuex";
// options:传入对象
const store = createStore({
// 返回一个对象
state: () => ({
counter: 111110,
}),
mutations: {
changeCounter(state) {
state.counter++
}
}
});
export default store
使用
createApp(App).use(store).mount("#app")
,通过插件安装template
中访问 counter: app:{{ $store.state.counter }}
option API
中的computed
中使用export default {
computed: {
storeCounter() {
return this.$store.state.counter;
},
},
};
在setup
中使用
修改 state
mutations: {
// 默认传入 state
changeCounter(state) {
state.counter++
}
}
<script setup>
import { toRefs } from "vue";
import { useStore } from "vuex";
const store = useStore();
// setup直接赋值
const setupCounter=store.state.counter
console.log(store.state.counter);
// 直接解构
// const { counter } = store.state
// toRefs
const { counter } = toRefs(store.state)
const changeCounter = () => {
// 提交事件:store 中 mutation 中对应的名称
store.commit("changeCounter");
// store.state.counter++
};
</script>
<template>
<div class="home">
<h2>home:{{ $store.state.counter }}</h2>
<button @click="changeCounter"> +1</button>
</div>
</template>
<script setup>
// 通过 hook 的方式拿到我们的 store
import { useStore } from 'vuex'
const store = useStore()
const changeCounter = () => {
// 提交事件
store.commit("changeCounter")
// store.state.counter++
}
</script>
<style lang="less" scoped>
</style>
vue2:
computed: { // 数组 ...mapState(['name','level','avatarURL']), // 返回函数 // 可以改名称 ...mapState({ sName:state=>state.name, sLevel:state=>state.level, sAvatarURL:state=>state.avatarURL }) }
vue3:
const store = useStore() const {name:sname,level:slevel} = toRefs(store.state)
computed
中import { createStore } from "vuex";
const store = createStore({
state: () => ({
counter: 111110,
name: "lisa",
level: 99,
avatarURL: "hhhh",
}),
mutations: {
changeCounter(state) {
state.counter++;
},
},
});
export default store;
使用:import { mapState } from 'vuex'
映射方式
适用于 vue2
<template>
<div class="app">
<!-- 1. 在模板中直接使用多个状态 -->
<h2>name:{{ $store.state.name }}</h2>
<h2>name:{{ $store.state.level }}</h2>
<h2>name:{{ $store.state.avatarURL }}</h2>
<!-- 2. 计算属性 (映射状态)-->
<h1>name:{{ name }}</h1>
<h1>name:{{ level }}</h1>
<h1>name:{{ avatarURL }}</h1>
<!-- 3. 计算属性:对象语法 -->
<h3>name:{{ sName }}</h3>
<h3>name:{{ sLevel }}</h3>
<h3>name:{{ sAvatarURL }}</h3>
</div>
</template>
<script >
import { computed } from 'vue';
import { mapState } from 'vuex'
export default{
computed: {
fullname() {
return "xxx"
},
// 数组
...mapState(['name','level','avatarURL']), // 返回函数
// 可以改名称
...mapState({
sName:state=>state.name,
sLevel:state=>state.level,
sAvatarURL:state=>state.avatarURL
})
}
}
</script>
适用于 vue3:
在 setup 语法中返回的是函数,可以computed一起使用需要结合bind使用
拿到函数
绑定 this
<script setup>
import { computed} from 'vue'
import { useStore, mapState } from 'vuex'
const { name, level, avatarURL } = mapState(['name', 'level', 'avatarURL'])
const store = useStore()
const cName = computed(name.bind({$store:store}))
const clevel = computed(level.bind({$store:store}))
const cavatarURL = computed(avatarURL.bind({$store:store}))
</script>
import { computed } from 'vue'
import { useStore ,mapState} from 'vuex'
export default function useState(mapper) {
const store = useStore()
const stateFnsObj = mapState(mapper)
const newStates = {}
Object.keys(stateFnsObj).forEach(key=>{
newStates[key] = computed(stateFnsObj[key].bind({$store:store}))
})
return newStates
}
import useState from "../hooks/useState";
const {name,level} = useState(["name", "level", "avatarURL"]);
const store = useStore()
const {name:sname,level:slevel} = toRefs(store.state)
import { createStore } from "vuex";
const store = createStore({
state: () => ({
counter: 111110,
users: [
{ id: "111", name: "lisa", age: 33 },
{ id: "131", name: "shawll", age: 33 },
{ id: "141", name: "hsih", age: 43 },
{ id: "151", name: "fef", age: 53 },
{ id: "161", name: "ffb", age: 53 },
],
}),
mutations: {
changeCounter(state) {
state.counter++;
},
},
getters: {
// 1. 基本使用
doubleCounter(state) {
return state.counter * 2;
},
totalAge(state) {
return state.users.reduce((preValue, item) => {
return item.age + preValue;
}, 0);
},
// 2. 在 getter 属性中使用另外一个getter
message(state, getters) {
// getter中使用其他的getters
return `name为${state.name} totalAge为${getters.totalAge}`;
},
// 3. getters 可以返回一个函数,调用这个函数可以传入参数
getUserById(state) {
return function (id) {
const friend = state.users.find(item => item.id == id)
return friend
}
}
},
});
export default store;
使用
template
中使用<template>
<div>
<h1>{{$store.getters.doubleCounter}}</h1>
<h1>totalAge:{{ $store.getters.totalAge }}</h1>
<h1>message:{{ $store.getters.message }}</h1>
// 返回一个函数:调用这个函数
<h1>user:{{ $store.getters.getUserById(111) }}</h1>
</div>
</template>
<script>
import { mapGetters, useStore } from "vuex";
export default {
computed: {
...mapGetters(["doubleCounter", "totalAge", "message"]),
...mapGetters(["getUserById"]),
},
};
</script>
<script setup>
import { computed, toRefs } from "vue";
import { mapGetters } from "vuex";
import { useStore } from "vuex";
const store = useStore();
// 1. 使用 mapGetters
const { message: messageFn } = mapGetters(["message"]);
// const message = computed(messageFn.bind({ $store: store }))
// 2. 直接解构,并且包裹成ref
// const { message } = toRefs(store.getters);
// 3. 针对某一个getters属性使用computed
const message = computed(() => store.getters.message);
</script>
<template>
<div class="home">
<h2>name:{{ $store.state.name }}</h2>
<button @click="changeName('小明')">changeName</button>
</div>
</template>
<script>
// export default {
// methods: {
// changeName() {
// this.$store.commit("changeName");
// },
// },
// };
</script>
<script setup>
import { useStore } from "vuex";
const store = useStore();
const changeName = (name) => {
// 第二个参数可以是对象{{name:"yyyy",count:33}}
store.commit("changeName",name);
};
</script>
<style lang="less" scoped></style>
mutations: {
changeCounter(state) {
state.counter++;
},
changeName(state,payload) {
state.name = payload
}
},
mutation
类型:mutation_types.js
:export const CHANGE_INFO = "changeInfo"
import { CHANGE_INFO } from "./mutation_types";
CHANGE_INFO]
<template>
<div>
<button @click = "changeName('jujuih')">
修改名字
</button>
</div>
</template>
<script>
import { mapMutations } from "vuex";
import { CHANGE_INFO } from "@/store/mutation_types";
export default {
computed: {},
methods: {
btnClick() {
console.log("btnClick");
},
// 直接映射过去了,不需要commit
...mapMutations(["changeName", "incrementLevel", CHANGE_INFO]),
},
};
</script>
<script setup>
// 返回了一个个函数
const mutations = mapMutations(["changeName", CHANGE_INFO]);
const newMutations = {};
Object.keys(mutations).forEach((key) => {
newMutations[key] = mutations[key].bind({ $store: store });
});
const { changeName, changeInfo } = newMutations;
</script>
目的就是为了完成异步操作
所有的修改都必须提交 mutation,包括 actions
Action类似于mutation,不同在于
有一个非常重要的参数 context
<template>
<div class="home">
<h2>home:{{ $store.state.counter }}</h2>
<button @click="actionBtnClick">发起action</button>
</div>
</template>
<script>
export default {
methods: {
actionBtnClick() {
this.$store.dispatch("incrementAction");
},
},
};
</script>
<script setup>
import { useStore } from "vuex";
const store = useStore();
store.dispatch("incrementAction");
</script>
<style lang="less" scoped></style>
actions: {
incrementAction(context,payload) {
console.log(context.commit);//用于提交mutation
console.log(context.getters);//getters
console.log(context.state);//state
context.commit("increment",payload)
}
}
mutations: {
increment(state) {
state.counter++;
}
},
<template>
<div class="home">
<h2>home:{{ $store.state.counter }}</h2>
<button @click="incrementAction">发起action</button>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
methods: {
// // incrementAction() {
// // this.$store.dispatch("incrementAction");
// // },
...mapActions(["incrementAction"]),
},
};
</script>
<script setup>
import { useStore, mapActions } from "vuex";
const store = useStore();
const { incrementAction } = mapActions(["incrementAction"]);
// store.dispatch("incrementAction");
</script>
数据管理方式一
数据管理方式二
async fetchHomeMulticatedAction(context) {
// 1. 返回promise,给promise设置then
// fetch("jjjj").then((res) => {
// res.json().then((data) => {
// console.log(data);
// });
// });
// 2. promise 的链式调用
// fetch("jjjj")
// .then((res) => {
// return res.json();
// })
// .then((data) => {
// console.log(data);
// });
// 3. await/async
const res = await fetch("http://");
const data = await res.json()
console.log(data);
// 修改state的数据
context.commit("changeBanner",data.data.banner.list)
},
<script setup>
import { useStore } from "vuex";
// 告诉 vuex 发送网络请求
const store = useStore();
store.dispatch("fetchHomeMulticatedAction");
</script>
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象,当应用变得非常复杂时,store 对象就有可能变得相当臃肿
为了解决以上问题,Vuex 可以将 store 分割成模块(module)
每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块
创建模块 modules/home.js ,在模板中使用需要加上模块名字
模块内的 getter 里面的函数默认会合并到总的 store 里面
store中创建 modules ==》counter.js
const counter = {
state: () => ({
count:99
}),
mutations: {
increment(state) {
state.counter++
}
},
getters: {
doubleCount(state, getters, rootState) {
return state.count + rootState.counter
}
},
actions: {
incrementCountAction(context) {
context.commit("increment")
}
}
}
modules: {
home: homeModule,
counter:counterModule
}
<template>
<div class="home">
<h2>home</h2>
<!-- 使用state时,是需要state.moduleName.xxx -->
<h2>counter模块的counter:{{ $store.state.counter.count }}</h2>
<!-- 使用getters时,是直接getters.xxx -->
<h2>counter模块的doubleCounter:{{ $store.getters.doubleCount }}</h2>
<button @click="incrementCountAction">counter模块+1</button>
</div>
</template>
<script setup>
import { useStore } from "vuex";
// 派发事件时候,也是不需要跟模块的
// 提交mutation时,也是不需要跟模块的,直接提交即可
const store = useStore();
function incrementCountAction() {
store.dispatch("incrementCountAction");
}
</script>
<style lang="less" scoped></style>
{{$store.getters["counter/doubleCount"]}}
store.dispatch("counter/incrementAciton")