В данный момент вы, кажется, используете store
, чтобы просто хранить значения.Но настоящая сила паттерна Flux / Store на самом деле осознается, когда вы также централизуете логику внутри магазина.Если вы рассредоточите логику, связанную с хранилищем, между компонентами по всему приложению, в конечном итоге ее будет все сложнее и сложнее поддерживать, потому что такую логику нельзя использовать повторно, и вам придется обходить дерево компонентов, чтобы достичь логики при исправлении ошибок.
На вашем месте я создам хранилище с помощью
- Определение «первичных данных», затем
- Определение «производных данных», которые могут быть получены из первичныхданные и, наконец,
- Определение «методов», которые вы можете использовать для взаимодействия с такими данными.
IMO, «первичными данными» являются user
, accounts
иselectedAccount
.И «производными данными» являются isLoggedIn
, isSelectedAccountAvailable
и hasMoreThanOneAccount
.Как компонент Vue вы можете определить его следующим образом:
import Vue from "vue";
export default new Vue({
data() {
return {
user: null,
accounts: [],
selectedAccount: null
};
},
computed: {
isLoggedIn() {
return this.user !== null;
},
isSelectedAccountAvailable() {
return this.selectedAccount !== null;
},
hasMoreThanOneAccount() {
return this.accounts.length > 0;
}
},
methods: {
login(username, password) {
console.log("performing login");
if (username === "johnsmith" && password === "password") {
console.log("committing user object to store and load associated accounts");
this.user = {
name: "John Smith",
username: "johnsmith",
email: "john.smith@somewhere.com"
};
this.loadAccounts(username);
}
},
loadAccounts(username) {
console.log("load associated accounts from backend");
if (username === "johnsmith") {
// in real code, you can perform check the size of array here
// if it's the size of one, you can set selectedAccount here
// this.selectedAccount = array[0];
console.log("committing accounts to store");
this.accounts = [
{
id: "001234",
domain: "domain001234"
},
{
id: "001235",
domain: "domain001235"
}
];
}
},
setSelectedAccount(account) {
this.selectedAccount = account;
}
}
});
Затем вы можете легко import
сохранить это хранилище в любом компоненте Vue и начать ссылаться на значения или вызывать методы из этого хранилища.
Например, предположим, что вы создаете компонент Login.vue
, и этот компонент должен перенаправиться, когда объект user
станет доступным в магазине, вы можете сделать это, выполнив следующие действия:
<template>
<div>
<input type="text" v-model="username"><br/>
<input type="password" v-model="password"><br/>
<button @click="submit">Log-in</button>
</div>
</template>
<script>
import store from '../basic-store';
export default {
data() {
return {
username: 'johnsmith',
password: 'password'
};
},
computed: {
isLoggedIn() {
return store.isLoggedIn;
},
},
watch: {
isLoggedIn(newVal) {
if (newVal) { // if computed value from store evaluates to 'true'
console.log("moving on to Home after successful login.");
this.$router.push({ name: "home" });
}
}
},
methods: {
submit() {
store.login(this.username, this.password);
}
}
};
</script>
Кроме того, с помощью isSelectedAccountAvailable
мы вычисляем, мы можем легко отключить / включить кнопку на экране, чтобы запретить пользователю выполнять вызовы API, пока не будет выбрана учетная запись:
<button :disabled="!isSelectedAccountAvailable" @click="performAction()">make api call</button>
Если выЕсли вы хотите увидеть весь проект, вы можете получить к нему доступ из этого запускаемого кода codesandbox .Обратите внимание на то, как basic-store.js
определяется и используется в Login.vue
и Home.vue
.И, если хотите, вы также можете увидеть, как определяется магазин в vuex
, взглянув на store.js
.
Удачи!
Обновлено: О том, как вы должны организовывать зависимые / связанные вызовы API, ответ на самом деле прямо перед вами.Если вы присмотритесь к магазину поближе, вы заметите, что мой метод login()
впоследствии вызывает this.loadAccounts(username)
после успешного входа в систему.Таким образом, в принципе, у вас есть все возможности для цепочки / вложенных вызовов API в методах магазина для соответствия вашим бизнес-правилам.watch()
существует просто потому, что пользовательский интерфейс должен выполнять навигацию на основе изменений, сделанных в store
.Для самых простых изменений данных будет достаточно вычисленных свойств.
Кроме того, из того, как я его спроектировал, причина watch()
, используемая в <Login>
компоненте, двойственна:
Разделение проблем: для меня, который годами работал над серверным кодом, я бы хотел, чтобы мой view
связанный код был четко отделен от model
.Ограничивая логику навигации внутри компонента, моему model
в store
вообще не нужно знать / заботиться о навигации.
Однако, даже если я этого не делаюОтдельные проблемы, все еще будет довольно трудно импортировать vue-router
в мой store
.Это потому, что мой router.js
уже импортирует basic-store.js
для выполнения защиты навигации , предотвращающей доступ неаутентифицированных пользователей к <Home>
компоненту:
router.beforeEach((to, from, next) => {
if (!store.isLoggedIn && to.name !== "login") {
console.log(`redirect to 'login' instead of '${to.name}'.`);
next({ name: "login" });
} else {
console.log(`proceed to '${to.name}' normally.`);
next();
}
});
И, потомуjavascript пока не поддерживает циклическую зависимость (например, router
import store
и store
import router
), чтобы мой код оставался ациклическим, мой магазин не может выполнять навигацию по маршруту.