快速掌握Vue3:速成Vue3前端开发看这篇就够啦

154 阅读10分钟

一、Vue基本概念

1.1-Vue3的优点

  • Vue3支持Vue2额大多数特性。

  • 更好的支持TypeScript。

  • 打包大小减少41%。

  • 初次渲染快55%,更新渲染快133%。

  • 内存减少54%。

  • 使用proxy代替defineProperty实现数据响应式。

  • 重写虚拟DOM的实现和Tree-Shaking。

二、API

2.1-setup

我们可以跟以前定义data和methods,但是vue3中我们更推荐使用setup函数。

  • setup是一个函数。只在初始化时执行一次。以后大部分代码都是在setup中写。
  • 返回一个对象,对象中的属性或方法,模板中可以直接使用。
  • setup返回的数据会和data和methods进行合并,setup优先级更高。
  • setup函数中没有this。 以后开发都不使用this了
  • setup不要写async函数。

因为async函数必须返回一个json对象供模板使用,如果setup是一个async函数,返回的将是一个promise对象。
如果setup是一个async函数,那该组件就成了一个异步函数,需要配合Suspense组件才能使用。

2.2-ref

让数据变成响应式。

	(1)先引用ref

	import {ref} from 'vue';

	 

	(2)将数据变成响应式的。

	let data1=ref(12);

	 

	(3)操作数据

	data1.value = 123;

2.3-reactive

作用:定义对象格式的响应式数据
如果用ref定义对象/数组,内部会自动将对象/数组转换为reactive代理器对象。

  • const proxy=reactive(obj):接收一个普通对象然后返回该普通对象的响应式代理器对象。

  • js中修改告诉不需要.value。

  • 一般用来定义一个引用类型的响应数据。

2.4-toRefs

将响应式对象中所有属性包装为ref对象,并返回包含这些ref对象的普通对象。
应用:对trsctive定义的对象进行toRefs包装,包装之后的对象中每个属性都是响应式的。

2.5-响应式原理

通过proxy(代理):拦截对对象本身的操作,包括属性的读写、删除等操作。
通过Reflect(反射):动态对被代理对象的响应式属性进行特定的操作。

2.6-watch和watchEffect

watch - 指定监听数据:

  • 监听指定的一个或多个响应式数据,一旦发生变化,就会自动执行监视回调。

    • 如果是监听reactive对象中的属性,必须通过函数来指定。
    • 监听多个数据,使用数组来指定。
  • 默认初始时不指定回调,但是通弄过配置immediate为true,来指定初始时立即执行第一次。

  • 通过配置deep为true,来指定深度监视。

watchEffect - 不指定监听数据:

  • 不用直接指定啦监视的数据,回调函数中使用的哪些响应式数据就监听哪些响应式数据。
  • 默认初始就会执行一次。

2.7-生命周期

vue2中的生命周期钩子函数依旧可以使用,不过建议使用vue3的钩子函数

image.png

2.8-ref获取元素

vue2中是用thisref.xxx来获取元素或组件,但是vue3中没有this的概念。
vue3通过ref创建响应式数据的api来获取元素。

1.使用ref创建响应式数据,假设叫x
2.模板中绑定ref属性,值为上面的x
注意不能使用v-bind动态绑定。
这是x就是一个dom元素或组件了。

2.9-自定义hook函数

hook函数翻译成中文就是钩子函数(注意并不是生命周期的钩子函数)
比如ref,reactive,computed,watch,onBeforeMount等都是hook函数,只不过他们都是vue内部hook函数。

1.创建一个函数,函数名称必须以"use"开头
2.函数必须return一些数据。

2.10-shallowReactive与shallowRef

他们都表示浅响应式。

  • shallowReactive:只处理了对象第一层属性的响应式(值响应第一层)

  • shallowRef:只有重新复制时才是响应式(不响应内部数据,只响应整体。)

2.11-readonly与shallowReadonly

  • 他们表示只读代理对象

  • readonly

    • 深度只读
    • 设置readonly后,修改响应式数据会报错。
  • shalloReadonly

    • 浅只读
    • 设置shalloReadonly后,修改响应式数据的第一层数据会报错。
  • 应用场景:

    • 在某些特定情况下,我们可能不希望对数据进行更新的操作,那就可以包装成一个只读代理对象,而不能修改或删除。

