vuex刷新页面数据丢失怎么解决_你是否真正了解Vuex

8ec8ef43e84681c08a2a2b65621d0781.png

很久不写文章了,断了有一年多。这期间加入了新的团队做leader,认识新的朋友,迎接新的挑战。本应该坐下来整理自己的所见与所得,但又担心是否会起到作用,是否大家会认同。就像以前有一个朋友问我写这么多入门的文章有用吗?还有一些文章好像在自说自话。

其实还真是这样,反观自己的文章,其实大部分是自己写给自己的。越长大越孤单,很多想法,很多疑问,很多心里话其实是通过文章间接的与自己沟通。换个角度审视自己的行为与思想。

我也尽可能的想通过自己的文章传达一些技术之外的东西,要明白 “授之以鱼不如授之以渔”。除了技术技巧之外,有什么东西可以受用一生,可以帮助你更快掌握知识点,更快实现目标?我有很多要整理的内容,不放在这里细说,后面我会整理一下自己关于项目管理,团队管理,前端工作流,前端组件化,前端模板化,前端直播的一些成果,在这里和大家分享。

好了,回归正题,今天我们聊一聊 Vuex。看看大家是否真正完全掌握了 Vuex。过去一段时间,我发现大部分同学对技术的理解没有到达一定深度。只是停留在简单的知识点上,并没有深入的去进行思考。这就导致很多知识点其实并没有完全掌握。随便找一个方向进行深入发问,就会触到你的知识盲区。今天我们就对 Vuex 的各个点进行发问,以一个初学者的角度来重新认识下 Vuex

下面的内容中我会用各种问题来进行串联,请大家遇到我的提问时能先简单做下思考。有了结果后再看我的答案。我说的是我的理解,不一定就是对的,希望你也有自己的看法和理解,我的答案只是你心中的答案一个对照。

什么是 Vuex?

在官网的介绍中这样写到:

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

我们去了解一个技术方案时,第一步应该想清楚 “这个方案解决什么样的问题?”

Q: 那么第一个问题来了, Vuex 是为了解决什么问题呢?是什么样的问题让我们必须要用 Vuex 解决呢?

A: 我们说 Vue.js 是单向数据流模型。在单页面应用中,数据由父组件向子组件进行传递,子组件通过触发父组件事件的方式可以进行数据回传。想象这样一个场景,如果页面有复杂的组件嵌套,并且嵌套层次可能很深。这种情况下我们的数据需要通过 prop 一级一级向下传递,叶子组件的事件需要一级一级向上触发。这样的方式显然是不好的,我们希望父组件能够更方便的与各个子孙组件进行通信。再想象一个场景,单页面多路由的情况下,A页面需要传递数据给B页面,我们如何做路由间的数据传递呢?按照以往的解决方案,一般会选择 storage,cookie或者url携带参数来解决。这种方案也存在数据安全,数据传递大小限制等问题。以上的问题有没有一个更优雅的解决方案呢?

Q: 如果让你来解决以上的问题,不使用现有框架,有什么好的办法吗?

A: 如果我来实现,我会在window下设置一个变量,值为一个空对象。可以将所有需要传递的变量数据存储在这个对象中。通过 Object.defineProperty 对所有属性添加 set 监听,每当属性值发生变化时 通过 window.dispatchEvent 触发一个全局事件。该事件注册到所有组件中,每当属性变化时通知到各个页面与组件,再由各个组件进行取出单独处理。

我们再看下Vuex是不是这样做的,Vuex的核心结构包括:State,Getter,Mutation, Action, Module。我们把 State 看作是那个存储全部共享数据的对象。当你需要跨路由,跨组件进行数据共享时,把需要共享的数据保存到 State 中即可。Vuex 框架会帮你进行同步,只要有修改就会同步到各个组件中。并及时更新页面数据。

数据设置好之后,我们就可以在各个组件中使用 this.$store.state 来获取 state 对象并使用数据。当你需要修改数据时,你需要触发一个 mutation 来进行数据修改,官网的示例我们看下:

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler), 在上面的例子中 type 为 increment当我们要修改 state 中的 count 属性时,需要这么做:

store.commit('increment')

通过触发 mutation 下的 increment 事件来修改 对此,官网也有一句解释:

“更改 Vuex 的 store 中的状态的唯一方法是提交 mutation”

Q: 第三个问题来了,为什么不能直接通过 this.$store.state.count = 3 来进行赋值?为什么不能直接对 state 下的数据进行赋值?为什么一定要使用 mutation?

