Estoy trabajando en mi primer proyecto Vue. Estoy acostumbrado a React y vanilla js, pero solo me estoy dando cuenta de algunos conceptos en Vue aquí.
En particular, importar accesorios de estado y acción de una tienda Pinia, y aparentemente tener que importarlos varias veces en un solo componente Vue (algo que no necesito hacer en React).
En este ejemplo, estoy importando un valor de conteo simple y una función de incremento, y trato de usarlos en algunos lugares diferentes:
<script setup> // I import everything initially in setup, which works fine, // and these props (currentCount and incrementCount) // can be used in my template: import { storeToRefs } from 'pinia'; import { useStore } from '@/stores/store'; const { currentCount } = storeToRefs(useStore()); const { incrementCount } = useStore(); </script> <template> <main> Current count: {{ currentCount }} <button @click="incrementCount">Increment</button> </main> </template> <script> // I can't use store values from setup here. // This doesn't work: // console.log(currentCount); // I also can't import store values here. // I get the following error: // "getActivePinia was called with no active Pinia" // const { currentCount } = storeToRefs(useStore()); export default { mounted() { // I have to import store values here for them to work: const { currentCount } = storeToRefs(useStore()); console.log(currentCount); }, watch: { // weirdly, this reference to watching "currentCount" works: currentCount() { // I also have to import store values here for them to work: const { currentCount } = storeToRefs(useStore()); console.log(currentCount); }, }, }; </script>Como puede ver, si quiero usar los valores de la tienda en mi plantilla, en el montaje y en un observador (por lo que usaría el gancho useEffect de React), tengo que importar los accesorios de la tienda 3 veces en total.
¿Es esto correcto/normal? ¿Existe una forma más sencilla de lograr lo que estoy haciendo, en la que solo importe accesorios una vez? Quiero estar seguro de que no me he perdido algo y no estoy haciendo algo de una manera inusual.
¡Gracias por cualquier ayuda y consejo!
Pinia se diseñó teniendo en cuenta la API de composición.
Por lo tanto, su uso previsto está dentro de la función setup() , donde solo lo importaría una vez.
Para usarlo fuera de una función de setup() , tiene dos rutas principales:
setup() y estará disponible en cualquier gancho/método/captador. Ya sea como this.store o spread: import { useStore } from '@/store' import { toRefs } from 'vue' // or from '@vue/composition-api' in Vue2 export default { setup: () => ({ ...toRefs(useStore()) }) } /* this makes every state prop, getter or action directly available on current component instance. In your case, `this.currentCount`. Obviously, you can also make the entire store available as `this.someStore`: setup: () => ({ someStore: useSomeStore() }) // now you can use `this.someStore` anywhere */pinia (devuelta por createPinia() ), desde main.(js|ts) , importarla donde necesite la tienda y luego llamar a useStore() pasando la instancia de pinia como argumento. import { pinia } from 'main.js' import { useSomeStore } from '@/store' const someStore = useSomeStore(pinia); Probablemente también debería mencionar el ayudante de mapState proporcionado por pinia . Le permite seleccionar solo algunas de las claves expuestas a la instancia actual. Ejemplo:
import { mapState } from 'pinia' // ... computed: { ...mapState(useSomeStore, [ 'currentCount']) } // Now `this.currentCount` is available Nota: mapState tiene un nombre extraño, ya que le permite acceder a más que solo accesorios de state (también getters y actions ). Fue nombrado mapState para que coincida con el asistente similar de vuex .
Un enfoque aún más general es agregar su tienda como global, utilizando la API de registro de complementos en Vue2:
import { useSomeStore } from '@/store'; import { createPinia } from 'pinia'; const pinia = createPinia(); const someStorePlugin = { install(Vue, options) { Vue.prototype.someStore = useSomeStore(options.pinia) } }; Vue.use(someStorePlugin, { pinia }); new Vue({ pinia }); Después de esto, cada componente de su instancia de Vue tendrá this.someStore disponible, sin necesidad de importarlo.
Nota: no he probado agregar una tienda en globales (y definitivamente desaconsejo hacerlo; debe evitar los globales), pero espero que funcione.
Si desea combinar las tiendas pinia con la API de opciones, una forma de hacerlo es usar la función setup() dentro de las opciones para llamar a useStore :
<script> import { useStore } from '@/stores/store'; export default { setup() { const store = useStore(); return {store} }, watch: { store.currentBrightness(newVal, oldVal){ // your code } }, methods: { // inside methods use this.store }, mounted() { console.log(this.store.currentCount); } } </script>Algunos podrían considerar esto como una mezcla no deseada de API de composición y opciones, pero en mi opinión, es una solución bastante buena para las tiendas pinia.
Nechoj, tiene la respuesta más directa. Además, si tiene varias tiendas, siempre puede importar las tiendas según sea necesario en un componente principal y luego usar inyectar solo agregar algunas partes. Por ejemplo, tengo una ruta de datos que se llama a través de una API, no la necesito en todas partes todo el tiempo, así que la llamo a un padre y luego uso inyectar para usar esas rutas en un menú desplegable que podría ser un bisnieto componente. No necesito toda esa tienda de utilidades, solo las rutas. Página de inicio:
import { useUtilsStore } from "src/stores/utilsStore"; const passengerRoutes = computed(() => utilsStore.getPassengerRoutes); provide("passengerRoutes", passengerRoutes);componente nieto:
const compRoutes = inject("passengerRoutes");