2.12-toRaw与markRaw

  • toRaw

    • 返回reactive或readonly对象的原始数据
    • 这是一个还原方法,可用于临时读取,得到的数据不具有响应式。
  • markRow:

    • 标记一个对象,使其不具有响应式

    • 应用场景:

      • 有些只不应被设置为响应式的,例如复杂的第三方实例或Vue组件对象。

      • 当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能。

2.13-toRef

  • 为响应式对象上的某个属性创建一个ref引用,更新是应用对象会同步更新。

  • 与ref的区别:ref是拷贝了一份新的数据指单独操作,更新时相互不影响。

2.14-customRef

  • 用于自定义一个ref,可以显示的控制依赖追踪和触发相应。

  • 接受一个工厂函数,两个参数分别用于追踪的track与用于触发相应的trigger,并方法一个带有get和set属性的对象。

  • 需求:使用customRef实现防抖函数

2.15-provide与inject

  • provide和inject提供依赖注入,功能类似2.0的provide/inject

  • 实现跨层级组件(祖孙)间通信。

2.16-响应式数据的判断

  • isRef:检查一个值是否为一个ref对象。

  • isReactive:检查一个对象是否否是由reactive对象的响应式代理。

  • isReadonly:检查一个对象是否由readonly创建的只读代理。

  • isProxy:检查一个对象是否是由reactive或者readonly方法创建的代理。

2.17-Fragment(片段)

  • 在vue2中:组件中必须有一个跟标签

  • 在vue3中:组价可以没有跟标签,内部会将多个标签包含在一个Fragment虚拟标签中。

    • 好处:减少标签层级,减小内存占用

2.18-Teleport(瞬移)

  • Teleport提供了一种干净的方法,让组件的html在父组件界面外的特定标签(很可能是body)下插入显示。

2.19-Suspense(不确定的)

Supense组件是配合一部组件使用的,它可以让一部组件返回数据前渲染一些后背内容。
那我们首先要学会一个异步组件。

  • 在setup函数总返回一个promise,就是一个异步组件。

  • setup函数携程async函数,也是一个异步组件。

2.20-其他新的API

  • 全新的全局API:

    • createApp()
    • defineProperty()
    • defineComponent()
    • nextTick()
  • 将原来的全局API转移到应用对象:

    • app.component()

    • app.config()

    • app.directive()

    • app.mount()

    • app.umount()

    • app.use()

2.21-useSlots和useAttrs

useSlots 和 useAttrs 是真实的运行时函数,它会返回与 setupContext.slots 和 setupContext.attrs 等价的值,同样也能在普通的组合式 API 中使用。
使用场景:父组件使用子组件的插槽

1.父组件

	<template>

	  <h1>这是父组件</h1>

	 

	  <p>插槽上面1</p>

	  <slots-attrs msg="我是props的msg" heihei="我是attr">

	    <template #header >

	      <div style="width:100%;height:100px;border:1px solid green;">我是父组件插槽--插入的内容。。。</div>

	    </template>

	  </slots-attrs>

	  <p>插槽下面2</p>

	 

	</template>

	 

	<script lang="ts" setup>

	import SlotsAttrs from '@/components/04/SlotsAttrs.vue'

	 

	</script>

2.子组件

	<template>

	  <Child ref="child"/>

	 

	  {{msg}}

	</template>

	 

	 

	<script lang="ts" setup>

	import { ref,onMounted } from 'vue';

	import Child from '@/components/03/Child.vue'

	 

	 

	const child = ref(null);

	const msg=ref('')

	 

	 

	 

	 

	onMounted(()=>{

	  console.log("进来了")

	  console.log(child.value.msg)

	  msg.value = child.value.msg;

	})

	 

	</script>

三、路由

(1)userRout:获得当前路由对象。
(2)useRouter:获得路由实例,可以进行路由跳转。

	import {useRoute,useRouter} from "vue-router"

四、Vue3+TS实战知识点

1.1-引入ElementPlus

(1)输入命令安装(全局引入)。

