美团面试官:那你讲一讲Vuex吧

给大家推荐一个实用面试题库

1、前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

前言

周一接到了美团的一面,面试官人很好,基本都是围绕着简历来问,下面就是我重新整理了一下怎么实现Vuex的min简单版本,可以看到Vuex的大致原理。

vuex的基本使用

//index.js
import { createStore } from './gvuex.js'const store = createStore({// 定义store的状态state() {return {count: 1 }},// 定义获取state状态数据的计算属性getters,以下的值都被认为是state的派生值getters: {double(state) { return state.count * 2}},// 定义修改state状态数据的方法mutationsmutations: {add(state) { state.count++}},// 定义异步操作的方法actionsactions: {asyncAdd({ commit }) { setTimeout(() => {commit('add')}, 1000)}}
})
// App.vue
<script setup>
import { useStore } from '../store/gvuex'
import { computed } from 'vue'let store = useStore();
let count = computed(()=>{ store.state.count })
let double = computed(()=>{ store.getters.double })
function add() {store.commit('add')
}
function asyncAdd() {store.dispatch('asyncAdd')
}
</script>
<template>
<div class="">{{ count }} * 2 = {{ double }}<button @click="add">add</button><button @click="asyncAdd">async add</button>
</div>
</template>
<style scoped></style>

知道了vuex的用法,你会不会发出以下疑问:

  1. 为什么要store.commit('add')才能触发事件执行呢? 可不可以进行直接调用mutation函数进行操作呢?
  2. 为什么不可以直接对state存储的状态进行修改,只能通过调用mutation函数的方式修改呢?
  3. 为什么存在异步调用的函数需要store.dispatch('asyncAdd')函数才能完成呢?可以直接调用store.commit('asyncAdd')嘛?如果不可以,为什么呢?
  4. createStore()useStore()到底发生了什么?

那么下面就来一一解密吧。

vue里注册全局组件

import { createApp } from 'vue'
import store from './store'
import App from './App.vue'const app = createApp(App)
app.use(store).mount('#app')

app.use() 用来安装插件,接受一个参数,通常是插件对象,该对象必须暴露一个install方法,调用app.use()时,会自动执行install()方法。

解析Store类里的流程

import { reactive, inject } from 'vue'// 定义了一个全局的 key,用于在 Vue 组件中通过 inject API 访问 store 对象
const STORE_KEY = '__store__'// 用于获取当前组件的 store 对象
function useStore() {return inject(STORE_KEY)
}// Store 类,用于管理应用程序状态
class Store {// 构造函数,接收一个包含 state、mutations、actions 和 getters 函数的对象 options,然后将它们保存到实例属性中constructor(options) {this.$options = options;// 使用 Vue.js 的 reactive API 将 state 数据转换为响应式对象,并保存到实例属性 _state 中        this._state = reactive({data: options.state()})// 将 mutations 和 actions 函数保存到实例属性中this._mutations = options.mutationsthis._actions = options.actions;// 初始化 getters 属性为空对象this.getters = {};// 遍历所有的 getters 函数,将其封装成 computed 属性并保存到实例属性 getters 中Object.keys(options.getters).forEach(name => {const fn = options.getters(name);this.getters[name] = computed(() => fn(this.state));})}// 用于获取当前状态数据get state() {return this._state.data}// 获取mutation内定义的函数并执行commit = (type, payload) => {const entry = this._mutations[type]entry && entry(this.state, payload)}// 获取actions内定义的函数并返回函数执行结果// 简略版dispatchdispatch = (type, payload) => { const entry = this._actions[type];return entry && entry(this, payload)}// 将当前 store 实例注册到 Vue.js 应用程序中install(app) {app.provide(STORE_KEY, this)}
}// 创建一个新的 Store 实例并返回
function createStore(options) {return new Store(options);
}// 导出 createStore 和 useStore 函数,用于在 Vue.js 应用程序中管理状态
export {createStore,useStore
}

是不是很惊讶于vuex的底层实现就短短几十行代码就实现了,嘿嘿那是因为从vue里引入了reactiveinjectcomputed,并且对很大一部分的源码进行了省略,dispatchcommit远比这复杂多了

解答

