首页 > 其他分享 >Vue3+TypeScript+Vite+Pinia+ElementPlus开发项目在线医疗服务平台

Vue3+TypeScript+Vite+Pinia+ElementPlus开发项目在线医疗服务平台

时间:2024-09-03 10:46:55浏览次数:7  
标签:TypeScript const vue ts return ElementPlus Pinia import config

 

前言

随着vue3.0的越来越受欢迎,开始有许多公司和个人开始学习并使用vue3开发项目。我从接触学习并使用vue2,到现在的vue3,工作中也一直在使用vue。vue3也发布很长时间了,目前vue3+vite+ts再结合一些优秀的UI框架,如Element plus,Ant design,Naive UI,移动端的Vant UI,成为了较为流行的前端技术之一。那么今天就带大家一起来搭建一个Vue3的项目吧!

 

一、使用 Vite 快速搭建脚手架

兼容性注意

Vite 需要 Node.js 版本 >= 12.0.0。

 

1. 命令行选项直接指定项目名称和想要使用的模板,Vite + Vue 项目,运行(推荐使用yarn)

 

 
  1.   # npm 6.x
  2.   npm init vite@latest my-vue-app --template vue
  3.    
  4.   # npm 7+, 需要额外的双横线:
  5.   npm init vite@latest my-vue-app -- --template vue
  6.    
  7.   # yarn
  8.   yarn create vite my-vue-app --template vue
  9.    
  10.   # pnpm
  11.   pnpm create vite my-vue-app -- --template vue
 
 

这里我们想要直接生成一个Vue3+Vite2+ts的项目模板,因此我们执行的命令是: yarn create vite my-vue-app --template vue-ts,这样我们就不需要你单独的再去安装配置ts了。

60cdf2259a1470ca51e88216186940cd.png

21a43a00975d21d39ff0bbe86491f9e6.png

2. cd 到项目文件夹,安装node_modules依赖,运行项目

70a1653673ed3f47a05a11475f46e25e.png

 
  1.   # cd进入my-vue-app项目文件夹
  2.   cd my-vue-app
  3.   # 安装依赖
  4.   yarn
  5.   # 运行项目
  6.   yarn dev
 
 

    项目结构如下:

b9ef196ea472b667412dda9da1ff1951.png

至此,一个最纯净的vue3.0+vite2+typescript项目就完成了。在浏览地址栏中输入http://localhost:3000/,就看到了如下的启动页,然后就可以安装所需的插件了。

87f189480557a45c9034e8f35668aa1a.png

64ba4cd17512070fd7c569b7995b59b2.png

d64a6693e29e49c37c9d0f0bf0f4613d.png

二、配置文件路径引用别名 alias

修改vite.config.ts中的reslove的配置

cc6b25d2f93122c0d40d38c9046a3920.png

 
  1.   import { defineConfig } from 'vite'
  2.   import vue from '@vitejs/plugin-vue'
  3.   import path from 'path'
  4.    
  5.   // https://vitejs.dev/config/
  6.   export default defineConfig({
  7.     plugins: [vue()],
  8.     resolve: {
  9.       alias: {
  10.         '@': path.resolve(__dirname, 'src'),
  11.       },
  12.     },
  13.   })
 
 

在修改tsconfig.json文件的配置

616a524da4e2ebfbfa9011f3650aa445.png

 
  1.   {
  2.     "compilerOptions": {
  3.       "target": "esnext",
  4.       "module": "esnext",
  5.       "moduleResolution": "node",
  6.       "strict": true,
  7.       "jsx": "preserve",
  8.       "sourceMap": true,
  9.       "resolveJsonModule": true,
  10.       "esModuleInterop": true,
  11.       "lib": ["esnext", "dom"],
  12.       "baseUrl": ".",
  13.       "paths": {
  14.         "@/*":["src/*"]
  15.       }
  16.     },
  17.     "include": [
  18.       "src/**/*.ts", 
  19.       "src/**/*.d.ts", 
  20.       "src/**/*.tsx", 
  21.       "src/**/*.vue"
  22.     ]
  23.   }
 
 

86907cb6d5667f00e6da7fb8c84e1b0d.png

三、配置路由

1. 安装

6b2d1aca3c313685b77335067994bd76.png

 
  1.   # npm
  2.   npm install vue-router@4
  3.    
  4.   # yarn
  5.   yarn add vue-router@4
 
 

2. 在src下新建router文件夹,用来集中管理路由,在router文件夹下新建    index.ts文件。

3f9ce6f1575a7551e8469ff84a50a30c.png

 
  1.   import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
  2.    
  3.   const routes: RouteRecordRaw[] = [
  4.     {
  5.       path: '/',
  6.       name: 'Login',
  7.       // 注意这里要带上文件后缀.vue
  8.       component: () => import('@/pages/login/Login.vue'), 
  9.       meta: {
  10.         title: '登录',
  11.       },
  12.     },
  13.   ]
  14.    
  15.   const router = createRouter({
  16.     history: createWebHistory(),
  17.     routes,
  18.     strict: true,
  19.     // 期望滚动到哪个的位置
  20.     scrollBehavior(to, from, savedPosition) {
  21.       return new Promise(resolve => {
  22.         if (savedPosition) {
  23.           return savedPosition;
  24.         } else {
  25.           if (from.meta.saveSrollTop) {
  26.             const top: number =
  27.               document.documentElement.scrollTop || document.body.scrollTop;
  28.             resolve({ left: 0, top });
  29.           }
  30.         }
  31.       });
  32.     }
  33.   })
  34.    
  35.   export function setupRouter(app: App) {
  36.     app.use(router);
  37.   }
  38.    
  39.   export default router
 
 

