pinia是一个vue的状态存储库,你可以使用它来存储、共享一些跨组件或者页面的数据,使用起来和vuex非常类似。pina相对Vuex来说,更好的ts支持和代码自动补全功能。本篇随笔介绍pinia的基础用法以及持久化存储的一些用法,供参考学习。
Pinia 是 Vuex4 的升级版,也就是 Vuex5; Pinia 极大的简化了Vuex的使用,是 Vue3的新的状态管理工具;Pinia 对 ts的支持更好,性能更优, 体积更小,无 mutations,可用于 Vue2 和 Vue3;Pinia支持Vue Devtools、 模块热更新和服务端渲染。
安装pinia(https://pinia.vuejs.org/)
npm install pinia
在main.j或者main.ts中引入使用
import { createPinia } from 'pinia'
app.use(createPinia())
下面就是使用pinia的一个例子。这样你就创建了一个状态存储。
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => {
return { count: 0 }
},
// 也可以这样定义状态
// state: () => ({ count: 0 })
actions: {
increment() {
this.count++
},
},
})
在组件中使用:
import { useCounterStore } from '@/stores/counter'
export default {
setup() {
const counter = useCounterStore()
counter.count++
// 编辑器会有代码提示
counter.$patch({ count: counter.count + 1 })
// 也可以使用action来代替
counter.increment()
},
}
const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
}
}
})
const useUserStore = defineStore('user', {
// ...
})
export default {
computed: {
// 其他计算属性
// ...
// 可以使用 this.counterStore 和 this.userStore获取
...mapStores(useCounterStore, useUserStore)
// 可以使用 this.count 和this.double获取
...mapState(useCounterStore, ['count', 'double']),
},
methods: {
// 可以使用 this.increment()调用
...mapActions(useCounterStore, ['increment']),
},
}
与vue4之前的版本相比,pinia的API是有很多不同的,即:
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
// other options...
})
上面只是定义了store,在setup函数中调用了useStore()时,才会创建store:
import { useStore } from '@/stores/counter'
export default {
setup() {
const store = useStore()
return {
// 你可以返回store这个对象,然后就可以在template中使用了
store,
}
},
}
在store实例化以后,你就可以调用到store中定义的state、getters和actions了。为了让解构的值还保持响应式,你需要用到storeToRefs()方法。它会给响应式的数据创建ref。
import { storeToRefs } from 'pinia'
export default defineComponent({
setup() {
const store = useStore()
// `name` 和 `doubleCount` 是响应式的
// 插件增加的属性也会创建ref
// 但是会自动跳过action或者不是响应性的属性
const { name, doubleCount } = storeToRefs(store)
return {
name,
doubleCount
}
},
})
默认情况下,你可以在store实例上直接获取或者修改state:
const store = useStore()
store.counter++
也可以调用$reset()方法来把state恢复为初始值:
const store = useStore()
store.$reset()
除了直接修改store里的值store.counter++,你也可以是用$patch方法。你可以同时修改多个值:
store.$patch({
counter: store.counter + 1,
name: 'Abalam',
})
或者$patch接收一个函数作为参数,来简化改变数组的写法:
store.$patch((state) => {
state.items.push({ name: 'shoes', quantity: 1 })
state.hasChanged = true
})
localStorage和sessionStorage差别
localStorage和sessionStorage一样都是用来存储客户端临时信息的对象。
他们均只能存储字符串类型的对象(虽然规范中可以存储其他原生类型的对象,但是目前为止没有浏览器对其进行实现)。
localStorage生命周期是永久,这意味着除非用户显示在浏览器提供的UI上清除localStorage信息,否则这些信息将永远存在。
sessionStorage生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过sessionStorage存储的数据也就被清空了。
不同浏览器无法共享localStorage或sessionStorage中的信息。相同浏览器的不同页面间可以共享相同的 localStorage(页面属于相同域名和端口),但是不同页面或标签页间无法共享sessionStorage的信息。这里需要注意的是,页面及标 签页仅指顶级窗口,如果一个标签页包含多个iframe标签且他们属于同源页面,那么他们之间是可以共享sessionStorage的。
JSON对象提供的parse和stringify将其他数据类型转化成字符串,再存储到storage中就可以了,操作的方式:
存:
var obj = {"name":"xiaoming","age":"16"}
localStorage.setItem("userInfo",JSON.stringify(obj));
取:
var user = JSON.parse(localStorage.getItem("userInfo"))
删除:
localStorage.remove("userInfo);
清空:
localStorage.clear();
pnia 使用订阅机制subscribe来实现数据的持久化存储的代码如下所示。
const instance = useMainStore();
// 订阅数据变化,变化时存储 instance.$id 这是storeId
instance.$subscribe((mutation, state) => {
localStorage.setItem(instance.$id, JSON.stringify(state));
});
//init 初始的时候获取
const val = localStorage.getItem(instance.$id);
if (val) {
instance.$state = JSON.parse(val);
}
也可以通过watch实现
watch(
pinia.state,
(state) => {
// persist the whole state to the local storage whenever it changes
localStorage.setItem('piniaState', JSON.stringify(state))
},
{ deep: true }
)
但是需要注意,这种方式持久化会提示pinia未安装挂载,所以需要在pinia挂载后再调用,这里可以将它封装成方法导出,在挂载后调用
xport const initStore = () => {
const instance = useMainStore();
// 订阅数据变化,变化时存储 instance.$id 这是storeId
instance.$subscribe((mutation, state) => {
localStorage.setItem(instance.$id, JSON.stringify(state));
});
//init 初始的时候获取
const val = localStorage.getItem(instance.$id);
if (val) {
instance.$state = JSON.parse(val);
}
}
export default {
setup() {
const someStore = useSomeStore()
// 组件卸载后,侦听也会有
someStore.$subscribe(callback, true)
// ...
},
}
或者watch状态的变化
watch(
pinia.state,
(state) => {
// 在state改变时,保存在localStorage中
localStorage.setItem('piniaState', JSON.stringify(state))
},
{ deep: true }
)
pinia plugin persist官方网站:pinia-plugin-persist
持久化存储也可以通过安装插件的方式,安装 pinia-plugin-persist 来实现。
npm i pinia-plugin-persist --save
使用main.js
import { createPinia } from 'pinia'
import piniaPluginPersist from 'pinia-plugin-persist'
const store = createPinia()
store.use(piniaPluginPersist)
createApp(App).use(store).mount('#app')
在对应的store中开启,数据默认存在 sessionStorage 里,并且会以 storeId 作为 key
import { defineStore } from 'pinia'
// 'main' 是storeId
export const useMainStore = defineStore('main', {
state: () => ({
counter: 2,
name: 'Eduardo',
isAdmin: true
}),
// ……
// 开启数据缓存
persist: {
enabled: true
}
})
如果需要自定义key和存储位置,则修改参数即可。
persist: {
enabled: true,
strategies: [ //使用插件自定义存储
{
key: 'settings', // key可以自己定义,不填的话默认就是这个store的ID
storage: localStorage,
}
]
},
一般项目开发,实际上存储的内容会比较多,可能根据不同的键值模块进行区分,因此把它们放在一个store/modules里面,方便的使用引用它来存取设置数据即可。
我们这里简单以一个settings的配置信息进行介绍,其中index.ts是一个统一的创建pinia的对象并挂接到全局App上的。

其中index.ts的代码如下所示。
import type { App } from "vue";
import { createPinia } from "pinia";
import piniaPluginPersist from 'pinia-plugin-persist';//使用插件持久化
const store = createPinia();
store.use(piniaPluginPersist) //使用插件持久化
export function setupStore(app: App<Element>) {
app.use(store);
}
export { store };
因此在main.js里面引入并挂接pinia即可。
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import 'normalize.css' // css初始化
import App from './App.vue'
import { setupStore } from "/@/store";
const app = createApp(App)
setupStore(app)
app.use(ElementPlus)
app.mount('#app')
这样我们就可以再次定义一个模块化的配置信息,以便于管理存储各种不同类型的内容。
如下面我们定义一个程序配置信息setttings.ts
import { defineStore } from "pinia";
import { store } from "/@/store";
export type settingType = {
title: string;
fixedHeader: boolean;
hiddenSideBar: boolean;
};
export const useSettingStore = defineStore({
id: "settings",
state: (): settingType => ({
title: "Vue3 + TypeScript + Element",
fixedHeader: false,
hiddenSideBar: false
}),
persist: {
enabled: true,
strategies: [ //使用插件自定义存储
{
key: 'settings', // key可以自己定义,不填的话默认就是这个store的ID
storage: localStorage,
}
]
},
getters: {
getTitle() {
return this.title;
},
getFixedHeader() {
return this.fixedHeader;
},
getHiddenSideBar() {
return this.HiddenSideBar;
}
},
actions: {
CHANGE_SETTING({ key, value }) {
// eslint-disable-next-line no-prototype-builtins
if (this.hasOwnProperty(key)) {
this[key] = value;
}
},
changeSetting(data) {
this.CHANGE_SETTING(data);
}
}
});
export function useSettingStoreHook() {
return useSettingStore(store);
}
然后在组件视图vue或者app.vue中使用即可
<script lang="ts">
import { defineComponent } from "vue";
import { useSettingStoreHook } from "/@/store/modules/settings";
import { storeToRefs } from "pinia";
export default defineComponent({
name: "app",
components: {
},
setup() {
const store = useSettingStoreHook();
const { fixedHeader, title } = storeToRefs(store);
return {
fixedHeader,
title,
};
},
methods: {
setTitle() {
this.title = "Vue3 + TypeScript + Element + Edit";
console.log(this.title);
},
},
});
</script>
查看数据修改后,存储在本地存储空间中的内容,如下所示。

我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h