问题一:为什么要store.commit('add')才能触发事件执行呢? 可不可以进行直接调用mutation函数进行操作呢?

解答:store类里根本没有mutation方法,只能通过调用commit方法来执行mutation里的函数列表。

问题二:为什么不可以直接对state存储的状态进行修改,只能通过调用函数的方式修改呢?

解答:Vuex 通过强制限制对 store 的修改方式来确保状态的可追踪性。只有通过 mutation 函数才能修改 store 中的状态,这样可以轻松地跟踪状态的变化,也可以避免无意中从不同的组件中直接修改 store 导致的代码难以维护和调试的问题。

问题三:为什么存在异步调用的函数需要store.dispatch('asyncAdd')函数才能完成呢?可以直接调用store.commit('asyncAdd')嘛?如果不可以,为什么呢?

解答:实际上dispatch方法和commit方法远不止这么简单,下面先贴出部分vuex的关于这两个方法的源码部分

Store.prototype.dispatch = function dispatch (_type, _payload) {var this$1$1 = this;// check object-style dispatchvar ref = unifyObjectStyle(_type, _payload);var type = ref.type;var payload = ref.payload;var action = { type: type, payload: payload };var entry = this._actions[type];if (!entry) {if ((process.env.NODE_ENV !== 'production')) {console.error(("[vuex] unknown action type: " + type));}return}try {this._actionSubscribers.slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe.filter(function (sub) { return sub.before; }).forEach(function (sub) { return sub.before(action, this$1$1.state); });} catch (e) {if ((process.env.NODE_ENV !== 'production')) {console.warn("[vuex] error in before action subscribers: ");console.error(e);}}var result = entry.length > 1? Promise.all(entry.map(function (handler) { return handler(payload); })): entry[0](payload);return new Promise(function (resolve, reject) {result.then(function (res) {try {this$1$1._actionSubscribers.filter(function (sub) { return sub.after; }).forEach(function (sub) { return sub.after(action, this$1$1.state); });} catch (e) {if ((process.env.NODE_ENV !== 'production')) {console.warn("[vuex] error in after action subscribers: ");console.error(e);}}resolve(res);}, function (error) {try {this$1$1._actionSubscribers.filter(function (sub) { return sub.error; }).forEach(function (sub) { return sub.error(action, this$1$1.state, error); });} catch (e) {if ((process.env.NODE_ENV !== 'production')) {console.warn("[vuex] error in error action subscribers: ");console.error(e);}}reject(error);});})
};Store.prototype.commit = function commit (_type, _payload, _options) {var this$1$1 = this;// check object-style commitvar ref = unifyObjectStyle(_type, _payload, _options);var type = ref.type;var payload = ref.payload;var options = ref.options;var mutation = { type: type, payload: payload };var entry = this._mutations[type];if (!entry) {if ((process.env.NODE_ENV !== 'production')) {console.error(("[vuex] unknown mutation type: " + type));}return}this._withCommit(function () {entry.forEach(function commitIterator (handler) {handler(payload);});});this._subscribers.slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe.forEach(function (sub) { return sub(mutation, this$1$1.state); });if ((process.env.NODE_ENV !== 'production') &&options && options.silent) {console.warn("[vuex] mutation type: " + type + ". Silent option has been removed. " +'Use the filter functionality in the vue-devtools');}
};

源码里我们能看到,dispatch方法返回的是一个Promise对象,而commit方法没有返回值,完全进行的是同步代码的操作,虽然返回值可能适用的场景不多,但是这样设计的主要目的还是为了确保状态的可追踪性

问题四: createStore()useStore()到底发生了什么?

当我们去调用 createStore()函数,其构造函数就会接收一个包含 statemutationsactions getters 函数的对象 options, 然后将它们保存到实例属性中,此时state中的值都会转换为响应式对象,同时遍历所有的getters函数,将其封装成computed属性并保存到实例属性getters中,而在main.js里调用了app.use(), install方法自动执行,将将当前 store 实例注册到 Vue.js 应用程序中,只需要调用useStore()就可以拿到全局状态管理的store实例了,可以靠injectprovide实现全局共享。

总结

