Vite于TS使用方法

文章详细阐述了在Vue3项目中如何使用TypeScript定义接口类型,包括接口的组织结构、数据模型、错误处理以及动态路由处理。同时,文章介绍了如何利用axios进行HTTP请求,包括请求拦截器、响应拦截器和状态检查。此外,还涉及到权限指令的实现、图片懒加载功能以及数据存储和深拷贝等实用技巧。
摘要由CSDN通过智能技术生成

获取环境变量数据

import.meta.env.VITE_API_URL as string,

或者本地打印
console.log(import.meta.env)  自行查看

TS接口定义类型,建议单独抽离文件处理

单独拉去一个文件夹进行接口api的类型定义
例如定义interface/index.ts文件,进行参数定义

interface 用来建立某种代码约定,使得其它开发者在调用某个方法或者创建新的类时,必须遵循接口所定义的代码约定

// * 请求响应参数(不包含data)
export interface Result {
	code: string;
	msg: string;
}

// * 请求响应参数(包含data)
export interface ResultData<T = any> extends Result {
	data: T;
}

// * 分页响应参数
export interface ResPage<T> {
	list: T[];
	pageNum: number;
	pageSize: number;
	total: number;
}

// * 分页请求参数
export interface ReqPage {
	pageNum: number;
	pageSize: number;
}

// * 文件上传模块
export namespace Upload {
	export interface ResFileUrl {
		fileUrl: string;
	}
}

// * 登录模块
export namespace Login {
	export interface ReqLoginForm {
		username: string;
		password: string;
	}
	export interface ResLogin {
		access_token: string;
	}
	export interface ResAuthButtons {
		[key: string]: string[];
	}
}

// * 用户管理模块
export namespace User {
// ReqUserParams 通过extends 方法实现 ReqPage继承分页
// 直接调用  User .ReqUserParams   
	export interface ReqUserParams extends ReqPage {
		username: string;
		gender: number;
		idCard: string;
		email: string;
		address: string;
		createTime: string[];
		status: number;
	}
	export interface ResUserList {
		id: string;
		username: string;
		gender: string;
		user: {
			detail: {
				age: number;
			};
		};
		idCard: string;
		email: string;
		address: string;
		createTime: string;
		status: number;
		avatar: string;
		children?: ResUserList[];
	}
	export interface ResStatus {
		userLabel: string;
		userValue: number;
	}
	export interface ResGender {
		genderLabel: string;
		genderValue: number;
	}
	export interface ResDepartment {
		id: string;
		name: string;
		children?: ResDepartment[];
	}
	export interface ResRole {
		id: string;
		name: string;
		children?: ResDepartment[];
	}
}

在接口页面引入使用
import { ResPage, User } from "@/api/interface/index";
一般分成两个情况
1.引入定义好的ts定义接口传入参数是使用
export const getUserList = (params: User.ReqUserParams) => {
    //<ResPage 响应参数(分页,page,size)校验<User.ResUserList  响应数据体校验>>
	return http.post<ResPage<User.ResUserList>>(PORT1 + `/user/list`, params);
};

2.在获取接口数据的时候使用
export const getUserList = (params: User.ReqUserParams) => {
	return http.post<ResPage<User.ResUserList>>(PORT1 + `/user/list`, params);
};

2.定义单个TS类型使用interface
export interface ReqPage {
	pageNum: number;
	pageSize: number;
}
如果是通过别名创建多个通过namespace ,使用就是 Login.ReqLoginForm 
export namespace Login {
	export interface ReqLoginForm {
		username: string;
		password: string;
	}
	export interface ResLogin {
		access_token: string;
	}
	export interface ResAuthButtons {
		[key: string]: string[];
	}
}
文件上次
(params: FormData)

TS定义后端接口状态类型

import { ElMessage } from "element-plus";

/**
 * @description: 校验网络请求状态码
 * @param {Number} status
 * @return void
 */
export const checkStatus = (status: number): void => {
	switch (status) {
		case 400:
			ElMessage.error("请求失败!请您稍后重试");
			break;
		case 401:
			ElMessage.error("登录失效!请您重新登录");
			break;
		case 403:
			ElMessage.error("当前账号无权限访问!");
			break;
		case 404:
			ElMessage.error("你所访问的资源不存在!");
			break;
		case 405:
			ElMessage.error("请求方式错误!请您稍后重试");
			break;
		case 408:
			ElMessage.error("请求超时!请您稍后重试");
			break;
		case 500:
			ElMessage.error("服务异常!");
			break;
		case 502:
			ElMessage.error("网关错误!");
			break;
		case 503:
			ElMessage.error("服务不可用!");
			break;
		case 504:
			ElMessage.error("网关超时!");
			break;
		default:
			ElMessage.error("请求失败!");
	}
};
使用封装的方法
import { checkStatus } from "./helper/checkStatus";
传入响应的状态码
if (response) checkStatus(response.status);

