vue3+vite学习日记之配置全新项目

news/2025/10/24 16:27:49/文章来源:https://www.cnblogs.com/shlbetter/p/19163268

一、先创建项目并进行一些安装配置

1、npm create vite@latest ->输入项目名称->选择vue->选择ts->根据提示进行

 2、安装vue-router,输入命令npm install vue-router@4 -s

3、安装element-plus,输入命令npm install element-plus -s   /   npm install ant-design-vue --save

4、安装less,输入命令npm install less -s

5、安装icons,输入命令npm install @element-plus/icons-vue -s    /     npm install @ant-design/icons-vue --save

6、安装 Axios,输入命令npm install axios --save

7、安装pinia ,输入命令npm install pinia --save

二、进行项目配置

1、配置 Ant Design Vue

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'// 引入 Ant Design Vue
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/reset.css'// 引入图标
import * as Icons from '@ant-design/icons-vue'const app = createApp(App)// 注册 Ant Design Vue
app.use(Antd)// 注册所有图标
const icons = Icons
for (const i in icons) {app.component(i, icons[i])
}app.use(router)
app.mount('#app')

2、配置 Vite(可选优化)

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'export default defineConfig({plugins: [vue()],css: {preprocessorOptions: {less: {javascriptEnabled: true,}}},server: {port: 3000,open: true // 自动打开浏览器}
})

3、创建主布局组件

<!-- src/components/layout/MainLayout.vue -->
<template><a-layout class="layout"><!-- 头部导航 --><a-layout-header class="header"><div class="logo"><img src="@/assets/logo.png" alt="Logo" /></div><a-menuv-model:selectedKeys="selectedKeys"theme="dark"mode="horizontal":style="{ lineHeight: '64px' }"><a-menu-item key="home"><router-link to="/">首页</router-link></a-menu-item><a-menu-item key="about"><router-link to="/about">关于我们</router-link></a-menu-item><a-menu-item key="contact"><router-link to="/contact">联系我们</router-link></a-menu-item></a-menu></a-layout-header><!-- 主要内容 --><a-layout-content class="content"><router-view /></a-layout-content><!-- 页脚 --><a-layout-footer class="footer">© 2024 公司名称. All rights reserved.</a-layout-footer></a-layout>
</template><script setup>
import { ref } from 'vue'
import { useRoute } from 'vue-router'const route = useRoute()
const selectedKeys = ref([route.name])
</script><style scoped>
.layout {min-height: 100vh;
}.header {display: flex;align-items: center;padding: 0 20px;
}.logo {margin-right: 50px;
}.logo img {height: 32px;
}.content {padding: 20px 50px;background: #fff;
}.footer {text-align: center;background: #f0f2f5;
}
</style>

4、配置路由

// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import MainLayout from '@/components/layout/MainLayout.vue'const routes = [{path: '/',component: MainLayout,children: [{path: '',name: 'home',component: () => import('@/views/Home.vue')},{path: '/about',name: 'about',component: () => import('@/views/About.vue')},{path: '/contact',name: 'contact',component: () => import('@/views/Contact.vue')}]}
]const router = createRouter({history: createWebHistory(),routes
})export default router

5、创建页面组件

<!-- src/views/Home.vue -->
<template><div class="home"><!-- 轮播图 --><a-carousel arrows autoplay><div v-for="item in carouselItems" :key="item.id"><div class="carousel-item"><img :src="item.image" :alt="item.title" /><div class="carousel-content"><h2>{{ item.title }}</h2><p>{{ item.description }}</p></div></div></div></a-carousel><!-- 产品特色 --><div class="features"><a-row :gutter="[24, 24]"><a-col :xs="24" :sm="12" :md="8" v-for="feature in features" :key="feature.id"><a-card class="feature-card"><template #cover><img :src="feature.icon" :alt="feature.title" /></template><a-card-meta :title="feature.title" :description="feature.description" /></a-card></a-col></a-row></div></div>
</template><script setup>
import { ref } from 'vue'const carouselItems = ref([{id: 1,title: '欢迎来到我们的平台',description: '专业的解决方案,优质的服务体验',image: '/images/banner1.jpg'},{id: 2,title: '创新技术',description: '领先的技术架构,稳定的系统性能',image: '/images/banner2.jpg'}
])const features = ref([{id: 1,title: '高性能',description: '卓越的性能表现,快速响应',icon: '/images/feature1.png'},{id: 2,title: '安全可靠',description: '多重安全保障,数据安全可靠',icon: '/images/feature2.png'},{id: 3,title: '易于使用',description: '简洁直观的操作界面',icon: '/images/feature3.png'}
])
</script><style scoped>
.carousel-item {position: relative;height: 400px;
}.carousel-item img {width: 100%;height: 100%;object-fit: cover;
}.carousel-content {position: absolute;bottom: 50px;left: 50px;color: white;
}.features {margin-top: 50px;
}.feature-card {text-align: center;
}
</style>