通过实现简单的vuex,完成基本的功能,对vuex有了不同的理解,但在vuex的源码方面,上面的demo只是简简单单做了大致的依赖关系整理,还有:不具备模块化功能、没有提供插件功能、没有提供严格模式、没有提供辅助函数和实现单例模式等等的实现。

给大家推荐一个实用面试题库

1、前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/3245.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DBeaver连接华为高斯数据库 DBeaver连接Gaussdb数据库 DBeaver connect Gaussdb

DBeaver连接华为高斯数据库 DBeaver连接Gaussdb数据库 DBeaver connect Gaussdb 一、概述 华为GaussDB出来已经有一段时间&#xff0c;最近工作中刚到Gauss数据库。作为coder&#xff0c;那么如何通过可视化工具来操作Gauss呢&#xff1f; 本文将记录使用免费、开源的DBeaver来…

【kubernetes系列】Kubernetes之资源配置范围管理LimitRange

概述 在默认情况下&#xff0c;Kubernetes不会对Pod加上CPU和内存限制&#xff0c;这意味着Kubernetes系统中任何Pod都可以使用其所在节点的所有可用的CPU和内存。在之前的章节https://blog.csdn.net/margu_168/article/details/131786000分享了针对集群中某个pod的资源限制&a…

云迁移第二波热潮来袭,你准备好了吗?

最近&#xff0c;云迁移再次被频繁提及&#xff0c;企业对云迁移的需求量有回升趋势&#xff0c;究其根本&#xff0c;主要有以下原因&#xff1a; 企业数字化进程加速&#xff0c;本地上云需求强劲 根据《2021中国企业上云指数洞察报告》&#xff0c;我国实体经济上云渗透率…

Appium+Python+PO 设计模式

目录 前言&#xff1a; 什么是 PageObject? 关于报告的输出 总结 前言&#xff1a; Appium是一个用于自动化移动应用程序测试的开源工具&#xff0c;Python是一种简单易学且功能强大的编程语言&#xff0c;PO&#xff08;Page Object&#xff09;设计模式是一种在自动化测…

使用shell监控应用运行状态通过企业微信接收监控通知

目的&#xff1a;编写shell脚本来监控应用服务运行状态&#xff0c;若是应用异常则自动重启应用通过企业微信接收监控告警通知 知识要点&#xff1a; 使用shell脚本监控应用服务使用shell脚本自动恢复异常服务通过企业微信通知接收监控结果shell脚本使用数组知识&#xff0c;…

行为型模式之迭代器模式

迭代器模式&#xff08;Iterator Pattern&#xff09; 迭代器模式&#xff08;Iterator Pattern&#xff09;是一种行为设计模式&#xff0c;它提供了一种顺序访问集合对象中各个元素的方法&#xff0c;而无需暴露集合的内部表示。 在迭代器模式中&#xff0c;有三个主要角色&a…

session 生命周期和经典案例-防止非法进入管理页面

文章目录 session 生命周期和Session 经典案例-防止非法进入管理页面session 生命周期Session 生命周期-说明代码演示说明 Session 的生命周期创建CreateSession2创建ReadSession2 解读Session 的生命周期代码示例创建DeleteSession Session 经典案例-防止非法进入管理页面需求…

bgp联邦

