vue3+Ant Design的项目搭建

一、基础说明

  • 使用技术:Vite3 + Vue3 + TypeScript + Ant Design + axios + Vue-Router

1.2 参考文档

二、 项目结构

practice-vue-project
.
├── index.html                # 页面入口
├── public                    # 该文件下的目录打包时将直接拷贝只根目录
├── src                       # 源码文件
│   ├── assets                # 静态资源
│   │   ├── base.css          # 全局的css变量
│   ├── constants             # 全局常量定义
│   ├── components            # 全局组件
│   ├── hooks                 # 封装的常用hooks
│   ├── layouts               # 公共布局
│   ├── pages                 # 页面组件(下面只列举主要文件)
│   ├── router                # 路由配置文件
│   ├── service               # 接口请求相关
│   │   ├── index.ts          # 整合接口请求,统一导出
│   ├── typings               # 全局类型声明
│   ├── App.vue
│   ├── main.ts               # 创建APP挂载
│   └── utils                 # 工具函数
│       └── request.ts        # 基于axios封装的请求类
├── .env.dev                  # 开发环境配置文件
├── .env.production           # 生产环境配置文件
├── .gitignore                # git跟踪忽略配置
├── env.d.ts                  # 环境变量类型定义
├── index.html                # 模板:包含网页标题图标
├── tsconfig.json             # TS配置文件
└── vite.config.js            # vite 配置文件

三、使用脚手架构建

  • 使用脚手架创建新项目:npm create vue@latest
  • 根据需要选择需要的功能
  • 安装依赖:
     cd <your-project-name>
     npm install
    
  • 启动项目:npm run dev

四、配置vite.config.js

  • 自动解析项目根目录下 vite.config.js 的配置文件

  • 只有以 VITE_ 为前缀的变量才会暴露给经过 vite 处理的代码

  • 获取环境变量的方法:import.meta.env.VITE_xxx

  • 需要在 package.json 中修改运行的命令,保证 vite 获取环境变量

  • 如下为示例的配置,详情参考官方文档

     // vite.config.js
    
      import { fileURLToPath, URL } from 'node:url'
    
      import { defineConfig, loadEnv } from 'vite'
      import vue from '@vitejs/plugin-vue'
      import vueJsx from '@vitejs/plugin-vue-jsx'
      import Components from 'unplugin-vue-components/vite'
      import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
    
      // mode: dev用于开发,production用于构建,可以使用命令行--mode重写
    
      // 项目配置项:默认不加载 .env 文件
      export default defineConfig(({ mode }: { mode: string }) => {
        // 根据当前工作目录中的 `mode` 加载 .env 文件
        // 设置第三个参数为 '' 来加载所有环境变量,而不管是否有 `VITE_` 前缀。
        const env = loadEnv(mode, process.cwd(), '')
    
        return {
          base: env.VITE_APP_BASE || '/',
          // 需要用到的插件数组。
          plugins: [
            vue(),
            vueJsx(),
            Components({
              resolvers: [
                AntDesignVueResolver({
                  importStyle: false // css in js
                })
              ]
            })
          ],
          // 路径解析
          resolve: {
            alias: {
              '@': fileURLToPath(new URL('./src', import.meta.url)),
              comps: fileURLToPath(new URL('./src/components', import.meta.url)),
              views: fileURLToPath(new URL('./src/views', import.meta.url))
            }
          },
          // 服务启动的端口等
          server: {
            https: false,
            port: 5173,
            host: '0.0.0.0',
            // 开发服务器启动时,自动在浏览器中打开应用程序
            open: false,
            // 跨域
            cors: false
          }
        }
      })
    
    
  • 修改默认的启动程序

    // package.json
      "scripts": {
           "dev": "vite --mode dev",
          //....
       }
    
  • 增加环境变量的文件.env.dev.env.production

五、使用 store

  • 配置

     // main.ts
    import { createPinia } from 'pinia'
    app.use(createPinia())
    
     // 避免刷新以后,store丢失,进行数据的持久化
    // 👉 持久化pinia
    const store = useStore()
    // 页面进入:合并状态
    const localState = localStorage.getItem(AiAppStorageKey)
    if (localState) {
      console.log('[温馨提示]:合并Store...')
      store.$state = JSON.parse(localState)
    }
    // 页面刷新:存储状态
    window.addEventListener('beforeunload', () => {
      console.log('[温馨提示]:缓存Store...')
      localStorage.setItem(AiAppStorageKey, JSON.stringify(store.$state))
    })
    
    
  • 定义

    // store/index.ts
    import { defineStore } from 'pinia'
    import type { UserInfoType } from '@/typings'
    interface StoreProps {
      token: string
      userInfo: UserInfoType | any
    }
    
    interface ActionProps {
      setToken: (token: string) => void
      resetToken: () => void
    }
    // 用于保存token
    export const useStore = defineStore<string, StoreProps, any, ActionProps>  ('appStore', {
      state: () => ({ token: '', userInfo: {} }),
      actions: {
        async setToken(token) {
          this.token = token
        },
        async resetToken() {
          this.token = ''
        }
      }
    })
    
    
  • 使用

    const store = useStore()
    store.setToken("测试")
    

