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.
- First of all, import
vuex
andvue
. - Directly in the
vue
Registered on thevuex
From here we can knowvuex
Can beVue. use
Method use,vuex
There must be one in thereinstall
Methods. - Just execute it
new Vuex.store
From here we can knowvuex
providesStore
Property, which is aclass
That can benew
The 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 first
getters
Object. - in
Store
It’s defined on the instancegetters
. - Traverse object
key
Through theObject.defineProperty
In their ownthis.getters
Defined on theget
Listen, will itselfstate
The configuration object is executed as a parametergetters
Callback 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)