background

With the gradual application of Vue3, there are more and more demands for state management. The initial state management was based on VEX4, but vex4 also exposed some problems. Personally, Vex4 is similar to a transition product, with incomplete support for TypeScript. If you write components in TypeScript, you need to follow certain steps to do type inference correctly, and modules aren’t user-friendly. Kia King, a key contributor to Vuex, also said that Vuex5 is already in the works, and full TypeScript support is available. Is there another way to manage state before Vuex5 comes out, or to “ditch “Vuex?

Provide / Inject

Provide and Inject are not new to Vue3; they already exist in Vue2. Provide and Inject binding mentioned in the documentation are not responsive. However, if you pass in a listening object, the object’s property is still responsive.

Vue3 adds reactive API ref and Reactive on the basis of Computed and Watch, which makes it more convenient to provide and inject. Combined with the idea of Composition API, can Vue3 achieve a simple version of state management?

Remove the shared state

// src/context/calculator.ts import { ref, inject, provide, readonly } from 'vue'; type Calculator = { count: number; increase: () => void; updateCount: (num: number) => void; }; //provide key, unique token const CalculatorSymbol = Symbol(); // provider export const calculatorProvide = () => {// number const count = ref<number>(1); Const increase = () => {count.value++; }; // Update method const updateCount = (num: number) => {count.value = num; }; Const depends = {count: readonly(count), // increase, updateCount}; // Implement state objects to provide provide using the provide API (CalculatorSymbol, depends); // Return the status object, so that the peer can call return depends; }; // Injection method Export const calculatorInject = () => {// Inject state using inject API const calculatorContext = inject<Calculator>(CalculatorSymbol); // Error check if (! calculatorContext) { throw new Error('Inject must be used affer Provide'); } // Return calculatorContext; };Copy the code

Provide data

Compared with global sharing of Vuex, Provide/Inject can be used to achieve global or local sharing.

For global sharing, you can inject global state in main.ts:

// src/main.ts import { createApp, h } from 'vue'; import App from '@/App.vue'; import { calculatorProvide } from '@/context/calculator'; Const app = createApp({setup() {calculatorProvide(); return () => h(App); }}); // Mount instance app.mount('#app');Copy the code

If you want to share locally, you can inject state into the parent component

// src/views/parent.vue import { defineComponent } from "vue"; import { calculatorProvide } from '@/context/calculator'; Export default defineComponent({name: "parent", setup() {// Share data calculatorProvide(); }});Copy the code

Into the data

Child components can use or modify state through state injection

// src/views/child.vue import { defineComponent } from "vue"; import { calculatorInject } from '@/context/calculator'; export default defineComponent({ name: "Child ", setup() {// Inject data const {count, increase, updateCount} = calculatorInject(); }});Copy the code

summary

In fact, you can think of Provide/Inject as “long range props”, except:

  • The parent component does not need to know which child components use the property it provides
  • Child components don’t need to know where inject’s property comes from

Vue3 mimics small state management by making dependency injection more flexible and easy to use, with full TypeScript support for personal testing

reactive

Is there another way to implement state management without providing/injecting? Go straight to the code.

Remove the shared state

// src/context/calculator.ts type Calculator = { count: number; increase: () => void; updateCount: (num: number) => void; }; Const calculatorStore = reactive<Calculator>({count: 1, increase: () => {calculatorstore.count+ +; }, updateCount: (num: number) => { calculatorStore.count = num; }}); export { calculatorStore };Copy the code

Using shared State

The way to use state is very simple, just need to import the state, the components that need to use state, need to import

// src/views/any.vue import { defineComponent } from "vue"; import { calculatorStore } from '@/context/calculator'; export default defineComponent({ name: "any", setup() { console.log(calculatorStore.count); }});Copy the code

summary

The solution uses reactive responsiveness and import the same instance, which is simpler than dependency injection and supports TypeScript validation correctly. But whereas dependency injection can share different data between different root nodes, reactive always shares an instance, which is not applicable in some business scenarios.

conclusion

First of all, Vuex is still a more mature and comprehensive solution, just for some simple state management, you can try to change the way to solve it. Of course, the above scheme may have a lot of consideration is not the whole place, welcome to give advice ~

reference

  • Vue3. Js document
  • In the Vue3 era, you should embrace dependency injection