六、使用 Ant Design

  • 安装依赖

    # 安装AntDesign:
    npm install ant-design-vue@4.x --save
    # 安装antDesign Icon
    npm install --save @ant-design/icons-vue
    
  • 引入

    // 文件:src\main.ts
    import Antd from 'ant-design-vue'
    import 'ant-design-vue/dist/reset.css'
    // 1. 创建app
    const app = createApp(App)
    
    // 2.使用插件Antd
    app.use(Antd)
    
    // 3. 挂在在id=app的根
    app.mount('#app')
    

6.1 使用时的问题

6.1.1 按照官方文档说明使用 message 出现不显示的问题处理

  • 使用网上各种方法后仍然不显示
  • 指明挂载的节点
     //  文件:src\main.ts
      import { message } from 'ant-design-vue'
      message.config({
        maxCount: 1,
        getContainer: () => document.getElementById('message_modal') || document.body
    })
    
  • 在#app 的节点同级增加 id=message_modal 的节点

6.1.2 动态加载 Icon 图标

  • 注册 Icon 组件

     // 文件:main.js
     import * as antIcons from '@ant-design/icons-vue'
     Object.keys(antIcons).forEach((key) => {
       app.component(key, antIcons[key as keyof typeof antIcons])
     })
     app.config.globalProperties.$antIcons = antIcons
    
  • 使用

    <script setup lang="ts">
    import { ref } from 'vue';
    const data = ref([{ "name": "a", "icon": "SettingOutlined" }])
    </script>
    
    <template>
      <template v-for="item in data">
        <component :is="item.icon">{{ item.name }}</component>
      </template>
    </template>
    

七、HTTP 请求的封装

// src\utils\request.ts
import axios from 'axios'
import type { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
import { useStore } from '@/store'
/********************
 ** 基础类型
 ********************/
export interface BaseResponse<T = any> {
  success: boolean
  errCode: number
  data: T
  errMessage: string
}

/********************
 ** 创建axios实例
 ********************/
const service: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_APP_HOST,
  timeout: 500000
})

/********************
 ** 请求拦截器
 ********************/
service.interceptors.request.use(
  (config: AxiosRequestConfig): any => {
    const store = useStore()
    // -- 配置请求头
    const token = store.token
    config.headers = {
      'Content-Type': 'application/json',
      Authorization: token ? `Bearer ${token}` : ''
    }
    return config
  },
  (error: AxiosError) => Promise.reject(error)
)

/********************
 ** 响应拦截器
 ********************/
service.interceptors.response.use(
  (response: AxiosResponse): any => {
    const { status } = response
    console.log(response)
    if (status === 200) {
      return response.data
    } else {
      return Promise.reject(new Error(response?.data?.errMessage ?? '未知错误'))
    }
  },
  (error: AxiosError) => {
    // 处理 HTTP 错误
    if (!error.response) {
      return Promise.reject({ code: 503, message: `网络连接失败` })
    } else {
      const store = useStore()
      let errorMsg = {
        code: error.response?.status,
        message: (error.response?.data as BaseResponse).errMessage,
        response: error.response
      }
      if (errorMsg?.code === 401) {
        store.resetStore()
        errorMsg.message = '权限过期,请重新登录'
      }
      return Promise.reject(errorMsg)
    }
  }
)

/********************
 ** 导出请求方法(重点)
 ********************/

export const http = {
  get<T = any>(
    url: string,
    params?: object,
    config?: AxiosRequestConfig
  ): Promise<BaseResponse<T>> {
    const conf = { params, ...config }
    return service.get(url, conf)
  },
  post<T = any>(url: string, data?: object, config?: AxiosRequestConfig): Promise<BaseResponse<T>> {
    return service.post(url, data, config)
  },

  put<T = any>(url: string, data?: object, config?: AxiosRequestConfig): Promise<BaseResponse<T>> {
    return service.put(url, data, config)
  },

  delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<BaseResponse<T>> {
    return service.delete(url, config)
  }
}

  • 使用
// src\service\users.ts
import { http } from '@/utils/request'

// 登录
export function login(data: USERAPI.LoginParamType) {
  return http.post<string>('/ai-api/ai-auth/auth/login', data)
}

// 退出登录
export function logout() {
  return http.post<string>('/ai-api/ai-auth/auth/logout')
}

// 获取用户信息
export function getUserInfo() {
  return http.get<USERAPI.UserInfo>('/ai-api/ai-auth/auth/info')
}

其他

  • 布局和完成的代码,见下一章
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容