iT邦幫忙

2022 iThome 鐵人賽

DAY 25
0
Modern Web

如何用TypeScript水30天鐵人賽系列 第 25

[Day25]:一個大Store - Store改寫

  • 分享至 

  • xImage
  •  

Day25 Banner

一個大Store

想要我的狀態嗎? 想要的話可以全部給你,
去找吧!我把所有狀態都放在Store裡。
───────────────────────── By 哥爾‧D‧羅傑


目標: 改寫Store


過程:

  • 原先的store

     // index.js
     import { createStore } from "vuex";
     import user from "./user"; // 登入、使用者相關
     import route from "./route"; // 路由相關
    
     export default createStore({
       state: {},
       getter: {},
       actions: {}, // 變動state 用(可非同步)
       mutations: {} // 變動 state 用(只能同步)
       modules: { user, route },
     });
    

    看的出來有兩個modules
    我們一個一個來處理:

    1. user.js

    這段式原先的JS
    我們複製一個,丟進 'src/store/'裡,
    並把檔名改成user.ts
    會發現很多地方開始提醒你有問題:

     // user.ts
     import { Base64 } from "js-base64";
    
     export const state = {
       isLogin: false, // 是否有登入
       jwtToken: '',
       userData: {
          // 登入資訊
          user: '',
          name: '',
          /** 使用者權限劃分 permission level
             *  0 最高管理者 全開
             *  1 網站管理者
             *  2 一般使用者
             *  3 訪客
             */
          auth: '3',
       },
     };
     export const actions = {};// 非同步
     export const mutations = { // 同步
       signIn(state) { state.isLogin = true; },
       signOut(state) { state.isLogin = false; },
       /** 將登入後的 Token 轉 成userData
       * @param {*} state
       * @param {JWT} JWT
       */
       setUser(state, JWT) {
          state.jwtToken = JWT;
    
          // JWT = header.payload.Signature
          const Token = JWT.split("."); // 解析使用者資料
          const data = JSON.parse(Base64.decode(Token[1]));
    
          // 記錄登入者資訊
          state.userData.user = data.user;
          state.userData.name = data.name;
          state.userData.auth = data.auth;
       },
     };
     export const getters = {
       isAuthenticated: state => !!state.user || !!sessionStorage.getItem("user"),
     };
    
     export default {
       state,
       getters,
       actions,
       mutations,
       namespaced: true,
     };
    

    看來看去,最主要的問題是,
    state的型別沒有宣告,所以我們改成這樣:

     // user.ts
     // 把使用者權限列舉起來
     enum PERMISSION_LEVEL {
       TOP_MANAGEMENT, // 0 最高管理者 全開
       MANAGER, // 1 網站管理者
       USER, // 2 一般使用者
       VISITOR // 3 訪客
     }
    
     // 宣告、並匯出user的State
     export interface iUserState {
       isLogin: Boolean, // 是否有登入
       jwtToken: string,
       userData: {
          user: string,
          name: string,
          auth: PERMISSION_LEVEL,
       },
     }
    
     export default {
       namespaced: true,
       state: {
          isLogin: false,
          jwtToken: '',
          userData: {
                user: '',
                name: '',
                auth: PERMISSION_LEVEL.VISITOR,
          },
       },
       // 把state通通補上型別
       mutations: { // 同步
          signIn(state: iUserState) { state.isLogin = true; },
          signOut(state: iUserState) { state.isLogin = false; },
          /** 將登入後的 Token 轉 成userData
             * @param {*} state
             * @param {JWT} JWT
             */
          setUser(state: iUserState, JWT: string) {
                state.jwtToken = JWT;
    
                // JWT = header.payload.Signature
                const Token = JWT.split("."); // 解析使用者資料
                const data = JSON.parse(Base64.decode(Token[1]));
    
                // 記錄登入者資訊
                state.userData.user = data.user;
                state.userData.name = data.name;
                state.userData.auth = data.auth;
          },
       },
       getters: { // 這邊順便被提醒state.user 不存在,把userData補上去。
          isAuthenticated: (state: iUserState) => !!state.userData.user || !!sessionStorage.getItem("user"),
       }
     }
    

    然後安裝一下:js-base64
    JWT機制,解碼用的:

     yarn add js-base64 -D
    

    由於js-base64已經自帶.d.ts了,
    所以你啥都不用做就可以直接用了。
    剛改寫第一支JS程式,馬上就體驗到TS的好用與方便。
    route.js的改寫方式大同小異,這邊就省略掉了。

    2. index.js

    modules都改寫完之後,把昨天調整過的src/store/index.js改成這樣:

     import { InjectionKey } from 'vue'
     import { createStore, useStore as baseUseStore, Store } from 'vuex'
    
     import { iRouteState } from './modules/route'; // 路由相關
     import { iUserState } from './modules/user'; // 登入、使用者相關
     export interface State {
       route: iRouteState,
       user: iUserState,
     }
    
     // 全部模組導入
     let modules = {}
     const modulesFiles = import.meta.glob("./modules/*.ts", { eager: true, import: 'default' });
     for (const path in modulesFiles) {
         const moduleName = path.replace(/(.*\/)*([^.]+).*/gi, '$2');
         console.log(modulesFiles);
         modules = {
             ...modules,
             [moduleName]: modulesFiles[path]
         }
     }
    
     export const key: InjectionKey<Store<State>> = Symbol()
     export const store = createStore<State>({
       modules
     })
    
     // 定義自己的 `useStore` composition(組合式) API
     export function useStore() {
       return baseUseStore(key)
     }
    

    這樣就大功告成啦~


小結:

實際使用TypeScript改寫JS,
發現好像沒有想像中的難,
有一種用實際的程式碼寫註解的既視感,
好像可以繼續改寫下去,有完賽的可能性ㄟ!!


上一篇
[Day24]:Vuex安裝&設定
下一篇
[Day26]: 這才不是PS5 - axios & router
系列文
如何用TypeScript水30天鐵人賽33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言