Meituan interviewer: Then tell me about Vuex

Recommend a practical interview question bank for everyone

1. Front-end interview question bank ( necessary for interview) recommendation: ★★★★★            

Address: front-end interview question bank

foreword

On Monday, I received an interview with Meituan. The interviewer was very nice, and they basically asked questions around the resume. The following is how I rearranged how to implement the simple version of Vuex. You can see the general principle of Vuex.

Basic use of 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状态数据的方法mutations
  mutations: {
    add(state) { 
      state.count++
    }
  },
  // 定义异步操作的方法actions
  actions: {
    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>

Knowing the usage of vuex, will you ask the following questions:

  1. Why is it necessary store.commit('add')to trigger event execution? Is it possible to directly call mutationthe function for operation?
  2. stateWhy can't the stored state be modified directly , only by calling mutationa function?
  3. Why do functions called asynchronously need store.dispatch('asyncAdd')functions to complete? Can it be called directly store.commit('asyncAdd')? If not, why?
  4. createStore()and useStore()what the hell happened?

Then let's decrypt it one by one.

Register global components in 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() It is used to install a plug-in and accepts a parameter, usually a plug-in object, which must expose a installmethod, which app.use()will be automatically executed when called install().

Parse the process in the Store class

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.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
}

reactiveAre you surprised that the underlying implementation of vuex is realized in just a few dozen lines of code? Hey, that’s because , injectand were introduced from vue, computedand a large part of the source code was omitted. dispatchHe commitis far more complicated than this

answer

Question 1: Why is it necessary store.commit('add')to trigger event execution? Is it possible to directly call mutationthe function for operation?

Answer: There is no mutation method in the store class at all, and the function list in the mutation can only be executed by calling the commit method.

Question 2: Why can't the stored state be modified directly state, only by calling a function?

Answer: Vuex ensures state traceability by enforcing restrictions on how the store can be modified. Only through the mutation function can the state in the store be modified, which can easily track state changes, and can also avoid unintentionally directly modifying the store from different components, which makes the code difficult to maintain and debug.

Question 3: Why do functions called asynchronously need store.dispatch('asyncAdd')functions to complete? Can it be called directly store.commit('asyncAdd')? If not, why?

Answer: In fact, the dispatch method and the commit method are far more than that simple. Let me post some of the source code parts of vuex about these two methods

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); }))
    : 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 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'
    );
  }
};

In the source code, we can see that the dispatch method returns a Promise object, while the commit method does not return a value, and it completely performs the operation of synchronous code. Although the return value may not be applicable to many scenarios, the main purpose of this design is to ensure the traceability of the state

Question 4: What exactly happened to createStore()and ?useStore()

When we call the createStore()function, the constructor will receive an object containing state,, mutationsand function , and then save them into the instance attribute. At this time, the value of the medium will be converted to a responding object. At the same time, it traverses all the functions , encapsulates them into attributes and saved it into instance attributes actions. Executive will register the current Store instance into the Vue.js application. You only need to call the Store instance of global state management, and you can rely on and realize the global sharing . getters optionsstategetterscomputedgettersuseStore()injectprovide

Summarize

By implementing simple vuex and completing the basic functions, I have a different understanding of vuex, but in terms of the source code of vuex, the demo above simply sorts out the dependencies, and also: does not have modular functions, does not provide plug-in functions, does not provide strict mode, does not provide auxiliary functions and implements singleton mode, etc.

Recommend a practical interview question bank for everyone

1. Front-end interview question bank ( necessary for interview) recommendation: ★★★★★            

Address: front-end interview question bank

Guess you like

Origin blog.csdn.net/weixin_42981560/article/details/131813044