npm install element-plus --save

(2)【main.ts】文件中配置

	import ElementPlus from 'element-plus'

	import 'element-plus/dist/index.css'

	 

	const app = createApp(App)

	app.use(ElementPlus)

1.2-引入axios

(1)输入命令安装。

npm install axios

(2)新建一个【http.ts】文件,写一个简单的请求封装。
image.png

	//导入axios

	import axios from "axios"

	 

	//axios.create创建一个axios实例

	//我们给这个实例编写配置,后端所有通过这个实例发送请求,都受这个配置约束。

	const $http=axios.create({

	    baseURL:"http://jsonplaceholder.typicode.com/",

	    timeout:1000

	});

	 

	 

	 

	// 添加请求拦截器

	$http.interceptors.request.use(function (config) {

	    // 在发送请求之前做些什么

	    return config;

	  }, function (error) {

	    // 对请求错误做些什么

	    return Promise.reject(error);

	  });

	 

	 

	 

	// 添加响应拦截器

	$http.interceptors.response.use(function (response) {

	    // 对响应数据做点什么

	    return response.data;

	  }, function (error) {

	    // 对响应错误做点什么

	    return Promise.reject(error);

	  });

	 

	export default $http;

(3)新建一个【test.ts】文件进行二次封装。

	import $http from "./http"

	 

	export const getData=$http.get("/posts");

(3)使用。

	<script lang="ts">

	import { defineComponent } from 'vue';

	import {getData} from "@/apis/test"

	 

	export default defineComponent({

	  name: 'Home',

	  setup(){

	      //请求接口

	      getData.then((data:any)=>{

	        console.log(data);

	      })

	 

	      return{

	 

	      }

	    }

	});

	</script>

1.3-引入vuex

(1)输入命令安装vuex

npm install vuex

(2)在【main.ts】中引入。

	import { createApp } from 'vue'

	import App from './App.vue'

	import store from './store'

	 

	createApp(App).use(store).mount('#app')

(3)在src文件夹下新建一个【store/index.ts】文件

	import { createStore } from 'vuex'

	import app from "./modules/app"

	import settings from "./modules/settings"

	import user from "./modules/user"

	 

	export default createStore({

	  // 导入模块

	  modules: {

	    app,

	    settings,

	    user

	  }

	})

	 

(4)【app.ts】格式如下。

	//【应用程序模块】

	const app={

	  //单一状态树,UI可通过this.$store.state.app.*获得数据

	  state: {

	 

	  },

	  //对state数据进行过滤后返回(可以认为是 store 的计算属性)

	  getters:{

	 

	  },

	  // 唯一拥有更改内存数据的接口,不可进行异步操作

	  mutations: {

	 

	  },

	  // 与mutation通讯,UI层写入内存数据的接口,可异步操作

	  actions: {

	 

	  }

	}

	export default app;

1.4-引入.env

(1)创建文件xxx.env文件。
image.png

	----------------【.env.development】---------------

	#以下的值webpack会根据不同的环境取得不同的值,使用方法:process.env.*

	#设定一个标题,代表这个环境是development

	NODE_ENV = 'development'

	 

	# Base URL

	BASE_URL = 'http://xxx:7881' 

	 

	# Base API

	VUE_APP_BASE_API = 'http://xxx:7881'

	 

	 

	 

	----------------【.env.production】---------------

	#以下的值webpack会根据不同的环境取得不同的值,使用方法:process.env.*

	#设定一个标题,代表这个环境是production

	NODE_ENV = 'production'

	 

	# Base URL

	BASE_URL = 'http://xxx:7881'

	 

	# Base API

	VUE_APP_BASE_API = 'http://xxx:7881'

	 

	 

	----------------【.env.staging】---------------

	#以下的值webpack会根据不同的环境取得不同的值,使用方法:process.env.*

	#设定一个标题,代表这个环境是development

	NODE_ENV = 'development'

	 

	# Base URL

	BASE_URL = 'http://xxx:7881'

	 

	# Base API

	VUE_APP_BASE_API = 'http://xxx:7881'

(2)使用。

process.env.VUE_APP_BASE_API

image.png

1.5-引入cookie

