快速理解前端开源代码-vuex篇

前言

不管是学习还是工作需要,我们都免不了阅读三方的源码。那么如何比较高效的阅读,笔者基于vuex分享一下自己的阅读经验。本文分为四个部分:

  • vuex功能介绍
  • 问题点&兴趣点
  • 源码阅读对比
  • 总结
一,Vuex功能介绍

vuex是专门为vue.js开发的状态管理模式,它解决的多个组件依赖同一个状态的情况。特别是子组件与子组件之间的交互,异常的曲折。所以在vue的项目实践中,一般有两种处理方式,一种是vuex,还有一种是较为轻量的event-bus,需要结合项目复杂度来判定具体使用哪种方式。
vuxe的运行架构图如下:


image.png

vuex的基本API:

定义状态:

  • State:存储在store里的数据
    读取状态:
  • 直接读取:this.$store.state.XXX
  • Getter:属性读取和方法读取(类似计算属性)
    修改状态:
  • Mutation:同步
  • Action:异步

vuex的实例创建:

import { createApp } from 'vue'
import { createStore } from 'vuex'

// Create a new store instance.
const store = createStore({
  state () {
    return {
      count: 0
    }
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

const app = createApp({ /* your root component */ })

// Install the store instance as a plugin
app.use(store)

简单总结就是:在全局放了一个数据块,所有组件都可以进行修改和读取。不用进行父子组件的层层传递。

二,问题点&兴趣点

在我们阅读源码之前,对目标一定要熟悉。如何初始化,一些常用方法要了然于心。其次呢,要想明白为什么要读源码?

  • 是对它哪一部分有疑问?
  • 是想知道它哪一部分的实现方式?
  • 是想做个思路实现方式对比?

笔者在读vuex源码之前,有下面三个问题:

  • 状态存在哪里?内存?Localstorage?
  • 如何控制state.XXX不直接被修改?
  • Mutation与Action的具体实现差异?
三,源码阅读对比
image.png

第一步先整体看一下目录结构:

  • module:提供module对象与module对象树的创建功能;
  • plugins:提供开发辅助插件
  • helpers.js:提供action、mutations以及getters的查找API;
  • index.js:是源码主入口文件,提供store的各module构建安装;
  • store.js:提供了store的实现;
  • Store-util.js:提供了工具方法

重点关注入口文件:index.cjs.js


image.png

结合实例的初始化,我们重点关注类Store,其代码全貌如下:

image.png

先了解一下大体结构,从上图我们可以找到日常使用的API方法。然后我们结合前面提出的问题去具体分析。

1,状态存在哪里?内存?Localstorage?

在初始化方法中,我们找到下述语句:

// initialize the store state, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
resetStoreState(this, state)

看注释是初始化store的state,然后再找到方法resetStoreState

image.png

在方法中我们在红框标记处,看到了vue的reactive方法。所以我们可以知道,state是存在于内存中。也就意味着刷新页面会丢失数据,vuex会重新初始化。

2,如何控制state.XXX不直接被修改?

文档中说不允许直接修改state的内容,可我要是硬改呢?vuex内部是如何应对的?

 get state () {
    return this._state.data
  }

  set state (v) {
    if (__DEV__) {
      assert(false, `use store.replaceState() to explicit replace store state.`)
    }
  }

通过该部分的代码可以看到,set state的时候:
开发模式会提示
运行时会直接忽略

所以,我们没法对其通过state.XXX=YYY的方式,进行强制修改。

3,Mutation与Action的具体实现差异?

从api功能上我们知道一个是同步一个是异步,哪代码具体有何不同呢?
commit方法如图:


image.png

image.png

红框处我们可以看到,异步是返回的Promise。整体实现上比我们想象的要简单。
除了计划的三个问题,在阅读过程中,也碰到一些有意思的点。

4,dispatch的入参从何而来?

文档:


image.png

代码:


image.png

从代码中我们并没有找到包含commit属性的对象。payload在实际执行中是event对象。那么commit执行的时候,从哪来的呢?

回顾Vuex的使用的地方:

  • 初始化
  • 调用

既然调用没有,那么我们就去找找初始化。
在初始化中,我们看到一行代码:

   // init root module.
    // this also recursively registers all sub-modules
    // and collects all module getters inside this._wrappedGetters
    installModule(this, state, [], this._modules.root)

然后看一下installModule的具体实现:


image.png

红框标记代码如下:

function registerAction (store, type, handler, local) {
  const entry = store._actions[type] || (store._actions[type] = [])
  entry.push(function wrappedActionHandler (payload) {

    let res = handler.call(store, {
      dispatch: local.dispatch,
      commit: local.commit,
      getters: local.getters,
      state: local.state,
      rootGetters: store.getters,
      rootState: store.state
    }, payload)
    if (!isPromise(res)) {
      res = Promise.resolve(res)
    }
    if (store._devtoolHook) {
      return res.catch(err => {
        store._devtoolHook.emit('vuex:error', err)
        throw err
      })
    } else {
      return res
    }
  })
}

在handler.call这个位置我们可以看到,在action注册的时候,就赋值了:

{
      dispatch: local.dispatch,
      commit: local.commit,
      getters: local.getters,
      state: local.state,
      rootGetters: store.getters,
      rootState: store.state
    }

所以,当我们阅读源码中断的时候,就需要考虑其他关联的地方,然后进行寻找。

5,vue注册的时候都做了些什么?

对于vue的插件我们重点看install方法:

install (app, injectKey) {
    app.provide(injectKey || storeKey, this)
    app.config.globalProperties.$store = this

    const useDevtools = this._devtools !== undefined
      ? this._devtools
      : __DEV__ || __VUE_PROD_DEVTOOLS__

    if (useDevtools) {
      addDevtools(app, this)
    }
  }

内容比较简单,就是把当前实例赋值给vue实例上的$store

6,在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误

这是一个很有意思的部分,可以先想想如果我们自己实现的话,如何处理?

其实题目拆分后可以分为两个部分:

状态是不是变更了(监听)
是不是mutation 函数引起的

下面我们看看vuex的具体实现


image.png

我们可以找到严格模式下的一段处理,基于vue的watch来实现状态变更的监听。在这里我们看到了一个变量_committing,这个值什么时候是true呢?
在commit方法中,我们找到了_withCommit方法:

image.png
_withCommit (fn) {
    const committing = this._committing
    this._committing = true
    fn()
    this._committing = committing
  }

这样结合起来看,和我们想定的目标是吻合的。

只有通过commit上来的变更,_committing才会为true
通过vue的watch来监听state的改变,当变化的时候对_committing做判定

四,总结

vuex的代码量不多,很适合作为一个入门阅读的范例。阅读的时候我们大体分下面三个步骤:
对阅读目标的使用,要足够熟练

  • 用都不会谈何理解呢
    先结构,再细节
  • 对于大型库,一定要先理结构,然后再入细节,不然容易懵圈
    读前猜想,读中印证,读后总结
  • 其实就是带着思考阅读,不然读完可能1-2个周就忘记了,没有什么收获。

以上就是个人在源码阅读方面的一些心得,通过vuex做一个示例,希望能给大家带来一定的启发。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容

  • 上一章总结了 Vuex 的框架原理,这一章我们将从 Vuex 的入口文件开始,分步骤阅读和解析源码。由于 Vuex...
    你的肖同学阅读 1,773评论 3 16
  • 写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue...
    染陌同学阅读 1,668评论 0 12
  • Vuex源码阅读分析 Vuex是专为Vue开发的统一状态管理工具。当我们的项目不是很复杂时,一些交互可以通过全局事...
    steinslin阅读 631评论 0 6
  • 前言 之前几篇解析 Vue 源码的文章都是完整的分析整个源码的执行过程,这篇文章我会将重点放在核心原理的解析,不会...
    心_c2a2阅读 1,461评论 1 8
  • 这几天忙啊,有绝地求生要上分,英雄联盟新赛季需要上分,就懒着什么也没写,很惭愧。这个vuex,vue-router...
    公子世无双ss阅读 613评论 0 0