2024年美团面试官:那你讲一讲Vuex吧,2024年最新蚂蚁金服Web前端架构面试题

紧跟潮流

大前端和全栈是以后前端的一个趋势,懂后端的前端,懂各端的前端更加具有竞争力,以后可以往这个方向靠拢。

这边整理了一个对标“阿里 50W”年薪企业高级前端工程师成长路线,由于图片太大仅展示一小部分

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

    this.$options = options;

    // 使用 Vue.js 的 reactive API 将 state 数据转换为响应式对象,并保存到实例属性 _state 中        
    this._state = reactive({
        data: options.state()
    })

    // 将 mutations 和 actions 函数保存到实例属性中
    this._mutations = options.mutations
    this._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内定义的函数并返回函数执行结果
// 简略版dispatch
dispatch = (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里引入了`reactive`、`inject`和`computed`,并且对很大一部分的源码进行了省略,`dispatch`和`commit`远比这复杂多了


## 解答


问题一:为什么要`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 dispatch
var 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); }))
: entry0;

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


## 总结


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


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


#### **前端面试题库 (**面试必备)****推荐:★★★★★****


地址:[前端面试题库](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)






# 学习分享,共勉

题外话,毕竟我工作多年,深知技术改革和创新的方向,Flutter作为跨平台开发技术、Flutter以其美观、快速、高效、开放等优势迅速俘获人心

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**

![](https://img-blog.csdnimg.cn/img_convert/482e072588ea2085e95e290b1275b18c.webp?x-oss-process=image/format,png)

分享,共勉

题外话,毕竟我工作多年,深知技术改革和创新的方向,Flutter作为跨平台开发技术、Flutter以其美观、快速、高效、开放等优势迅速俘获人心

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**

[外链图片转存中...(img-amvQa8lH-1715765708888)]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值