(1)安装。

npm install vue-cookies --save

1.6-引入nprogress

(1)安装。

npm install nprogress -S
npm i @types/nprogress-D

(2) 现在我们对NProgress进行一下简单的封装,首先我们在【common/utils/nporgress.ts】目录下创建文件,然后引入NProgress和CSS样式文件 。
image.png
(3)内容如下。

	import NProgress from 'nprogress' // 进度条

	import 'nprogress/nprogress.css' // 进度条样

	 

	//全局进度条的配置

	NProgress.configure({

	  easing: 'ease', // 动画方式

	  speed: 1000, // 递增进度条的速度

	  showSpinner: false, // 是否显示加载ico

	  trickleSpeed: 200, // 自动递增间隔

	  minimum: 0.3, // 更改启动时使用的最小百分比

	  parent: 'body', //指定进度条的父容器

	})

	 

	//打开进度条

	export const start =()=>{

	  NProgress.start();

	}

	 

	//关闭进度条

	export const close =()=>{

	  NProgress.done();

	}

(4)在【router/index.ts】文件中使用

	--引入文件

	import { close, start } from '@/common/utils/nprogress'//进度条

	 

	--使用

	const router = createRouter({

	  routes,

	  history: createWebHistory(),

	})

	 

	router.beforeEach((pre, next) => {

	  start()

	})

	 

	router.afterEach(() => {

	  close()

	})

1.7-引入Element Plus(按需自动引入)

环境:
image.png
参考文献:www.yisu.com/zixun/72354…

(1)输入命令安装

npm install element-plus
npm install -D unplugin-vue-components unplugin-auto-import

(2)在【vue.config.ts】中配置。

	const AutoImport = require('unplugin-auto-import/webpack')

	const Components = require('unplugin-vue-components/webpack')

	const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

	 

	 

	// 【每次修改,都需要重新build】

	// 插件(module.exports={configureWebpack:[]})

	    plugins: [

	      AutoImport({

	        resolvers: [ElementPlusResolver()]

	      }),

	      Components({

	        resolvers: [ElementPlusResolver()]

	      })

	    ],

(3)然后执行【npm run dev】报错了(如果报node-sass将这个卸载,安装sass即可)。
image.png
image.png
解决: 卸载nodev12,更新到nodev16在重新构建一下项目成功解决 了。
image.png

命令:
npm uninstall node-sass --dev-S
npm install sass --dev-S

1.8-引入sross-env环境变量配置

(1)安装。

npm i --save-dev cross-env
npm install dotenv-cli --dev-s

(2)创建文件。
image.png

	--【.env.dev.build】

	#以下的值webpack会根据不同的环境取得不同的值,使用方法:process.env.*

	#设定一个标题,代表这个环境是development

	NODE_ENV = production

	 

	# Base URL

	BASE_URL = 'http://xxx.xxx.xxx:7881' 

	 

	# Base API

	VUE_APP_BASE_API = 'http://xxx.xxx.xxx:7990'

	 

	# 统一认证中心配置

	VUE_APP_AUTH_BASEURL = 'http://xxx.xxx.xxx:5021'

	VUE_APP_AUTH_LOGIN_RRDIRECTURL = 'http://xxx.xxx.xxx:7881/callback'

	VUE_APP_AUTH_LOGOUT_RRDIRECTURL = 'http://xxx.xxx.xxx:7881'

	 

	 

	--【.env.dev.serve】

	#以下的值webpack会根据不同的环境取得不同的值,使用方法:process.env.*

	#设定一个标题,代表这个环境是development

	NODE_ENV = development

	 

	# Base URL

	BASE_URL = 'http://xxx.xxx.xxx:7881' 

	 

	# Base API

	VUE_APP_BASE_API = 'http://xxx.xxx.xxx:7990'

	 

	# 统一认证中心配置

	VUE_APP_AUTH_BASEURL = 'http://xxx.xxx.xxx:5021'

	VUE_APP_AUTH_LOGIN_RRDIRECTURL = 'http://localhost:7881/callback'

	VUE_APP_AUTH_LOGOUT_RRDIRECTURL = 'http://localhost:7881'