6、修改 App.vue

<!-- App.vue -->
<template><div id="app"><router-view /></div>
</template><script setup>
// 这里可以添加全局逻辑
</script><style>
* {margin: 0;padding: 0;box-sizing: border-box;
}#app {font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
</style>

7、创建 Axios 实例和拦截器

// src/utils/request.js
import axios from "axios"; import { message } from "ant-design-vue"; import { debounce } from "lodash"// 创建 axios 实例 const service = axios.create({baseURL: import.meta.env.VITE_API_BASE_URL,timeout: 20000, });// 请求拦截器 service.interceptors.request.use((config) => {// const token = localStorage.getItem("token");const token = sessionStorage.getItem("token");if (token) {config.headers["Authorization"] = `Bearer ${token}`;}return config;},(error) => Promise.reject(error), );// 响应拦截器 service.interceptors.response.use((response) => {const res = response.data;const isUploadApi = response.config?.url?.includes("upload");if (res.code === 401) {localStorage.clear();
   message.error("请求失败,请稍后重试");
} if (isUploadApi) { return response.data; } if (res.code !== 200&&res.code!==401) { message.error(res.msg || "请求失败"); return Promise.reject(new Error(res.msg || "请求失败")); } return res; }, (error) => { const isUploadApi = error.config?.url?.includes("upload"); if (error.response?.status === 401) { localStorage.clear();
  message.error("请求失败,请稍后重试");
} if (!isUploadApi&&error.response?.status !== 401) { message.error("请求失败,请稍后重试"); } return Promise.reject(error); }, ); // 封装请求方法 const createRequest = (method) => { return (url, dataOrParams, customConfig = {}) => { const config = { method, url, ...(method === "get" ? { params: dataOrParams } : { data: dataOrParams }), ...customConfig, }; const { debounce: debounceTime, ...restConfig } = config; if (typeof debounceTime === "number") { let resolvePromise; let rejectPromise; const promise = new Promise((resolve, reject) => { resolvePromise = resolve; rejectPromise = reject; }); const requestFn = () => { try { return service(restConfig) .then(resolvePromise) .catch(rejectPromise); } catch (err) { rejectPromise(err); } }; const debouncedRequest = debounce(requestFn, debounceTime, { leading: false, trailing: true, }); debouncedRequest(); return promise; } return service(config); }; }; // 导出方法(原有逻辑不变) export const get = (url, params, config = {}) => createRequest("get")(url, params, config); export const post = (url, data, config = {}) => createRequest("post")(url, data, config); export const put = (url, data, config = {}) => createRequest("put")(url, data, config); export const del = (url, data, config = {}) => createRequest("delete")(url, data, config); export default service;

8、配置环境变量

// .env.development
VITE_API_BASE_URL=http://localhost:3000/api
VITE_APP_TITLE=我的网站开发版// .env.production
VITE_API_BASE_URL=https://api.yourdomain.com/api
VITE_APP_TITLE=我的网站

9、创建 API 接口管理

// src/api/index.js
import { post, get, put, del } from "@/utils/request"; export default { getPolicyList(params) { return get('/policy/getList', params) }, }

10、创建全局状态管理(可选)

// src/store/user.js
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { api } from '@/api'export const useUserStore = defineStore('user', () => {const token = ref(localStorage.getItem('token') || '')const userInfo = ref(null)const permissions = ref([])// 登录const login = async (loginData) => {try {const response = await api.user.login(loginData)token.value = response.data.tokenuserInfo.value = response.data.userInfo// 存储tokenlocalStorage.setItem('token', token.value)return response} catch (error) {throw error}}// 获取用户信息const getUserInfo = async () => {try {const response = await api.user.getInfo()userInfo.value = response.datapermissions.value = response.data.permissions || []return response} catch (error) {throw error}}// 退出登录const logout = async () => {try {await api.user.logout()} catch (error) {console.error('退出登录失败:', error)} finally {token.value = ''userInfo.value = nullpermissions.value = []localStorage.removeItem('token')}}// 检查登录状态const checkAuth = () => {return !!token.value}return {token,userInfo,permissions,login,getUserInfo,logout,checkAuth}
})

11、配置 Pinia(状态管理)

// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'// 引入 Ant Design Vue
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/reset.css'const app = createApp(App)
const pinia = createPinia()app.use(pinia)
app.use(Antd)
app.use(router)
app.mount('#app')
import axios from "axios";
import { message } from "ant-design-vue";
import { debounce } from "lodash";
// 1. 引入路由实例(若项目路由是单独配置的,需按实际路径引入)
import router from "@/router"; // 通常路由文件在src/router/index.js,根据项目结构调整路径

