2.1.7 • Published 2 days ago

pinia v2.1.7

Weekly downloads
919
License
MIT
Repository
github
Last release
2 days ago

Pinia Build Status npm package coverage thanks

Pronounced like the fruit, in Spanish Piña

Piña is also an invalid package name... that's why it has to be pinia

🍍Automatically Typed, Modular and lightweight (but Experimental) Store for Vue based on the composition api with devtools support

Demo (TODO link)

⚠️⚠️⚠️ This project is experimental, it's an exploration of what a Store could be like using the composition api. It works for Vue 2 by using the official library.

What I want is to inspire others to think about ways to improve Vuex and come up with something that works very well with the composition api but that can also be used without it.

There are the core principles that I try to achieve with this experiment:

  • Flat modular structure 🍍 No nesting, only stores, compose them as needed
  • Light layer on top of Vue 💨 keep it under 1kb gzip
  • Only state and getters 👐 patch is the new mutation
  • Actions are just functions ⚗️ Group your business there
  • Import what you need, let webpack code split 📦 No need for dynamically registered modules
  • SSR support ⚙️
  • DevTools support 💻 Which is crucial to make this enjoyable

Help me keep working on Open Source in a sustainable way 🚀. Help me with as little as \$1 a month, sponsor me on Github.


FAQ

A few notes about the project and possible questions:

Q: Does this replace Vuex, is it its successor?

A: No, or at least that's not the main intention

Q: What about dynamic modules?

A: Dynamic modules are not type safe, so instead we allow creating different stores that can be imported anywhere

Roadmap / Ideas

  • List Getters on DevTools
  • Nuxt Module
  • Automatic fresh store on Server Side?
  • Flag to remove devtools support (for very light production apps)
  • Allow grouping stores together into a similar structure and allow defining new getters

Installation

yarn add pinia
# or with npm
npm install pinia

Usage

Creating a Store

You can create as many stores as you want, and they should each exist in isolated files:

import { createStore } from 'pinia'

export const useMainStore = createStore(
  // name of the store
  // it is used in devtools and allows restoring state
  'main',
  // a function that returns a fresh state
  () => ({
    counter: 0,
    name: 'Eduardo',
  }),
  // optional getters
  {
    doubleCount: state => state.counter * 2,
  }
)

createStore returns a function that has to be called to get access to the store:

import { useMainStore } from '@/stores/main'

export default createComponent({
  setup() {
    const main = useMainStore()

    return {
      // gives access to the whole store
      main,
      // gives access to the state
      state: main.state,
      // gives access to specific getter,
    }
  },
})

There is one important rule for this to work: the useMainStore (or any other useStore function) must be called inside of deferred functions. This is to allow the Vue Composition API plugin to be installed. Never, ever call useStore like this:

import { useMainStore } from '@/stores/main'
// ❌ Depending on where you do this it will fail
// so just don't do it
const main = useMainStore()

export default createComponent({
  setup() {
    return {}
  },
})

Once you have access to the store, you can access the state through store.state and any getter directly on the store itself as a computed property (from @vue/composition-api) (meaning you need to use .value to read the actual value on the JavaScript but not in the template):

export default createComponent({
  setup() {
    const main = useMainStore()
    const text = main.state.name
    const doubleCount = main.doubleCount.value // notice the `.value` at the end
    return {}
  },
})

state is the result of a ref while every getter is the result of a computed. Both from @vue/composition-api.

Mutating the state

To mutate the state you can either directly change something:

main.state.counter++

or call the method patch that allows you apply multiple changes at the same time with a partial state object:

main.patch({
  counter: -1,
  name: 'Abalam',
})

The main difference here is that patch allows you to group multiple changes into one single entry in the devtools.

Replacing the state

Simply set it to a new object;

main.state = { counter: 666, name: 'Paimon' }

SSR

The main part about SSR is not sharing state between requests. So we can pass true to useStore once when getting a new request on the server. If we follow the SSR guide, our createApp should look like this:

export function createApp() {
  // Here there could also be a router
  const store = useStore(true)

  // we can change the state now!
  store.state.counter++

  // create the app instance
  const app = new Vue({
    render: h => h(App),
  })

  // expose the app and the store.
  return { app, store }
}

Actions

Actions are simply function that contain business logic. As with components, they must call useStore to retrieve the store:

export async function login(user, password) {
  const store = useUserStore()
  const userData = await apiLogin(user, password)

  store.patch({
    name: user,
    ...userData,
  })
}

Composing Stores

Composing stores may look hard at first glance but there is only one rule to follow really:

If multiple stores use each other or you need to use multiple stores at the same time, you must create a separate file where you import all of them.

If one store uses an other store, there is no need to create a new file, you can directly import it. Think of it as nesting.

Shared Getters

If you need to compute a value based on the state and/or getters of multiple stores, you may be able to import all the stores but one into the remaining store, but depending on how your stores are used across your application, this would hurt your code splitting as you importing the store that imports all others stores, would result in one single big chunk with all of your stores. To prevent this, we follow the rule above and we create a new file:

import { computed } from '@vue/composition-api'
import { useUserStore } from './user'
import { useCartStore } from './cart'

export const summary = computed(() => {
  const user = useUserStore()
  const cart = useCartStore()

  return `Hi ${user.state.name}, you have ${cart.state.list.length} items in your cart. It costs ${cart.price}.`
})

Shared Actions

When an actions needs to use multiple stores, we do the same, we create a new file:

import { useUserStore } from './user'
import { useCartStore, emptyCart } from './cart'

export async function orderCart() {
  const user = useUserStore()
  const cart = useCartStore()

  try {
    await apiOrderCart(user.state.token, cart.state.items)
    emptyCart()
  } catch (err) {
    displayError(err)
  }
}

Creating Pinias

Not implemented. Replaces the examles above about combining state and getters and about composing stores.

Combine multiple stores (gajos) into a new one:

import { pinia } from 'pinia'
import { useUserStore } from './user'
import { useCartStore, emptyCart } from './cart'

export const useCartUserStore = pinia(
  {
    user: useUserStore,
    cart: useCartStore,
  },
  {
    combinedGetter: state =>
      `Hi ${user.state.name}, you have ${cart.state.list.length} items in your cart. It costs ${cart.price}.`,
  }
)

export async function orderCart() {
  const store = useCartUserStore()

  try {
    await apiOrderCart(store.state.user.token, store.state.cart.items)
    emptyCart()
  } catch (err) {
    displayError(err)
  }
}

Related

License

MIT

vufireyuan-qingdao-zld-browsercognitoapi@nuxtjs-alt/pinia@jiqi/vue-helpers@takuma-ru/vue-library-docs@hovrcat/translatorcf-designercesium_yaoml_plugin_vitebisom-lib@elecerp/directus-extension-elecmarket-module@elecerp/directus-extension-energysee-module@elecerp/directus-extension-eleciq-moduledebtonateyunjing-shop-managemysuni-ui-vue@eb3n/pinia-plugin-persistpinia-state-sync@jiabinbin/vue3-library@braks/revue-flowpinap@branchito/wccli-vue3-vite2-tsvite-vue3-tselement-plus-admin-layout@astel/test@avid-ui/testpc-temp-v3-vitelibrary-nowifun-cli-vuei-lab-componentsi-lab-lowcodelab-low-codesohelp-test@bithotel/bithotel-vue-walletviewe-img-tut-vuefa-adminflymopet-vuepwp@no_idea/pinia-plugin-persistentvite-basevue3-typescript-admin-template@sorcecloud/sectionluoyi-demouni-components-testrjl_vue3_vitezhaifanhuablog.managevue3-plat-form@frgr91/vue3-plugin-typescriptcommon-componentsshogan-eagle-uikotsmile-test-app-newkotsmile-test-app-new-newsjn-usual-toolsvue3examplewaytohealth-inbox@infinitebrahmanuniverse/nolb-piniabinzt-ui-plus@moreillon/excel-parser@moreillon/vue-excel-parserav-theme@tamnt-work/nuxt3-layerhywd-archives-datedechnic-webtopo-client4dcruder_testco-cc-components@sunhy/soft-ui@vui-admin/dependencie@vui-admin/dependenciesplugin-system-utilsrich-plussuikitdcruderdefine-orbitenterprise-matchingdmg-navbarheaderfront-npm-test-workplusionic-vue-vite-maingeneral-world-modeleps-ui-kitmouse-checkvy-andvtest-package-vuemapeng-uivue3-resume-builderquick-games-vibratestor-dm-component-libsx-best-low-codechron-corekkxxxsilvia-test-room@everything-registry/sub-chunk-2438quick-games-common-utils-v1quick-games-componentsmf-hr-appbillbear-micro-dependencyyunjing-form-editordevtools-paneldectool-panelvite-vue3-test-model
2.1.8-beta.0