(3) 【package.json】中的配置

	 "scripts": {

	    "dev": "npm run serve:dev",

	    "start": "npm run serve:dev",

	    "server": "npm run serve:dev",

	    "build": "npm run build:dev",

	    "serve:dev": "cross-env NODE_ENV=development dotenv -e .env.dev.serve vue-cli-service serve",

	    "build:dev": "cross-env NODE_ENV=production  dotenv -e .env.dev.build vue-cli-service build",

	    "serve:test": "cross-env NODE_ENV=development dotenv -e .env.test.serve vue-cli-service serve",

	    "build:test": "cross-env NODE_ENV=production  dotenv -e .env.test.build vue-cli-service build",

	    "serve:prod": "cross-env NODE_ENV=development dotenv -e .env.prod.serve vue-cli-service serve",

	    "build:prod": "cross-env NODE_ENV=production  dotenv -e .env.prod.build vue-cli-service build",

	    "lint": "vue-cli-service lint"

	  },

1.9-引入js-cookie

(1)安装

npm install js-cookie -s
npm install @types/js-cookie --dev-s

(2)封装单独的ts文件。

	import Cookies from 'js-cookie'

	 

	/*

	封装操作Cookie本地存储的方法:

	说明:主要用途有保存登录信息。

	存储大小:4KB

	*/

	const cookies = {

	  /**

	   *设置Cookies

	   * @param {String} key 键

	   * @param {Object} value 值:存储的值可能是数组/对象,不能直接存储,需要转换 JSON.stringify()

	   * @param {Int} expiresTime 过期时间(单位:秒)

	   */

	  set(key: string, value: any, expiresTime: number) {

	    expiresTime=arguments[2] ? arguments[2] : (60*60)*24;//默认值为:24小时

	    let expires = new Date(new Date().getTime() * 1 + expiresTime * 1000);

	    return Cookies.set(key, value, {

	      expires: expires

	    });

	  },

	 

	  /**

	   * 获得Cookies

	   * @param {String} key 键

	   * @return {Object} 根据键取对应的值

	   */

	  get(key: string) {

	    return Cookies.get(key);

	  },

	 

	  /**

	   * 删除Cookies

	   * @param {String} key 键

	   */

	  remove(key: string) {

	    return Cookies.remove(key);

	  }

	};

	 

	export default cookies;

	 

1.10-引入el-plus面包屑导航

(1)安装

npm install path-to-regexp -s

(2)写代码。

	<template>

	  <el-breadcrumb class="app_breadcrumb" separator="/">

	    <transition-group name="breadcrumb">

	      <el-breadcrumb-item v-for="(item,index) in breadcrumbs" :key="item.path">

	        <span v-if="

	          item.path === ' ' ||

	          item.path === '/' ||

	          item.path === '-' ||

	          item.redirect === 'noredirect' ||

	          index == breadcrumbs.length - 1" class="no_redirect">

	          {{item.meta.title}}

	        </span>

	        <a v-else @click.prevent="handleLink(item)">{{item.meta.title}}</a>

	      </el-breadcrumb-item>

	    </transition-group>

	  </el-breadcrumb>

	</template>

	 

	<script lang="ts" setup>

	import { compile } from 'path-to-regexp'

	import { useRouter, useRoute, RouteLocationMatched } from 'vue-router'

	import { onBeforeMount, reactive, toRefs, watch } from 'vue'

	 

	const route = useRoute();

	const router = useRouter();

	 

	const pathCompile = (path: string) => {

	  const { params } = route;

	  const toPath = compile(path);

	  return toPath(params);

	}

	 

	const state = reactive({

	  // 面包屑路由

	  breadcrumbs: [] as Array<RouteLocationMatched>,

	 

	  // 获得面包屑路由

	  getBreadcrumb: () => {

	    let matched = route.matched.filter((item: RouteLocationMatched) => {

	      return item.meta && item.meta.title;

	    });

	    const first = matched[0];

	    if (!state.isIndex(first)) {

	        matched = [{ path: '/index', meta: { title: '首页' } } as any].concat(matched);

	    }

	    state.breadcrumbs = matched.filter(

	      (item) => item.meta && item.meta.title

	    );

	  },

	 

	  // 判断是不是首页

	  isIndex: (route: RouteLocationMatched):boolean => {

	    const name = route && route.name;

	    if (!name) {

	      return false;

	    }

	    return (

	      name.toString().trim().toLocaleLowerCase() === "Index".toLocaleLowerCase()

	    );

	  },

	 

	  // 跳转路由

	  handleLink(item: any){

	    const { redirect, path } = item;

	    if (redirect) {

	      router.push(redirect).catch((err) => {

	        console.warn(err);

	      })

	      return

	    }

	    router.push(pathCompile(path)).catch((err) => {

	      console.warn(err);

	    })

	  }

	});

	 

	// 加载

	onBeforeMount(() => {

	  state.getBreadcrumb();

	})

	 

	// 监听路由变化

	watch(() => route.path, (path: string) => {

	  if (path.startsWith('/redirect/')) {

	    return;

	  }

	  state.getBreadcrumb();

	})

	 

	const { breadcrumbs, handleLink } = toRefs(state);

	</script>

	 

	<style lang="scss" scoped>

	.app_breadcrumb.el-breadcrumb{

	  font-size: 14px;

	  line-height: 42px;

	  height:42px;

	 

	  .el-breadcrumb__inner,

	  .el-breadcrumb__inner a {

	    font-weight: 400 !important;

	  }

	 

	  .no_redirect{

	    color: #c0c4cc;

	    cursor: text;

	  }

	}

	</style>

	 