A: 最初提出这个问题时,我听到两个回答是:

“mutation 保证数据可溯源”

“mutation更明确地追踪到状态的变化”

在官网我找到这么一段话:

”再次强调,我们通过提交 mutation 的方式,而非直接改变 store.state.count,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。“

按照文档内容,上面的两个回答是没问题的。我们可以引申另一个问题:

Q: 数据为什么要溯源?为什么要记录每次状态改变?为什么要保存状态快照?

A: 首先,通过 this.$store.state.count 进行赋值是没有任何问题的,在非严格模式下这么写不会报错,数据修改可以被监听到。为什么非要使用 mutation 使数据可溯源呢?试想:

当我在A页面使用 this.$store.state.count = 10;

然后在B页面再次修改 this.$store.state.count = 3;

你是否可以观察到这个修改顺序?你不能,你只能看到修改的结果是 count 值为3,并不知道过程中是谁修改了他。为了在调试工具中观察到数据变化过程,我们统一使用 mutation 来修改数据,会有一个log系统记录下你的每一次修改,如图:

79e9b617e9cd804650f1be7b83fab7e7.png

理解了为什么必须使用 mutation 更新数据后,我们再来看一看 mutation 还有什么问题。在官网对 mutation 的介绍中有这么一句话:

”一条重要的原则就是要记住 mutation 必须是同步函数。“

Q: 为什么 mutation 必须是同步函数?我在接口请求的异步回调中执行 store.commit('increment') 可以吗?

A: 首先,官网的 mutation 必须是同步函数,是要求 mutation 下的每个事件的回调函数必须是同步代码。对 commit 触发事件的时机并没有要求,也就是你可以在任意异步方法中执行 store.commit() 来修改数据。为什么 mutation 的事件回调必须是同步代码呢?其实还是为了保证溯源的准确性,当遇到下面情况时:

store.commit('A');
store.commit('B');
store.commit('B');

我们希望观察数据变化时,log中能体现正确的执行顺序:A -> B -> C。当这其中任意一个方法是异步处理时,就有可能出现偏差,数据失真,加大了调试的复杂度,其结果可能是:A -> C -> B / C -> A -> B。所以这就是为什么 mutation 的事件回调必须是同步代码。

除了 mutation 外还有一个修改 state 的办法,就是通过 Action。Action 类似于 mutation,不同在于:

1. Action 提交的是 mutation,而不是直接变更状态。

2. Action 可以包含任意异步操作。

也就是说 Action 是 mutation 的包装,但是 Action 中可以执行异步代码。代码示例:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment ({ commit }) {
  setTimeout(() => {
    commit('increment')
  }, 1000)
}
  }
})

Q: 就因为异步执行代码就发明 actions 吗?actions 和 mutations 为何一个可以异步一个不可以呢?actions 如何保证溯源的准确性呢?

A: 还是那句话,了解一个方案时,要先看这个方案解决什么问题。actions 解决什么问题呢?试想,一个单页面应用下,在各个页面都要获取一下最新的活动数据,然后在接口请求成功后通过 store.commit('updateDialogData') 来更新全局数据。这样做是不是很傻,我们不能在一个公共的地方写这个数据请求吗?

mutations 下是不可以的。所以我们提供一个 actions。这样你就可以把之前需要写到各个组件,各个页面的异步代码放到一起统一管理了。通过 this.$store.dispatch('xxx') 来触发事件。这样的情况下如何保证正确溯源呢?以往我们是通过 mutations 下的 type 来记录历史的。此处同样可以根据 mutations 记录真实的数据变化。但他不是原始的操作顺序,原始的操作顺序需要按照 actions 的触发顺序记录。

与 actions 的做法类似,当我们每次取出 state 下的数据后都要进行一次格式化,那为什么不能在一个公共的位置统一处理呢?Getter 就是解决这个问题,内部包含了对 state 下数据的 格式化整理函数。

同理,当state上的数据堆积过多,我们需要划分不同的数据区时,Module 可以更简单的提供这个功能。

总结:

以上问题其实没有任何深度,只是记录了一次内部探讨过程。分享出来与大家共同学习。我们只需要多思考就可以明白作者为什么要这么设计,这样设计的好处是什么。但往往我们对一个知识点的深度理解就缺少这么几个问题。多去发现问题,提出问题,解决问题。这样才能够快速提高,做到知其然知其所以然。

以上,与君共勉。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值