用简短的60行代码实现mini-vuex

前言

60 行实现了部分功能,并没有全部实现,但是代码非常简单

功能包括:

  • State
  • Mutations
  • dispatch 【注:vuex中是操作异步,我这里并没有这样写,目的是实现功能】

如图:

mini-vuex 工作原理

  1. 初始化 Store:首先,在 Vue 应用中初始化 Vuex 的 Store。这个 Store 包含了应用的所有状态、操作。
  2. 组件中访问状态 :在 Vue 组件中,通过 this.$store 访问到 Vuex 的 Store 对象。这使得组件能够直接从 Store 中读取状态。
  3. 提交 Mutation 修改状态:当组件需要修改状态时,它会通过提交 Mutation 的方式来请求状态的修改。Mutation 是同步操作,它们包含了状态的修改逻辑。
  4. 状态变更:当一个 Mutation 被提交后,它会修改 Store 中的状态。这个状态的修改是响应式的,所以任何依赖于这个状态的组件都会立即更新。

实现mini-vuex

清楚了工作原理这些我们现在分步来实现 mini-vuex

一、初始化 Store

想要使用 Vue.use() 那就必须得实现一个 install 那么我们来实现这一段,代码如下:

js 复制代码
let Vue

export Class Store {
}

function install(_Vue) {
  Vue = _Vue;
  Vue.mixin({
    beforeCreate: vuexInit,
  });
  function vuexInit() {
    const options = this.$options;
    if (options.store) {
      this.$store =
        typeof options.store === "function" ? options.store() : options.store;
    } else if (options.parent && options.parent.$store) {
      this.$store = options.parent.$store;
    }
  }
}

export default {
  Store,
  install,
};

vuexInit 这个方法的主要目的是在 Vue 组件中初始化 Vuex store。

在 Vue 组件的选项对象中,如果存在 store 属性,那么这个方法会检查 store 属性的类型。如果 store 是一个函数,那么它会调用这个函数并将返回的结果赋值给 this.$store。如果 store 不是一个函数,那么它会直接将 store 的值赋给 this.$store

如果在 Vue 组件的选项对象中没有 store 属性,但是存在 parent 属性,并且 parent 对象有 $store 属性,那么这个方法会将 parent$store 属性的值赋给 this.$store。使得在组件中可以通过 this.$store 访问到 Vuex store。

二、组件中访问状态

简单理解就是 我想通过 this.$store.state.xxx 访问到某个具体属性,上述代码中,我们是并没有对 Store 这个类进行赋值的,那么接下来,我们就实现这个功能

js 复制代码
let Vue;

export class Store {
  constructor({ state = {}, mutations = {} } = {}) {
    // 使用一个 Vue 实例来存储状态树
    this._vm = new Vue({
      data: state,
    });
  }
  
  get state() {
    return this._vm._data;
  }
}

我们在外部存储了一份 Vue ,在我们 install 的时候 Vue = _Vue 完成存储,所以我们在 constructor 中定义了一个存储状态树将 state 保存在Vue上,实现了响应式,通过我们的 get关键字 ,当你访问 this.state 会执行这个 get state() 返回 this._vm._data,即 Vue 实例的数据对象。这样,通过这个 getter 函数,你可以直接访问 Vue 实例的数据对象,而无需直接访问 Vue 实例的 _data 属性

三、提交 Mutation 修改状态 和 状态变更

我们实现 this.$store.dispatch("事件名","值") ,通过他来改变和更新我们的状态

js 复制代码
let Vue;

export class Store {
  constructor({ state = {}, mutations = {} } = {}) {
    // 将 dispatch 绑定到自身
    const dispatch = this.dispatch;
    this.dispatch = (...args) => {
      dispatch.apply(this, args);
    };
    // 使用一个 Vue 实例来存储状态树
    this._vm = new Vue({
      data: state,
    });

    this._mutations = Object.create(null); // 用于存储可调用的 mutation 函数
    this._setupMutations(mutations); // 初始化 mutations
  }
  
  get state() {
    return this._vm._data;
  }

  _setupMutations(mutations) {
    this._mutations = Array.isArray(mutations)
      ? mergeObjects(mutations, true)
      : mutations;
  }
  
  // 提交 Mutation 修改状态 状态变更
  dispatch(type, ...payload) {
    const mutation = this._mutations[type];
    const state = this.state;
    if (!mutation) {
      throw new Error(`[vuex] Unknown action type: ${type}`);
    }
    mutation(state, ...payload);
  }
}

首先将当前对象的 dispatch 方法引用赋值给 dispatch 变量。然后,将 this.dispatch 重新定义为一个新的函数,这个新的函数接收任意数量的参数(由 ...args 表示),并调用 dispatch 函数,同时保证 dispatch 函数内部的 this 上下文仍然指向当前对象。

然后实现我们的 dispatch ,我们将取出对应的 mutation 函数,将 state 传入 mutation 于是 我们可以看这一段

js 复制代码
const mutations = {
  ADD_TODO: (state, todo) => {
    state.todos.push(todo);
  },
};

如上面代码,我们会在这里 (state,payload)=>{} 访问到值并调用,state 指向的就是响应式,后面就是你要传递的值

小结

到这里我们的 mini-vuex 就完成了,感兴趣的朋友可以去实现一个 todo 案例,那么我们总结一下几点,可能面试能说的:

  • Vuex 通过混入的方式实现挂载 this.$store
  • Vuex 内部保存了一份Vue,通过 new Vue 实现数据响应式 所以变更状态,才可以更改视图
相关推荐
java_nnnn3 分钟前
面试题(网络部分前传)-13问
服务器·网络·网络协议·tcp/ip·面试
Setsuna_F_Seiei7 小时前
前端转型 Agent 开发 04 之 MCP 与 Skill(赋予 Agent 更广工作能力)
前端·agent
刘发财8 小时前
前端2秒生成500页矢量PDF,rust真的强到没朋友
前端·javascript·rust
找方案8 小时前
AI+人力资源:AI招聘面试的兴起与争议
人工智能·面试·职场和发展
郑州光合科技余经理9 小时前
国际版外卖系统:税率字段怎么和订单主流程解耦
android·java·开发语言·前端·后端·php·ai编程
梦想平凡9 小时前
百游棋牌源代码开发搭建教程(十):隔离部署、备份恢复与双端验收
java·前端·javascript·数据库·源代码管理
yume_sibai10 小时前
06-Rust Web 开发实战(Axum 框架 + 数据库 + JWT 认证 + 中间件 + 部署)
前端·数据库·rust
计算机魔术师12 小时前
特朗普上台打给黄仁勋:AI末日论是骗局,我们绝不让它发生
前端
troy12812 小时前
Python 基础语法(八):Web 后端开发、数据分析与可视化、网络爬虫、人工智能 / 大模型应用
前端·python·数据分析
计算机魔术师12 小时前
CEO说要慢下来,黑客说别做梦了——同一篇论文,两种命运
前端