这是我参与8月更文挑战的第29天,活动详情查看:8月更文挑战
一,前言
上篇,介绍了 vuex 的 install 插件安装逻辑,包含以下几个点:
- 创建 vuex 插件目录;
- Vuex 插件的模块化设计;
- 实现插件安装 install 方法,store 实例混入逻辑;
- 混入效果测试;
本篇,继续介绍 Vuex 中 State 状态的实现;
二,前文回顾
当 Vue 项目执行Vue.use(Vuex)安装 Vuex 插件时:
- 会调用 Vuex 插件(一个对象)的 install 方法执行插件安装逻辑;
- 通过
Vue.mixin全局混入方式,将new Vue时,注入到根组件中的 store 容器实例,在组件创建前,通过 beforeCreate 生命周期,为所有组件混入 store 属性; - 混入原理:组件的渲染是先渲染父组件再渲染子组件,所以,根组件中注入的 store 实例,就可以被混入到 App.vue 组件上。同理,逐层地,由父组件向子组件传递 store 实例,实现所有组件共享 store 容器实例;
在视图中,直接使用 Vuex 提供的方法:
// src/App.vue
<template>
<div id="app">
商品数量: {{this.$store.state.num}} 个<br>
商品单价: 10 元<br>
订单金额: {{this.$store.getters.getPrice}} 元<br>
<button @click="$store.commit('changeNum',5)">同步更新:数量+5</button>
<button @click="$store.dispatch('changeNum',-5)">异步更新:数量-5</button>
</div>
</template>
备注:state、getters、commit、dispatch 这些方法均来自 store 实例;
三,创建 Store 类中的 State 状态对象
当 new Vuex.Store 时,传入一个配置对象:
// 实例化容器容器:Vuex.Store
const store = new Vuex.Store({state, getters, mutation, actions});
所以,在 Store 类的构造方法中,需要有一个配置对象 options 作为入参:
// src/vuex/store.js
// Vuex 的容器
export class Store {
constructor(options) { // options:{state, getters, mutation, actions}
const state = options.state; // 获取 options 选项中的 state 状态对象
}
}
四,State 状态的响应式问题
在 Vuex 中,要求 state 状态必须是响应式的,即数据变化能够触发视图更新;
但当前 Store 类中的 state 并不具备数据的响应式能力;
测试当前 state 状态对象的响应式:
<template>
<div id="app">
商品数量: {{this.$store.state.num}} 个<br>
商品单价: 10 元<br>
订单金额: {{this.$store.getters.getPrice}} 元<br>
<button @click="$store.commit('changeNum',5)">同步更新:数量+5</button>
<button @click="$store.dispatch('changeNum',-5)">异步更新:数量-5</button>
<!-- 测试 State 数据响应式 -->
<button @click="$store.state.num = 100">测试 State 数据响应式</button>
</div>
</template>
- 测试效果:点击页面按钮,直接修改 $store 实例中 State 对象的值,视图不更新;
- 原因分析:num 并不是响应式数据,除渲染时不会进行依赖收集,数据变化不会触发视图更新;
五,State 状态的响应式实现
为了实现 State 数据响应式能力,可以借助 Vue 将 State 数据放到 vue 的 data 中:
// src/vuex/store.js
export class Store {
constructor(options) {
const state = options.state;
// 响应式数据:new Vue({data})
this._vm = new Vue({
data: {
// 在 data 中,默认不会将以$开头的属性挂载到 vm 实例上
$$state: state // $$state 对象将通过 defineProperty 进行属性劫持
}
})
}
get state() { // 对外提供属性访问器:当访问state时,实际是访问 _vm._data.$$state
return this._vm._data.$$state
}
}
在 Vue 中,以 $ 开头表示 Vue 的内部属性,当做数据代理时默认不会被挂载到 vm 实例上;即不能通过vm.$$state获取,只能通过 _vm._data.$$state 进行访问,_data表示私有属性,不可被外部访问;
当外部通过 store.state 获取状态对象时,相当于拿到了 this._vm._data.$$state,get state()是 ES6 语法属性访问器,相当于 Object.defineProperty({}) 中的 getter;
// todo _data 相关知识可联动 Vue2.x 源码的《数据代理实现》
总结:
vuex 中 state 状态的响应式是借助了 Vue 来实现的;
响应式数据在页面渲染时会进行依赖收集,
当 store 中的 state 状态改变时,就会触发视图的更新操作;
六,结尾
本篇,介绍了 Vuex 中 State 状态的实现,主要涉及以下几个点:
- 创建 Store 类中的 State 状态;
- 借助 Vue 实现 State 状态的响应式;
下一篇, Vuex 中 getter 的实现;
维护日志
-
20210918:
- 优化了“前文回顾”部分;
- 添加代码高亮,修复乱码;
- 调整文章格式与部分语句描述,使表述更清晰易懂;
- 目录变更:添加“State 状态的响应式问题”部分