使用vite搭建vue3项目
1,使用命令创建项目
创建条件:1,node 版本需要 20+ 以上 ;2,全局安装vite: npm install vite -g
- 使用命令创建项目
npm create vite@latest
// 使用命令创建项目
1,项目命名
Project name:
| vite-vue3-demo
2,选择框架
Select a framework:
| Vanilla
| > Vue
| React
| Preact
| Lit
| Svelte
| Solid
| Qwik
| Angular
| Others
3,选择项目使用语法
Select a variant:
| > TypeScript
| JavaScript
| Official Vue Starter ↗
| Nuxt ↗
4,项目创建完成
— Done. Now run:
cd vite-vue3-demo
npm install // 下载更新项目依赖
npm run dev // 运行命令
就此vue3项目算是运行成功
项目文件结构:
2,处理项目文件中报错
- vue3.0找不到模块“./App.vue”或其相应的类型声明
报错原因/解决:
```
因为 TypeScript 编译器无法识别 Vue 单文件组件 (.vue 文件) 的内容,要解决在随意一个vite-env.d.ts文件中添加配置,告诉 TypeScript 编译器,当它遇到 .vue 文件时,应该将其视为一个 Vue 组件,并导出一个默认的组件实例。
// vite-env.d.ts 文件
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
```
-
导入内置模块错误
在vite.config.ts 配置 报错 找不到模块“path”或其相应的类型声明
或 在定义环境变量时,process出现报错
解决方法:
npm install @types/node --save-dev
3,配置vite.config.ts
初始vite.config.ts 文件中是没有多少内容,如下图,
接下来就是对vite.config.ts进行改造
注:
-
export default defineConfig({})
:适用于配置固定不变的场景,代码简洁,易于理解和维护。 -
export default defineConfig(({ command, mode }) => { return {} })
:适用于需要根据不同的命令和模式动态调整配置的场景,提供了更大的灵活性。
1,配置别名(此处有两步,vite.config.ts和ts编译 都需要配置)
// vite.config.ts
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "node:path";
import { fileURLToPath } from "node:url";
// https://vite.dev/config/
export default defineConfig(({ command, mode }) => {
// mode 表示是那个环境 process.cwd() 当前工作目录的根目录
// 这句代码表示: 在那个环境下加载那个文件
let env = loadEnv(mode, process.cwd()) // vite支持loadEnv 执行此方法能够获取当前环境变量
return {
plugins: [vue()],
resolve: {
// 1, 先配置别名
alias: {
// 第一种方式(最简洁)
"@": path.resolve("./src"),
// 第二种方式
"@style": fileURLToPath(new URL("./src/assets/style", import.meta.url)),
"@images": fileURLToPath(
new URL("./src/assets/images", import.meta.url)
),
},
},
};
});
**TypeScript 编译配置**
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
"paths": { //路径映射,相对于baseUrl
"@/*": ["src/*"]
}
}
}
注: 在你的 tsconfig.json 文件中,你使用了 references 字段来包含其他 tsconfig 文件。确保这些被引用的文件(如 tsconfig.app.json 和 tsconfig.node.json)也正确地继承了根 tsconfig.json 文件中的配置,所以【references】字段来判断,是在(tsconfig.json)还是在(tsconfig.app.json) 配置
使用方式:
组件:import HelloWorld from '@/components/HelloWorld.vue'
图片:<img src="@images/1.png" alt="" srcset="">
2,配置环境变量
创建 三个文件,如果有uat环境则是四个文件 分别是:
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
.env.loc // 本地开发环境
NODE_ENV = 'development'
VITE_APP_MODE = 'loc'
.env.test // 测试环境
NODE_ENV = 'test'
VITE_APP_MODE = 'test'
.env.prd // 生产环境
NODE_ENV = 'production'
VITE_APP_MODE = 'prd'
在vue等文件中通过console.log('env', import.meta.env) 获取环境变量
配置运行命令:package.json
"scripts": {
"dev": "vite --open --mode loc",
"build:test": "vue-tsc && vite build --mode test",
"build:pro": "vue-tsc && vite build --mode prd",
"preview": "vite preview"
},
mode 后面的值是创建文件名的后置,例如:本地文件是.env.loc 若--mode dev 则打印环境变量是为空
在vite.config.ts中需要使用环境变量通过参数的方式来使用
export default defineConfig(({ command, mode }) => {
// 通过这样获取环境变量
let env = loadEnv(mode, process.cwd())
return {}
})
3,vite插件配置 vite-plugin-vue-setup-extend 应用
VueSetupExtend允许在<script setup>
语法中直接定义组件的name
属性,并支持自动暴露组件的导出,从而简化了组件的定义和使用。
安装命令:
npm i vite-plugin-vue-setup-extend -D
在vite.config.ts中配置插件
**目的: ** 可以通过扩展的方式,给 setup 函数添加新的功能,例如
1.允许在 setup 函数中使用 ES6 模块语法
2.允许在 setup 函数中使用 async/await 异步操作
3.支持在 setup 函数中使用源代码映射(source map)
4,集成sass 或者 less
安装命令:
npm install -D sass sass-loader
-
删除项目中style.css, 创建style文件将样式文件都存放在这个文件夹
-
去npm官网搜scss-reset插件,在里面复制出清除默认样式的代码,在style文件夹中创建reset.scss文件,将代码复制进去
-
在style文件夹中新建index.scss 文件并将清除默认样式引入进去
如果@import 在编译时报错则使用@use
-
在main.ts中引入index.scss,这样系统就可以使用全局样式
-
在vite.config.ts中配置全局样式变量
export default defineConfig((config) => {
css: {
preprocessorOptions: {
scss: {
javascriptEnabled: true, // 允许使用 JavaScript 表达式
additionalData: '@use "./src/styles/variable.scss";',
// 或者在assets中创建
// additionalData: `@use "@/assets/styles/variables.scss" as *;`,
},
},
},
}
}
注: 在配置sass全局变量时,一直出现报错,通常是由于 Sass 无法找到你要导入的样式表文件导致的, 首先去核实文件名和路径是否没问题! ! !
我在这里是上面设置了@styles和@images两个别名,我把这两个别名注释后再去接入就没报错,后面再将注释放开
5,createSvgIconsPlugin 全局注册svg
安装命令:
npm install vite-plugin-svg-icons -D
在vite.config.ts中配置插件
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
// import path from "node:path";
import { fileURLToPath, resolve } from "node:url";
import VueSetupExtend from "vite-plugin-vue-setup-extend";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import path from "node:path";
// https://vite.dev/config/
export default defineConfig(({ command, mode }) => {
return {
plugins: [
vue(),
VueSetupExtend(),
createSvgIconsPlugin({
// 指定图标文件夹路径
iconDirs: [path.resolve(process.cwd(), "src/assets/icons")],
// 指定symbolId格式
symbolId: "icon-[name]",
}),
],
resolve: {
// 1, 先配置别名
alias: {
// 第一种方式(最简洁)
// "@": path.resolve("./src"),
// 第二种方式
"@": fileURLToPath(new URL("./src", import.meta.url)),
"@styles": fileURLToPath(new URL("./src/styles", import.meta.url)),
"@images": fileURLToPath(
new URL("./src/assets/images", import.meta.url)
),
},
},
// 3, 配置全局css样式
css: {
// 全局 css样式
preprocessorOptions: {
scss: {
javascriptEnabled: true, // 允许使用 JavaScript 表达式
additionalData: `@use "@/styles/variable.scss" as *;`,
},
},
},
};
});
安装配置好后,启动项目会有一个报错: Cannot find package ‘fast-glob’
解决: 重新安装一下这个插件就能运行项目
cnpm i fast-glob -D
在入口文件main.ts中导入
import 'virtual:svg-icons-register'
在入口文件这里导入时也可能存在一个ts错误提示:
解决方式:
在项目中找到一个env.d.ts的文件,在里面添加这窜代码
declare module "virtual:svg-icons-register" {
const component: any;
export default component;
}
创建svgIcon.vue组件,并全局挂载,直接通过标签的形式使用
// 在components 文件夹中新增一个svgIcon.vue文件
<template>
<svg class="svg-icon" :style="'width:' + size + ';height:' + size">
<use :xlink:href="symbolId" :fill="color" />
</svg>
</template>
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps({
prefix: { // 前缀
type: String,
default: 'icon',
},
name: { // svg图标名称
type: String,
required: false,
default: '',
},
color: { // svg图标颜色
type: String,
default: '',
},
size: { // svg图标大小
type: String,
default: '16px',
},
})
console.log('props', props)
const symbolId = computed(() => `#${props.prefix}-${props.name}`)
</script>
<style scoped>
.svg-icon {
display: inline-block;
width: 1em;
height: 1em;
overflow: hidden;
vertical-align: -0.15em;
/* 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 */
outline: none;
fill: currentcolor;
/* 定义元素的颜色,currentColor是一个变量,这个变量的值就表示当前元素的color值,如果当前元素未设置color值,则从父元素继承 */
}
</style>
在components文件中新增一个index.ts文件
import type { App, Component } from 'vue'
import SvgIcon from './svgIcon.vue'
// 定义一个对象,用于存储组件映射关系,键为组件名称,值为组件对象
const components: { [name: string]: Component } = {
SvgIcon,
// ...
}
// 导出默认的插件
export default {
// install 方法在插件安装时被调用,参数为 Vue 应用实例
install(app: App) {
Object.keys(components).forEach((key: string) => {
// 以 key 的名字在全局注册每个组件
app.component(key, components[key]);
});
},
};
将在入口文件(main.ts)引入全局注册的插件
// 注册封装的组件
import gloablComponent from '@/components'
app.use(gloablComponent)
使用svg图标: 在asset文件夹中新增icons文件存放svg图标
全局通过标签的形式使用
<svgIcon name="phone" size="50" color="#ccc" />
6,配置代理 serve
看 【集成axios封装请求方法中】
4,配置路由
1.安装路由
npm install vue-router@4
2.创建路由文件和定义路由
在vue项目文件中src下新增一个router的文件夹,里面包含两个文件,index.ts和router.ts
index.ts文件
import { createRouter, createWebHistory } from 'vue-router'
// 项目中定义的路由对象
import { constantRoutes } from './router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: constantRoutes,
})
export default router
router.ts
// 在这个文件中定义所以的路由对象,并将其导出
export const constantRoutes = [
{
path: "/home",
name: "home",
component: () => import("@/views/home/index.vue"),
meta: {
title: "首页", // 菜单标题
hidden: true, // 在左侧菜单中是否隐藏 true: 隐藏 false:显示
icon: "",
},
},
{
path: "/about",
name: "about",
component: () => import("@/views/about/index.vue"),
meta: {
title: "关于", // 菜单标题
hidden: true, // 在左侧菜单中是否隐藏 true: 隐藏 false:显示
icon: "",
},
},
];
3.在入口文件中挂载路由
// 挂载路由
import router from '@/router';
app.use(router);
4.使用路由
此处根据系统来做布局,目前只是简单使用路由跳转.说明路由添加成功
这里使用router-link 和 router-view 两个标签来实现路由跳转和显示,不过多描述
5,集成状态管理 pinia
1, 安装命令
npm install pinia
2, 在src下新建文件夹 stores,包含 index.ts文件和modules文件夹
index.ts文件
// 创建大仓库
import { createPinia } from "pinia";
// 创建
const pinia = createPinia();
// 对外暴露: 入口文件需要安装仓库
export default pinia;
modules文件夹中存放各个功能的小仓库
//创建用户相关的小仓库
import { defineStore } from "pinia";
// 各个小仓库定义的数据类型
import type { UserState } from "./type";
const useUserStore = defineStore("user", {
//小仓库存储数据地方 也需要定义数据类型
state: (): UserState => ({
name: "张三",
age: 18,
}),
// 异步|逻辑的地方
actions: {
setAge(age: number) {
this.age = age;
},
},
});
export default useUserStore;
type.ts
// 各个小仓库需要定义的ts类型,统一放在这里,方便维护
export interface UserState {
name: string
age: number
}
3, 在入口文件中挂载 pinia 状态管理
// 挂载pinia
import pinia from '@/stores';
app.use(pinia);
4, 使用状态管理
<template>
<div class="text">
姓名: <span>{{ UserStore.name}}</span> ;
年龄: <span>{{ UserStore.age }}</span>
</div>
</template>
<script lang='ts' setup name="home">
// 引入小仓库
import useUserStore from '@/stores/modules/user';
// 定义变量接收小仓库
let UserStore = useUserStore()
console.log('userStore', UserStore)
</script>
6,集成axios封装请求方法
1,安装命令
npm install axios
2,封装axios,在utils文件夹中新增http.ts文件
//进行axios的 二次封装: 使用请求和响应拦截器
import axios from 'axios'
import { ElMessage } from 'element-plus'
import useUserStore from '@/stores/modules/user'
import { showLoading, hideLoading } from './loading'
// 1,使用axios 对象的create方法,去创建axios 实现做些配置
const request = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API, // 基础路径上带上api
timeout: 5000,
})
// 2, axios 示例添加请求和响应拦截器
request.interceptors.request.use(config => {
// 判断请求头中是否需要显示laoding
if (config.headers.loading) {
showLoading()
}
// 从config配置对象中header属性请求头,经常给服务器端携带公共参数
// 从仓库中获取token将其放到header中
const userStore = useUserStore()
if (userStore.token) {
config.headers.token = userStore.token
}
console.log('config', config)
//需要返回配置对象
return config
})
// axios 示例添加响应拦截器
request.interceptors.response.use(
response => {
// 响应拦截进来隐藏loading效果,此处采⽤延时处理是合并loading请求效果,避免多次请求loading关闭⼜开启
setTimeout(() => {
hideLoading()
}, 200)
// 成功回调
return response.data
},
error => {
setTimeout(() => {
hideLoading()
}, 200)
console.log('err', error)
// 失败回调: 处理http 网络错误
let message = '' // 定义一个错误提示问题
const status = error.response ? error.response.status : 888 // 错误状态码
switch (status) {
case 401:
message = 'TOKEN过期'
break
case 403:
message = '无权访问'
break
case 404:
message = '请求地址错误'
break
case 500:
message = '服务器异常'
break
default:
message = '网络异常'
break
}
//提示错误信息
ElMessage({
type: 'error',
message,
})
return Promise.reject(error)
},
)
// 对外暴露
export default request
3,封装全局loading组件,在二次封装axios中使用
/**
* 全局loading效果:合并多次loading请求,避免重复请求
* 当调⽤⼀次showLoading,则次数+1;当次数为0时,则显⽰loading
* 当调⽤⼀次hideLoading,则次数-1; 当次数为0时,则结束loading
*/
import { ElLoading } from 'element-plus'
// 定义⼀个请求次数的变量,⽤来记录当前页⾯总共请求的次数
// import loading from '@/assets/icons/loading.svg'
let loadingRequestCount = 0
// 初始化loading
let loadingInstance: any = null
// 显⽰loading的函数并且记录请求次数 ++
const showLoading = () => {
if (loadingRequestCount === 0) {
// 全局实现loading效果,不⽤每个页⾯单独去v-loading
// loading样式
loadingInstance = ElLoading.service({
lock: true,
text: '正在加载中,请稍后....',
background: 'rgba(0, 0, 0, 0.6)',
customClass: 'loading',
})
}
loadingRequestCount++
}
// 隐藏loading的函数,并且记录请求次数 --
const hideLoading = () => {
if (loadingRequestCount <= 0) return
loadingRequestCount--
if (loadingRequestCount === 0) {
loadingInstance.close()
}
}
export { showLoading, hideLoading }
4,新建文件夹API存放各个模块的接口里面有多个文件,每个文件下包含index.ts和type.ts
index.ts
import http from '@/utils/http';
import type { reqUserData } from "./type";
// 定义接口
enum API {
GET_USERLISET_URL = "admin/acl/user", // 手机号登入
}
export const getUserList = (page:any,size:any, username:string) => {
return http.get<any, reqUserData>(
API.GET_USERLISET_URL + `/${page}/${size}`,
{
headers: { loading: true },
params: { username }
}
);
};
type.ts
// 接口类型
export interface reqUserData {
code: number;
message: string;
ok: boolean;
data: {
records: records[];
size: number;
pages:number;
total: number;
};
}
// 接口返回列表中对象的类型
export interface records {
createTime: string;
id: number;
name: string;
password: string;
phone: null;
roleName: string;
updateTime: string;
username: string;
}
5,在vite.config.ts中配置代理
import { defineConfig, loadEnv, type build } from "vite";
import vue from "@vitejs/plugin-vue";
// import path from "node:path";
import { fileURLToPath, resolve } from "node:url";
import VueSetupExtend from "vite-plugin-vue-setup-extend";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import path from "node:path";
// https://vite.dev/config/
export default defineConfig(({ command, mode }) => {
// mode 表示是那个环境 process.cwd() 当前工作目录的根目录
//2, 这句代码表示: 在那个环境下加载那个文件
let env = loadEnv(mode, process.cwd()); // vite支持loadEnv 执行此方法能够获取当前环境变量
console.log("当前环境", env); // 打印出当前环境变量)
return {
base: env.VITE_BASE_URL,
build: {
outDir: "dist",
},
plugins: [
vue(),
VueSetupExtend(),
createSvgIconsPlugin({
// 指定图标文件夹路径
iconDirs: [path.resolve(process.cwd(), "src/assets/icons")],
// 指定symbolId格式
symbolId: "icon-[dir]-[name]",
}),
],
resolve: {
// 1, 先配置别名
alias: {
// 第一种方式(最简洁)
// "@": path.resolve("./src"),
// 第二种方式
"@": fileURLToPath(new URL("./src", import.meta.url)),
"@styles": fileURLToPath(new URL("./src/styles", import.meta.url)),
"@images": fileURLToPath(
new URL("./src/assets/images", import.meta.url)
),
},
},
// 3, 配置全局css样式
css: {
// 全局 css样式
preprocessorOptions: {
scss: {
javascriptEnabled: true, // 允许使用 JavaScript 表达式
additionalData: `@use "@/styles/variable.scss" as *;`,
},
},
},
// 配置代理
server: {
proxy: {
"/api": {
target: "http://sph-api.atguigu.cn", // 目标服务器地址
changeOrigin: true, // 是否跨域
rewrite: (path) => path.replace(/^\/api/, ""), // 重写路径
},
},
},
};
});
6, 调接口获取数据
//调接口获取列表
const getuserData = async () => {
try{
let res: reqUserData = await getUserList(1, 10, '')
console.log('res', res)
list.value = res.data.records
}catch(e){
console.log('xxxx',e)
}
}
结果