Pinia(发音为/piːnjʌ/,如英语中的“peenya”)是最接近piña(西班牙语中的菠萝)的词;
Pinia开始于大概2019年,最初是作为一个实验为Vue重新设计状态管理,让它用起来像组合式API(Composition API)。 从那时到现在,最初的设计原则依然是相同的,并且目前同时兼容Vue2、Vue3,也并不要求你使用Composition API; Pinia本质上依然是一个状态管理的库,用于跨组件、页面进行状态共享(这点和Vuex、Redux一样);

1. 使用Pinia之前,我们需要先对其进行安装:
-- yarn add pinia
-- npm install pinia
2. 创建一个pinia并且将其传递给应用程序:
import { createdPinia } from "pinia
const pinia = createdPinia()
3. 在main.js中导入
import pinia from "./store"
createApp(App).use(pinia).mount("#app")
1. 什么是Store?
-- 一个 Store (如 Pinia)是一个实体,它会持有为绑定到你组件树的状态和业务逻辑,也就是保存了全局的状态;
-- 它有点像始终存在,并且每个人都可以读取和写入的组件;
-- 你可以在你的应用程序中定义任意数量的Store来管理你的状态;
2. Store有三个核心概念:
-- state、getters、actions;
-- 等同于组件的data、computed、methods;
-- 一旦 store 被实例化,你就可以直接在 store 上访问 state、getters 和 actions 中定义的任何属性;
1. 定义一个Store:
-- 我们需要知道 Store 是使用 defineStore() 定义的,
-- 并且它需要一个唯一名称,作为第一个参数传递;
export const useCounter = defineStore(" counter",{
state() {
return {
counter: 0
}
}
})
2. 这个 name,也称为 id,是必要的,Pinia 使用它来将 store 连接到 devtools。
3. 返回的函数统一使用useX作为命名方案,这是约定的规范;

1. state 是 store 的核心部分,因为store是用来帮助我们管理状态的。
2. 在 Pinia 中,状态被定义为返回初始状态的函数;








1. 创建项目:npm create vite@latest my-vite-app --template vue -ts
2. 安装依赖: npm install
3.运行项目: npm run dev
4. 安装pinia: npm install
5. npm install pinia
// main.ts
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";
const pinia = createPinia();
const app = createApp(App);
app.use(pinia);
app.mount("#app");
store简单来说就是数据仓库的意思,我们数据都放在store里面。当然你也可以把它理解为一个公共组件,只不过该公共组件只存放数据,这些数据我们其它所有的组件都能够访问且可以修改。
我们需要使用pinia提供的defineStore()方法来创建一个store,该store用来存放我们需要全局使用的数据。
首先在项目src目录下新建store文件夹,用来存放我们创建的各种store,然后在该目录下新建user.ts文件,主要用来存放与user相关的store。
/src/store/user.ts
import { defineStore } from 'pinia'
// 第一个参数是应用程序中 store 的唯一 id
export const useUsersStore = defineStore('users', {
// 其它配置项
})
创建store很简单,调用pinia中的defineStore函数即可,该函数接收两个参数:
name:一个字符串,必传项,该store的唯一id。
options:一个对象,store的配置项,比如配置store内的数据,修改数据的方法等等。
我们可以定义任意数量的store,因为我们其实一个store就是一个函数,这也是pinia的好处之一,让我们的代码扁平化了,这和Vue3的实现思想是一样的
前面我们创建了一个store,说白了就是创建了一个方法,那么我们的目的肯定是使用它,假如我们要在App.vue里面使用它,该如何使用呢?
/src/App.vue
<script setup lang="ts">
import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
console.log(store);
</script>

我们都知道store是用来存放公共数据的,那么数据具体存在在哪里呢?前面我们利用defineStore函数创建了一个store,该函数第二个参数是一个options配置项,我们需要存放的数据就放在options对象中的state属性内。
假设我们往store添加一些任务基本数据,修改user.ts代码。
export const useUsersStore = defineStore("users", {
state: () => {
return {
name: "小猪课堂",
age: 25,
sex: "男",
};
},
});
我们往store存储数据的目的就是为了操作它,那么我们接下来就尝试操作state中的数据。
读取state数据很简单,前面我们尝试过在App.vue中打印store,那么我们添加数据后再来看看打印结果:
image.png
这个时候我们发现打印的结果里面多了几个属性,恰好就是我们添加的数据,修改App.vue,让这几个数据显示出来。
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<p>性别:{{ sex }}</p>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
const name = ref<string>(store.name);
const age = ref<number>(store.age);
const sex = ref<string>(store.sex);
</script>

