Article Directory
foreword
Pinia
is Vue
a repository for , which allows you to share state across components/pages. If you're familiar Composition API
, you might think that you can already pass a simple export const state = reactive({})
. This is true for a single-page application, but if it's server-side rendered, it exposes your application to security holes. But even in small single-page applications, you can Pinia
gain a lot from using :
-
dev-tools support
1, track actions, a timeline of mutations
2,Store
appear in the components that use them
3,time travel
and easier debugging -
Hot Module Replacement
1. Modify yours without reloading the pageStore
2. Keep any existing state while developing -
Plugins
Pinia
: Extend functionality with plugins -
Proper TypeScript support or autocompletion for JS users
-
Server-side rendering support
1. Basic example
This is what usage looks like pinia
in API
terms of . You first create a Store
:
// stores/counter.js
import {
defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => {
return {
count: 0 }
},
// 也可以定义为
// state: () => ({ count: 0 })
actions: {
increment() {
this.count++
},
},
})
Then you use it in a component:
import {
useCounterStore } from '@/stores/counter'
export default {
setup() {
const counter = useCounterStore()
counter.count++
// 带自动补全 ✨
counter.$patch({
count: counter.count + 1 })
// 或使用 action 代替
counter.increment()
},
}
You can even use a function (similar to a component setup()
) to define one for more advanced use cases Store
:
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return {
count, increment }
})
If you're not familiar setup()
with and Composition API
, don't worry, Pinia
a similar set is also supported map helpers like Vuex
. You define storage the same way, but then use mapStores()
, mapState()
or mapActions()
:
const useCounterStore = defineStore('counter', {
state: () => ({
count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
}
}
})
const useUserStore = defineStore('user', {
// ...
})
export default {
computed: {
// other computed properties
// ...
// gives access to this.counterStore and this.userStore
...mapStores(useCounterStore, useUserStore)
// gives read access to this.count and this.double
...mapState(useCounterStore, ['count', 'double']),
},
methods: {
// gives access to this.increment()
...mapActions(useCounterStore, ['increment']),
},
}
2. A more realistic example
Here's a more complete example of the API you'll Pinia
use with even JavaScript
having types in . For some, this may be enough to get started without further reading, but we still recommend looking at the rest of the documentation, or even skipping this example, and coming back after reading all of the _core concepts_.
import {
defineStore } from 'pinia'
export const todos = defineStore('todos', {
state: () => ({
/** @type {
{ text: string, id: number, isFinished: boolean }[]} */
todos: [],
/** @type {'all' | 'finished' | 'unfinished'} */
filter: 'all',
// type 会自动推断为 number
nextId: 0,
}),
getters: {
finishedTodos(state) {
// 自动完成! ✨
return state.todos.filter((todo) => todo.isFinished)
},
unfinishedTodos(state) {
return state.todos.filter((todo) => !todo.isFinished)
},
/**
* @returns {
{ text: string, id: number, isFinished: boolean }[]}
*/
filteredTodos(state) {
if (this.filter === 'finished') {
// 自动调用其他 getter ✨
return this.finishedTodos
} else if (this.filter === 'unfinished') {
return this.unfinishedTodos
}
return this.todos
},
},
actions: {
// 任何数量的参数,返回一个 Promise 或者不返回
addTodo(text) {
// 你可以直接改变状态
this.todos.push({
text, id: this.nextId++, isFinished: false })
},
},
})
3. Comparison with Vuex
Pinia
It was originally intended to explore Vuex
what the next iteration of , incorporating Vuex 5
many of the ideas discussed in the core team. Eventually, we realized that Pinia
already implemented Vuex 5
most of what we wanted in and decided to implement it instead with new suggestions.
Provides a simpler one than Vuex
, has fewer specifications, provides style , and most importantly, has solid type inference support when used with .Pinia
API
Composition-API
API
TypeScript
RFC#
Although Vuex
passed to RFC
collect as much feedback as possible from the community, Pinia
it did not. I test ideas based on my experience developing applications, reading other people's code, Pinia
working for clients who use , and answering questions on . Discord
This allows me to provide an efficient solution for various situations and application sizes. I release frequently and API
keep the library evolving while keeping its core intact.
1. Comparison with Vuex 3.x/4.x
| Vuex 3.x is Vuex's Vue 2 and Vuex 4.x is Vue 3
Pinia API is very different from Vuex ≤4, namely:
mutations
no longer exists. They are often considered very verbose. They initially broughtdevtools
integration, but that's no longer an issue.- There's no need to create custom complex wrappers to support that
TypeScript
, everything is typed, and isAPI
designed in such a way to leverageTS
type inference as much as possible. - No more injecting, importing functions, calling functions, enjoying autocompletion!
- No need to add dynamically
Store
, they are all dynamic by default and you won't even notice. Note that you can still manuallyStore
register with at any time, but since it's automatic, you don't need to worry about it. - There is no nested structure of modules anymore. You can still
Store
implicitly nest by importing and using within anotherStore
, butPinia
provide a flat structure by design while still supportingStore
intersecting composability between . You can even haveStore
circular dependencies for . - There is no namespace module. Given
Store
the flat architecture of a 'namespace'Store
is inherent in the way it's defined, you could say that allStore
are namespaced.
Summarize
The above is what I want to talk about today. This article only briefly introduces Pinia
the use of .