(3)引入组件。

	 <breadcrumb class="breadcrumb_container" />

1.11-封装http请求【axios-mapper】

(1)安装需要的依赖

npm install @types/qs
npm install qs

(2)创建【types.ts】
image.png

	// 请求内容类型

	export enum ContentType {

	  form = 'application/x-www-form-urlencoded',

	  json = 'application/json; charset=utf-8',

	  multipart = 'multipart/form-data'

	}

	 

	// 请求方式

	export enum Method {

	  GET = 'GET',

	  POST = 'POST',

	  PUT = 'PUT',

	  PATCH = 'PATCH',

	  DELETE = 'DELETE'

	}

	 

	// 网络请求参数

	export interface RequestParams {

	  [key: string]: any

	}

(3)创建【convert-model.ts】文件。

	// Json-Model相互转换

	export class ConvertModel {

	  /**

	   * @description:  json转model

	   * @param {string} json

	   * @return {*}

	   */

	  public static jsonToModel<T>(json: string): T {

	    return JSON.parse(json) as T;

	  }

	 

	  /**

	   * @description: model转json

	   * @param {any} model

	   * @return {*}

	   */

	  public static modelToJson(model: any): string {

	    return JSON.stringify(model);

	  }

	}

(4)创建【index.ts】文件

	import { RequestParams, Method, ContentType } from './types';

	import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';

	import { ConvertModel } from './convert-model';

	 

	// 导出所有的类型

	export * from './types';

	 

	export interface HttpClientConfig extends AxiosRequestConfig {

	  defaultParams?: RequestParams;

	}

	 

	// 导出默认请求

	export default class HttpClient {

	  private _defaultConfig: HttpClientConfig;

	  public httpClient: AxiosInstance;

	 

	  // 构造函数

	  constructor(options: HttpClientConfig = {}){

	    this.httpClient = axios.create(options);

	    this._defaultConfig = options;

	  }

	 

	  /**

	   * @description: 封装请求类

	   * @param {string} path 请求路径 

	   * @param {Method} method 请求方式(默认:GET)

	   * @param {RequestParams} params 参数

	   * @param {ContentType} contentType http配置

	   * @param {HttpClientConfig} optionsSource

	   * @return {*}

	   */

	  async request<T>(

	    path: string = '',

	    method: Method = Method.GET,

	    params?: RequestParams,

	    contentType: ContentType = ContentType.json,

	    optionsSource?: HttpClientConfig

	  ):Promise<T> {

	    const options: any = Object.assign(

	      {},

	      this._defaultConfig,

	      optionsSource

	    );

	 

	    const { headers } = options;

	    headers['content-type'] = contentType;

	 

	    const allParams = Object.assign(

	      {},

	      this._defaultConfig.defaultParams,

	      params

	    );

	 

	    // 发送请求

	    const requestConfig: HttpClientConfig = {

	      url: `${path}`,

	      method,

	      headers,

	    };

	 

	    if(contentType === ContentType.form) {

	      requestConfig.params = allParams;

	    } else {

	      requestConfig.data =  ConvertModel.modelToJson(allParams);

	    }

	 

	    return this.httpClient.request(requestConfig)

	    .then(res => {

	      const data: string = ConvertModel.modelToJson(res.data);

	      if (res.status >= 200 && res.status < 300) {

	        return ConvertModel.jsonToModel(data) as T;

	      } else {

	        return Promise.reject(data);

	      }

	    })

	    .catch(async error => {

	      return Promise.reject(error);

	    })

	  };

	 

	  // GET请求

	  get(url: string, params?: any) {

	    return this.httpClient.request({

	      url: url,

	      method: Method.GET,

	      params

	    });

	  };

	 

	  // POST请求

	  post(url: string, data?: any) {

	    return this.httpClient.request({

	      url: url,

	      method: Method.POST,

	      data

	    });

	  };

	 

	  // PUT请求

	  put(url: string, data?: any) {

	    return this.httpClient.request({

	      url: url,

	      method: Method.PUT,

	      data

	    });

	  };

	 

	  // Delete请求

	  del (url: string, params?: any) {

	    return this.httpClient.request({

	      url: url,

	      method: Method.DELETE,

	      params

	    });

	  };

	 

	  // Delete批量请求(Post方式传参)

	  del_batch(url: string, data?: any) {

	    return this.httpClient.request({

	      url: url,

	      method: Method.DELETE,

	      data

	    });

	  }

	}

