preface
Vue3 is here, the new version of Vuex is also in beta, before it is applied to the project, it went through the water first, I almost broke my computer, why did I break it, let’s start the text…
The installation
npm init vite-app vue3-vuex-ts
npm install
Copy the code
To the body
1. Convert js files into TS files
- main.js -> main.ts
- Change script type in vue file to lang = “ts”
- Modified index. HTML
<script type="module" src="/src/main.ts"></script>
Copy the code
2. Create a vUE file declaration
- There is no VUE module declaration in the TS environment
- SRC create vue.d.ts file
declare module '*.vue' {
import { FunctionalComponent, defineComponent } from 'vue';
const component: ReturnType<typeof defineComponent> | FunctionalComponent;
export default component;
}
Copy the code
Access vuex
npm install vuex@next --save
Copy the code
Installing, let’s talk about vuex
Why can VEX2 be bidirectional binding?
let Vue;
class Store {
constructor(options) {
const { getters, state, mutations, actions } = options;
this._mutations = mutations;
this._actions = actions;
if (getters) {
this.handleGetters(getters);
}
this._vm = new Vue({
data: {
$$state: state,
},
});
this.commit = this.commit.bind(this);
this.dispatch = this.dispatch.bind(this);
}
get state() {
return this._vm.data.$$state;
}
commit(type, payload) {
const entry = this._mutations[type];
if (entry) {
entry(this.state, payload); }}dispatch(type, payload) {
const entry = this._actions[type];
if (entry) {
entry(this, payload); }}handleGetters(getters) {
this.getters = {};
Object.keys(getters).forEach(key= > {
Object.defineProperty(this.getters, key, {
get: () = > getters[key](this.state), }); }); }}function install(_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate() {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store; }}}); }export default {
Store,
install,
};
Copy the code
- Vuex is strongly bound to Vue(new Vue for observing data), so VUX cannot be used as a state tool for other states
- Mixin’s beforeCreate will attach $store to Vue’s prototype chain without blowing it. Every time a component is used, go through the prototype chain. During the Vue rendering process, beforeCreate will be executed countless times ๐
vuex3
- Reactive and provide are used for bidirectional binding. I won’t go into details here, but point out a few key points
store._state = reactive({
data: state
})
install (app, injectKey) {
app.provide(injectKey || storeKey, this)
app.config.globalProperties.$store = this
}
Copy the code
Get ready to squat
1.main.ts
import { createApp } from 'vue';
import App from './App.vue';
import store from './store';
import './index.css';
createApp(App).use(store).mount('#app');
Copy the code
2. Create something related to the Store
- index.ts
import { createStore } from 'vuex';
import modules from './modules';
import { userState } from './modules/user';
import { detailState } from './modules/detail';
export interface State {
user: userState;
detail: detailState;
}
export default createStore <
State >
{
modules,
};
Copy the code
- modules.ts
import user from './modules/user';
import detail from './modules/detail';
export default {
user,
detail,
};
Copy the code
- modules/user.ts
export type userState = {
isLogin: boolean,
};
const state: userState = {
isLogin: true};export default {
namespaced: true,
state,
getters: {
loginInfo: (state: userState): string= > {
return `${state.isLogin ? 'Logged in' : 'Not landed'}`; }},mutations: {
setUserInfo(state: userState, payload: boolean): void {
console.log('Data Request', payload); state.isLogin = payload; }},actions: {
changeUserInfo({ commit }, payload: { data: boolean }): void {
console.log('Action executed successfully');
setTimeout(function () {
commit('setUserInfo'.false);
}, 2000); ,}}};Copy the code
-
Detail.ts is omitted here
-
HelloWorld.vue
<template>
<div>
<h1>{{ msg }}</h1>
<button @click="count++">count is: {{ count }}</button>
<div>{{ info1 }}</div>
<div>{{ info2 }}</div>
<button @click="logout">exit</button>
</div>
</template>
<script lang="ts">
import { useStore } from 'vuex';
import { defineComponent, computed } from 'vue';
import { State } from '.. /store';
export default defineComponent({
name: 'HelloWorld'.props: {
msg: String,},data() {
return {
count: 0}; },setup(props) {
const { state, getters, dispatch, commit } = useStore<State>();
console.log('๐', state.user.isLogin); / / ๐ true
console.log('๐', state.detail.title); / / ๐ hello
console.log('๐', getters['user/loginInfo']); // ๐ is logged in
const info1 = computed(() = > getters['user/loginInfo']);
const info2 = computed(() = > getters['detail/detailInfo']);
const logout = () = > commit('user/setUserInfo'.false); // Data request false
// dispatch()
return{ info1, info2, logout }; }});</script>
Copy the code
At the end of the time, feel lovely ๐ถ
No, No, No, No, No, No, No, No, No, No, No, No, No. ๐คฏ
Is this really over?
- Look at the picture speak
Oh oh, State I wrapped a layer, the hint is rough
Ahiba, what the hell is this? Any, he’s too broken to talk…
Dispath: pass a string type of type. I know what to pass, so I might as well write in js
So much preparation has been made in the early stage, and the result is that the prompt let me pass the type of string, how the hell do I know what to pass, the work given to TS, let me do it myself, all the previous in vain?
It’s like going to a bar with plenty of foreplay, only to find out it’s lace ~ ๐
Do it or do it, speechless ING
Began to climb the pit
1.ไฟฎๅค State ้ฎ้ข๏ผmodules/user.ts
const state = {
isLogin: true};export type userState = typeof state;
export default {
/ /...
};
Copy the code
But that doesn’t work either. I can’t invoke State once for every component. Let’s go to the next step
2. Fix getters
-
Like React, vue uses hooks to handle state, so let’s do the whole thing
-
Gettters annotations such as ‘loginInfo’, dispatch and commit annotations need to be provided
-
- Traverse modules, find all getters, Actions, mutations, and get their type
-
- Pass them through hooks
-
Modify the HelloWorld. Vue
<script lang="ts"> // import { useStore } from 'vuex'; import { defineComponent, computed } from 'vue'; // import { State } from '.. /store'; import { useStoreHooks } from '.. /hooks'; export default defineComponent({ name: 'HelloWorld', props: { msg: String, }, data() { return { count: 0, }; }, setup(props) { // const { state, getters, dispatch, commit } = useStore<State>(); const { state, getters, dispatch, commit } = useStoreHooks(); The console. The log (' ๐ 'state. User. IsLogin); / / ๐ true on the console. The log (' ๐ 'state. The detail. The title); / / ๐ hello console. The log (' ๐, getters [' user/loginInfo ']); // ๐ logged in const info1 = computed(() => getters['user/loginInfo']); const info2 = computed(() => getters['detail/detailInfo']); const logout = () => commit('user/setUserInfo', false); // Dispatch () return {info1, info2, logout}; }}); </script>Copy the code
- Create hooks/index. Ts
import { useStore } from 'vuex';
import { State } from '.. /store';
// The type of these things
import { Getters, Dispatch, Commit } from './utils';
interface UseStoreHooks {
state: State;
getters: Getters;
commit: Commit;
dispatch: Dispatch;
}
const useStoreHooks = (): UseStoreHooks= > {
const store = useStore<State>();
console.log(store);
// return store;
// Custom output results
const { state, getters, dispatch, commit }: UseStoreHooks = store;
return {
state,
getters,
commit,
dispatch,
};
};
export { useStoreHooks };
export default useStoreHooks;
Copy the code
- hooks/utils.ts
-
- Write type, this is not JS, each paragraph needs to be viewed from the bottom up
-
- Install the latest typescript, yarn Add typescript@next (currently)
-
- Vscode switches typescrit versions
/* Get modules */ from the store
import modules from '.. /store/modules';
/* Get the getters structure type */
Infer matches the getters under a single module and infer an entry
type GetGetter<GetterType> = GetterType extends { getters: infer G } ? G : unknown;
// Get all vuex getters modules
type GetGetters<GetterTypes> = {
[K in keyof GetterTypes]: GetGetter<GetterTypes[K]>;
};
type ModuleGetters = GetGetters<typeof modules>;
// --------------------
/* Obtain the structural type */
// mutations are available on a single module
type GetMutation<MutationType> = MutationType extends { mutations: infer M } ? M : unknown;
// Obtain all mutations modules of VUEX
type GetMutations<MutationTypes> = {
[K in keyof MutationTypes]: GetMutation<MutationTypes[K]>;
};
type ModuleMutations = GetMutations<typeof modules>;
// --------------------
/* Get the actions structure type */
// assign an action to a single module
type GetAction<ActionType> = ActionType extends { actions: infer A } ? A : unknown;
// Get all vuex actions modules
type GetActions<ActionTypes> = {
[K in keyof ActionTypes]: GetAction<ActionTypes[K]>;
};
type ModuleActions = GetActions<typeof modules>;
// --------------------
/* Getter/Commit/Dispatch Intelligent prompt processing */
// gettters[module name/method], COMMIT [module name/method], dispatch[module name/method]
Ts4.1 supports template string syntax. Install the latest YARN typescript (currently yarn add typescript@next).
/ / incoming keyof is probably the symbol | number, so P & string in the string
type AddPrefix<P, K> = `${P & string}/${K & string}`;
// Switch the order: user: "user/loginInfo" => "user/loginInfo": user
type GetSpliceKey<Module, Key> = AddPrefix<Key, keyof Module>
/** * { 'user/loginInfo': () => {} } */
// type GetSpliceKeys<Modules> = {
// [K in keyof Modules]: GetSpliceKey
[k],>
// }
// type xx = GetSpliceKeys<ModuleGetters>
type GetSpliceKeys<Modules> = {
[K in keyof Modules]: GetSpliceKey<Modules[K], K>
}[keyof Modules]
// type xx = GetSpliceKeys<ModuleGetters>
type GetFunc<T, A, B> = T[A & keyof T][B & keyof T[A & keyof T]];
type GetSpliceObj<T> = {
// K extends' ${infer A}/${infer B} 'equivalent to user/loginInfo A=>user B=>loginInfo
[K in GetSpliceKeys<T>]:K extends `${infer A}/${infer B}` ? GetFunc<T, A, B> : unknown
}
// --------------------
/ * Getters/Mutations Actons together * / / XXX XXX format
type GenGetters = GetSpliceObj<ModuleGetters>;
type Getters = {
[K in keyof GenGetters]:ReturnType<GenGetters[K]>
}
// --------------------
type GenMutations = GetSpliceObj<ModuleMutations>;
type Mutations = {
[K in keyof GenMutations]:ReturnType<GenMutations[K]>
}
// --------------------
type GenActions = GetSpliceObj<ModuleActions>;
type Actions = {
[K in keyof GenActions]:ReturnType<GenActions[K]>
}
// --------------------
// commit Obtains the payload parameter type
type MutationsPayload = {
// Parameters gets function Parameters
[K in keyof GenMutations]:Parameters<GenMutations[K]>[1]
}
interface GetCommit<T> {
// Optional parameter type. Undefined is automatically added<K extends keyof T>(mutation: K, payload? : T[K]): void; } type Commit = GetCommit<MutationsPayload>; // -------------------- // dispatch Obtain payload Parameter Type Type ActionPayload = {// Parameters Obtain function Parameters [K in keyof GenActions]:Parameters<GenActions[K]>[1]} interface GetDispatch<T> { Undefined <K extends keyof T>(action: K, payload? : T[K]): Promise<unknown>; } type Dispatch = GetDispatch<ActionPayload>; // -------------------- export { Getters, Mutations, Actions, Dispatch, Commit };Copy the code
- So let’s see what happens
Hooks, type is already in there
I’m f * * king mad. I can get getters in hooks, but I can’t get them outside
Wake up from a dream, like a different world
โค๏ธ full of holes
-
Ts can be obtained, vue can not be obtained, ha ha ~
-
Ok, get the script out, that’s enough, patience is running out…
1. Put all the ts parts of HelloWorld.vue into HelloWorld.ts
2. Modify the HelloWorld. Vue
<template>
<div>
<h1>{{ msg }}</h1>
<button @click="count++">count is: {{ count }}</button>
<div>{{ info1 }}</div>
<div>{{ info2 }}</div>
<button @click="logout">exit</button>
</div>
</template>
<script lang="ts" src="./HelloWorld.ts"></script>
Copy the code
- Go to HelloWorld.ts and check it out. It finally worked
getters
commit
dispatch
In this paper, the code
The end of the
Look out of the window, the day is already bright, smoke has finished. Neither happy nor sad, tired of…
Really learn not to move: hand lighter, where will not point where, mother no longer need to worry about my learning…
Join us at โค๏ธ
Bytedance Xinfoli team
Nice Leader: Senior technical expert, well-known gold digger columnist, founder of Flutter Chinese community, founder of Flutter Chinese community open source project, well-known developer of Github community, author of dio, FLY, dsBridge and other well-known open source projects
We look forward to your joining us to change our lives with technology!!
Recruitment link: job.toutiao.com/s/JHjRX8B