深海鱼的博客 深海鱼的博客
首页
  • 《ES6 教程》
  • 《TypeScript》
  • 《Vue》
  • 《React》
  • 《Git》
  • Javascript
  • CSS
手写系列
  • React项目实战
  • Vue3项目实战
  • 服务端项目实战
  • 鸿蒙项目实战
  • 小小实践
  • Vue全家桶

    • Vue2.0
    • Vue3.0
    • VueRouter
    • Vuex
  • React

    • React
  • Axios
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

深海鱼

尽人事,知天命,知行合一。
首页
  • 《ES6 教程》
  • 《TypeScript》
  • 《Vue》
  • 《React》
  • 《Git》
  • Javascript
  • CSS
手写系列
  • React项目实战
  • Vue3项目实战
  • 服务端项目实战
  • 鸿蒙项目实战
  • 小小实践
  • Vue全家桶

    • Vue2.0
    • Vue3.0
    • VueRouter
    • Vuex
  • React

    • React
  • Axios
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 导读
  • vue2

  • vuex

    • 前言
    • 调式环境准备
    • 源码基本结构与install函数
    • ModuleCollection与Module类
    • Store实例化
    • Store实例方法
    • 辅助函数
    • 内置插件
      • devtoolPlugin插件
      • Logger插件
    • 总结与常见问题
  • vue-router

  • vue3

  • react

  • Axios

  • 源码解读
  • vuex
深海鱼
2024-07-01
目录

内置插件

Vuex支持使用插件来扩展状态管理功能。使用new Vuex.Store(options)时会执行以下代码:

const {
  plugins = [] // 插件列表
} = options
// ...
plugins.forEach(plugin => plugin(this))

// 安装devtools插件
if (Vue.config.devtools) {
  devtoolPlugin(this)
}
1
2
3
4
5
6
7
8
9
10

所有的插件都是一个函数,通过options.plugins传入插件列表,实例化时会遍历插件列表,传入当前实例执行插件。同时,在开启了调试工具的情况下,Vuex会自动安装devtoolPlugin插件。另外,Vuex还提供了一个Logger插件,用于打印状态变更日志。默认情况下是不安装的,需要手动导入并安装。

import createLogger from 'vuex/dist/logger'
const store = new Vuex.Store({
  plugins: [createLogger()]
})
1
2
3
4

本文将讲解Vuex内置的devtoolPlugin和Logger如何实现,进而理解Vuex插件的使用方式。

# devtoolPlugin插件

devtoolPlugin插件用于集成Vue Devtools,安装此插件后可以在Vue Devtools中查看Vuex状态变化过程。

devtoolPlugin插件定义的文件在/plugins/devtool.js中。

const devtoolHook =
  typeof window !== 'undefined' &&
  window.__VUE_DEVTOOLS_GLOBAL_HOOK__ // 获取devtools的钩子

// 开发者工具插件
export default function devtoolPlugin(store) {
  if (!devtoolHook) return

  store._devtoolHook = devtoolHook // 给store添加_devtoolHook属性

  devtoolHook.emit('vuex:init', store) // 触发vuex:init事件

  // 监听vuex:travel-to-state事件
  devtoolHook.on('vuex:travel-to-state', targetState => {
    store.replaceState(targetState)
  })

  // 订阅mutation
  store.subscribe((mutation, state) => {
    devtoolHook.emit('vuex:mutation', mutation, state)
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

devtoolHook:Vue Devtools的钩子,当window.__VUE_DEVTOOLS_GLOBAL_HOOK__存在时,表示Vue Devtools已安装。

  • 实例化时触发vuex:init事件,将当前实例store传入。
  • 监听vuex:travel-to-state事件,将传入的targetState替换当前实例store的state。
  • 订阅mutation,将mutation和state传入,触发vuex:mutation事件。

# Logger插件

Logger插件定义在/plugins/logger.js文件中。

其内部是一个工厂函数,参数定义和说明如下:

export default function createLogger({
  collapsed = true, // 自动展开记录的mutation
  // 需要被记录的mutation过滤器
  // 接收当前触发的mutation,上一次的state和当前state
  filter = (mutation, stateBefore, stateAfter) => true,
  transformer = state => state, // 在开始记录之前返回的state
  mutationTransformer = mut => mut, //格式化要记录的mutation格式
  logger = console // 日志记录器实现,默认值console
} = {}) {
  return store => {

  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

createLogger接收一些配置,并根据配置生成一个插件函数,供Vuex实例化时使用。返回的插件函数会接收一个store实例,定义如下:

 return store => {
    // 记录上一次state
    let prevState = deepCopy(store.state)
    // 定于mutation订阅
    store.subscribe((mutation, state) => {
      if (typeof logger === 'undefined') { // logger未定义,则不记录日志
        return
      }
      // 记录当前的state
      const nextState = deepCopy(state)

      if (filter(mutation, prevState, nextState)) { // 需要记录
        const time = new Date() // 时间
        // 格式化后的时间 @00:00:00.000
        const formattedTime = ` @ ${pad(time.getHours(), 2)}:${pad(time.getMinutes(), 2)}:${pad(time.getSeconds(), 2)}.${pad(time.getMilliseconds(), 3)}`
        const formattedMutation = mutationTransformer(mutation) //格式化后的mutation
        const message = `mutation ${mutation.type}${formattedTime}` // 日志消息
        // 日志分组
        const startMessage = collapsed
          ? logger.groupCollapsed
          : logger.group

        // render
        try {
          // 记录日志
          startMessage.call(logger, message)
        } catch (e) {
          console.log(message)
        }

        // 记录日志
        logger.log('%c prev state', 'color: #9E9E9E; font-weight: bold', transformer(prevState)) // 上一次state
        logger.log('%c mutation', 'color: #03A9F4; font-weight: bold', formattedMutation) // 格式化后的mutation
        logger.log('%c next state', 'color: #4CAF50; font-weight: bold', transformer(nextState)) // 格式化后的state

        try {
          // 分组结束
          logger.groupEnd()
        } catch (e) {
          logger.log('—— log end ——')
        }
      }

      // 更新上一次的state
      prevState = nextState
    })
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

处理的过程如下:

  • 保存上一次的state,保存是方式是使用deepCopy函数,将store.state深拷贝一份,保存到prevState中。初始化时,prevState为store.state。
  • 订阅mutation,在mutation触发时
    • 获取新的state
    • 如果未指定日志记录的方法,则不记录日志。(默认是console)
    • 根据传入的filter过滤器判断当前日志是否需要记录(默认是记录),需要记录则执行后续的操作
    • 生成时间和日志分组名称信息
    • 尝试输出日志分组标记,发生异常时则直接输出分组名称
    • 分别输出上一次state、mutation执行信息和当前state。
    • 分组结束
  • 将上prevState设置成当前的state,即prevState = nextState。

以上就是Vuex提供的两个内置插件的实现,基本的使用就是通过订阅mutation,在mutation触发时,做相关的操作或记录。

最近更新: 2024/07/01, 18:59
辅助函数
总结与常见问题

← 辅助函数 总结与常见问题→

最近更新
01
Axios源码解读
07-29
02
基于React16的Webpack升级与构建速度优化
07-09
03
Vue-Router源码解读
07-09
更多文章>
Theme by Vdoing | Copyright © 2024-2024 深海鱼 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式