This article will show you how to manually implement a mini version of Vuex, which we callKStore
)
What we want to do with KStore:
- state
- commit
- dispatch
- getters
Environment set up
The main thing to do in this step is not to use the official VUex library, but to use our own.
-
Create a project
vue create myapp npm run serve Copy the code
-
Create the SRC /KStore/index.js and SRC /KStore/vuex.js files
/ / SRC/KStore/index. Js file import Vue from "vue"; import Vuex from "./vuex"; Vue.use(Vuex); export default new Vuex.Store({ state: { counter: 1,},getters: { doubleCounter(state) { return state.counter * 2; }},mutations: { add(state) { state.counter += 1; }},actions: { add({ commit }) { commit("add"); }},modules: {},});/ / SRC/KStore/vuex js file class Store { // 2. Implement state // 3. Implement commit // 4. Implement dispatch // implement getters } function install() { 1. Implement the install method } export default { Store, install, }; Copy the code
-
Modify the main.js file
import store from "./store"; / / changed to import store from "./KStore"; Copy the code
-
Add the use of vuex in home.vue for our test
<template> <div class="home"> <p style="font-size: 30px">State: {{$store.state.counter}}<br />Getters: {{$store. Getters. DoubleCounter}}<br /> <button @click="$store.commit('add')">Mutation +</button> <button @click="$store.dispatch('add')">Actions +</button> </p> </div> </template> Copy the code
-
Look at the browser, the error is ok, then we implement our vuex
The install method
Install method, is a necessary method for each plug-in is also the format, mainly for vue prototype extension method, we this is no exception.
The code looks like this:
let Vue;
class Store {}
function install(_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate() {
// Check if because we only passed store in the root instance, every component will execute if if
if (this.$options.store) {
Vue.prototype.$store = this.$options.store; }}}); }export default {
Store,
install,
};
Copy the code
If you look at the browser at this point, $Store is already accessible.
state
usage
this.$store.state.xxx
Copy the code
Implementation approach
$options: new Vue(); commit: new Vue(); commit: new Vue();
In the previous article: Writing the Mini version of Vue-Router, we used a responsive approach. Today we are going to use a new vue () approach. Hahaha!!
Matters needing attention
1. Why is $$state used here?
If a variable in data starts with $$, then Vue will not delegate that variable. This mechanism is internal to Vue. Remember that.
Code implementation
let Vue;
class Store {
constructor(options) {
this.$options = options;
this._vm = new Vue({
data() {
return {
$$state: options.state, }; }}); }/ / implementation of the state
get state() {
return this._vm._data.$$state;
}
set state(val) {
console.log("State value cannot be changed");
return; }}function install(_Vue) {... }export default {
Store,
install,
};
Copy the code
Comment out the reference to getters in the home.vue file, otherwise you will not see the effect and will report an error!!
commit
usage
this.$store.commit("add".1);
Copy the code
Implementation approach
First, we determined to execute the mutation[event] method based on the parameters passed in the COMMIT method, and then passed this. State into FN as a parameter. This is the process of realizing the commit.
Code implementation
let Vue;
class Store {
constructor(options) {
this.$options = options;
this._vm = new Vue({
data() {
return {
$$state: options.state, }; }}); }/ / implementation of the state
get state() {
return this._vm._data.$$state;
}
set state(val) {
console.log("You can only change the value of state by committing");
return;
}
// Implement commit
commit(event, payload) {
const fn = this.$options.mutations[event]; // Find the corresponding function in mutations
if(! fn) {console.error("No such mutation method");
return;
} // Check whether this method exists
fn(this.state, payload); // Execute this method by passing in the value of state}}function install(_Vue) {... }export default {
Store,
install,
};
Copy the code
-
Results demonstrate
Click the mutation button, and the value is incremented by one. So, perfect implementation commit, is not easy!
dispatch
usage
// Declared in actions
{
actions: {add({commit},payload){
commit("add"); }}}// called in the page
this.$store.dispatch("add".1);
Copy the code
Implementation approach
The dispatch implementation works the same way as the COMMIT implementation, passing in the event parameter to determine which function to execute from actions.
Matters needing attention
Commit is called in dispatch, so we need to pay attention to the execution context of the commit, which in plain English refers to this
Code implementation
let Vue;
class Store {
constructor(options) {
this.$options = options;
this._vm = new Vue({
data() {
return {
$$state: options.state, }; }}); }get state() {
return this._vm._data.$$state;
}
set state(val) {
console.log("You can only change the value of state by committing");
return;
}
commit(event, payload){... }// Implement the dispatch method
dispatch(event, payload) {
const fn = this.$options.actions[event];// Find the function in the corresponding actions
if(! fn) {console.error("No such mutation method");
return;
}
fn(this, payload); }}function install(_Vue) {... }export default {
Store,
install,
};
Copy the code
If the above code is implemented, the following error will be reported:
The solution, of course, is to specify this via call, apply, and bind, where bind is more appropriate.
let Vue;
class Store {
constructor(options) {
this.$options = options;
this._vm = new Vue({
data() {
return {
$$state: options.state, }; }});this.commit = this.commit.bind(this);// Important code, bind this to.}... }function install(_Vue) {... }export default {
Store,
install,
};
Copy the code
-
Results demonstrate
getters
usage
this.$store.getters.xxx
Copy the code
Implementation approach
Getters is a read-only property, and we can use closures to hold references to functions and set computed at the same time
Code implementation
let Vue;
class Store {
constructor(options) {
this.$options = options;
// Explicitly bind this to dispatch (call commit in dispatch,commit is undefined)
this.commit = this.commit.bind(this);
/ / implementation getters
this.getters = {};
let computed = {};
var store = this;
Object.keys(this.$options.getters).forEach((key) = > {
const fn = this.$options.getters[key];
computed[key] = function () {
return fn(store.state);
};
Object.defineProperty(this.getters, key, {
get() {
return store._vm[key];// Computed values are proxied to instances, so access _vm directly}}); });this._vm = new Vue({
data() {
return {
$$state: options.state,
};
},
computed,
});
}
get state() {
return this._vm._data.$$state;
}
set state(val) {
console.log("You can only change the value of state by committing");
return;
}
commit(event, payload){... }dispatch(event, payload){... }}function install(_Vue) {... }export default {
Store,
install,
};
Copy the code
conclusion
In the last article, I wrote the implementation of a mini version of VUE-Router, and in this article, I implemented a mini version of Vuex, which is actually for the convenience of future review. It is easy to write a detailed point, to share with you, is not very good at writing articles, hope forgive me!
- Finally paste the complete code (can run directly)
let Vue;
class Store {
constructor(options) {
// Receives the parameters passed by the user
this.$options = options;
// Explicitly bind this to dispatch (call commit in dispatch,commit is undefined)
this.commit = this.commit.bind(this);
/ / implementation getters
this.getters = {};
let computed = {};
var store = this;
Object.keys(this.$options.getters).forEach((key) = > {
const fn = this.$options.getters[key];
computed[key] = function () {
return fn(store.state);
};
Object.defineProperty(this.getters, key, {
get() {
returnstore._vm[key]; }}); });// Reactive processing
this._vm = new Vue({
data() {
return {
$$state: options.state,
};
},
computed,
});
}
/ / implementation of the state
get state() {
return this._vm._data.$$state;
}
set state(val) {
console.log("You can only change the value of state by committing");
return;
}
/ / the commit
commit(event, payload) {
const fn = this.$options.mutations[event]; // Find the corresponding function in mutations
if(! fn) {console.error("No such mutation method");
return;
} // Check whether this method exists
fn(this.state, payload); // Execute this method by passing in the value of state
}
/ / dispatch
dispatch(event, payload) {
const fn = this.$options.actions[event];
if(! fn) {console.error("No such mutation method");
return;
}
fn(this, payload); }}function install(_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate() {
// If we pass store only in the root instance
if (this.$options.store) {
Vue.prototype.$store = this.$options.store; }}}); }export default {
Store,
install,
};
Copy the code