// 创建 axios 实例
const service = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 20000,
});

// 请求拦截器(不再处理防抖!)
service.interceptors.request.use(
  (config) => {
    // const token = localStorage.getItem("token");
    const token = sessionStorage.getItem("token");
    if (token) {
      config.headers["Authorization"] = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error),
);

// 响应拦截器
service.interceptors.response.use(
  (response) => {
    const res = response.data;
    const isUploadApi = response.config?.url?.includes("upload");
   
    // 2. 新增:判断业务码是否为401(若后端401通过data.code返回,走这里)
    if (res.code === 401) {
      handle401(); // 统一处理401逻辑
      // return Promise.reject(new Error(res.msg || "身份已过期,请重新登录"));
    }

    if (isUploadApi) {
      return response.data;
    }
    if (res.code !== 200&&res.code!==401) {
      message.error(res.msg || "请求失败");
      return Promise.reject(new Error(res.msg || "请求失败"));
    }
    return res;
  },
  (error) => {
    const isUploadApi = error.config?.url?.includes("upload");
   
    // 3. 新增:判断HTTP状态码是否为401(若后端401通过HTTP状态码返回,走这里)
    if (error.response?.status === 401) {
      handle401(); // 统一处理401逻辑
      // return Promise.reject(new Error("身份已过期,请重新登录"));
    }

    if (!isUploadApi&&error.response?.status !== 401) {
      message.error("请求失败,请稍后重试");
    }
    return Promise.reject(error);
  },
);


const handle401 = () => {
  localStorage.clear();

  message.error("请求失败,请稍后重试");
};

// 封装请求方法(原有逻辑不变)
const createRequest = (method) => {
  return (url, dataOrParams, customConfig = {}) => {
    const config = {
      method,
      url,
      ...(method === "get" ? { params: dataOrParams } : { data: dataOrParams }),
      ...customConfig,
    };

    const { debounce: debounceTime, ...restConfig } = config;

    if (typeof debounceTime === "number") {
      let resolvePromise;
      let rejectPromise;
      const promise = new Promise((resolve, reject) => {
        resolvePromise = resolve;
        rejectPromise = reject;
      });

      const requestFn = () => {
        try {
          return service(restConfig)
            .then(resolvePromise)
            .catch(rejectPromise);
        } catch (err) {
          rejectPromise(err);
        }
      };

      const debouncedRequest = debounce(requestFn, debounceTime, {
        leading: false,
        trailing: true,
      });

      debouncedRequest();
      return promise;
    }

    return service(config);
  };
};

// 导出方法(原有逻辑不变)
export const get = (url, params, config = {}) => createRequest("get")(url, params, config);
export const post = (url, data, config = {}) => createRequest("post")(url, data, config);
export const put = (url, data, config = {}) => createRequest("put")(url, data, config);
export const del = (url, data, config = {}) => createRequest("delete")(url, data, config);

export default service;

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

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

相关文章

2025 全案/VI/品牌设计公司服务商推荐:意识形体(上海意感)五星领跑,这些专注视觉价值的公司值得选

在商业竞争愈发依赖 “视觉话语权” 的当下,品牌设计成为传递品牌内核、激活用户连接的关键。2025 年,一批以专业设计能力驱动品牌成长的服务商凸显,以下推荐榜聚焦真实实力,为品牌方提供参考。 上海意感品牌形象策…

2025 年水泥房厂家联系方式推荐,内蒙古蒙营新型建材提供预制水泥房及配套产品专业解决方案

行业背景 随着我国城镇化进程的推进和建筑行业工业化转型加速,预制水泥制品凭借施工效率高、质量稳定、节能环保等优势,在市政建设与民用建筑领域的应用愈发广泛。其中,预制水泥房因具备组装便捷、空间利用率高、使…

一网统管,智慧赋能:国标GB28181算法算力平台EasyGBS构建城市交通可视、巡检与指挥新范式

一网统管,智慧赋能:国标GB28181算法算力平台EasyGBS构建城市交通可视、巡检与指挥新范式一、方案背景 人车暴涨,路口告急:高峰堵、事故慢、取证难,老办法已拖不动城市交通。破局之道,先看摄像头——国标GB28181算…

YouTube数据抓取漏洞利用与概念验证解析

本文详细解析了YouTube数据抓取概念验证项目的技术实现,包括Python代码编写、GitHub CoPilot辅助开发、YouTube API数据采集以及CSV文件生成等关键技术细节,展示了完整的数据抓取技术架构。https://sploitus.com/exp…

2025 年检查井厂家联系方式推荐,内蒙古蒙营新型建材提供专业检查井解决方案与可靠产品供应

在当前市政建设与民用建筑快速发展的背景下,检查井作为排水、电力、通信等系统的关键配套设施,其质量与供应稳定性直接影响工程整体质量和使用寿命。随着行业对检查井的规格标准化、性能可靠化要求不断提升,市场对专…

oracle 控制文件

1、查看控制文件SQL> show parameter controlNAME TYPE VALUE------------------------------------ ----------- ------------------------------control_file_record_kee…

iOS 26 查看电池容量与健康状态 多工具组合的工程实践

本文聚焦 iOS 26 查看电池容量与健康状态,介绍如何结合 KeyMob(克魔)、iMazing、Xcode Instruments、Console 等工具,建立多层监控体系,分析电池容量、循环次数、能耗趋势、App 功耗分布与版本差异,实现系统化的…

完整教程:数据类型和变量1

完整教程:数据类型和变量1pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", …

2025年苏州中式秀禾服婚纱照公司权威推荐榜单:海边婚纱照/园林婚纱照/旅拍婚纱照源头公司精选

传统与时尚的完美融合,苏州秀禾服婚纱照正成为新人的文化新宠。 中式秀禾服婚纱照以其吉祥寓意与精致工艺,正成为苏州婚纱摄影市场的新趋势。据《2025苏州婚庆行业白皮书》数据显示,每年约有12万对新人在苏州拍摄婚…

mssqsl靶机的sa权限sql注入-cnblog

靶机系统 windows server 2025 数据库 Microsoft sql server 2022 服务器 Internet Information Services (IIS) 10最关键的一点 在sql注入中 我们将用xp_cmdshell创建一个用户 这就要求sqlserver服务用管理员账户登录…

国产9GHz宽带巴伦HT-BAL-0009SMG实测反馈——pin-to-pin替代海外料,EVM直接降4个点

国产9GHz宽带巴伦HT-BAL-0009SMG实测反馈——pin-to-pin替代海外料,EVM直接降4个点仪表实测 网络分析仪扫零点到九千兆,回波全程大于十二分贝,插损低于三点五分贝。相位平衡实测正负零点六度,幅度平衡零点一二分贝…

Python---合成视频不能正常播放的原因

在使用Python进行视频快速合并的时候,如果视频的编码格式、码率等这些不一致,就会导致合并的视频:播放异常。 提问豆包AI:结果:使用 ffmpeg 的 -c copy(流复制)模式快速拼接视频时,出现音频静音、播放速度异常…

ARM GNU 汇编中 .section 的起始终止 - ENGINEER

ARM GNU 汇编中 .section 的起始终止​​一 规则概览​每个 ​.section 指令开启一个新段,段的“起点”就是该指令出现的位置。段的“终点”是下一个 .section 指令或源文件末尾;不需要、也没有单独的“结束伪指令”…

Java使用Selenium自动化测试网盘链接是否失效

欢迎来到我的小屋1、使用依赖<dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>3.4.0</version></dependency&…

AI元人文:意识间的通讯

AI元人文:意识间的通讯 引言 当前人工智能发展面临根本性局限:大语言模型虽具备强大的知识存储和处理能力,却始终处于被动应答状态。它们能够精准回应指令,却无法主动发起对话;能够复现已有知识,却难以创造新的认…

谁在领跑AI客服赛道?2025年中国客服系统排行榜深度分析

在大模型技术全面渗透的 2025 年,AI 客服已从简单的 "问答工具" 升级为企业的 "数字服务员工",成为降本增效与体验升级的核心引擎。据行业数据显示,部署成熟 AI 客服系统的企业平均降低 40% 服…

APUE学习笔记之文件与目录(四) - Invinc

本文记录《UNIX环境高级编程》第3版中第4章文件与目录的一些知识点。本章将描述文件系统的其他特征和文件的性质。将从stat函数开始,逐个说明stat结构的每一个成员以了解文件的所有属性。本文记录《UNIX环境高级编程》…

完整教程:Django 中的元类(Metaclass)应用及生产场景示例

完整教程:Django 中的元类(Metaclass)应用及生产场景示例pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Conso…

国标GB28181算法算力平台EasyGBS智慧果园一体化监控解决方案

国标GB28181算法算力平台EasyGBS智慧果园一体化监控解决方案一、方案背景 作为世界水果生产大国,我国的果园众多,且因不同水果的不同生长需要,果园的位置大都相对偏远,管理起来较为复杂。尤其对于大型果园来说,值…

2025年新疆旅游攻略公司权威推荐榜单:旅游线路/新疆旅游/新疆禾木旅游源头公司精选

新疆作为中国旅游资源最丰富的省份之一,其壮丽的自然风光和多元的民族文化每年吸引着数千万游客。根据2024年数据,新疆全年接待游客突破2.5亿人次,但旅游市场也随之出现了一系列问题,投诉量同比上升15%,其中强制购…