目录
- 1,Vuex
- 2,provide & inject
- 2,global state
- 4,Pinia
- 5,对比
1,Vuex
vue2 的官方状态管理器,vue3 也是可以用的,需要使用 4.x 版本。
相对于 vuex3.x,有两个重要变动:
- 去掉构造函数
Vuex
,而使用createStore()
创建仓库 - 为了配合 compositionAPI,新增
useStore()
获取仓库对象
先看一个使用 vuex 的例子:实现登录、刷新页面恢复登录、退出登录的状态管理。
// store/index.js
import loginUser from "./loginUser";
import { createStore, createLogger } from "vuex";
export default createStore({modules: {loginUser,},plugins: [createLogger()], // 用于调试,会在控制台打印日志。
});
createLogger 官网参考
// store/loginUser.js
export default {namespaced: true,state: {user: null,loading: false,},mutations: {setUser(state, payload) {state.user = payload;},setLoading(state, payload) {state.loading = payload;},},actions: {async login({ commit }, { loginId, loginPwd }) {commit("setLoading", true);// 登录接口const user = await _faker.login(loginId, loginPwd);commit("setUser", user);commit("setLoading", false);return user;},async loginOut({ commit }) {commit("setLoading", true);// 退出登录接口await _faker.loginOut();commit("setUser", null);commit("setLoading", false);},async whoAmI({ commit }) {commit("setLoading", true);// 恢复登录接口const user = await _faker.whoAmI();commit("setUser", user);commit("setLoading", false);},},
};
组件中使用 store
<script setup>
import { computed, ref } from "vue";
import { useStore } from "vuex";const store = useStore();const loginId = ref("");
const loginPwd = ref("");
const loading = computed(() => store.state.loginUser.loading),const handleSubmit = async () => {const user = await store.dispatch("loginUser/login", {loginId: loginId.value,loginPwd: loginPwd.value,});if (user) {// 登录成功,跳转首页。} else {alert("账号/密码错误");}
};
</script>
注册
// main.js
import { createApp } from "vue";
import App from "./App.vue";
import store from "./store";
createApp(App).use(store).mount("#app");// 恢复登录,其实就是把存在本地的用户信息,再次放到 store 中。
store.dispatch("loginUser/whoAmI");
2,provide & inject
vue2 中就有这2个配置,可以在祖先组件中注入数据,然后在后代组件中使用。
vue3 的 optionAPI 做了兼容的同时,compositionAPI 也提供了 provide()
和 inject()
-官网-依赖注入。
另外,考虑到部分数据会在整个 vue 应用中使用,所以 vue3 在应用实例中也添加了 provide()
, 用于提供整个应用的共享数据。
import { createApp } from "vue";
import App from "./App.vue";
creaetApp(App).provide("foo", ref(1)).provide("bar", ref(2)).mount("#app");
来模仿 vuex 的使用方式来实现上面的例子。
// store/index.js
import { provideStore as provideLoginUserStore } from "./useLoginUser";
// 继续导入其他共享数据模块...
// import { provideStore as provideNewsStore } from "./useNews"// 提供统一的数据注入接口
export default function provideStore(app) {provideLoginUserStore(app);// 继续注入其他共享数据// provideNewsStore(app);
}
// store/userLoginUser.js
import { readonly, reactive, inject } from "vue";
const key = Symbol(); // Provide的key// 在传入的vue应用实例中提供数据
export function provideStore(app) {// 创建默认的响应式数据const state = reactive({ user: null, loading: false });// 登录async function login(loginId, loginPwd) {state.loading = true;const user = await _faker.login(loginId, loginPwd);state.user = user;state.loading = false;}// 退出async function loginOut() {state.loading = true;await _faker.loginOut();state.loading = false;state.user = null;}// 恢复登录状态async function whoAmI() {state.loading = true;const user = await _faker.whoAmI();state.loading = false;state.user = user;}// 提供全局数据app.provide(key, {state: readonly(state), // 对外只读login,loginOut,whoAmI,});
}export function useStore(defaultValue = null) {return inject(key, defaultValue);
}
组件中使用 store
<script setup>
import { computed, ref } from "vue";
import { useStore } from "../store/useLoginUser";const store = useStore();const loginId = ref("");
const loginPwd = ref("");
const loading = computed(() => store.state.loading),const handleSubmit = async () => {const user = await store.login(loginId.value, loginPwd.value);if (store.state.user) {// 登录成功,跳转首页。} else {alert("账号/密码错误");}
};
</script>
全局注册
// main.js
import { createApp } from "vue";
import App from "./App.vue";
import provideStore from "./store";
const app = createApp(App);
provideStore(app); // 上面的封装形式,即便项目中存在多个应用实例,也可以应对。
app.mount("#app");// 恢复登录,要放到 App.vue 中执行了。
// whoAmI();
2,global state
得益于 vue3 的响应式系统是可以脱离组件而存在,所以可轻松创建多个全局响应式数据。
// store/useLoginUser.js
import { reactive, readonly } from "vue";// 创建默认的全局单例响应式数据,仅供该模块内部使用
const state = reactive({ user: null, loading: false });// 对外暴露的数据是只读的,不能直接修改
export const loginUserStore = readonly(state);// 登录
export async function login(loginId, loginPwd) {state.loading = true;const user = await _faker.login(loginId, loginPwd);state.user = user;state.loading = false;
}
// 退出
export async function loginOut() {state.loading = true;await _faker.loginOut();state.loading = false;state.user = null;
}
// 恢复登录状态
export async function whoAmI() {state.loading = true;const user = await _faker.whoAmI();state.loading = false;state.user = user;
}
组件中使用 store
<script setup>
import { computed, ref } from "vue";
import { loginUserStore, login } from "../store/useLoginUser";const loginId = ref("");
const loginPwd = ref("");
// 模版也可以直接使用 loginUserStore.loading
const loading = computed(() => loginUserStore.loading),const handleSubmit = async () => {const user = await login(loginId.value, loginPwd.value);if (user) {// 登录成功,跳转首页。} else {alert("账号/密码错误");}
};
</script>
全局注册
// main.js
import { createApp } from "vue";
import App from "./App.vue";
import { whoAmI } from "./store/useLoginUser";
createApp(App).mount("#app");// 恢复登录
whoAmI();
4,Pinia
官网参考
5,对比
vuex | global state | Provide&Inject | |
---|---|---|---|
组件数据共享 | ✅ | ✅ | ✅ |
可否脱离组件 | ✅ | ✅ | ❌ |
量级 | 重 | 轻 | 轻 |
以上。