好嘞,各位靓仔靓女,今天咱们来聊聊 Vuex 和 Pinia 里面那个神秘兮兮的 devtools
集成,看看它们是怎么勾搭上浏览器扩展,然后把咱们的代码扒个底朝天,方便咱们调试的。放心,保证通俗易懂,看完你也能自己写一个简易版的。
开场白:Debugger 的诞生,要感谢八卦的心
话说,程序员最喜欢干的事情除了写Bug,就是Debug了。但是,如果啥都看不见,全靠猜,那Debug就跟瞎猫碰死耗子一样,效率低到令人发指。所以,Debug工具就应运而生了。devtools
其实就是这样一个超级 Debugger,它能让你看到 Vue 应用的状态、数据流、甚至还能时光倒流。这背后,Vuex 和 Pinia 功不可没。
第一节:devtools 集成的核心思路:监听 + 通知
简单来说,devtools
集成的核心就是两件事:
- 监听: 监听 Vuex/Pinia 的状态变化、mutation/action 的触发、甚至组件的渲染。
- 通知: 把这些变化通知给浏览器扩展,让它能展示出来。
这个过程就像一个八卦记者(Vuex/Pinia),时刻关注着明星(Vue 应用)的一举一动,然后把这些八卦消息(状态变化)告诉吃瓜群众(浏览器扩展)。
第二节:Vuex 的 devtools 集成:传统艺能,稳定可靠
Vuex 的 devtools
集成相对来说比较成熟,它的实现方式也比较直接:
- 依赖:
@vue/devtools
这个 npm 包是关键,它提供了connect
方法,用于连接devtools
扩展。 - 连接: 在 Vuex 的
store
创建的时候,会判断当前环境是否支持devtools
,如果支持,就调用connect
方法建立连接。 - 监听: Vuex 通过
subscribe
和subscribeAction
方法监听状态变化和 action 的触发。 - 通知: 当状态变化或者 action 触发的时候,Vuex 会把相关信息通过
devtools
连接发送给浏览器扩展。
代码示例:Vuex 连接 devtools
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
},
plugins: process.env.NODE_ENV !== 'production' && typeof window !== 'undefined' && window.__VUE_DEVTOOLS_GLOBAL_HOOK__
? [createVuexDevtools()]
: []
})
function createVuexDevtools() {
return store => {
// vuex-devtools 的核心逻辑
const devtools = window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
if (!devtools) return;
devtools.emit('vuex:init', store);
store.subscribe((mutation, state) => {
devtools.emit('vuex:mutation', mutation, state);
});
store.subscribeAction((action, state) => {
devtools.emit('vuex:action', action, state);
});
};
}
export default store
代码解析:
window.__VUE_DEVTOOLS_GLOBAL_HOOK__
:这是devtools
扩展注入到页面中的一个全局变量,是 Vuex 和devtools
沟通的桥梁。devtools.emit
:这个方法用于向devtools
扩展发送消息。vuex:init
:初始化事件,告诉devtools
扩展有一个新的 Vuex store。vuex:mutation
:mutation 事件,告诉devtools
扩展有一个 mutation 被提交了。vuex:action
:action 事件,告诉devtools
扩展有一个 action 被触发了。
Vuex devtools 的数据结构:
事件类型 | 数据 | 描述 |
---|---|---|
vuex:init |
store 对象 |
初始化事件,包含 store 的状态、mutations、actions 等信息。 |
vuex:mutation |
{ type: string, payload: any } , state 对象 |
Mutation 事件,包含 mutation 的类型和 payload,以及修改后的 state。 |
vuex:action |
{ type: string, payload: any } , state 对象 |
Action 事件,包含 action 的类型和 payload,以及执行 action 前后的 state。(注意: action 只能拿到触发前的 state) |
第三节:Pinia 的 devtools 集成:后起之秀,更加灵活
Pinia 的 devtools
集成更加现代化,它利用了 Vue 3 的 Composition API,提供了更灵活的监听和通知机制。
- 依赖:
pinia
本身就内置了对devtools
的支持,不需要额外的依赖。 - 连接: Pinia 会自动检测当前环境是否支持
devtools
,如果支持,就会建立连接。 - 监听: Pinia 利用
effectScope
和onBefore/onAfter
等 API 监听状态变化和 action 的触发。 - 通知: Pinia 会把相关信息通过
devtools
连接发送给浏览器扩展。
代码示例:Pinia 连接 devtools
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
// 在 store 中
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
doubleCount: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
代码解析:
- Pinia 默认就集成了 devtools,只要安装了 Vue Devtools 扩展,就可以直接使用。
- Pinia 利用 Vue 3 的响应式系统,可以更精细地监听状态变化。
- Pinia 的 API 更加简洁,使用起来更方便。
Pinia devtools 的数据结构:
事件类型 | 数据 | 描述 |
---|---|---|
pinia:init |
pinia 实例 |
初始化事件,包含 pinia 的所有 store 信息。 |
pinia:store |
store 对象 |
单个 store 的信息,包含 state、getters、actions 等。 |
pinia:patch |
{ storeId: string, type: 'direct' | 'merge', payload: Partial<State> } |
state 被直接修改或者通过 store.$patch 合并修改。 |
pinia:action |
{ storeId: string, name: string, args: any[], result: any, error: any } |
action 被调用,包含 action 的名称、参数、返回值和错误信息。 |
pinia:getter |
{ storeId: string, name: string, result: any } |
getter 被访问,包含 getter 的名称和返回值。 |
第四节:devtools 扩展的实现:接收 + 展示
浏览器扩展才是真正把这些数据展示出来的功臣。它的主要工作就是:
- 接收: 接收 Vuex/Pinia 发送过来的消息。
- 处理: 对接收到的消息进行处理,提取出关键信息。
- 展示: 把这些信息以友好的方式展示出来,例如状态树、时间线等等。
- 交互: 提供一些交互功能,例如时光倒流、修改状态等等。
简易版 devtools 扩展代码示例:
// background.js (扩展的后台脚本)
chrome.devtools.panels.create(
"My Vuex/Pinia Debugger",
"icon.png",
"panel.html",
function(panel) {
console.log("Panel created");
}
);
// panel.html (扩展的面板页面)
<!DOCTYPE html>
<html>
<head>
<title>My Vuex/Pinia Debugger</title>
</head>
<body>
<h1>Vuex/Pinia State</h1>
<div id="state"></div>
<script>
// 从 content script 接收消息
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.type === "vuex:mutation" || request.type === "pinia:patch") {
document.getElementById("state").textContent = JSON.stringify(request.payload, null, 2);
}
});
</script>
</body>
</html>
// content.js (注入到页面的脚本)
window.addEventListener('message', function(event) {
if (event.source != window)
return;
if (event.data.type && (event.data.type.startsWith('vuex:') || event.data.type.startsWith('pinia:'))) {
chrome.runtime.sendMessage(event.data);
}
}, false);
// 在 Vuex/Pinia 中发送消息 (简化版)
// Vuex:
devtools.emit('vuex:mutation', { type: 'MY_MUTATION', payload: { data: 'some data' } }, store.state);
// Pinia:
window.postMessage({ type: 'pinia:patch', payload: { count: 10 } }, '*');
代码解析:
- background.js: 创建
devtools
面板。 - panel.html:
devtools
面板的 UI,用于展示状态信息。 - content.js: 注入到页面中的脚本,用于监听 Vuex/Pinia 发送的消息,并转发给
background.js
。 chrome.devtools.panels.create
: 创建devtools
面板的 API。chrome.runtime.onMessage.addListener
: 监听来自 content script 的消息。chrome.runtime.sendMessage
: 向devtools
面板发送消息。window.postMessage
: 用于在Vue应用中向content.js发送消息。
第五节:细节深挖:时间旅行的秘密
devtools
最酷的功能之一就是时间旅行,它可以让你回到过去的状态,看看之前的代码是怎么运行的。这个功能的实现原理其实也很简单:
- 记录: 每次状态变化,都记录下当前的状态和对应的操作。
- 回放: 当你想回到过去的状态时,
devtools
会把之前的操作重新执行一遍,直到达到目标状态。
这个过程就像录像带倒带一样,只不过录的是代码的状态。
第六节:总结与展望:devtools 的未来
devtools
集成是 Vuex 和 Pinia 的重要组成部分,它极大地提升了开发效率,让调试变得更加轻松。随着 Vue 生态的不断发展,devtools
也会变得更加强大,例如:
- 更智能的分析: 自动检测性能瓶颈、代码错误等等。
- 更强大的交互: 支持更复杂的调试操作,例如断点调试、单步执行等等。
- 更友好的界面: 提供更直观、更易用的界面。
最后的彩蛋:自己动手,丰衣足食
如果你对 devtools
集成感兴趣,不妨自己动手写一个简易版的,这样可以更深入地理解它的工作原理。相信我,这绝对是一件很有趣的事情!
好了,今天的讲座就到这里。希望大家有所收获,下次再见!
表格总结:Vuex vs Pinia devtools 集成
特性 | Vuex | Pinia |
---|---|---|
依赖 | @vue/devtools |
内置 |
连接方式 | 手动连接 | 自动连接 |
监听机制 | subscribe 和 subscribeAction |
effectScope 和 onBefore/onAfter 等 |
数据结构 | 事件类型:vuex:init 、vuex:mutation 、vuex:action |
事件类型:pinia:init 、pinia:store 、pinia:patch 、pinia:action 、pinia:getter |
灵活性 | 相对固定 | 更灵活 |
易用性 | 相对复杂 | 更简洁 |
是否支持TS | 良好 | 更好(原生支持) |
状态管理方式 | 通过 store 对象集中管理 |
通过 defineStore 定义多个 store,更模块化 |
Devtools 集成 | 需要手动配置,相对繁琐 | 开箱即用,自动集成 |