Автор: Инь Жунхуэй@Tencent
эта статьяадрес, добро пожаловать на просмотр
эта статьяадрес кода репозитория github, добро пожаловать звезда, спасибо.
Если вы заинтересованы в самостоятельной реализации каждого фреймворка с небольшим объемом кода, вот некоторые вещи, на которые вы можете обратить внимание:
содержание:
1. Простейший способ определения глобальных переменных через vuex, который можно использовать напрямую на любой странице через this.$store.state.count
2. Реализация метода геттера в vuex
3. Реализация методов мутации и фиксации
4. Реализация действий и способов отправки
5. Реализация модульного метода
6. Реализация: Vue.use (Vuex)
Давайте сначала посмотрим на эффект замены реального vuex на vuex, реализованный вами, и посмотрим, может ли он нормально работать и есть ли ошибка:
Судя по текущим результатам, работа нормальная и проблем нет. Рассмотрим пошаговый процесс внедрения:
1. Простейший способ определения глобальных переменных через vuex, который можно использовать напрямую на любой странице через this.$store.state.count
Код main.js выглядит следующим образом:
let store = new Vuex.Store({
state: {
count: 0
}
}, Vue);
new Vue({
store,
render: h => h(App),
}).$mount('#app')
Код store.js выглядит следующим образом:
export class Store {
constructor(options = {}, Vue) {
this.options = options;
Vue.mixin({ beforeCreate: vuexInit });
}
get state () {
return this.options.state;
}
}
function vuexInit () {
const options = this.$options
if (options.store) {
// 组件内部设定了store,则优先使用组件内部的store
this.$store = typeof options.store === 'function'
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
// 组件内部没有设定store,则从根App.vue下继承$store方法
this.$store = options.parent.$store
}
}
Код интерфейса выглядит следующим образом:
<script>
export default {
name: 'app',
created() {
console.log('打印出this.$store.state.count的结果',this.$store.state.count);
},
}
</script>
Результат выполнения: значение this.$store.state.count успешно напечатано как 0.
2. Реализация метода геттера в vuex
Код main.js выглядит следующим образом:let store = new Vuex.Store({
state: {
count: 0
},
getters: {
getStatePlusOne(state) {
return state.count + 1
}
}
}, Vue);
new Vue({
store,
render: h => h(App),
}).$mount('#app')
Код store.js выглядит следующим образом:
export class Store {
constructor(options = {}, Vue) {
this.options = options;
this.getters = {}
Vue.mixin({ beforeCreate: vuexInit });
forEachValue(options.getters, (getterFn, getterName) => {
registerGetter(this, getterName, getterFn);
})
}
get state() {
return this.options.state;
}
}
function registerGetter(store, getterName, getterFn) {
Object.defineProperty(store.getters, getterName, {
get: () => {
return getterFn(store.state)
}
})
}
// 将对象中的每一个值放入到传入的函数中作为参数执行
function forEachValue(obj, fn) {
Object.keys(obj).forEach(key => fn(obj[key], key));
}
function vuexInit() {
const options = this.$options
if (options.store) {
// 组件内部设定了store,则优先使用组件内部的store
this.$store = typeof options.store === 'function' ?
options.store() :
options.store
} else if (options.parent && options.parent.$store) {
// 组件内部没有设定store,则从根App.vue下继承$store方法
this.$store = options.parent.$store
}
}
Код интерфейса выглядит следующим образом:
<script>
export default {
name: 'app',
created() {
console.log('打印出this.$store.getters.getStatePlusOne的结果',this.$store.getters.getStatePlusOne);
},
}
</script>
результат операции: Значение this.$store.getters.getStatePlusOne успешно напечатано как 1.
3. Реализация методов мутации и фиксации
Код main.js выглядит следующим образом:let store = new Vuex.Store({
state: {
count: 0
},
mutations: {
incrementFive(state) {
// console.log('初始state', JSON.stringify(state));
state.count = state.count + 5;
}
},
getters: {
getStatePlusOne(state) {
return state.count + 1
}
}
}, Vue);
Код store.js выглядит следующим образом:
export class Store {
constructor(options = {}, Vue) {
Vue.mixin({ beforeCreate: vuexInit })
this.options = options;
this.getters = {};
this.mutations = {};
const { commit } = this;
this.commit = (type) => {
return commit.call(this, type);
}
forEachValue(options.getters, (getterFn, getterName) => {
registerGetter(this, getterName, getterFn);
});
forEachValue(options.mutations, (mutationFn, mutationName) => {
registerMutation(this, mutationName, mutationFn)
});
this._vm = new Vue({
data: {
state: options.state
}
});
}
get state() {
// return this.options.state; // 无法完成页面中的双向绑定,所以改用this._vm的形式
return this._vm._data.state;
}
commit(type) {
this.mutations[type]();
}
}
function registerMutation(store, mutationName, mutationFn) {
store.mutations[mutationName] = () => {
mutationFn.call(store, store.state);
}
}
function registerGetter(store, getterName, getterFn) {
Object.defineProperty(store.getters, getterName, {
get: () => {
return getterFn(store.state)
}
})
}
// 将对象中的每一个值放入到传入的函数中作为参数执行
function forEachValue(obj, fn) {
Object.keys(obj).forEach(key => fn(obj[key], key));
}
function vuexInit() {
const options = this.$options
if (options.store) {
// 组件内部设定了store,则优先使用组件内部的store
this.$store = typeof options.store === 'function' ?
options.store() :
options.store
} else if (options.parent && options.parent.$store) {
// 组件内部没有设定store,则从根App.vue下继承$store方法
this.$store = options.parent.$store
}
}
Код интерфейса выглядит следующим образом:
<script>
export default {
name: 'app',
created() {
console.log('打印出this.$store.getters.getStatePlusOne的结果',this.$store.getters.getStatePlusOne);
},
mounted() {
setTimeout(() => {
this.$store.commit('incrementFive');
console.log('store state自增5后的结果', this.$store.state.count);
}, 2000);
},
computed: {
count() {
return this.$store.state.count;
}
}
}
</script>
Результат выполнения: успешно вывести результат 5 после того, как счетчик увеличится на 5 через 2 секунды.
4. Реализация действий и способов отправки
Код main.js выглядит следующим образом:let store = new Vuex.Store({
state: {
count: 0
},
actions: {
countPlusSix(context) {
context.commit('plusSix');
}
},
mutations: {
incrementFive(state) {
// console.log('初始state', JSON.stringify(state));
state.count = state.count + 5;
},
plusSix(state) {
state.count = state.count + 6;
}
},
getters: {
getStatePlusOne(state) {
return state.count + 1
}
}
}, Vue);
Код store.js выглядит следующим образом:
export class Store {
constructor(options = {}, Vue) {
Vue.mixin({ beforeCreate: vuexInit })
this.options = options;
this.getters = {};
this.mutations = {};
this.actions = {};
const { dispatch, commit } = this;
this.commit = (type) => {
return commit.call(this, type);
}
this.dispatch = (type) => {
return dispatch.call(this, type);
}
forEachValue(options.actions, (actionFn, actionName) => {
registerAction(this, actionName, actionFn);
});
forEachValue(options.getters, (getterFn, getterName) => {
registerGetter(this, getterName, getterFn);
});
forEachValue(options.mutations, (mutationFn, mutationName) => {
registerMutation(this, mutationName, mutationFn)
});
this._vm = new Vue({
data: {
state: options.state
}
});
}
get state() {
// return this.options.state; // 无法完成页面中的双向绑定,所以改用this._vm的形式
return this._vm._data.state;
}
commit(type) {
this.mutations[type]();
}
dispatch(type) {
return this.actions[type]();
}
}
function registerMutation(store, mutationName, mutationFn) {
store.mutations[mutationName] = () => {
mutationFn.call(store, store.state);
}
}
function registerAction(store, actionName, actionFn) {
store.actions[actionName] = () => {
actionFn.call(store, store)
}
}
function registerGetter(store, getterName, getterFn) {
Object.defineProperty(store.getters, getterName, {
get: () => {
return getterFn(store.state)
}
})
}
// 将对象中的每一个值放入到传入的函数中作为参数执行
function forEachValue(obj, fn) {
Object.keys(obj).forEach(key => fn(obj[key], key));
}
function vuexInit() {
const options = this.$options
if (options.store) {
// 组件内部设定了store,则优先使用组件内部的store
this.$store = typeof options.store === 'function' ?
options.store() :
options.store
} else if (options.parent && options.parent.$store) {
// 组件内部没有设定store,则从根App.vue下继承$store方法
this.$store = options.parent.$store
}
}
Код интерфейса выглядит следующим образом:
export default {
name: 'app',
created() {
console.log('打印出this.$store.getters.getStatePlusOne的结果',this.$store.getters.getStatePlusOne);
},
mounted() {
setTimeout(() => {
this.$store.commit('incrementFive');
console.log('store state自增5后的结果', this.$store.state.count);
}, 2000);
setTimeout(() => {
this.$store.dispatch('countPlusSix');
console.log('store dispatch自增6后的结果', this.$store.state.count);
}, 3000);
},
computed: {
count() {
return this.$store.state.count;
}
}
}
Результат выполнения: через 3 секунды дипатч увеличивается на 6 и выводит 11.
5. Реализация модульного метода
Код main.js выглядит следующим образом:const pageA = {
state: {
count: 100
},
mutations: {
incrementA(state) {
state.count++;
}
},
actions: {
incrementAAction(context) {
context.commit('incrementA');
}
}
}
let store = new Vuex.Store({
modules: {
a: pageA
},
state: {
count: 0
},
actions: {
countPlusSix(context) {
context.commit('plusSix');
}
},
mutations: {
incrementFive(state) {
// console.log('初始state', JSON.stringify(state));
state.count = state.count + 5;
},
plusSix(state) {
state.count = state.count + 6;
}
},
getters: {
getStatePlusOne(state) {
return state.count + 1
}
}
}, Vue);
Код store.js выглядит следующим образом:
let _Vue;
export class Store {
constructor(options = {}, Vue) {
_Vue = Vue
Vue.mixin({ beforeCreate: vuexInit })
this.getters = {};
this._mutations = {}; // 在私有属性前加_
this._wrappedGetters = {};
this._actions = {};
this._modules = new ModuleCollection(options)
const { dispatch, commit } = this;
this.commit = (type) => {
return commit.call(this, type);
}
this.dispatch = (type) => {
return dispatch.call(this, type);
}
const state = options.state;
const path = []; // 初始路径给根路径为空
installModule(this, state, path, this._modules.root);
this._vm = new Vue({
data: {
state: state
}
});
}
get state() {
// return this.options.state; // 无法完成页面中的双向绑定,所以改用this._vm的形式
return this._vm._data.state;
}
commit(type) {
this._mutations[type].forEach(handler => handler());
}
dispatch(type) {
return this._actions[type][0]();
}
}
class ModuleCollection {
constructor(rawRootModule) {
this.register([], rawRootModule)
}
register(path, rawModule) {
const newModule = {
_children: {},
_rawModule: rawModule,
state: rawModule.state
}
if (path.length === 0) {
this.root = newModule;
} else {
const parent = path.slice(0, -1).reduce((module, key) => {
return module._children(key);
}, this.root);
parent._children[path[path.length - 1]] = newModule;
}
if (rawModule.modules) {
forEachValue(rawModule.modules, (rawChildModule, key) => {
this.register(path.concat(key), rawChildModule);
})
}
}
}
function installModule(store, rootState, path, module) {
if (path.length > 0) {
const parentState = rootState;
const moduleName = path[path.length - 1];
_Vue.set(parentState, moduleName, module.state)
}
const context = {
dispatch: store.dispatch,
commit: store.commit,
}
const local = Object.defineProperties(context, {
getters: {
get: () => store.getters
},
state: {
get: () => {
let state = store.state;
return path.length ? path.reduce((state, key) => state[key], state) : state
}
}
})
if (module._rawModule.actions) {
forEachValue(module._rawModule.actions, (actionFn, actionName) => {
registerAction(store, actionName, actionFn, local);
});
}
if (module._rawModule.getters) {
forEachValue(module._rawModule.getters, (getterFn, getterName) => {
registerGetter(store, getterName, getterFn, local);
});
}
if (module._rawModule.mutations) {
forEachValue(module._rawModule.mutations, (mutationFn, mutationName) => {
registerMutation(store, mutationName, mutationFn, local)
});
}
forEachValue(module._children, (child, key) => {
installModule(store, rootState, path.concat(key), child)
})
}
function registerMutation(store, mutationName, mutationFn, local) {
const entry = store._mutations[mutationName] || (store._mutations[mutationName] = []);
entry.push(() => {
mutationFn.call(store, local.state);
});
}
function registerAction(store, actionName, actionFn, local) {
const entry = store._actions[actionName] || (store._actions[actionName] = [])
entry.push(() => {
return actionFn.call(store, {
commit: local.commit,
state: local.state,
})
});
}
function registerGetter(store, getterName, getterFn, local) {
Object.defineProperty(store.getters, getterName, {
get: () => {
return getterFn(
local.state,
local.getters,
store.state
)
}
})
}
// 将对象中的每一个值放入到传入的函数中作为参数执行
function forEachValue(obj, fn) {
Object.keys(obj).forEach(key => fn(obj[key], key));
}
function vuexInit() {
const options = this.$options
if (options.store) {
// 组件内部设定了store,则优先使用组件内部的store
this.$store = typeof options.store === 'function' ?
options.store() :
options.store
} else if (options.parent && options.parent.$store) {
// 组件内部没有设定store,则从根App.vue下继承$store方法
this.$store = options.parent.$store
}
}
Основной код интерфейса выглядит следующим образом:
<template>
<div id="app">
==============主页================<br>
主页数量count为: {{count}}<br>
pageA数量count为: {{countA}}<br>
==========以下为PageA内容==========<br>
<page-a></page-a>
</div>
</template>
<script>
import pageA from './pageA';
export default {
name: 'app',
components: {
pageA
},
created() {
console.log('打印出this.$store.getters.getStatePlusOne的结果',this.$store.getters.getStatePlusOne);
},
mounted() {
setTimeout(() => {
this.$store.commit('incrementFive');
console.log('store state自增5后的结果', this.$store.state.count);
}, 2000);
setTimeout(() => {
this.$store.dispatch('countPlusSix');
console.log('store dispatch自增6后的结果', this.$store.state.count);
}, 3000);
},
computed: {
count() {
return this.$store.state.count;
},
countA() {
return this.$store.state.a.count;
}
}
}
</script>
Страница pageA выглядит следующим образом:
<template>
<div>
页面A被加载
</div>
</template>
<script>
export default {
name: 'pageA',
mounted() {
setTimeout(() => {
this.$store.dispatch('incrementAAction');
}, 5000)
},
}
</script>
Результат выполнения: действие incrementAAction запускается на странице A через 5 секунд, а значение countA в основном интерфейсе изменяется на 101, что является успешным.
С тех пор: было использовано около 150 строк кода для достижения примерно 80% функций vuex. Среди них нельзя использовать пространства имен. Другие в основном совпадают с синтаксисом исходного кода. Если вам интересно посмотреть поближе , вы можете двигаться шагкод репозитория на гитхабе, код написан после прочтения исходного кода vuex, поэтому, прочитав код этой статьи, а затем взглянув на код vuex, я думаю, вы поймете его с первого взгляда.
6. Реализация: Vue.use (Vuex)
Наконец, чтобы быть максимально похожим на исходный код vuex, также используется Vue.use (Vuex), а для реализации используется следующий код:export function install(_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate: function vuexInit() {
const options = this.$options;
if (options.store) {
this.$store = options.store;
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store;
}
}
})
}
Отдел набирает новых сотрудников, который является отделом корпоративных продуктов Tencent и входит в бизнес-группу CSIG. Много льгот, высокая зарплата, только и ждут вас. Если вы заинтересованы, пожалуйста, нажмите на две ссылки ниже. Lagoo.com/Jobs/521039… woohoo.products.com/job_detail/…
Использованная литература:
How does a minimal Vuex implementation look like?
Напишите свой собственный Vuex с нуля