3. 修改入口文件 mian.ts

29f9437551b21fd070ebff42ef1c6367.png

 
  1.   import { createApp } from "vue";
  2.   import App from "./App.vue";
  3.   import router, { setupRouter } from './router';
  4.    
  5.   const app = createApp(App);
  6.   // 挂在路由
  7.   setupRouter(app);
  8.   // 路由准备就绪后挂载APP实例
  9.   await router.isReady();
  10.    
  11.   app.mount('#app', true);
 
 

更多的路由配置可以移步vue-router(https://next.router.vuejs.org/zh/introduction.html)。

vue-router4.x支持typescript,路由的类型为RouteRecordRaw。meta字段可以让我们根据不同的业务需求扩展 RouteMeta 接口来输入它的多样性。以下的meta中的配置仅供参考:

 
  1.   // typings.d.ts or router.ts
  2.   import 'vue-router'
  3.    
  4.   declare module 'vue-router' {
  5.     interface RouteMeta {
  6.       // 页面标题,通常必选。
  7.       title: string; 
  8.       // 菜单图标
  9.       icon?: string; 
  10.       // 配置菜单的权限
  11.       permission: string[];
  12.       // 是否开启页面缓存
  13.       keepAlive?: boolean;
  14.       // 二级页面我们并不想在菜单中显示
  15.       hidden?: boolean; 
  16.       // 菜单排序
  17.       order?: number; 
  18.       // 嵌套外链
  19.       frameUrl?: string; 
  20.     }
  21.   }
 
 

be48c37f0151d4cf0d14d42b0601e314.png

四、配置 css 预处理器 scss

1. 安装

576410f9f4b0ee81ebd1cf931df34ffb.png

 
  1.   yarn ass sass-loader --dev
  2.   yarn add dart-sass --dev
  3.   yarn add sass --dev
 
 

2.配置全局 scss 样式文件

在 src文件夹下新增 styles 文件夹,用于存放全局样式文件,新建一个 varibles.scss文件,用于统一管理声明的颜色变量:

217b501dabfa945e6686a60904443b44.png

 
  1.   $white: #FFFFFF;
  2.   $primary-color: #1890ff;
  3.   $success-color: #67C23A;
  4.   $warning-color: #E6A23C;
  5.   $danger-color: #F56C6C;
  6.   $info-color: #909399;
 
 

3. 组件中使用

在vite.config.ts中将这个样式文件全局注入到项目即可全局使用,

不需要在任何组件中再次引入这个文件或者颜色变量。

f668a2ec33d5a3927f81f9c1eadb2309.png

 
  1.   css: {
  2.     preprocessorOptions: {
  3.       scss: {
  4.         modifyVars: {},
  5.         javascriptEnabled: true,
  6.         // 注意这里的引入的书写
  7.         additionalData: '@import "@/style/varibles.scss";'
  8.       }
  9.     }
  10.   },
 
 

在组件中使用

16a547147580e32c9724d0657e1d83b3.png

 
  1.   .div {
  2.     color: $primary-color;
  3.     background-color: $success-color;
  4.   }
 
 

916d31ca6e600d4e385b3d58d941d9b2.png

五、统一请求封装

在src文件夹下,新建http文件夹,在http文件夹下新增index.ts,config.ts,core.ts,types.d.ts,utils.ts

c455a7fc51eba5275dea74151675eb6a.png

   core.ts

 
  1.   import Axios, { AxiosRequestConfig, CancelTokenStatic, AxiosInstance } from "axios";
  2.   import NProgress from 'nprogress'
  3.   import { genConfig } from "./config";
  4.   import { transformConfigByMethod } from "./utils";
  5.   import {
  6.     cancelTokenType,
  7.     RequestMethods,
  8.     HttpRequestConfig,
  9.     HttpResoponse,
  10.     HttpError
  11.   } from "./types.d";
  12.    
  13.   class Http {
  14.     constructor() {
  15.       this.httpInterceptorsRequest();
  16.       this.httpInterceptorsResponse();
  17.     }
  18.     // 初始化配置对象
  19.     private static initConfig: HttpRequestConfig = {};
  20.    
  21.     // 保存当前Axios实例对象
  22.     private static axiosInstance: AxiosInstance = Axios.create(genConfig());
  23.    
  24.     // 保存 Http实例
  25.     private static HttpInstance: Http;
  26.    
  27.     // axios取消对象
  28.     private CancelToken: CancelTokenStatic = Axios.CancelToken;
  29.    
  30.     // 取消的凭证数组
  31.     private sourceTokenList: Array<cancelTokenType> = [];
  32.    
  33.     // 记录当前这一次cancelToken的key
  34.     private currentCancelTokenKey = "";
  35.    
  36.     public get cancelTokenList(): Array<cancelTokenType> {
  37.       return this.sourceTokenList;
  38.     }
  39.    
  40.     // eslint-disable-next-line class-methods-use-this
  41.     public set cancelTokenList(value) {
  42.       throw new Error("cancelTokenList不允许赋值");
  43.     }
  44.    
  45.     /**
  46.      * @description 私有构造不允许实例化
  47.      * @returns void 0
  48.      */
  49.     // constructor() {}
  50.    
  51.     /**
  52.      * @description 生成唯一取消key
  53.      * @param config axios配置
  54.      * @returns string
  55.      */
  56.     // eslint-disable-next-line class-methods-use-this
  57.     private static genUniqueKey(config: HttpRequestConfig): string {
  58.       return `${config.url}--${JSON.stringify(config.data)}`;
  59.     }
  60.    
  61.     /**
  62.      * @description 取消重复请求
  63.      * @returns void 0
  64.      */
  65.     private cancelRepeatRequest(): void {
  66.       const temp: { [key: string]: boolean } = {};
  67.    
  68.       this.sourceTokenList = this.sourceTokenList.reduce<Array<cancelTokenType>>(
  69.         (res: Array<cancelTokenType>, cancelToken: cancelTokenType) => {
  70.           const { cancelKey, cancelExecutor } = cancelToken;
  71.           if (!temp[cancelKey]) {
  72.             temp[cancelKey] = true;
  73.             res.push(cancelToken);
  74.           } else {
  75.             cancelExecutor();
  76.           }
  77.           return res;
  78.         },
  79.         []
  80.       );
  81.     }
  82.    
  83.     /**
  84.      * @description 删除指定的CancelToken
  85.      * @returns void 0
  86.      */
  87.     private deleteCancelTokenByCancelKey(cancelKey: string): void {
  88.       this.sourceTokenList =
  89.         this.sourceTokenList.length < 1
  90.           ? this.sourceTokenList.filter(
  91.               cancelToken => cancelToken.cancelKey !== cancelKey
  92.             )
  93.           : [];
  94.     }
  95.    
  96.     /**
  97.      * @description 拦截请求
  98.      * @returns void 0
  99.      */
  100.    
  101.     private httpInterceptorsRequest(): void {
  102.       Http.axiosInstance.interceptors.request.use(
  103.         (config: HttpRequestConfig) => {
  104.           const $config = config;
  105.           NProgress.start(); // 每次切换页面时,调用进度条
  106.           const cancelKey = Http.genUniqueKey($config);
  107.           $config.cancelToken = new this.CancelToken(
  108.             (cancelExecutor: (cancel: any) => void) => {
  109.               this.sourceTokenList.push({ cancelKey, cancelExecutor });
  110.             }
  111.           );
  112.           this.cancelRepeatRequest();
  113.           this.currentCancelTokenKey = cancelKey;
  114.           // 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
  115.           if (typeof config.beforeRequestCallback === "function") {
  116.             config.beforeRequestCallback($config);
  117.             return $config;
  118.           }
  119.           if (Http.initConfig.beforeRequestCallback) {
  120.             Http.initConfig.beforeRequestCallback($config);
  121.             return $config;
  122.           }
  123.           return $config;
  124.         },
  125.         error => {
  126.           return Promise.reject(error);
  127.         }
  128.       );
  129.     }
  130.    
  131.     /**
  132.      * @description 清空当前cancelTokenList
  133.      * @returns void 0
  134.      */
  135.     public clearCancelTokenList(): void {
  136.       this.sourceTokenList.length = 0;
  137.     }
  138.    
  139.     /**
  140.      * @description 拦截响应
  141.      * @returns void 0
  142.      */
  143.     private httpInterceptorsResponse(): void {
  144.       const instance = Http.axiosInstance;
  145.       instance.interceptors.response.use(
  146.         (response: HttpResoponse) => {
  147.           const $config = response.config;
  148.           // 请求每次成功一次就删除当前canceltoken标记
  149.           const cancelKey = Http.genUniqueKey($config);
  150.           this.deleteCancelTokenByCancelKey(cancelKey);
  151.    
  152.           NProgress.done();
  153.           // 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
  154.           if (typeof $config.beforeResponseCallback === "function") {
  155.             $config.beforeResponseCallback(response);
  156.             return response.data;
  157.           }
  158.           if (Http.initConfig.beforeResponseCallback) {
  159.             Http.initConfig.beforeResponseCallback(response);
  160.             return response.data;
  161.           }
  162.           return response.data;
  163.         },
  164.         (error: HttpError) => {
  165.           const $error = error;
  166.           // 判断当前的请求中是否在 取消token数组理存在,如果存在则移除(单次请求流程)
  167.           if (this.currentCancelTokenKey) {
  168.             const haskey = this.sourceTokenList.filter(
  169.               cancelToken => cancelToken.cancelKey === this.currentCancelTokenKey
  170.             ).length;
  171.             if (haskey) {
  172.               this.sourceTokenList = this.sourceTokenList.filter(
  173.                 cancelToken =>
  174.                   cancelToken.cancelKey !== this.currentCancelTokenKey
  175.               );
  176.               this.currentCancelTokenKey = "";
  177.             }
  178.           }
  179.           $error.isCancelRequest = Axios.isCancel($error);
  180.           NProgress.done();
  181.           // 所有的响应异常 区分来源为取消请求/非取消请求
  182.           return Promise.reject($error);
  183.         }
  184.       );
  185.     }
  186.    
  187.     public request<T>(
  188.       method: RequestMethods,
  189.       url: string,
  190.       param?: AxiosRequestConfig,
  191.       axiosConfig?: HttpRequestConfig
  192.     ): Promise<T> {
  193.       const config = transformConfigByMethod(param, {
  194.         method,
  195.         url,
  196.         ...axiosConfig
  197.       } as HttpRequestConfig);
  198.       // 单独处理自定义请求/响应回掉
  199.       return new Promise((resolve, reject) => {
  200.         Http.axiosInstance
  201.           .request(config)
  202.           .then((response: undefined) => {
  203.             resolve(response);
  204.           })
  205.           .catch((error: any) => {
  206.             reject(error);
  207.           });
  208.       });
  209.     }
  210.    
  211.     public post<T>(
  212.       url: string,
  213.       params?: T,
  214.       config?: HttpRequestConfig
  215.     ): Promise<T> {
  216.       return this.request<T>("post", url, params, config);
  217.     }
  218.    
  219.     public get<T>(
  220.       url: string,
  221.       params?: T,
  222.       config?: HttpRequestConfig
  223.     ): Promise<T> {
  224.       return this.request<T>("get", url, params, config);
  225.     }
  226.   }
  227.    
  228.   export default Http;
 
 

  config.ts

 
  1.   import { AxiosRequestConfig } from "axios";
  2.   import { excludeProps } from "./utils";
  3.   /**
  4.    * 默认配置
  5.    */
  6.   export const defaultConfig: AxiosRequestConfig = {
  7.     baseURL: "",
  8.     //10秒超时
  9.     timeout: 10000,
  10.     headers: {
  11.       Accept: "application/json, text/plain, */*",
  12.       "Content-Type": "application/json",
  13.       "X-Requested-With": "XMLHttpRequest"
  14.     }
  15.   };
  16.    
  17.   export function genConfig(config?: AxiosRequestConfig): AxiosRequestConfig {
  18.     if (!config) {
  19.       return defaultConfig;
  20.     }
  21.    
  22.     const { headers } = config;
  23.     if (headers && typeof headers === "object") {
  24.       defaultConfig.headers = {
  25.         ...defaultConfig.headers,
  26.         ...headers
  27.       };
  28.     }
  29.     return { ...excludeProps(config!, "headers"), ...defaultConfig };
  30.   }
  31.    
  32.   export const METHODS = ["post", "get", "put", "delete", "option", "patch"];
 
 

   utils.ts

 
  1.   import { HttpRequestConfig } from "./types.d";
  2.    
  3.   export function excludeProps<T extends { [key: string]: any }>(
  4.     origin: T,
  5.     prop: string
  6.   ): { [key: string]: T } {
  7.     return Object.keys(origin)
  8.       .filter(key => !prop.includes(key))
  9.       .reduce((res, key) => {
  10.         res[key] = origin[key];
  11.         return res;
  12.       }, {} as { [key: string]: T });
  13.   }
  14.    
  15.   export function transformConfigByMethod(
  16.     params: any,
  17.     config: HttpRequestConfig
  18.   ): HttpRequestConfig {
  19.     const { method } = config;
  20.     const props = ["delete", "get", "head", "options"].includes(
  21.       method!.toLocaleLowerCase()
  22.     )
  23.       ? "params"
  24.       : "data";
  25.     return {
  26.       ...config,
  27.       [props]: params
  28.     };
  29.   }
 
 

   types.d.ts

 
  1.   import Axios, {
  2.     AxiosRequestConfig,
  3.     Canceler,
  4.     AxiosResponse,
  5.     Method,
  6.     AxiosError
  7.   } from "axios";
  8.    
  9.   import { METHODS } from "./config";
  10.    
  11.   export type cancelTokenType = { cancelKey: string; cancelExecutor: Canceler };
  12.    
  13.   export type RequestMethods = Extract<
  14.     Method,
  15.     "get" | "post" | "put" | "delete" | "patch" | "option" | "head"
  16.   >;
  17.    
  18.   export interface HttpRequestConfig extends AxiosRequestConfig {
  19.     // 请求发送之前
  20.     beforeRequestCallback?: (request: HttpRequestConfig) => void; 
  21.     // 相应返回之前
  22.     beforeResponseCallback?: (response: HttpResoponse) => void; 
  23.   }
  24.    
  25.   export interface HttpResoponse extends AxiosResponse {
  26.     config: HttpRequestConfig;
  27.   }
  28.    
  29.   export interface HttpError extends AxiosError {
  30.     isCancelRequest?: boolean;
  31.   }
  32.    
  33.   export default class Http {
  34.     cancelTokenList: Array<cancelTokenType>;
  35.     clearCancelTokenList(): void;
  36.     request<T>(
  37.       method: RequestMethods,
  38.       url: string,
  39.       param?: AxiosRequestConfig,
  40.       axiosConfig?: HttpRequestConfig
  41.     ): Promise<T>;
  42.     post<T>(
  43.       url: string,
  44.       params?: T,
  45.       config?: HttpRequestConfig
  46.     ): Promise<T>;
  47.     get<T>(
  48.       url: string,
  49.       params?: T,
  50.       config?: HttpRequestConfig
  51.     ): Promise<T>;
  52.   }
 
 

  index.ts

 
  1.   import Http from "./core";
  2.   export const http = new Http();
 
 

9f3fa46b6767ccb648bf5890127d420c.png

六、统一api管理

在src下新增api文件夹,对项目中接口做统一管理,按照模块来划分。

例如,在 api 文件下新增 user.ts和types.ts ,分别用于存放登录,注册等模块的请求接口和数据类型。

236e272e17d731238dc35f93ce9c20c5.png

 
  1.   // login.ts
  2.   import { http } from "@/http/index";
  3.   import { ILoginReq, ILoginRes } from "./types";
  4.    
  5.   export const getLogin = async(req: ILoginParams): Promise<ILoginRes> => {
  6.     const res:any = await http.post('/login/info', req)
  7.     return res as ILoginRes
  8.   }
  9.   # 或者
  10.   export const getLogin1 = async(req: ILoginParams): Promise<ILoginRes> => {
  11.     const res:any = await http.request('post', '/login/info', req)
  12.     return res as ILoginRes
  13.   }
 
 
 
  1.   // types.ts
  2.   export interface ILoginReq {
  3.     userName: string;
  4.     password: string;
  5.   }
  6.    
  7.   export interface ILoginRes {
  8.     access_token: string;
  9.     refresh_token: string;
  10.     scope: string
  11.     token_type: string
  12.     expires_in: string
  13.   }
 
 

除了自己手动封装 axios ,这里还推荐一个十分非常强大牛皮的 vue3 的请求库: VueRequest,里面的功能非常的丰富(偷偷告诉你我也在使用中)。官网地址:https://www.attojs.com/

9ba1cb4ef3961f9aab18dc60ffda7e36.png

be9c6088178469969160a6a92df3f599.png

VueRequest 旨在为开发者提供便捷、快速的方式来管理接口的状态。只需要简单的配置即可使用,专注于业务核心的开发。

f7ee7ac4b86e3abde17ba48470345001.jpeg

b269b90fc7fe6f8eace541e81735e635.png

七、状态管理 Pinia

Pinia 是 Vue.js 的轻量级状态管理库,最近很受欢迎。它使用 Vue 3 中的新反应系统来构建一个直观且完全类型化的状态管理库。

由于 vuex 4 对 typescript 的支持很不友好,所以状态管理弃用了 vuex 而采取了 pinia, pinia 的作者是 Vue 核心团队成员,并且pinia已经正式加入了Vue,成为了Vue中的一员。尤大佬 pinia 可能会代替 vuex,所以请放心使用(公司项目也在使用中)。

Pinia官网地址(https://pinia.vuejs.org)

0164d44bdd8335d9994bce3906e39f9f.png

Pinia的一些优点:

(1)Pinia 的 API 设计非常接近 Vuex 5 的提案。

(2)无需像 Vuex 4 自定义复杂的类型来支持 typescript,天生具备完美的类型推断。

(3)模块化设计,你引入的每一个 store 在打包时都可以自动拆分他们。

(4)无嵌套结构,但你可以在任意的 store 之间交叉组合使用。

(5)Pinia 与 Vue devtools 挂钩,不会影响 Vue 3 开发体验。

6f12af6c055044cef333faf4500a40c6.png

b1cf40cc4b8298527c42186a726d8b20.jpeg

Pinia的成功可以归功于其管理存储数据的独特功能(可扩展性、存储模块组织、状态变化分组、多存储创建等)。

另一方面,Vuex也是为Vue框架建立的一个流行的状态管理库,它也是Vue核心团队推荐的状态管理库。Vuex高度关注应用程序的可扩展性、开发人员的工效和信心。它基于与Redux相同的流量架构。

Pinia和Vuex都非常快,在某些情况下,使用Pinia的web应用程序会比使用Vuex更快。这种性能的提升可以归因于Pinia的极轻的体积,Pinia体积约1KB。

76529e4e6640a31c904c0227adaed8a7.png

1.安装

2d38cb10a58ba9cd89c550b4767aba11.png

 
  1.   # 安装
  2.   yarn add pinia@next
 
 

2.在src下新建store文件夹,在store文件夹下新建index.ts,mutation-types(变量集中管理),types.ts(类型)和modules文件夹(分模块管理状态)

53e6b1bc756bb9231dc235a531424a32.png

b3957cb1b12065a82a9875a921272f23.jpeg

 
  1.   // index.ts
  2.   import type { App } from "vue";
  3.   import { createPinia } from "pinia";
  4.    
  5.   const store = createPinia();
  6.   export function setupStore(app: App<Element>) {
  7.       app.use(store)
  8.   }
  9.    
  10.   export { store }
 
 
 
  1.   // modules/user.ts
  2.   import { defineStore } from 'pinia';
  3.   import { store } from '@/store';
  4.   import { ACCESS_TOKEN } from '@/store/mutation-types';
  5.   import { IUserState } from '@/store/types'
  6.    
  7.   export const useUserStore = defineStore({
  8.     // 此处的id很重要
  9.     id: 'app-user',
  10.     state: (): IUserState => ({
  11.       token: localStorge.getItem(ACCESS_TOKEN)
  12.     }),
  13.     getters: {
  14.       getToken(): string {
  15.         return this.token;
  16.       }
  17.     },
  18.     actions: {
  19.       setToken(token: string) {
  20.         this.token = token;
  21.       },
  22.       // 登录
  23.       async login(userInfo) {
  24.         try {
  25.           const response = await login(userInfo);
  26.           const { result, code } = response;
  27.           if (code === ResultEnum.SUCCESS) {
  28.             localStorage.setItem(ACCESS_TOKEN, result.token);
  29.             this.setToken(result.token);
  30.           }
  31.           return Promise.resolve(response);
  32.         } catch (e) {
  33.           return Promise.reject(e);
  34.         }
  35.       },
  36.     }
  37.   })
  38.    
  39.   // Need to be used outside the setup
  40.   export function useUserStoreHook() {
  41.     return useUserStore(store);
  42.   }
 
 
 
  1.   /// mutation-types.ts
  2.   // 对变量做统一管理
  3.   export const ACCESS_TOKEN = 'ACCESS-TOKEN'; // 用户token
 
 

3.修改main.ts

40b925b1bdf765fa57667417b8f208b8.png

 
  1.   import { createApp } from 'vue'
  2.   import App from './App.vue'
  3.   import { setupStore } from '@/store'
  4.   import router from './router/index'
  5.    
  6.   const app = createApp(App)
  7.   // 挂载状态管理
  8.   setupStore(app);
  9.    
  10.   app.use(router)
  11.    
  12.   app.mount('#app')
 
 

4.在组件中使用

2e987245424b243de4a7955cf06d6360.png

 
  1.   <template>
  2.     <div>{{userStore.token}}</div>
  3.   </template>
  4.    
  5.   <script lang="ts">
  6.   import { defineComponent } from 'vue'
  7.   import { useUserStoreHook } from "@/store/modules/user"
  8.    
  9.   export default defineComponent({
  10.     setup() {
  11.       const userStore = useUserStoreHook()
  12.       
  13.       return {
  14.         userStore
  15.       }
  16.     },
  17.   })
  18.   </script>
 
 

5.getters的用法介绍

5c03d41e114fffd233cc0c8a71153442.png

 
  1.   // modules/user.ts
  2.   import { defineStore } from 'pinia';
  3.   import { store } from '@/store';
  4.   import { ACCESS_TOKEN } from '@/store/mutation-types';
  5.   import { IUserState } from '@/store/types'
  6.    
  7.   export const useUserStore = defineStore({
  8.     // 此处的id很重要
  9.     id: 'app-user',
  10.     state: (): IUserState => ({
  11.       token: localStorge.getItem(ACCESS_TOKEN),
  12.       name: ''
  13.     }),
  14.     getters: {
  15.       getToken(): string {
  16.         return this.token;
  17.       },
  18.       nameLength: (state) => state.name.length,
  19.     },
  20.     actions: {
  21.       setToken(token: string) {
  22.         this.token = token;
  23.       },
  24.       // 登录
  25.       async login(userInfo) {
  26.         // 调用接口,做逻辑处理
  27.       }
  28.     }
  29.   })
  30.    
  31.   // Need to be used outside the setup
  32.   export function useUserStoreHook() {
  33.     return useUserStore(store);
  34.   }
 
 
 
  1.   <template>
  2.     <div>
  3.      <span>{{userStore.name}}</span>
  4.     <span>{{userStore.nameLength}}</span>
  5.     <buttton @click="changeName"></button>
  6.     </div>
  7.   </template>
  8.    
  9.   <script lang="ts">
  10.   import { defineComponent } from 'vue'
  11.   import { useUserStoreHook } from "@/store/modules/user"
  12.    
  13.   export default defineComponent({
  14.     setup() {
  15.       const userStore = useUserStoreHook()
  16.       
  17.       const changeName = ()=>{
  18.       // $patch 修改 store 中的数据
  19.         userStore.$patch({
  20.           name: '名称被修改了,nameLength也改变了'
  21.         })
  22.     }
  23.       
  24.       return {
  25.         userStore,
  26.         updateName
  27.       }
  28.     },
  29.   })
  30.   </script>
 
 

6.actions

bef119c6cc72ce3012d17517564d8953.png

0cfc085aaf7c602c29f89b0c1f63ea47.png

这里与 Vuex 有极大的不同,Pinia 仅提供了一种方法来定义如何更改状态的规则,放弃 mutations 只依靠 Actions,这是一项重大的改变。

Pinia 让 Actions 更加的灵活

  • 可以通过组件或其他 action 调用

  • 可以从其他 store 的 action 中调用

  • 直接在商店实例上调用

  • 支持同步异步

  • 有任意数量的参数

  • 可以包含有关如何更改状态的逻辑(也就是 vuex 的 mutations 的作用)

  • 可以 $patch 方法直接更改状态属性

    更多详细的用法请参考Pinia中的actions官方网站:

    actions的用法(https://pinia.vuejs.org/core-concepts/actions.html)

ee5e697346bbcd2d86a5c2fde958e2ef.png

八、环境变量配置

vite 提供了两种模式:具有开发服务器的开发模式(development)和生产模式(production)。在项目的根目录中我们新建开发配置文件.env.development和生产配置文件.env.production。

ff212cfe7f0e8b7dbd934cf03faa6875.png

 
  1.   # 网站根目录
  2.   VITE_APP_BASE_URL= ''
 
 

组件中使用:

49383297da74a17c8276e3561f50c5d7.png

console.log(import.meta.env.VITE_APP_BASE_URL)
 

配置 package.json,打包区分开发环境和生产环境

0301424fed1f76e423691f84f1e7b252.png

 
  1.   "build:dev": "vue-tsc --noEmit && vite build --mode development",
  2.   "build:pro": "vue-tsc --noEmit && vite build --mode production",
 
 

九、使用组件库

根据自己的项目需要选择合适的组件库即可,这里推荐两个优秀的组件库Element-plus和Naive UI。下面简单介绍它们的使用方法。

b26a52841336802ba95e84e303d3c56e.png

1.使用element-plus(https://element-plus.gitee.io/zh-CN/)

ce8d86eb383f512bcbcdc065e8bf607b.png

yarn add element-plus
 

推荐按需引入的方式:

按需引入需要安装unplugin-vue-components和unplugin-auto-import两个插件。

7e40ab8710c1a7c35c60c44a06b8be4e.png

yarn add -D unplugin-vue-components unplugin-auto-import
 

再将vite.config.ts写入一下配置,即可在项目中使用element plus组件,无需再引入。

0b9886e832315cb709855e5d350faedc.png

 
  1.   // vite.config.ts
  2.   import AutoImport from 'unplugin-auto-import/vite'
  3.   import Components from 'unplugin-vue-components/vite'
  4.   import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
  5.    
  6.   export default {
  7.     plugins: [
  8.       // ...
  9.       AutoImport({
  10.         resolvers: [ElementPlusResolver()],
  11.       }),
  12.       Components({
  13.         resolvers: [ElementPlusResolver()],
  14.       }),
  15.     ],
  16.   }
 
 

2.Naive UI(https://www.naiveui.com/zh-CN/os-theme)

ade73476522349dfba24a92c5906af5a.png

 
  1.   # 安装naive-ui
  2.   npm i -D naive-ui
  3.    
  4.   # 安装字体
  5.   npm i -D vfonts
 
 

按需全局安装组件

b758b8d1326a45c3a3d703816f0be105.png

 
  1.   import { createApp } from 'vue'
  2.   import {
  3.     // create naive ui
  4.     create,
  5.     // component
  6.     NButton
  7.   } from 'naive-ui'
  8.    
  9.   const naive = create({
  10.     components: [NButton]
  11.   })
  12.    
  13.   const app = createApp()
  14.   app.use(naive)
 
 

安装后,你可以这样在 SFC 中使用你安装的组件。

47ae3176ba41a3b5a6590782db28fbfe.png

 
  1.   <template>
  2.     <n-button>naive-ui</n-button>
  3.   </template>
 
 

9eacb24b7df4de128e0e9b82d3c27dc5.png

十、Vite 常用基础配置

1.基础配置

0d7936229eef62f33b44af9e045afeda.png

运行代理和打包配置

fd085349d71b04e83324e4aa3ef43472.png

 
  1.   server: {
  2.       host: '0.0.0.0',
  3.       port: 3000,
  4.       open: true,
  5.       https: false,
  6.       proxy: {}
  7.   },
 
 

生产环境去除 console debugger

4c63d35d00dc82a6268cfff557df9661.png

 
  1.   build:{
  2.     ...
  3.     terserOptions: {
  4.         compress: {
  5.           drop_console: true,
  6.           drop_debugger: true
  7.         }
  8.     }
  9.   }
 
 

生产环境生成 .gz 文件,开启 gzip 可以极大的压缩静态资源,对页面加载的速度起到了显著的作用。使用 vite-plugin-compression 可以 gzip 或 brotli 的方式来压缩资源,这一步需要服务器端的配合,vite 只能帮你打包出 .gz 文件。此插件使用简单,你甚至无需配置参数,引入即可。

d6b2764e3431d8a8e775b193b878898a.png

 
  1.   # 安装
  2.   yarn add --dev vite-plugin-compression
 
 
 
  1.   // vite.config.ts中添加
  2.   import viteCompression from 'vite-plugin-compression'
  3.    
  4.   // gzip压缩 生产环境生成 .gz 文件
  5.   viteCompression({
  6.     verbose: true,
  7.     disable: false,
  8.     threshold: 10240,
  9.     algorithm: 'gzip',
  10.     ext: '.gz',
  11.   }),
 
 

最终 vite.config.ts文件配置如下(自己根据项目需求配置即可)

e9baf6a31e124b19cdab53583f955031.png

 
  1.   import { defineConfig } from 'vite'
  2.   import vue from '@vitejs/plugin-vue'
  3.   import path from 'path'
  4.   //@ts-ignore
  5.   import viteCompression from 'vite-plugin-compression'
  6.    
  7.   // https://vitejs.dev/config/
  8.   export default defineConfig({
  9.     base: './', //打包路径
  10.     plugins: [
  11.       vue(),
  12.       // gzip压缩 生产环境生成 .gz 文件
  13.       viteCompression({
  14.         verbose: true,
  15.         disable: false,
  16.         threshold: 10240,
  17.         algorithm: 'gzip',
  18.         ext: '.gz',
  19.       }),
  20.     ],
  21.     // 配置别名
  22.     resolve: {
  23.       alias: {
  24.         '@': path.resolve(__dirname, 'src'),
  25.       },
  26.     },
  27.     css:{
  28.       preprocessorOptions:{
  29.         scss:{
  30.           additionalData:'@import "@/assets/style/mian.scss";'
  31.         }
  32.       }
  33.     },
  34.     //启动服务配置
  35.     server: {
  36.       host: '0.0.0.0',
  37.       port: 8000,
  38.       open: true,
  39.       https: false,
  40.       proxy: {}
  41.     },
  42.     // 生产环境打包配置
  43.     //去除 console debugger
  44.     build: {
  45.       terserOptions: {
  46.         compress: {
  47.           drop_console: true,
  48.           drop_debugger: true,
  49.         },
  50.       },
  51.     },
  52.   })
 
   

标签:TypeScript,const,vue,ts,return,ElementPlus,Pinia,import,config
From: https://www.cnblogs.com/web1123/p/18394135

相关文章

  • vue3 pinia 的基本用法
    ‌Pinia是Vue3的状态管理器,用于跨组件或页面共享状态。以下是使用Pinia的基本步骤:‌安装Pinia‌:首先,你需要在项目中安装Pinia。你可以使用npm或yarn进行安装。例如,使用npm,你可以运行 npminstallpinia 命令来安装Pinia。‌创建Store‌:在Vue3中,你可以使用......
  • [Typescript Library] Navigate to Source Files instead of Declaration Files
    Thesolutionistomodifythe tsconfig.json filetoenable declarationMap underthe compilerOptions.//insidetsconfig.json{"compilerOptions":{"declaration":true,"declarationMap":true//...Byenabling ......
  • Pinia 使用(一分钟了解)
    Pinia使用(一分钟了解)Pinia官网地址:Pinia官方文档文章目录Pinia使用(一分钟了解)一、Pinia是什么二、Vue中如何使用Pinia1.安装Pinia2.创建Pinia实例3.定义一个Store4.在组件中使用Store5.模块化和插件三、Pinia包含哪些属性或方法API1.state2.......
  • typescript 中type和interface的区别
    在TypeScript中,type和interface都是用来定义类型别名的工具,但它们之间有一些细微的区别。了解这些区别有助于更好地选择何时使用哪一个。相同点类型定义:两者都可以用来定义对象的形状(shape),即指定一个对象应该具有哪些属性、方法及其类型。可选属性:都可以定义可选属性......
  • Vue期末考试速成复习指南附编程题(js开发基础+路由+Pinia)
    前文:本文参考书籍《Vue.js前端开发实战(第二版)》--黑马程序员/编著重点在于本科期末速成和0基础入门目录:一.初识Vue1.包管理工具:npmyarn2.创建Vue项目二.js开发基础1.什么是单文件组件?2.单文件组件基本结构3.切换页面显示组件3.数据绑定与输出4.Vue引入Html页面5.......
  • 第七章 项目布局实现(7.4.5)——ElementPlus 自定义命名空间
    7.4.5ElementPlus自定义命名空间参考:https://cn.element-plus.org/zh-CN/guide/namespace.htmlElementPlus提供的默认命名空间为el。在特殊情况下,我们需要自定义命名空间。我们使用sass书写样式,必须同时设置ElConfigProvider和scss$namespace。设置ElC......
  • 第二节:封装pinia和持久化
    封装pinia构建用户仓库和持久化通过持久化,可以实现应用程序重新启动时恢复这些状态官方文档:https://prazdevs.github.io/pinia-plugin-persistedstate/zh/安装插件pinia-plugin-persistedstatepnpmaddpinia-plugin-persistedstate-D使用main.js(之后统一管理,......
  • Pinia入门(快速上手)
    定义一个Store 在深入了解核心概念之前,我们需要知道Store是使用 defineStore() 定义的,并且它需要一个唯一名称,作为第一个参数传递:import{defineStore}from'pinia'//useStore可以是useUser、useCart之类的任何东西//第一个参数是应用程序中store的唯一i......
  • vue3-pinia保持数据时效性,不会因为刷新浏览器丢失实时数据(pinia-plugin-persistedstat
    1.方法一(pinia-plugin-persistedstate)1.安装插件-pinia-plugin-persistedstateyarnaddpinia-plugin-persistedstatenpmipinia-plugin-persistedstate2.在pinia中注册import{createPinia}from'pinia';importpiniaPluginPersistedstatefrom'pinia-pl......
  • 在Vue3应用中使用TypeScript的最佳实践
    随着Vue3的推出,TypeScript逐渐成为了前端开发中的一种必备技能。Vue3的设计更好地支持TypeScript,这使得开发者可以在开发过程中充分利用TypeScript的强类型系统,从而提高代码的可维护性和可读性。在这篇博客中,我们将深入探讨在Vue3应用中使用TypeScript的最佳实践,并通过示例......