(5)使用,封装【https.ts】请求。

	import HttpClient, { HttpClientConfig } from '../kimi-axios/index';

	import { ElMessage } from 'element-plus';

	import { getApiUrl, getToken } from './utils';

	 

	const config: HttpClientConfig = {

	  baseURL: getApiUrl(),

	  headers: {

	    'Content-Type': 'application/json;charset=utf-8'

	  },

	  timeout: 60 * 1000 // 请求超时时间(默认:60秒)

	};

	 

	const https = new HttpClient(config);

	// 请求拦截器

	https.httpClient.interceptors.request.use(

	  config => {

	    // 在发送请求之前做些什么...

	    console.log('https:请求拦截器...');

	 

	    // // 如果存在Token,让请求携带令牌。

	    // var curTime = new Date()

	    // var expiretime = new Date(Date.parse(storeTemp.state.user.tokenExpire))

	    // // 判断是否存在token,如果存在的话,则每个http header都加上token

	    // if (storeTemp.state.user.token && (curTime < expiretime && storeTemp.state.user.tokenExpire)) {

	    //   //['Authorization']是一个自定义头密钥,请根据实际情况进行修改。

	    //   config.headers['Authorization'] = 'Bearer ' + storeTemp.state.user.token;

	    // }

	 

	    // ['Authorization']是一个自定义头密钥,请根据实际情况进行修改。

	    // config.headers['Authorization'] = 'Bearer ' + store.state.user.kimiToken;

	    config.headers = {

	      Authorization: `Bearer ${getToken()}`

	    };

	 

	    return config;

	  },

	  error => {

	    // 处理请求错误...

	    return Promise.reject(error);

	  }

	);

	 

	// 响应拦截器

	https.httpClient.interceptors.response.use(

	  /**

	 * 如果您想获取http信息,如标题或状态

	 * 请返回response=>response

	 */

	  /**

	 * 通过自定义代码确定请求状态

	 * 这里只是一个例子

	 * 您还可以通过HTTP状态代码来判断状态

	 */

	  response => {

	    // 自定义错误

	    // if(!response.data.success){

	    //   Message({

	    //     message: response.data.error.message,

	    //     type: 'error',

	    //     duration: 2 * 1000

	    //   })

	    // }

	 

	    console.log('https:响应拦截器...');

	    const res = response;

	    return res;

	  },

	  error => {

	    let statusCode = 0;

	    try {

	      statusCode = error.response.status;

	    } catch (e) {

	      // 网络请求错误

	      if (error.toString().indexOf('Error: Network Error') !== -1) {

	        ElMessage({

	          message: '您的请求网络发生错误,请稍后重试!',

	          type: 'error',

	          duration: 2 * 1000

	        });

	        return Promise.reject(error);

	      }

	    }

	 

	    // 超时请求处理

	    var originalRequest = error.config;

	    if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1 && !originalRequest._retry) {

	      ElMessage({

	        message: '您的请求超时,请稍后重试!',

	        type: 'error',

	        duration: 2 * 1000

	      });

	      originalRequest._retry = true;

	      return Promise.reject(error);

	    }

	 

	    // 根据状态码处理

	    if (statusCode) {

	      if (statusCode === 401) {

	        // 401:未登录

	        ElMessage({

	          message: '您当前未登录,请先登录!',

	          type: 'error',

	          duration: 2 * 1000

	        });

	      } else if (statusCode === 403) {

	        // 403:无权限

	        ElMessage({

	          message: '您访问的权限等级不够,拒绝访问!',

	          type: 'error',

	          duration: 2 * 1000

	        });

	      } else if (statusCode === 429) {

	        // 429:IP限流

	        ElMessage({

	          message: '您刷新次数过多,请稍后重试!',

	          type: 'error',

	          duration: 2 * 1000

	        });

	      } else if (statusCode === 500) {

	        // 500:自动错误&&系统自定义错误

	        if (!error.response.data.success && error.response.data.result === 'error_constom') {

	          ElMessage({

	            message: error.response.data.error.message,

	            type: 'error',

	            duration: 2 * 1000

	          });

	        } else {

	          ElMessage({

	            message: '对不起,在处理您的请求期间,产生了一个服务器内部错误!',

	            type: 'error',

	            duration: 2 * 1000

	          });

	        }

	      } else {

	        // 其他

	        let errorMsg = '';

	        switch (statusCode) {

	        case 400:

	          errorMsg = '请求报文中存在语法错误!';

	          break;

	        case 404:

	          errorMsg = '服务器找不到请求的接口!';

	          break;

	        case 405:

	          errorMsg = '请求类型出错!';

	          break;

	        case 408:

	          errorMsg = '请求超时!';

	          break;

	        case 415:

	          errorMsg = '请重新登录!';

	          break;

	        case 501:

	          errorMsg = '服务未实现!';

	          break;

	        case 502:

	          errorMsg = '服务器作为网关或代理,从上游服务器收到无效响应!';

	          break;

	        case 503:

	          errorMsg = '服务不可用!';

	          break;

	        case 504:

	          errorMsg = '服务器连接超时!';

	          break;

	        case 505:

	          errorMsg = 'HTTP版本不受支持!';

	          break;

	        default:

	          errorMsg = '其他错误!';

	        }

	        ElMessage({

	          message: errorMsg,

	          type: 'error',

	          duration: 3 * 1000

	        });

	      }

	    } else {

	      ElMessage({

	        message: '您的接口请求失败,请稍后重试!',

	        type: 'error',

	        duration: 2 * 1000

	      });

	    }

	    return Promise.reject(error);

	  }

	);

	 

	export default https;

1.12-引入全局loading

(1)element-plus需要在【main.ts】中先单独引入loading样式。

	// 引入loading样式

	import 'element-plus/theme-chalk/el-loading.css';

(2)新建【loading.ts】文件全局使用。

	// 【全局 Loading】:以服务的方式调用的全屏 Loading 是单例的。

	import { ElLoading } from 'element-plus'

	 

	export default function() {

	  const loading = (title: string) => {

	    const loadingInstance = ElLoading.service({ 

	      lock: true,

	      text: title,

	      background: 'rgba(0, 0, 0, 0.7)',

	    });

	    return loadingInstance;

	  };

	 

	  return {

	    loading

	  }

	}

(3)使用。

	// 导入

	import Loading from '@/utils/loading';

	 

	//启用

	const loadingInstance = loading('登录中...');

	 

	//关闭

	loadingInstance.close();

原创地址:www.cnblogs.com/kimiliucn/p…