vue3 动态路由
调用路由处理

export function adminUser(router, to, next) {
    //此处res就是获取后端拼接好的路由数据
	let res = setUserInfosTable
	//定义动态路由名字
	dynamicRoutes[0].children = res.data;
	const awaitRoute = dynamicRouter(dynamicRoutes);
	//把白名单路由
	[...awaitRoute, { path: '*', redirect: '/404' }].forEach((route) => {
		router.addRoute({ ...route });
	});
	setCacheTagsViewRoutes(JSON.parse(JSON.stringify(res.data)));
	next({ ...to, replace: true });
}

递归处理每一项 component 中的路径

export function dynamicRouter(routes) {
	return routes.map((view) => {
		if (view.component) view.component = loadView(view.component);
		if (view.children) dynamicRouter(view.children);
		return view;
	});
}

处理区分不同环境使用不同路由

// 处理后端返回的 `component` 路径,拼装实现懒加载
export function loadView(path) {
	if (是不是开发环境) return () => import(`@/views/${path}`)
	else return () => Promise.resolve(require(`@/views/${path}`));
}

TS 封装axios请求

import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Session } from '/@/utils/storage';
// qs.parse():将URL解析成对象的形式
//  qs.stringify():将对象 序列化成URL的形式,以&进行拼接
import qs from 'qs';
// # 本地环境接口地址
// VITE_API_URL = 'http://localhost:8888/'
// 配置新建一个 axios 实例
const service: AxiosInstance = axios.create({
	baseURL: import.meta.env.VITE_API_URL,
	timeout: 50000,
	headers: { 'Content-Type': 'application/json' },
	paramsSerializer: {
		serialize(params) {
			return qs.stringify(params, { allowDots: true });
		},
	},
});

// 添加请求拦截器
service.interceptors.request.use(
	(config: AxiosRequestConfig):any => {
		// 在发送请求之前做些什么 token
		if (Session.get('token')) {
			config.headers!['Authorization'] = `${Session.get('token')}`;
		}
		return config;
	},
	(error) => {
		// 对请求错误做些什么
		return Promise.reject(error);
	}
);

// 添加响应拦截器
service.interceptors.response.use(
	(response) => {
		// 对响应数据做点什么
		const res = response.data;
		if (res.code && res.code !== 0) {
			// `token` 过期或者账号已在别处登录
			// 调用上述封装的TS请求
			if (res.code === 401 || res.code === 4001) {
				Session.clear(); // 清除浏览器全部临时缓存
				window.location.href = '/'; // 去登录页
				ElMessageBox.alert('你已被登出,请重新登录', '提示', {})
					.then(() => {})
					.catch(() => {});
			}
			return Promise.reject(service.interceptors.response);
		} else {
			return response.data;
		}
	},
	(error) => {
		// 对响应错误做点什么
		if (error.message.indexOf('timeout') != -1) {
			ElMessage.error('网络超时');
		} else if (error.message == 'Network Error') {
			ElMessage.error('网络连接错误');
		} else {
			if (error.response.data) ElMessage.error(error.response.statusText);
			else ElMessage.error('接口路径找不到');
		}
		return Promise.reject(error);
	}
);

// 导出 axios 实例
export default service;

在api文件使用 
import request from '/@/utils/request';
使用期间配合  自定义接口返回或者参数校验TS(上述第一条)

自定义权限指令

// 文件permission.js
 
 
// 判断按钮权限逻辑
const checkPermission = (el, binding) => {
 
    // 获取自定义指令传过来的数组(binding.value)
    const btnRoles = binding.value
    // 取一下本地存的账号权限
    const userRoles = JSON.parse(localStorage.getItem("role"))
 
    // 判断自定义指令的传值,在账号权限数组中能否找到 
    if (btnRoles && btnRoles instanceof Array) {
        if (btnRoles.length) {
            // 能找到返回true
            const hasPermission = userRoles.some(v => {
                return btnRoles.includes(v)
            })
            // 找不到返回false,使用自定义指令的钩子函数,操作dom元素删除该节点
            if (!hasPermission) {
                el.parentNode && el.parentNode.removeChild(el)
            }
        }
        else {
            throw new Error(`传入关于权限的数组,如 v-permission="['super','normal']"`)
        }
    }
}
 