1、ip配置 [r1-LoopBack0]ip address 192.168.1.1 24 [r1-LoopBack1]ip address 10.0.0.1 24 [r1-GigabitEthernet0/0/0]ip address 12.0.0.1 24[r2-GigabitEthernet0/0/0]ip address 12.0.0.2 24 [r2-GigabitEthernet0/0/1]ip address 172.16.1.1 29 [r2-GigabitEthernet0/0/…

【从零开始学习CSS | 第三篇】选择器优先级

目录 前言&#xff1a; 常见选择器的优先级&#xff08;从高到低&#xff09; 选择器的权重&#xff1a; 总结&#xff1a; 前言&#xff1a; 在前几篇文章中我们介绍了大量的选择器&#xff0c;那么大量的选择器在使用的时候&#xff0c;一定是有一个优先级顺序的&#xff…

【计算机视觉】DINOv2(视觉大模型)代码使用和测试(完整的源代码)

文章目录 一、环境部署二、导入原图2.1 使用vit_s14的模型 三、使用其他模型3.1 使用vit_b14的模型3.2 使用vit_l14的模型3.3 使用vit_g14的模型 一、环境部署 !git clone https://ghproxy.com/https://github.com/facebookresearch/dinov2.git输出为&#xff1a; Cloning in…

DT灯光基础(辉光 雾 阴影 渲染选项)

点光源 不能宣染&#xff0c;换个版本。不能正常预览 聚光灯 t 手柄 挡光版 平行光阴影 光线追踪阴影 没有看见阴影 灯光使用贴图 环境光 不进行渲染物体 不渲染阴影 接收阴影 不反射 可以看到反射 没有反射了 灯光链接 取消灯照 灯光雾 辉光 变化不明显

C++初阶 - 3.类和对象(中)

目录 1.类的6个默认成员函数 2.构造函数 2.2特性 3.析构函数 3.1 概念 3.2 特性 4. 拷贝构造函数 4.1 概念 4.2 特征 5.赋值运算符重载 5.1运算符重载 5.2 赋值运算符重载 5.3 前置和后置重载 6.日期类的实现 7.const成员 8.取地址及const取地址操作符重载 1.类…

FCOS 论文学习

1. 解决了什么问题&#xff1f; 之前的目标检测器如 RetinaNet、SSD、YOLOv3 都依赖于 anchors。基于 anchors 的检测器有如下三个缺点&#xff1a; 检测表现对于 anchors 的大小、宽高比和数量等超参数很敏感&#xff1b;即使精心设计了 anchors&#xff0c;但由于大小和宽高…

论文投稿前细节检查

参考文献的doi统一要或不要&#xff0c;要参考期刊/会议之前的版本参考文献的会议和期刊作者的缩写方式&#xff0c;论文标题的首字母&#xff0c;发表会议或期刊的首字母&#xff0c;年份的位置等是否统一。同时&#xff0c;建议会议直接给缩写&#xff0c;期刊给全称。Fig、T…

【x-shell】介绍以及命令大全

提示&#xff1a;x-shell命令 介绍&#xff0c;以及命令大全 文章目录 前言一、x-shell是什么&#xff1f;二、x-shell功能三、x-shell使用四、x-shell的常用命令五、 ps -ef | grep 命令总结 前言 x-shell 提示&#xff1a; 一、x-shell是什么&#xff1f; x-shell命令&…

EasyCVR告警类型设置后首页需要刷新才能更新的问题优化

EasyCVR视频融合平台基于云边端一体化架构&#xff0c;可支持多协议、多类型设备接入&#xff0c;包括&#xff1a;NVR、IPC、视频编码器、无人机、车载设备、智能手持终端、移动执法仪等。平台具有强大的数据接入、处理及分发能力&#xff0c;可在复杂的网络环境中&#xff0c…

【 NLP 】 句子transformer调用备忘录

一、说明 本篇对句子嵌入、文本相似性、语义搜索和图像搜索等诸方面调用实现进行记录,以便再次调用时,可以参考。

数据结构之List(双向链表)的实现

链节 链表由节点连成&#xff0c;节点的定义如下 # pragma once # include <iostream>template <typename T> struct listNode {T data;listNode <T>* pred;listNode <T>* succ;listNode(){}listNode(T d, listNode<T>* p, listNode<T>*…

Zabbix 自动发现及注册

1、依次选择 Configuratio、Discovery、Create discovery rule&#xff08;配置、自动发现、创建发现规则&#xff09; 创建客户端发现规则 2、zabbix客户端安装 agent zabbix客户端一键安装脚本 脚本参考链接 #!/bin/bash #Zabbix-Agent 5.0Zabbix_Service192.168.63.20#安…

【PHP面试题44】PHP5的版本和PHP7之间有哪些区别

文章目录 一、前言二、底层调整2.1性能提升2.2 新的引擎2.3 数据类型改进2.4 错误处理改进2.5 语言特性增加 三、应用层差异3.1 兼容性3.2 类和方法改进3.3 错误处理机制3.4 性能优化3.5 新的扩展支持 四、一些语法糖示例4.1 标量类型声明示例4.2 新增了Spaceship操作符&#x…