import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
const { name, age, sex } = store;
我们使用store的最重要的目的就是为了组件之间共享数据,那么接下来我们新建一个child.vue组件,在该组件内部也使用state数据。
child.vue代码如下:
<template>
<h1>我是child组件</h1>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<p>性别:{{ sex }}</p>
</template>
<script setup lang="ts">
import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
const { name, age, sex } = store;
</script>

如果我们想要修改store中的数据,可以直接重新赋值即可,我们在App.vue里面添加一个按钮,点击按钮修改store中的某一个数据。
代码如下:
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<p>性别:{{ sex }}</p>
<button @click="changeName">更改姓名</button>
</template>
<script setup lang="ts">
import child from './child.vue';
import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
const { name, age, sex } = store;
const changeName = () => {
store.name = "张三";
console.log(store);
};
</script>

很多小伙伴可能会说那可以用监听函数啊,监听store变化,刷新页面...
其实,pinia提供了方法给我们,让我们获得的name等属性变为响应式的,我们重新修改代码。
import { storeToRefs } from 'pinia';
const store = useUsersStore();
const { name, age, sex } = storeToRefs(store);
我们两个组件中获取state数据的方式都改为上段代码的形式,利用pinia的storeToRefs函数,将state中的数据变为了响应式的。
除此之外,我们也给child.vue也加上更改state数据的方法。
child.vue代码如下:
<template>
<h1>我是child组件</h1>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<p>性别:{{ sex }}</p>
<button @click="changeName">更改姓名</button>
</template>
<script setup lang="ts">
import { useUsersStore } from "../src/store/user";
import { storeToRefs } from 'pinia';
const store = useUsersStore();
const { name, age, sex } = storeToRefs(store);
const changeName = () => {
store.name = "小猪课堂";
};
</script>

