本文最初发布于 austingil.com 网站,经原作者授权由 InfoQ 中文站翻译并分享。
在构建 Vue.js 应用程序时,当这些应用程序的规模扩张到一定程度后,你就可能会遇到管理全局状态的需求了。还好,他们的核心开发团队提供了 Vuex 这个便利的工具,这是 Vue.js 应用程序状态管理库的事实标准。
要入门这个库非常简单,所以我假设你已经熟悉了 Vuex 的实现。这篇文章并不是一篇入门教程。如果你需要从头开始了解的话,那么我建议你查阅文档:
https://vuex.vuejs.org/
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
user: null
},
mutations: {
setUser (state, user) {
state.user = user
}
},
})
<template>
<div>
<p v-if="user">Hi {{ user.name }}, welcome back!</p>
<p v-else>You should probably log in.</p>
</div>
</template>
<script>
export default {
computed {
user() {
return this.$store.state.user
}
}
}
</script>
这样一来,在应用加载时,如果用户已登录,则应用会向用户显示一条欢迎消息。否则,它将告诉用户他们需要登录。我知道这是一个简单的示例,不过你可能也遇到过类似的情况。你可能也会像我一样产生这样的疑问:
如何在我的应用加载之前将数据添加到存储中?
还好,我们倒是有一些方法可选。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
user: { name: "Austin" }
},
mutations: {
setUser (user) {
state.user = user
}
}
})
显然,这只有在你提前知道关于用户的详细信息时才能用。可是在构建应用程序时,我们可能不知道用户会起什么名字,但是还有另一种选择。
我们可以利用 localStorage 保留用户信息的一个副本。当他们登录时,你可以在 localStorage 中设置详细信息;而当他们登出时,你可以从 localStorage 中删除详细信息。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
user: localStorage.get('user')
},
mutations: {
setUser (user) {
state.user = user
}
}
})
如果你使用的数据不需要严格的安全限制,那么这种方法的效果会很好。我建议使用 vuex-persistedstate 库来帮助自动化这里的操作。请记住,切勿将诸如身份验证令牌之类的非常敏感的数据存储在 localStorage 中,因为 XSS 攻击可能会将其作为目标。
https://codeburst.io/web-storage-and-xss-attacks-4f83b0d08725
因此,我们的示例适合存储用户名,但不适用于身份令牌。这些敏感数据应该只存储在内存中(仍然可以选择 Vuex,只是不能持久保存)。
<template>
<div>
<p v-if="user">Hi {{ user.name }}, welcome back!</p>
<p v-else>You should probably log in.</p>
</div>
</template>
<script>
export default {
computed {
user() {
return this.$store.state.user
}
},
async mounted() {
const user = await getUser() // Assume getUser returns a user object with a name property
this.$store.commit('setUser', user)
}
}
</script>
<template>
<div>
<p v-if="loading">Loading...</p>
<p v-else-if="user">Hi {{ user.name }}, welcome back!</p>
<p v-else>You should probably log in.</p>
</div>
</template>
<script>
export default {
data: () => ({
loading: false
}),
computed {
user() {
return this.$store.state.user
}
},
async mounted() {
this.loading = true
const user = await fetch('/user').then(r => r.json()) // Assume getUser returns a user object with a name property
this.$store.commit('setUser', user)
this.loading = false
}
}
</script>
请记住,这是一个非常简单的示例。在你自己的项目里,你可能会有用于加载动画的专用组件,并且可能有一个<router-view>
组件来代替此处的用户消息。你也可以选择通过一个 Vuex 动作来发出这个 HTTP 请求。不过基本概念都是一样的。
本文的最后一个示例是发出与上一个示例中类似的 HTTP 请求,但是在应用程序加载 之前,就等待请求返回并更新存储。
请记住,Vuex 存储只是具有某些属性和方法的一个对象,所以可以将它和其他任何 JavaScript 对象一样来看待。
import Vue from "vue"
import store from "./store"
import App from "./App.vue"
fetch('/user')
.then(r => r.json())
.then((user) => {
store.commit('setUser', user)
new Vue({
store,
render: (h) => h(App),
}).$mount("#app")
})
.catch((error) => {
// Don't forget to handle this
})
这种方法的好处是,在应用程序加载之前,就会将需要从 API 获取的所有数据都预加载到全局存储中。这是避免前面提到的问题跳转或管理某些加载逻辑的便捷方法。
然而……
这里有一个警告需要注意。的确,你不必担心在 HTTP 请求挂起时应该显示什么加载提示,但与此同时,你的应用不会显示任何内容。如果你的应用程序是单页应用程序,则你的用户可能会一直盯着空白的页面,直到请求返回为止。
因此,你并没有真正解决延迟问题,只是选择了在用户等待数据时要显示哪种 UI 体验。
哪种方法更好?我没有什么确切的结论。实际上,你可以同时使用这三种方法,具体取决于你要获取的数据类型以及应用程序的需求。
我还应该提一下,我的示例是在做 fetch 请求,然后使用 Vuex 突变直接提交到存储。而你可以轻松地使用 Vuex 动作来实现 fetch。你还可以将这些原理应用于其他任何状态管理工具,例如 Vue.observable。
延伸阅读
https://austingil.com/3-ways-prepopulate-vue-js-global-stores-state
文章评论