直接使用全局对象的问题
- 全局对象里面的数据是普通的JavaScript数据类型,它们不是响应式的,也就说第一次读取渲染有可能没有问题,但是当数据修改过以后,界面没法作出对应的更新,这是一个很大的问题
- 全局对象的修改无法追踪,也就是说在应用中的任何一处代码,都可以有机会拿到全局数据,并作出对应的修改,但是我们根本没有办法搞清楚是哪行代码,哪个文件修改了全局数据,这样就非常容易产生bug,而且难以追踪,这就很危险了。
- vue是组件化的世界,就像我们的程序,大家也可以发现,组件的构成就像一棵树一样,全局数据一般是从父组件一层层的传递给子组件的。直接从一个组件获取数据被视为一种反模式,这样很容易造成数据的混乱
状态管理三杰
设计理念
- 一个类似object的全局数据结构 - 称之为store
- 只能调用特定的方法完成数据的修改
Vuex简介和安装
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。 Vuex 和单纯的全局对象有以下两点不同:
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
新版 Vuex 安装
npm install vuex@next --save // 保证安装完毕的版本是 4.0.0 以上的 复制代码
测试 Vuex store
import { createStore } from 'vuex' // 从 vuex 导入 createStore 这个函数,我们发现 vue3 以后,这些第三方的官方库,名字出奇的相似,vue-router 也是以create 开头的,看起来非常的清楚。 const store = createStore({ state: { count: 0 }, }) // createStore 接受一个对象作为参数,这些对象中包含了 vuex 的核型概念,第一个概念称之为 state,这里面包含的是我们想放入的在全局共享的数据,这里我们放入一个简单的 count。 // 现在我们已经可以直接访问这个值了,我们可以直接使用 store.state.count 来访问它。 console.log('store', store.state.count) // 接下来我们来更改状态,更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数: mutations: { add (state) { state.count++ } } // 有了 mutations 以后,让我们来触发它,要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法: store.commit('add') console.log('count', store.state.count) 复制代码
Vuex 整合当前应用
定义 store 文件
import { createStore } from 'vuex' import { testData, testPosts, ColumnProps, PostProps } from './testData' interface UserProps { isLogin: boolean; name?: string; id?: number; } export interface GlobalDataProps { columns: ColumnProps[]; posts: PostProps[]; user: UserProps; } const store = createStore<GlobalDataProps>({ state: { columns: testData, posts: testPosts, user: { isLogin: false } }, mutations: { login(state) { state.user = { ...state.user, isLogin: true, name: 'viking' } } } }) export default store 复制代码
使用
import { useStore } from 'vuex' import { GlobalDataProps } from '../store' ... const store = useStore<GlobalDataProps>() const list = computed(() => store.state.columns) 复制代码
Vuex getters
vuex getters 文档 :vuex.vuejs.org/zh/guide/ge…
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
getters: { biggerColumnsLen(state) { return state.columns.filter(c => c.id > 2).length } } // 定义完毕,就可以在应用中使用这个 getter 了 // Getter 会暴露为 store.getters 对象,你可以以属性的形式访问这些值: const biggerColumnsLen =computed(()=>store.getters.biggerColumnsLen) 复制代码
getColumnById: (state) => (id: number) => { return state.columns.find(c => c.id === id) }, getPostsByCid: (state) => (id: number) => { return state.posts.filter(post => post.columnId === id) } // 定义完毕以后就可以在应用中使用 getter 快速的拿到这两个值了 const column = computed(() => store.getters.getColumnById(currentId)) const list = computed(() => store.getters.getPostsByCid(currentId))