preface

Unconsciously found that many articles now around the interview, I also come to linger this atmosphere.

I believe that it is far from enough to only apply VUE to the siege lion whose technology stack is VUE. The text is mainly developed by vuex, the state manager. Let’s write our own VUex by hand.

What is VUex?

Vuex is a state management mode developed specifically for vue.js applications. It uses centralized storage to manage the state of all components of an application and rules to ensure that the state changes in a predictable way. Vuex is also integrated into Vue’s official debugging tool devTools Extension (Opens New Window), providing advanced debugging functions such as zero-configuration time-travel debugging and state snapshot import and export.

1. Understand vuex πŸ€ͺ

In our project, if there is a dependency between multiple pages or components, and these pages need to share a state, the following problems arise:

  • Multiple pages share a state.
  • When one page changes its state, other pages need to follow suit.

Many friends may say that the parent component can be used to solve the problem. But when our project became too large, it was not conducive to maintaining this state, so that in the end ~~~~~ you know.

2.🀯 Use vuex for installation

Enter the project, enter the installation instruction on the command line, and press Enter

npm install vuex --save

Then create an index.js file under SRC /store/

import Vue from "vue"; import Vuex from ".. /vuex/index"; Vue.use(Vuex); export default new Vuex.Store({ state: { name: 'zxl' }, mutations: { setName(state) { state.name += '1'; }, }, actions: { setName(store){ store.commit("setName"); } }, getters: { }, })Copy the code

Import in the entry file main.js

import Vue from 'vue'; import App from './App.vue'; import store from ".. /src/store/store"; // Introduce vue.config. productionTip = false; new Vue({ store,++++++++++++++++++++ render: h => h(App), }).$mount('#app');Copy the code

Reference the state in the page

<template>
  <div class="hello">
    {{$store.state.name}}
  </div>
</template>
Copy the code

The specific application of small make up here will not do too much introduction.

Implement a VUEX manually

1.πŸ˜‡ Check the configuration file

To make writing vuex clearer, let’s take a look at the index.js file we configured above.

  1. First of all, importvuexandvue.
  2. Directly in thevueRegistered on thevuexFrom here we can knowvuexCan beVue. useMethod use,vuexThere must be one in thereinstallMethods.
  3. Just execute itnew Vuex.storeFrom here we can knowvuexprovidesStoreProperty, which is aclassThat can benewThe constructor is passed in our configuration object.

Let’s follow this step to achieve our own state manager!

2.πŸ‘Άinstall Implementation of the registration function

We create a vuex file in our own project SRC directory, which contains our own code, and then create an index.js file in the directory.

In index.js we need to export an object that contains two things: the install method and the Store class.

export default {
    Store,
    install
}
Copy the code

First we will now implement the install method.

let Vue; function install(_Vue) { Vue = _Vue; Mixin ({beforeCreate() {if (this.$options.store) {this.$store = this.$options.store; $store this.$store = this.$parent && this.$parent.$store; }}})}Copy the code

Using vue. mixin for global blending, the beforeCreate life cycle injects an object that sets this.$store on all components.

2. 😨 state implementation

Now that we’ve implemented the install method, let’s implement the core Store class.

