大家好,我是前端宝哥。
在编程界有句老话:“命名和缓存失效是世上两大难题。” 我得说,在现代Web应用的状态管理上,这难题得排第三!
今天,咱们来深挖一下Vue的状态管理之道,并介绍一个超直观的解决方案——Pinia。
Vue状态管理:那些坑和局限性
自Vue 2起,我们用data
属性来定义组件的状态,就像这样:
<template>
<div>{{ user.name }}</div>
</template>
<script>
export default {
data() {
return { user: { name: 'John', age: 25 } };
}
};
</script>
这就是所谓的 选项 API,在Vue 3中依然可用。Vue 3还带来了 组合式 API,它用reactive
和ref
等新招数来定义状态。用这新API,我们可以这样写组件脚本:
<script setup>
import { reactive } from 'vue'
const user = reactive({ name: 'John', age: 25 });
</script>
但如果要跨组件访问状态,比如在导航栏显示用户名,在个人资料页展示详细信息,这就尴尬了。通常,我们用props
逐层传递,但层级一多,就得在每个组件里加props
,不管用不用得上。这叫prop 穿透,真不推荐。
更新共享数据时,子组件不能直接改props
,得发事件让父组件来更新,再传下去。这...感觉有点笨。
Vue 3的救星来了:组合式 API 让我们能在任何脚本里用ref
和reactive
,还能导出状态,整个应用都能用。
我们可以把这状态叫做存储 (store)。比如,创建个store/user.js
:
import { reactive } from 'vue'
const user = reactive({ name: 'John', age: 25 });
export { user };
然后在组件里这么用:
<script setup>
import { user } from './stores/user.js';
</script>
<template>
<h1>Hello, {{ user.name }}! You are {{ user.age }} years old.</h1>
</template>
看,现在状态有单一来源,组件间还能共享。
但这种模式虽简单,却不适合服务器端渲染 (SSR),因为状态只创建一次,可能导致数据泄露。而且,随着应用变大,可能需要更强大的状态管理。
Pinia:现代Vue应用的存储解决方案
Pinia 不仅支持SSR,还有Vue Devtools集成、热更新、TypeScript友好等优点。
Pinia由Vue Router的开发者Eduardo打造,现已取代Vuex,成为Vue 3官方推荐的状态管理库。
安装和设置
安装Pinia就一行命令:
npm install pinia
然后创建Pinia实例,传给Vue应用:
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
现在,创建和管理存储就这么简单。
创建存储
用defineStore
方法创建Pinia存储,第一参数是名,第二参数是配置。比如,我们改写下user
存储:
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({ name: 'John', age: 25 }),
getters: {
canVote: (state) => state.age >= 18,
},
actions: {
blowCandles() {
this.age++;
}
}
});
这就是选项存储 (Option Stores)。
喜欢组合式 API?Pinia也支持。用设置存储 (Setup Stores),用ref
、computed
定义状态和计算属性,函数返回要公开的:
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useUserStore = defineStore('user', () => {
const name = ref('John');
const age = ref(25);
const canVote = computed(() => age.value >= 18);
const blowCandles = () => age.value++;
return { name, age, canVote, blowCandles };
});
设置存储的好处包括定义观察器、使用其他组合函数、注入属性等。
使用存储
定义了Pinia存储后,就可以在组件或组合函数里导入使用了:
<script setup>
import useUserStore from './stores/user.js'
const user = useUserStore();
</script>
<template>
<button @click="user.blowCandles">
I am {{ user.name }} and it's my birthday!
</button>
</template>
通过user
对象访问状态和操作,简单直观。解构时,用storeToRefs
保持反应性。
真实例子
实际应用中,我们不会用固定值初始化存储。来看个登录示例:
import { ofetch } from 'ofetch'
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', () => {
const data = ref();
const token = ref();
const isLoggedIn = computed(() => Boolean(token.value));
async function login({ email, password }) {
const { data, token: tok } = await ofetch('https://example.com/login', {
method: 'POST',
body: { email, password }
});
data.value = data;
token.value = tok;
}
// ... logout, other methods
});
存储是封装应用逻辑的好地方。比如,登录操作请求API,保存用户数据和令牌。
然后在组件里这么用:
<script setup>
import useUserStore from './stores/user.js'
const user = useUserStore();
// ... form, error, handleSubmit
</script>
<template>
<!-- ... template code -->
</template>
Pinia不仅管理用户会话,还能跟踪其他数据,减少服务器请求,让应用更快。
Pinia也适用于Vue 2
Pinia完全兼容Vue 2,所以如果你的Vue 2应用用Vuex,迁移到Pinia是升级到Vue 3的好起点。
Pinia Vue Devtools 插件
如果你用Vue Devtools,Pinia会有个新标签让你浏览存储,检查状态,甚至导入导出JSON。
总结
状态管理可能看起来有点吓人,但一旦掌握了,就简单多了。Pinia帮你组织数据,轻松访问。开发体验棒,集成简单。想试试?去官网看看。
往期推荐
38个Vue、Nuxt 和 Vite 技巧、窍门和实践的合集
Vue 如何处理异步组件加载错误
Vue 3 将推出新特性,可以抛弃虚拟DOM了!
Vue 小技巧:何时使用可组合函数
怎么才能做出一个牛逼的Vue 组件库?
掌握插槽魔法,助你进阶 Vue 开发,赋予组件无限可能!
微软 Edge 推出 "WebUI 2.0":从 React 到 Web Components + HTML,速度提升了42%
我是前端宝哥,每日分享前端开发技术,关注下面二维码,围观我的朋友圈。
备注【文章群】可以进文章分享群,
备注【技术群】可以进技术交流群,
备注【副业群】可以进程序员副业群。
关注下方公众号加星标,送我的电子书资料
回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF
回复「Vue脑图」获取 Vue 相关脑图
回复「思维图」获取 JavaScript 相关思维图
回复「简历」获取简历制作建议
回复「简历模板」获取精选的简历模板
回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。
回复「知识点」下载高清JavaScript知识点图谱
回复「读书」下载成长的相关电子书
觉得好看,请关注我,点“在看”