// 导出一个对象用作自定义指令的第二个参数
export default {
  mounted(el, binding) {
    checkPermission(el, binding)
  },
  updated(el, binding) {
    checkPermission(el, binding)
  }
}
使用方法
<el-button v-permission="['super']">超级管理员</el-button>
  • 判断两个对象是否相同
  • @param a 要比较的对象一
  • @param b 要比较的对象二
  • @returns 相同返回 true,反之则反
export function isObjectValueEqual(a: { [key: string]: any }, b: { [key: string]: any }) {
	if (!a || !b) return false;
	let aProps = Object.getOwnPropertyNames(a);
	let bProps = Object.getOwnPropertyNames(b);
	if (aProps.length != bProps.length) return false;
	for (let i = 0; i < aProps.length; i++) {
		let propName = aProps[i];
		let propA = a[propName];
		let propB = b[propName];
		if (!b.hasOwnProperty(propName)) return false;
		if (propA instanceof Object) {
			if (!isObjectValueEqual(propA, propB)) return false;
		} else if (propA !== propB) {
			return false;
		}
	}
	return true;
}

import commonFunction from '/@/utils/commonFunction';

vue3实现图片懒加载

/**
 * 图片懒加载
 * @param el dom 目标元素
 * @param arr 列表数据
 * @description data-xxx 属性用于存储页面或应用程序的私有自定义数据
 */
export const lazyImg = (el: string, arr: EmptyArrayType) => {
	const io = new IntersectionObserver((res) => {
		res.forEach((v: any) => {
			if (v.isIntersecting) {
				const { img, key } = v.target.dataset;
				v.target.src = img;
				v.target.onload = () => {
					io.unobserve(v.target);
					arr[key]['loading'] = false;
				};
			}
		});
	});
	nextTick(() => {
		document.querySelectorAll(el).forEach((img) => io.observe(img));
	});
};
// 页面加载时     
onMounted(() => {
	other.lazyImg('[data-lazy-img-list]', state.tableData.data);
});
第一个参数  	<img :data-img="v.img" :data-key="k" :data-lazy-img-list="k" />
第二个参数是获取到的所有数据

/**

  • 对象深克隆
  • @param obj 源对象
  • @returns 克隆后的对象
    */
export function deepClone(obj: EmptyObjectType) {
	let newObj: EmptyObjectType;
	try {
		newObj = obj.push ? [] : {};
	} catch (error) {
		newObj = {};
	}
	for (let attr in obj) {
		if (obj[attr] && typeof obj[attr] === 'object') {
			newObj[attr] = deepClone(obj[attr]);
		} else {
			newObj[attr] = obj[attr];
		}
	}
	return newObj;
}

设置缓存

import Cookies from 'js-cookie';

/**
 * window.localStorage 浏览器永久缓存
 * @method set 设置永久缓存
 * @method get 获取永久缓存
 * @method remove 移除永久缓存
 * @method clear 移除全部永久缓存
 */
export const Local = {
	// 设置永久缓存
	set(key: string, val: any) {
		window.localStorage.setItem(key, JSON.stringify(val));
	},
	// 获取永久缓存
	get(key: string) {
		let json = <string>window.localStorage.getItem(key);
		return JSON.parse(json);
	},
	// 移除永久缓存
	remove(key: string) {
		window.localStorage.removeItem(key);
	},
	// 移除全部永久缓存
	clear() {
		window.localStorage.clear();
	},
};

/**
 * window.sessionStorage 浏览器临时缓存
 * @method set 设置临时缓存
 * @method get 获取临时缓存
 * @method remove 移除临时缓存
 * @method clear 移除全部临时缓存
 */
export const Session = {
	// 设置临时缓存
	set(key: string, val: any) {
		if (key === 'token') return Cookies.set(key, val);
		window.sessionStorage.setItem(key, JSON.stringify(val));
	},
	// 获取临时缓存
	get(key: string) {
		if (key === 'token') return Cookies.get(key);
		let json = <string>window.sessionStorage.getItem(key);
		return JSON.parse(json);
	},
	// 移除临时缓存
	remove(key: string) {
		if (key === 'token') return Cookies.remove(key);
		window.sessionStorage.removeItem(key);
	},
	// 移除全部临时缓存
	clear() {
		Cookies.remove('token');
		window.sessionStorage.clear();
	},
};