有时候我们修改了state数据,想要将它还原,这个时候该怎么做呢?就比如用户填写了一部分表单,突然想重置为最初始的状态。
此时,我们直接调用store的$reset()方法即可,继续使用我们的例子,添加一个重置按钮。
代码如下:
<button @click="reset">重置store</button>
// 重置store
const reset = () => {
store.$reset();
};
当我们点击重置按钮时,store中的数据会变为初始状态,页面也会更新。
前面我们修改state的数据是都是一条一条修改的,比如store.name="张三"等等,如果我们一次性需要修改很多条数据的话,有更加简便的方法,使用store的$patch方法,修改app.vue代码,添加一个批量更改数据的方法。
代码如下:
<button @click="patchStore">批量修改数据</button>
// 批量修改数据
const patchStore = () => {
store.$patch({
name: "张三",
age: 100,
sex: "女",
});
};
有经验的小伙伴可能发现了,我们采用这种批量更改的方式似乎代价有一点大,假如我们state中有些字段无需更改,但是按照上段代码的写法,我们必须要将state中的所有字段例举出了。
为了解决该问题,pinia提供的$patch方法还可以接收一个回调函数,它的用法有点像我们的数组循环回调函数了。
示例代码如下:
store.$patch((state) => {
state.items.push({ name: 'shoes', quantity: 1 })
state.hasChanged = true
})
上段代码中我们即批量更改了state的数据,又没有将所有的state字段列举出来。
pinia提供了方法让我们直接替换整个state对象,使用store的$state方法。
示例代码:
store.$state = { counter: 666, name: '张三' }
上段代码会将我们提前声明的state替换为新的对象,可能这种场景用得比较少,这里我就不展开说明了。
getters是defineStore参数配置项里面的另一个属性,前面我们讲了state属性。getter属性值是一个对象,该对象里面是各种各样的方法。大家可以把getter想象成Vue中的计算属性,它的作用就是返回一个新的结果,既然它和Vue中的计算属性类似,那么它肯定也是会被缓存的,就和computed一样。
当然我们这里的getter就是处理state数据。
我们先来看一下如何定义getter吧,修改user.ts。
代码如下:
export const useUsersStore = defineStore("users", {
state: () => {
return {
name: "小猪课堂",
age: 25,
sex: "男",
};
},
getters: {
getAddAge: (state) => {
return state.age + 100;
},
},
});
上段代码中我们在配置项参数中添加了getter属性,该属性对象中定义了一个getAddAge方法,该方法会默认接收一个state参数,也就是state对象,然后该方法返回的是一个新的数据。
我们在store中定义了getter,那么在组件中如何使用呢?使用起来非常简单,我们修改App.vue。
<template>
<p>新年龄:{{ store.getAddAge }}</p>
<button @click="patchStore">批量修改数据</button>
</template>
<script setup lang="ts">
import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
// 批量修改数据
const patchStore = () => {
store.$patch({
name: "张三",
age: 100,
sex: "女",
});
};
</script>
上段代码中我们直接在标签上使用了store.gettAddAge方法,这样可以保证响应式,其实我们state中的name等属性也可以以此种方式直接在标签上使用,也可以保持响应式。
当我们点击批量修改数据按钮时,页面上的新年龄字段也会跟着变化。
前面我们的getAddAge方法只是简单的使用了state方法,但是有时候我们需要在这一个getter方法中调用其它getter方法,这个时候如何调用呢?
其实很简单,我们可以直接在getter方法中调用this,this指向的便是store实例,所以理所当然的能够调用到其它getter。
示例代码如下:
export const useUsersStore = defineStore("users", {
state: () => {
return {
name: "小猪课堂",
age: 25,
sex: "男",
};
},
getters: {
getAddAge: (state) => {
return state.age + 100;
},
getNameAndAge(): string {
return this.name + this.getAddAge; // 调用其它getter
},
},
});
上段代码中我们又定义了一个名为getNameAndAge的getter函数,在函数内部直接使用了this来获取state数据以及调用其它getter函数。
细心的小伙伴可能会发现我们这里没有使用箭头函数的形式,这是因为我们在函数内部使用了this,箭头函数的this指向问题相信大家都知道吧!所以这里我们没有采用箭头函数的形式。
那么在组件中调用的形式没什么变化,代码如下:
<p>调用其它getter:{{ store.getNameAndAge }}</p>
既然getter函数做了一些计算或者处理,那么我们很可能会需要传递参数给getter函数,但是我们前面说getter函数就相当于store的计算属性,和vue的计算属性差不多,那么我们都知道Vue中计算属性是不能直接传递参数的,所以我们这里的getter函数如果要接受参数的话,也是需要做处理的。
示例代码:
export const useUsersStore = defineStore("users", {
state: () => {
return {
name: "小猪课堂",
age: 25,
sex: "男",
};
},
getters: {
getAddAge: (state) => {
return (num: number) => state.age + num;
},
getNameAndAge(): string {
return this.name + this.getAddAge; // 调用其它getter
},
},
});
上段代码中我们getter函数getAddAge接收了一个参数num,这种写法其实有点闭包的概念在里面了,相当于我们整体返回了一个新的函数,并且将state传入了新的函数。
接下来我们在组件中使用,方式很简单,代码如下:
<p>新年龄:{{ store.getAddAge(1100) }}</p>
前面我们提到的state和getters属性都主要是数据层面的,并没有具体的业务逻辑代码,它们两个就和我们组件代码中的data数据和computed计算属性一样。
那么,如果我们有业务代码的话,最好就是写在actions属性里面,该属性就和我们组件代码中的methods相似,用来放置一些处理业务逻辑的方法。
actions属性值同样是一个对象,该对象里面也是存储的各种各样的方法,包括同步方法和异步方法。
我们可以尝试着添加一个actions方法,修改user.ts。
代码如下:
export const useUsersStore = defineStore("users", {
state: () => {
return {
name: "小猪课堂",
age: 25,
sex: "男",
};
},
getters: {
getAddAge: (state) => {
return (num: number) => state.age + num;
},
getNameAndAge(): string {
return this.name + this.getAddAge; // 调用其它getter
},
},
actions: {
saveName(name: string) {
this.name = name;
},
},
});
上段代码中我们定义了一个非常简单的actions方法,在实际场景中,该方法可以是任何逻辑,比如发送请求、存储token等等。大家把actions方法当作一个普通的方法即可,特殊之处在于该方法内部的this指向的是当前store。
使用actions中的方法也非常简单,比如我们在App.vue中想要调用该方法。
代码如下:
const saveName = () => {
store.saveName("我是小猪");
};
我们点击按钮,直接调用store中的actions方法即可。
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";
const pinia = createPinia();
const app = createApp(App);
app.use(pinia);
app.mount("#app");
import { defineStore } from "pinia";
// 第一个参数是应用程序中 store 的唯一 id
export const useUsersStore = defineStore("users", {
state: () => {
return {
name: "小猪课堂",
age: 25,
sex: "男",
};
},
getters: {
getAddAge: (state) => {
return (num: number) => state.age + num;
},
getNameAndAge(): string {
return this.name + this.getAddAge; // 调用其它getter
},
},
actions: {
saveName(name: string) {
this.name = name;
},
},
});
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<p>性别:{{ sex }}</p>
<p>新年龄:{{ store.getAddAge(1100) }}</p>
<p>调用其它getter:{{ store.getNameAndAge }}</p>
<button @click="changeName">更改姓名</button>
<button @click="reset">重置store</button>
<button @click="patchStore">批量修改数据</button>
<button @click="saveName">调用aciton</button>
<!-- 子组件 -->
<child></child>
</template>
<script setup lang="ts">
import child from "./child.vue";
import { useUsersStore } from "../src/store/user";
import { storeToRefs } from "pinia";
const store = useUsersStore();
const { name, age, sex } = storeToRefs(store);
const changeName = () => {
store.name = "张三";
console.log(store);
};
// 重置store
const reset = () => {
store.$reset();
};
// 批量修改数据
const patchStore = () => {
store.$patch({
name: "张三",
age: 100,
sex: "女",
});
};
// 调用actions方法
const saveName = () => {
store.saveName("我是小猪");
};
</script>
<template>
<h1>我是child组件</h1>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<p>性别:{{ sex }}</p>
<button @click="changeName">更改姓名</button>
</template>
<script setup lang="ts">
import { useUsersStore } from "../src/store/user";
import { storeToRefs } from 'pinia';
const store = useUsersStore();
const { name, age, sex } = storeToRefs(store);
const changeName = () => {
store.name = "小猪课堂";
};
</script>
总结:
pinia的知识点很少,如果你有Vuex基础,那么学起来更是易如反掌。其实我们更应该关注的是它的函数思想,大家有没有发现我们在Vue3中的所有东西似乎都可以用一个函数来表示,pinia也是延续了这种思想。
所以,大家理解这种组合式编程的思想更重要,pinia无非就是以下3个大点:
state
getters
actions
当然,本篇文章只是讲解了基础使用部分,但是在实际工作中也能满足大部分需求了,如果还有兴趣学习`pinia`的其它特点,比如插件、订阅等等,
可以移步官网:pinia官网。
我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested
我安装了ruby版本管理器,并将RVM安装的ruby实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby。有没有办法让emacs像shell一样尊重ruby的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el
对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
我想为我的Task模型创建一个status属性,该属性将按以下顺序指示它在三部分进度中的位置:打开=>进行中=>完成。它的工作方式类似于亚马逊包裹的交付方式:已订购=>已发货=>已交付。我想知道设置此属性的最佳方法是什么。我可能是错的,但创建三个独立的bool属性似乎有点多余。实现此目标的最佳方法是什么? 最佳答案 Rails4有一个内置的enummacro.它使用单个整数列并映射到键列表。classOrderenumstatus:[:ordered,:shipped,:delivered]end状态映射如下:{ordered:0,
s=Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)s.connect(Socket.pack_sockaddr_in('port','hostname'))ssl=OpenSSL::SSL::SSLSocket.new(s,sslcert)ssl.connect从这里开始,如果ssl连接和底层套接字仍然是ESTABLISHED,或者它是否在默认值7200之后进入CLOSE_WAIT,我想检查一个线程几秒钟甚至更糟的是在实际上不需要.write()或.read()的情况下关闭。是用select()、IO.select()还是其他方法完成