2 days ago

2.1.7

6 months ago

2.1.4

10 months ago

2.1.6

9 months ago

2.1.5

9 months ago

2.0.35

1 year ago

2.0.36

12 months ago

2.1.2

11 months ago

2.1.1

11 months ago

2.1.3

11 months ago

2.1.0

11 months ago

2.0.29

1 year ago

2.0.33

1 year ago

2.0.34

1 year ago

2.0.31

1 year ago

2.0.32

1 year ago

2.0.30

1 year ago

2.0.28

1 year ago

2.0.26

1 year ago

2.0.27

1 year ago

2.0.24

1 year ago

2.0.25

1 year ago

2.0.23

2 years ago

2.0.22

2 years ago

2.0.21

2 years ago

2.0.15

2 years ago

2.0.16

2 years ago

2.0.19

2 years ago

2.0.17

2 years ago

2.0.18

2 years ago

2.0.20

2 years ago

2.0.14

2 years ago

2.0.13

2 years ago

2.0.12

2 years ago

2.0.5

2 years ago

2.0.7

2 years ago

2.0.6

2 years ago

2.0.9

2 years ago

2.0.8

2 years ago

2.0.11

2 years ago

2.0.10

2 years ago

2.0.3

2 years ago

2.0.4

2 years ago

2.0.2

2 years ago

2.0.1

2 years ago

2.0.0

2 years ago

2.0.0-rc.15

2 years ago

2.0.0-rc.13

3 years ago

2.0.0-rc.14

3 years ago

2.0.0-rc.12

3 years ago

2.0.0-rc.11

3 years ago

2.0.0-rc.10

3 years ago

2.0.0-rc.9

3 years ago

2.0.0-rc.8

3 years ago

2.0.0-rc.7

3 years ago

2.0.0-rc.5

3 years ago

2.0.0-rc.6

3 years ago

0.5.4

3 years ago

2.0.0-rc.4

3 years ago

2.0.0-rc.2

3 years ago

2.0.0-rc.3

3 years ago

0.5.3

3 years ago

2.0.0-rc.1

3 years ago

2.0.0-rc.0

3 years ago

2.0.0-beta.5

3 years ago

2.0.0-beta.4

3 years ago

2.0.0-beta.3

3 years ago

2.0.0-beta.2

3 years ago

2.0.0-beta.1

3 years ago

0.5.2

3 years ago

2.0.0-alpha.19

3 years ago

2.0.0-alpha.18

3 years ago

2.0.0-alpha.17

3 years ago

2.0.0-alpha.16

3 years ago

2.0.0-alpha.15

3 years ago

2.0.0-alpha.14

3 years ago

0.5.0

3 years ago

0.4.1

3 years ago

0.4.0

3 years ago

0.5.1

3 years ago

2.0.0-alpha.13

3 years ago

0.3.1

3 years ago

2.0.0-alpha.11

3 years ago

2.0.0-alpha.12

3 years ago

0.3.0

3 years ago

2.0.0-alpha.10

3 years ago

0.2.5

3 years ago

2.0.0-alpha.9

3 years ago

0.2.3

3 years ago

0.2.2

3 years ago

0.2.4

3 years ago

2.0.0-alpha.8

3 years ago

0.2.1

3 years ago

0.2.0

3 years ago

2.0.0-alpha.7

3 years ago

2.0.0-alpha.6

3 years ago

2.0.0-alpha.5

4 years ago

2.0.0-alpha.4

4 years ago

2.0.0-alpha.3

4 years ago

2.0.0-alpha.2

4 years ago

2.0.0-alpha.1

4 years ago

0.1.0

4 years ago

0.0.7

4 years ago

0.0.6

4 years ago

0.1.0-alpha.1

4 years ago

0.0.5

4 years ago

0.0.4

4 years ago

0.0.3

4 years ago

0.1.0-alpha.0

4 years ago

0.0.2

4 years ago

0.0.1

4 years ago

0.0.0

4 years ago