/**

  • 金额用 , 区分开
  • @param val 当前值字符串
  • @returns 返回处理后的字符串
    */
export function verifyNumberComma(val: string) {
	// 调用小数或整数(不可以负数)方法
	let v: any = verifyNumberIntegerAndFloat(val);
	// 字符串转成数组
	v = v.toString().split('.');
	// \B 匹配非单词边界,两边都是单词字符或者两边都是非单词字符
	v[0] = v[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
	// 数组转字符串
	v = v.join('.');
	// 返回结果
	return v;
}

/**

  • 手机号码
  • @param val 当前值字符串
  • @returns 返回 true: 手机号码正确
    */
export function verifyPhone(val: string) {
	// false: 手机号码不正确
	if (!/^((12[0-9])|(13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0|1,5-9]))\d{8}$/.test(val)) return false;
	// true: 手机号码正确
	else return true;
}

Element plus全局挂在以及使用

main.tS里面进行按需挂载 

import { ElMessage , ElNotification ,ElMessageBox } from 'element-plus'
const app = createApp(App);
app.use(createPinia());
app.use(router);
app.use(ElMessage);
挂在之后可以在系统直接使用

 ElMessage({
        message: "Congrats, this is a success message.",
        type: "success",
      });

全局挂载所有

import ElementPlus from "element-plus";

app.use(router).use(I18n).use(pinia).use(directives).use(ElementPlus).mount("#app");

创建模板

<template>
  <div class="feedback_wrapper">33</div>
</template>
<script lang="ts">
import { ref, reactive, toRefs } from "vue";
export default {
//获取组件传值
  props: {
    from: {
      type: Object,
    },
  },
//   props: ["from"],
  setup(props) {
    console.log(111, props.from);

    const data = reactive({});
    return {
      ...toRefs(data),
    };
  },
};
</script>


vue3 引入组件以及传值

import headertiele from "./headertiele.vue";
export default {
  components: {
    headertiele,
  }
}

vue3通过ref获取子组件的方法以及封装新增修改

    const numfont = ref();
    const drapt = () => {
      data.drawer = true;
      nextTick(() => {
        numfont.value.acceptParams({
          name: "22",
          font: "334",
          ok: "45",
          delivery: true,
        });
      });
    };
        // 接收父组件传过来的参数
    const acceptParams = (params) => {
      data.ruleForm = params;
    };
    defineExpose({
      acceptParams,
    });

封装新增修改

<template>
  <div class="feedback_wrapper">
    <el-upload
      list-type="picture-card"
      :limit="3"
      :file-list="fileList"
      :show-file-list="true"
      :on-change="fileChange"
      :auto-upload="false"
      accept="image/*"
    >
      <el-icon>
        <Plus />
      </el-icon>
      <template #file="{ file }">
        <div>
          <img class="el-upload-list__item-thumbnail" :src="file.url" alt />
          <span class="el-upload-list__item-actions">
            <span
              class="el-upload-list__item-preview"
              @click="handlePictureCardPreview(file)"
            >
              <el-icon>
                <zoom-in />
              </el-icon>
            </span>
            <span class="el-upload-list__item-delete" @click="handleRemove(file)">
              <el-icon>
                <Delete />
              </el-icon>
            </span>
          </span>
        </div>
      </template>
    </el-upload>
    <el-dialog v-model="dialogVisible">
      <img w-full class="full_img" :src="dialogImageUrl" alt="Preview Image" />
    </el-dialog>
    <el-button @click="Default">{{ permiss.doubleCount }}</el-button>
    <el-button
      type="text"
      style="margin-left: 16px"
      @click="(drawer = true), (title = '新增')"
    >
      新增
    </el-button>
    <el-button type="text" style="margin-left: 16px" @click="(title = '编辑'), drapt()">
      编辑
    </el-button>
    <!-- 新增或者编辑 -->
    <el-drawer v-model="drawer" title="I am the title" :close-on-click-modal="false">
      <template #header>
        <h4>{{ title }}</h4>
      </template>
      <div>
        <Headertiele v-if="drawer" ref="numfont" @fonterdelivery="fonterdelivery" />
      </div>
    </el-drawer>
  </div>
