1. 状态管理
1.1 状态管理做的是什么事情?
-
状态即数据,对于一个原生的 Web 应用来说,某一时刻页面展示的结构和样式取决于此时的状态,状态可能会由于用户交互动作发生变化。Web 应用有很多状态。
-
局部状态:页面局部可以完成自治。比如表单的勾选按钮状态,该状态的变化不会导致页面其它部分发生变化;
-
全局状态:深化用户体验设计,上例中勾选按钮的状态会同步影响表单提交按钮是否处于可点击的状态,此时一个状态在页面两个部分都有影响,对于更复杂的 Web 应用来说,一个状态可能影响到页面数十个部分,我们就需要对状态的维护更新机制进行设计,将状态的维护从页面进行解耦,独立到全局来进行,需要一个全局中心化的“数据库”来进行管理。
-
状态管理需要提供一个类似中心化的“数据库”,同时对于状态要提供更新机制,而状态可以被多个部分依赖,状态更新的同时依赖方可以及时获取到最新状态。也就是软件架构中典型的发布/订阅模式。
-
Vue和React的通用:
(props + 事件) 实现父子间数据通信
(provide + inject)Vue实现组件间数据通信
(context + useContext)react-redux实现组件间数据通信
// Redux API
createStore(reducer, [preloadedState], [enhancer])
// Store
getState()
subscribe(listener)
dispatch(action)
- Vue 3 全局变量:
const store = ref/reactive() // reactive不用加.value
- pinia 「Vue的状态管理--解决了vue 3 全局变量store的跨请求状态污染」
// Pinia API
useMeStore() // currentUser
storeToRefs() // 解构实现响应式
useAfterMe()
const useStore = defineStore('name', { // 第一个参数为store名字
state(){ } // 第二个参数的第一项为 数据
actions:{ } // 第二个参数的第二项为 操作
}
)
- Zustand 「React轻量级状态管理工具」
// zustand
createStore()
// Store
getState()
subscribe()
setState()
不需要像redux 那样在最外层包裹一层高阶组件,直接在组件中使用selector 后就自动将组件和状态关联,修改状态后,该组件会自动更新,并且可结合useCallback 做一些memorized 的selector,做渲染性能优化。
2. 为什么要用Pinia 「主要是解决跨请求状态污染的问题」
使用简单状态管理模式下的 Reactivity APIs 是有局限性的,主要在SSR(服务器端渲染)环境下,有bug。(同样的模块实例在多个请求中被重复使用)。Pinia是Vue的存储库,允许在不同的组件/页面之间共享一个状态。
- Chrome浏览器第一次打开页面时,会初始化store,存到数据库中,然后各组件去引用store,关闭页面时store随即也消亡,store保留在数据库中;新开页面时,重新在数据库中初始化store。
- Node.js服务器端,创建全局变量时,就把store也放在全局变量中,也就是所有用户共用一个store,第一个用户打开浏览器请求user1,id=1 ,服务端返回一个存有id为1的store,若用户打开浏览器请求user1,修改id为400,此时服务端就会返回一个存有id为400的store,也就是服务端只有在发送请求时,才会返回数据。
3. 使用Pinia API
pnpm i pinia // 安装
npm config set save-prefix=’‘ // 去掉 ^ | "pinia":"^2.0.16"
// 配置app 初始化Pinia
import {createPinia} from 'pinia'
const pinia = createPinia()
const app = createApp(App);
app.use(pinia);
app.mount("#app");
在安装完配置时,遇到这种报错:[vite] error while updating dependencies 解决方案是clean up the node_modules and try again with a clean install,完美解决报错
- Store 是一个保存状态和业务逻辑的实体,它承载着全局状态。有三个概念:
state、getter和actions相当于组件中的data、computed和methods
// store 用defineStore()定义
// 返回值 useMeStore 以use开头 以store结尾
// 第一个参数'store' 是应用中的唯一 ID
// 第二个参数可接受两类值:Setup 函数或 Option 对象。
import {defineStore} from 'pinia'
export const useMeStore = defineStore("me", {
state: () => ({
me: undefined,
mePromise: undefined,
}),
actions: {
refreshMe() {
this.mePromise = http.get<Resource<User>>("/me");
},
fetchMe() {
this.refreshMe();
},
},
});
- ID
// ItemSummary.tsx
// 把所有的数据相关封装成 useItemStore
setup: (props, context) => {
const items = ref<Item[]>([]);
const hasMore = ref(false);
const page = ref(0);
参考
jirengu.com