大家好,距离上次写文章已经不知不觉过了一周了,因为忙着投简历和面试。星期一的面试也是我人生中的第一次面试,面试前颇感紧张,但是在面试的时候并没有想象中的紧张。
平均每天三场面试的生活多少会有点疲惫,看着同学以及过了字节一面,我觉得我也需要加把劲了,但是目前还是需要沉淀,今天主要跟大家分享的是vuex的一些用法(ps:面试常常被考到)
当然,手写vuex也是必不可少的。问就是被面试官问到过读过哪些vue的源码,这之前只看过hash路由的写法。
闲话不多说,开始今天的正文了
正文
随着应用程序规模的不断增大,组件间的通信和状态管理会变得越来越复杂。传统的vue组件之间的通信已经不够用了,在没有集中状态管理的情况下,组件可能需要通过多层嵌套的父组件传递 props 或者使用事件来与其他组件通信,这种方法在简单应用中可行,但在大型应用中会导致代码结构混乱,难以维护。所以vuex被设计出来了
vuex其实是一种状态管理模式,首先它使用createStore创建了一个单一状态树。
在 Vuex
中,state
、mutations
、actions
和 getters
构成了其核心组成部分,每个部分都有其特定的功能和使用场景:
-
State:
State
是Vuex
中存储应用程序状态的地方。它是一个普通的 JavaScript 对象,可以包含任意数量的属性。这些属性代表了应用程序的当前状态,例如用户登录状态、购物车中的商品列表等。State
的值是响应式的,意味着当State
发生改变时,所有依赖于这些状态的组件会自动更新。
-
Mutations:
Mutations
是唯一可以改变Vuex
State
的方式。它们本质上是一些函数,每个函数都有一个字符串类型的事件类型作为标识。Mutations
必须是同步函数,这使得状态的变更变得可预测和容易调试。通过提交Mutations
,你可以更新State
,并且这些更新会被Vuex
的响应式系统捕获,触发组件的重新渲染。
-
Actions:
Actions
类似于Mutations
,不同之处在于它们可以包含任意异步操作,如 API 调用。Actions
不直接改变State
,而是通过提交Mutations
来触发状态的变更。Actions
提供了一种解耦的方式,使得业务逻辑不会直接与状态变更绑定,同时也方便了错误处理和测试。
-
Getters:
Getters
可以理解为Vuex
中的计算属性。它们基于State
返回派生状态,可以被组件用来获取经过加工的状态信息。Getters
是响应式的,也就是说当State
发生变化时,Getters
的结果也会自动更新。
当数据的状态比较多的时候,vuex还可以支持模块化管理,每个模块都应由上面四个组成。
例如,当我需要打造一个购物车的状态数据时:
import { createStore, } from 'vuex'
// vuex pinia 复杂, 中央仓库的概念 store 单例 单一状态树
import cart from './modules/cart';
import products from './modules/products';
export default createStore({
//全局状态
state: {
count: 0
},
modules: {
cart,//购物车状态
products//商品状态
}
})
可以划分为购物车状态和商品状态,这两个状态中会有相应的状态数据和方法。
注意当我们使用actions
时,需要使用dispacth()
去触发,而在actions
中需要使用commit
提交mutations
去修改状态。这种做法使得vuex在状态管理中更加严格可控。
当我们了解完vuex的工作原理之后,就可以开始手写一个简单的vuex了。
手写vuex
首先,我准备了一个仓库store/index.js
:
import { createStore } from './gvuex.js';
const store = createStore({
// 全局状态
state() {
return {
count: 1
}
},
getters: {
double(state) {
return state.count * 2
}
},
mutations: {
add(state) {
state.count++
}
},
actions: {
asyncAdd({ commit }) {
setTimeout(() => {
commit('add')
}, 1000)
}
}
})
export default store
我们需要在gvuex.js中打造一个自己的vuex
先定义一个createStore()
去返回Store的实例对象
export const createStore = (options) => {
return new Store(options)
}
接着,打造Store类,这里我使用了es6的class语法糖,在constructor中传入创建的仓库,然后进行了初始化
把它们设为私有属性
- this.$options: 保存了传入的配置选项。
- this._state: 使用 reactive 创建了一个响应式的对象,其中包含状态数据。状态数据由 options.state() 方法返回。
- this._mutations: 保存了提交 mutations 的方法。
- this._actions: 保存了调度 actions 的方法。
vuex中的getters其实就是计算属性
class Store {
constructor(options) {
this.$options = options // 保存
// 私有属性
this._state = reactive({
data: options.state()
})
// 私有属性 commit dispatch
this._mutations = options.mutations
this._actions = options.actions
this.getters = {}
Object.keys(options.getters).forEach(name => {
const fn = options.getters[name] // 计算函数
this.getters[name] = computed(() => fn(this.state))
})
}
}
然后我使用get 关键字让store.state里的数据设为只读
get state() {
return this._state.data
}
接着,实现了commit 和dispatch
commit = (type, payload) => {
const entry = this._mutations[type]
entry && entry(this.state, payload)
}
dispatch(type, payload) {
const entry = this._actions[type]
entry && entry(this, payload)
}
根据type找到actions和mutations里的函数并调用。
最后,使用install
方法,把Store的实例对象提供给vue,并且使用provide
注入将Store实例传给子组件
install (app) {
// KEY, store对象
app.provide(STORE_KEY, this)
}
全局,让子组件可以调用store实例:
const STORE_KEY = '__store__'
export const useStore = () => {
return inject(STORE_KEY)
}
注意还需要在入口文件中加载这个插件
main.js
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import store from './store'
createApp(App)
// install 方法, vue 和其他生态的接口
.use(store)
.mount('#app')
到这里一个vuex状态管理模式就完成了。
我们可以在App.vue 中去试验一下:
<template>
<div>
{{ count }}
</div>
</template>
<script setup>
import { computed } from 'vue';
import { useStore } from './store/gvuex'
const store = useStore()
const count = computed(
() => store.state.count
)
// store.state.count++
store.dispatch('asyncAdd')
</script>
<style lang="scss" scoped>
</style>
当然可以!这里为您准备一段结语:
结语
感谢您阅读本文!通过本文的介绍,我们探讨了 Vuex 在 Vue.js 应用程序中的重要性,以及如何通过手写一个简化的 Vuex 实现来加深对其工作原理的理解。希望这些内容能够帮助您更好地掌握 Vuex 的使用,并在未来的工作中发挥重要作用。