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

news/2024/11/15 1:00:09/文章来源:https://www.cnblogs.com/web1123/p/18394135

 

前言

随着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.  
    })
 
 
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/791498.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

idea 设置代码样式

设置类注释模板类注释:File-->Settings-->Editor-->File and CodeTemplates将如下代码拷贝到上图右侧空白区域即可(这个更为详细,具体用哪个自己决定) /**** @description * @author ${USER}* @date ${DATE}* @version 1.0* @email marydon20170307@163.com*/需要…

第1天-行业介绍和计算机基础

一、 简单总结计算机发展相关历史,详细总结服务器硬件和计算机分类相关知识。 第一代计算机(1946-1957) 电子管时代 第二代计算机(1958-1964) 晶体管时代 第三代计算机(1965-1970) 集成电路时代 第四代计算机(1971以后) 大规模集成电路时代 服务器硬件:内存、cpu、硬盘、raid卡…

莫言语录

走近一个人的时候,要慢一点,以免看不清。离开一个人的时候,要快一点,以免舍不得。人生海海,先有不甘,后有心安。年人的世界做筛选,不做教育。说服一个人的从来不是道理 ,而是南墙。能点醒一个人的,从来不是说教,而是磨难 。人生是一个漫长的修行,我们每个人都在为自…

产品经理与项目经理:职场双子星的深度解析与全面对比

在现代商业环境中,产品经理和项目经理是两个至关重要的角色(产品经理的英文缩写是 PM(Product Manager),而项目经理的英文缩写是 PM(Project Manager)。)。尽管他们都以“经理”为名,但在实际工作中却扮演着截然不同的角色。 本文将从日常工作、专业技能、职能划分、工…

P7技术专家30k前端架构-商用级产品架构,业务实现+开发提效双线并进

P7技术专家30k前端架构训练营课程-商用级产品架构,业务实现+开发提效双线并进P7技术专家30k前端架构-商用级产品架构,业务实现+开发提效双线并进最近部门招聘,很多工程师,包括我在内都参与了内推和面试的过程,经过这次招聘,我发现能够最终拿到offer的人,基本上在看到简历…

在 PbootCMS 首页上调用公司简介等单页内容

在 PbootCMS 首页上调用公司简介等单页内容 在 PbootCMS 中,可以在首页或其他页面上调用特定的单页内容,如公司简介。以下是如何使用标签 {pboot:content} 来调用单页内容的具体方法: 示例代码html{pboot:content id=1} [content:content drophtml=1 dropblank=1 len=300 mo…

最近写贪吃蛇有些上瘾,canvas版本贪吃蛇,贪吃蛇是逻辑最简单的游戏了

代码:<!Doctype html> <html lang="zh_cn"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>贪吃蛇</title><meta name="Keywords" content="&quo…

PLC结构化文本(ST)——方法(Method)

PLC Structured Text Object Oriented Programming PLC结构化文本(ST)——方法(Method) 什么是方法一个方法是把一些相关的语句组织在一起,用来执行一个任务的语句块。---C#方法方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合 方法包含于…

闲话假期特供

小假期鲜花前 出于一些众所周知的原因,我们获得了一周假期离校时间,所以就打算和同学们一起去玩点什么。 Day 0 || 8.23 早起搬宿舍。 到了 GGrun 发现他开始就没把床垫搬过去,于是还要搬回去,并借走了我的车,然后我忘了直到放假后才想起来,希望 GGrun 没忘。 10:00 因为…

PostgreSQL的安装与配置(包含多种可能遇到的报错或者无法安装问题)

1.Windows安装 1. 官网下载安装包,EDB: Open-Source, Enterprise Postgres Database Management (enterprisedb.com) 2.按照提示步骤进行安装(文件路径很重要!!!) 这是PostgreSQL的安装目录(自定义目录安装的一定要命名清楚,不要和后面的data混淆) 这个全选这个是存储…

福州大学第19届206智能车队摄像头培训 三、帧率和快门时间

总钻风MT9V034灰度摄像头的帧率与快门原理原文于2023.10.25发布于本人CSDN主页,现同步至cnblogs 1.摄像头帧率 某天看了学弟学妹们练习PID编写的程序,为了降低串口通信的频率在int main() while(1)内用了delay函数。实际上在未来实际应用中是不可取的。 在平时的单片机工程中…

神经网络之卷积篇:详解简单卷积网络示例(A simple convolution network example)

详解简单卷积网络示例 假设有一张图片,想做图片分类或图片识别,把这张图片输入定义为\(x\),然后辨别图片中有没有猫,用0或1表示,这是一个分类问题,来构建适用于这项任务的卷积神经网络。针对这个示例,用了一张比较小的图片,大小是39393,这样设定可以使其中一些数字效果…