Class Store{constructor(options){//options is the configuration object we passed in}}Copy the code

The difference between state and our global objects is that when we modify the state, we trigger a response to update the attempt. So here yudada is indeed a wise man. We know that in the component, the properties defined on data are already reactive, so can we define state on vUE data as well?

Class Store{constructor(options){this.vm = new Vue({// here a new Vue data: {state: Options. state,// define state object on data}})} get state() {return this.vm.state; }}Copy the code

3. 🀩 getters

Vuex allows us to define “getters” (you can think of them as computed properties of the store) in the store. Just like evaluating properties, the return value of a getter is cached based on its dependency and is recalculated only if its dependency value changes.

Class Store{constructor(options){constructor(options){// Let getters = options.getters; Getters = {}; // Define getters Object.keys(getters).foreach (getterName => {object.defineProperty (this.defineProperty, { get() { return getters[getterName](this.state); // Execute the callback function in getters, passing its state in}})})}}Copy the code
  • Get the configuration object firstgettersObject.
  • inStoreIt’s defined on the instancegetters.
  • Traverse objectkeyThrough theObject.definePropertyIn their ownthis.gettersDefined on thegetListen, will itselfstateThe configuration object is executed as a parametergettersCallback function.

4. πŸ€” mutations.

The only way to change the state in Vuex’s store is to commit mutation. Mutations in Vuex are very similar to events: each mutation has a string event type (type) and a callback function (handler). This callback is where we actually make the state change, and it accepts state as the first argument

Class Store{constructor(options){// Options is the configuration object we passed in. this.mutations = {}; Object.keys(mutations).forEach(mutationName => { this.mutations[mutationName] = (payload) => { mutations[mutationName](this.state, payload); } }) } commit(mutationName, payload) { this.mutations[mutationName](payload); }}Copy the code

The code logic is roughly the same, with a commit method added.

5. πŸ€– actions implementation.

The Action function accepts a context object with the same methods and properties as the store instance, so you can submit a mutation by calling context.mit. Or get state and getters via context.state and context.getters. When we cover Modules later, you’ll see why the context object is not the Store instance itself.

Class Store{constructor(options){// Options is the configuration object we passed in let actions = options.actions; this.actions = {}; Object.keys(actions).forEach((actionName) => { this.actions[actionName] = (payload) => { actions[actionName](this, payload); } }) } dispatch(actionName, payload) { this.actions[actionName](payload); }}Copy the code

Here you can see that a basic state manager works

Change the vuex import to your own file path.

6. πŸ€– modules

Because of the use of a single state tree, all the states of an application are grouped into one large object. When the application becomes very complex, the Store object can become quite bloated. To solve these problems, Vuex allows us to split the Store into modules. Each module has its own state, mutation, action, getter, and even nested submodules — split the same way from top to bottom.

Here I need to maintain such a parent-child relationship of modules.

Constructor (options){constructor(options){this.modules = new ModuleCollection(options); installModule(this, this.state, [], this.modules.root); }}Copy the code
class ModuleCollection { constructor(options) { this.register([], options); } register(path, rootModule) {let rawModule = {_RAW: rootModule, _children: {},// modules state: Rootmodule. state,// Current state} if (! This. root) {// If root module this.root = rawModule; } else { let parentModule = path.slice(0, -1).reduce((root, current) => { return root._children[current]; }, this.root); parentModule._children[path[path.length - 1]] = rawModule; } if (rootModule.modules) {// If there are children, Modules.keys (rootModule.modules).foreach (moduleName => {this.register(path.concat(moduleName)), rootModule.modules[moduleName]); })}}}Copy the code

I don’t know why nuggets failed to upload pictures to write an article today, but I wanted to show you the generated modules. You can print this.$store in your project, and there will be _modules in it. This code is used to add a module to a module, maintaining the relationship between the child and parent modules.

Modify the previous code

function installModule(store, rootState, path, RawModule) {if (path.length > 0) {// Let parentState = path.slice(0, -1).reduce((root, current) => { return rootState[current]; }, rootState) Vue.set(parentState, path[path.length - 1], rawModule.state); // Define the child module's state on the root module. } let getters = rawModule._raw.getters; If (getters) {// define getters if (! store.getters) { store.getters = {}; } Object.keys(getters).forEach(getterName => { Object.defineProperty(store.getters, getterName, { get() { return getters[getterName](rawModule.state); } }) }) } let mutations = rawModule._raw.mutations; if (mutations) { Object.keys(mutations).forEach(mutationName => { if (! store.mutations) { store.mutations = {}; } let arr = store.mutations[mutationName] || (store.mutations[mutationName] = []); arr.push((payload) => { mutations[mutationName](rawModule.state, payload); }) }) } let actions = rawModule._raw.actions; if (actions) { Object.keys(actions).forEach(actionName => { if (! store.actions) { store.actions = {}; } let arr = store.actions[actionName] || (store.actions[actionName] = []); arr.push((payload) => { actions[actionName](store, payload); }) }) } let keys = Object.keys(rawModule._children); keys.forEach(moduleName => { installModule(store, rootState, path.concat(moduleName), rawModule); })}Copy the code

This code is basically the same as what we did above, except that it defines the state of the child module to the root module via vue.set.

7. πŸ€— registerModule

RegisterModule (moduleName, module) {// Dynamically load the module if (! Array.isArray(moduleName)) { moduleName = [moduleName]; } this.modules.register(moduleName, module); installModule(this, this.state, moduleName, module); }Copy the code

Refer to the link

# Hand on hand to teach you how to use Vuex

Bytedance Interviewer: Tell me how VUex works (just a few lines of code)