</template>
<script lang="ts">
import { ref, reactive, toRefs, nextTick } from "vue";
import { Delete, Plus, ZoomIn } from "@element-plus/icons-vue";
import { usePermissStore } from "../store/permiss";
import Headertiele from "./headertiele.vue";
const permiss = usePermissStore();
export default {
  components: {
    Headertiele,
  },
  setup() {
    const data = reactive({
      fileList: [],
      dialogImageUrl: "",
      dialogVisible: false,
      hideUpload: false,
      drawer: false,
      title: "",
    });
    // 更新上传加号按钮显示状态
    const updateUploadShown = () => {
      if (data.fileList.length == 3) {
        data.hideUpload = true;
      } else {
        data.hideUpload = false;
      }
    };
    // 文件改变
    const fileChange = (file, resfileList) => {
      data.fileList = resfileList;
      updateUploadShown();
    };
    // 移除图片
    const handleRemove = (file) => {
      const list = data.fileList;
      for (const i in list) {
        if (list[i].uid === file.uid) {
          list.splice(i, 1);
        }
      }
      data.fileList = list;
      updateUploadShown();
    };
    // 预览图片
    const handlePictureCardPreview = (file) => {
      data.dialogImageUrl = file.url;
      data.dialogVisible = true;
    };
    const Default = () => {
      permiss.$patch({
        num: 44,
      });
      permiss.hfrs(66);
      console.log(permiss.$state.num);
      ElMessage({
        message: "Congrats, this is a success message.",
        type: "success",
      });
    };
    const numfont = ref();
    const drapt = () => {
      data.drawer = true;
      nextTick(() => {
        numfont.value.acceptParams({
          name: "22",
          font: "334",
          ok: "45",
          delivery: true,
        });
      });
    };
    const fonterdelivery = (val) => {
      console.log(val);
      data.drawer = val;
    };
    return {
      ...toRefs(data),
      updateUploadShown,
      fileChange,
      handleRemove,
      handlePictureCardPreview,
      Default,
      permiss,
      drapt,
      fonterdelivery,
      numfont,
    };
  },
};
</script>
弹窗

vue3获取父组件传值

const props = defineProps<{ route: Menu }>()
const {route} = toRefs(props)

子组件 -----→ 传值 ----→父组件

子组件
<templete>
    <button @click='send'>子组件向父组件派发事件</button>
</templete>

<script>
    let str:string='我是子组件参数'
	const emit = defineEmits(['fatherFun'])
	const send = ()=>{
		 emit('fatherFun',str)     // 第一个参数是子组件派发的函数名(子传父是通过函数事件派发)   
	}
</script>


父组件
<child @fatherFun='getChildData' ></child>  // 子组件

<script>
const getChildData = (val)=>{  
    console.log(val)
}
</script>


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Vue 3、Vite 和 TypeScript 的项目中,使用图片通常涉及以下几个步骤: 1. **安装依赖**: 首先,你需要在项目中安装 `vue-loader` 和 `vite-plugin-vue3`,它们能很好地支持 Vue 的模板和类型检查。可以使用 npm 或 yarn 安装: ``` npm install vue-loader vite-plugin-vue3 @types/vue ``` 2. **配置 Vite**: 在 `vite.config.ts` 中添加对 Vue 3 的支持,并启用 TypeScript: ```ts import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; export default defineConfig({ plugins: [vue(), ...], build: { target: 'esnext', // 使用 ESNext 规范构建 transpileDependencies: ['@vue/types'], // 转译需要的库 }, }); ``` 3. **引用图片**: 在 `.vue` 文件中,使用 `<img>` 标签引用图片资源,同时指定 `src` 属性,并且因为 TypeScript 支持静态类型,你可以给 `src` 字符串加上类型注解: ```html <template> <img :src="require('./assets/myImage.png')" alt="My Image" :type="imageType" /> </template> <script lang="ts"> import { Component, Prop } from 'vue'; interface ImageProps { imageType: string; // 图片类型,如 "image/jpeg" } export default class MyComponent extends Component<ImageProps> { //... } </script> ``` 使用 `require` 函数是为了在运行时动态加载图片,避免打包所有图片到静态文件中。 4. **处理图片路径**: 如果图片位于静态资产目录(例如 `public/assets`),可以使用相对路径或者绝对 URL。如果你想让 Vite 自动处理,需要在 `vite.config.ts` 中设置 `alias` 或者使用 Vite 插件 `vite-plugin-vuetify` 或 `vite-plugin-eslint` 等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值