一、Vue基本概念
1.1-Vue3的優(yōu)點(diǎn)
- Vue3支持Vue2額大多數(shù)特性。
- 更好的支持TypeScript。
- 打包大小減少41%。
- 初次渲染快55%,更新渲染快133%。
- 內(nèi)存減少54%。
- 使用proxy代替defineProperty實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式。
- 重寫虛擬DOM的實(shí)現(xiàn)和Tree-Shaking。
二、API
2.1-setup
我們可以跟以前定義data和methods,但是vue3中我們更推薦使用setup函數(shù)。
- setup是一個(gè)函數(shù)。只在初始化時(shí)執(zhí)行一次。以后大部分代碼都是在setup中寫。
- 返回一個(gè)對(duì)象,對(duì)象中的屬性或方法,模板中可以直接使用。
- setup返回的數(shù)據(jù)會(huì)和data和methods進(jìn)行合并,setup優(yōu)先級(jí)更高。
- setup函數(shù)中沒有this。 以后開發(fā)都不使用this了
- setup不要寫async函數(shù)。
因?yàn)閍sync函數(shù)必須返回一個(gè)json對(duì)象供模板使用,如果setup是一個(gè)async函數(shù),返回的將是一個(gè)promise對(duì)象。
如果setup是一個(gè)async函數(shù),那該組件就成了一個(gè)異步函數(shù),需要配合Suspense組件才能使用。
2.2-ref
讓數(shù)據(jù)變成響應(yīng)式。
(1)先引用ref
import {ref} from 'vue';
(2)將數(shù)據(jù)變成響應(yīng)式的。
let data1=ref(12);
(3)操作數(shù)據(jù)
data1.value = 123;
2.3-reactive
作用:定義對(duì)象格式的響應(yīng)式數(shù)據(jù)
如果用ref定義對(duì)象/數(shù)組,內(nèi)部會(huì)自動(dòng)將對(duì)象/數(shù)組轉(zhuǎn)換為reactive代理器對(duì)象。
- const proxy=reactive(obj):接收一個(gè)普通對(duì)象然后返回該普通對(duì)象的響應(yīng)式代理器對(duì)象。
- js中修改告訴不需要.value。
- 一般用來定義一個(gè)引用類型的響應(yīng)數(shù)據(jù)。
2.4-toRefs
將響應(yīng)式對(duì)象中所有屬性包裝為ref對(duì)象,并返回包含這些ref對(duì)象的普通對(duì)象。
應(yīng)用:對(duì)trsctive定義的對(duì)象進(jìn)行toRefs包裝,包裝之后的對(duì)象中每個(gè)屬性都是響應(yīng)式的。
2.5-響應(yīng)式原理
通過proxy(代理):攔截對(duì)對(duì)象本身的操作,包括屬性的讀寫、刪除等操作。
通過Reflect(反射):動(dòng)態(tài)對(duì)被代理對(duì)象的響應(yīng)式屬性進(jìn)行特定的操作。
2.6-watch和watchEffect
watch - 指定監(jiān)聽數(shù)據(jù):
- 監(jiān)聽指定的一個(gè)或多個(gè)響應(yīng)式數(shù)據(jù),一旦發(fā)生變化,就會(huì)自動(dòng)執(zhí)行監(jiān)視回調(diào)。
- 如果是監(jiān)聽reactive對(duì)象中的屬性,必須通過函數(shù)來指定。
- 監(jiān)聽多個(gè)數(shù)據(jù),使用數(shù)組來指定。
- 默認(rèn)初始時(shí)不指定回調(diào),但是通弄過配置immediate為true,來指定初始時(shí)立即執(zhí)行第一次。
- 通過配置deep為true,來指定深度監(jiān)視。
watchEffect - 不指定監(jiān)聽數(shù)據(jù):
- 不用直接指定啦監(jiān)視的數(shù)據(jù),回調(diào)函數(shù)中使用的哪些響應(yīng)式數(shù)據(jù)就監(jiān)聽哪些響應(yīng)式數(shù)據(jù)。
- 默認(rèn)初始就會(huì)執(zhí)行一次。
2.7-生命周期
vue2中的生命周期鉤子函數(shù)依舊可以使用,不過建議使用vue3的鉤子函數(shù)
2.8-ref獲取元素
vue2中是用thisref.xxx來獲取元素或組件,但是vue3中沒有this的概念。
vue3通過ref創(chuàng)建響應(yīng)式數(shù)據(jù)的api來獲取元素。
1.使用ref創(chuàng)建響應(yīng)式數(shù)據(jù),假設(shè)叫x
2.模板中綁定ref屬性,值為上面的x
注意不能使用v-bind動(dòng)態(tài)綁定。
這是x就是一個(gè)dom元素或組件了。
2.9-自定義hook函數(shù)
hook函數(shù)翻譯成中文就是鉤子函數(shù)(注意并不是生命周期的鉤子函數(shù))
比如ref,reactive,computed,watch,onBeforeMount等都是hook函數(shù),只不過他們都是vue內(nèi)部hook函數(shù)。
1.創(chuàng)建一個(gè)函數(shù),函數(shù)名稱必須以"use"開頭
2.函數(shù)必須return一些數(shù)據(jù)。
2.10-shallowReactive與shallowRef
他們都表示淺響應(yīng)式。
- shallowReactive:只處理了對(duì)象第一層屬性的響應(yīng)式(值響應(yīng)第一層)
- shallowRef:只有重新復(fù)制時(shí)才是響應(yīng)式(不響應(yīng)內(nèi)部數(shù)據(jù),只響應(yīng)整體。)
2.11-readonly與shallowReadonly
- 他們表示只讀代理對(duì)象
- readonly
- 深度只讀
- 設(shè)置readonly后,修改響應(yīng)式數(shù)據(jù)會(huì)報(bào)錯(cuò)。
- shalloReadonly
- 淺只讀
- 設(shè)置shalloReadonly后,修改響應(yīng)式數(shù)據(jù)的第一層數(shù)據(jù)會(huì)報(bào)錯(cuò)。
- 應(yīng)用場(chǎng)景:
- 在某些特定情況下,我們可能不希望對(duì)數(shù)據(jù)進(jìn)行更新的操作,那就可以包裝成一個(gè)只讀代理對(duì)象,而不能修改或刪除。
2.12-toRaw與markRaw
- toRaw
- 返回reactive或readonly對(duì)象的原始數(shù)據(jù)
- 這是一個(gè)還原方法,可用于臨時(shí)讀取,得到的數(shù)據(jù)不具有響應(yīng)式。
- markRow:
- 標(biāo)記一個(gè)對(duì)象,使其不具有響應(yīng)式
- 應(yīng)用場(chǎng)景:
- 有些只不應(yīng)被設(shè)置為響應(yīng)式的,例如復(fù)雜的第三方實(shí)例或Vue組件對(duì)象。
- 當(dāng)渲染具有不可變數(shù)據(jù)源的大列表時(shí),跳過代理轉(zhuǎn)換可以提高性能。
2.13-toRef
- 為響應(yīng)式對(duì)象上的某個(gè)屬性創(chuàng)建一個(gè)ref引用,更新是應(yīng)用對(duì)象會(huì)同步更新。
- 與ref的區(qū)別:ref是拷貝了一份新的數(shù)據(jù)指單獨(dú)操作,更新時(shí)相互不影響。
2.14-customRef
- 用于自定義一個(gè)ref,可以顯示的控制依賴追蹤和觸發(fā)相應(yīng)。
- 接受一個(gè)工廠函數(shù),兩個(gè)參數(shù)分別用于追蹤的track與用于觸發(fā)相應(yīng)的trigger,并方法一個(gè)帶有g(shù)et和set屬性的對(duì)象。
- 需求:使用customRef實(shí)現(xiàn)防抖函數(shù)
2.15-provide與inject
- provide和inject提供依賴注入,功能類似2.0的provide/inject
- 實(shí)現(xiàn)跨層級(jí)組件(祖孫)間通信。
2.16-響應(yīng)式數(shù)據(jù)的判斷
- isRef:檢查一個(gè)值是否為一個(gè)ref對(duì)象。
- isReactive:檢查一個(gè)對(duì)象是否否是由reactive對(duì)象的響應(yīng)式代理。
- isReadonly:檢查一個(gè)對(duì)象是否由readonly創(chuàng)建的只讀代理。
- isProxy:檢查一個(gè)對(duì)象是否是由reactive或者readonly方法創(chuàng)建的代理。
2.17-Fragment(片段)
- 在vue2中:組件中必須有一個(gè)跟標(biāo)簽
- 在vue3中:組價(jià)可以沒有跟標(biāo)簽,內(nèi)部會(huì)將多個(gè)標(biāo)簽包含在一個(gè)Fragment虛擬標(biāo)簽中。
- 好處:減少標(biāo)簽層級(jí),減小內(nèi)存占用
2.18-Teleport(瞬移)
- Teleport提供了一種干凈的方法,讓組件的html在父組件界面外的特定標(biāo)簽(很可能是body)下插入顯示。
2.19-Suspense(不確定的)
Supense組件是配合一部組件使用的,它可以讓一部組件返回?cái)?shù)據(jù)前渲染一些后背內(nèi)容。
那我們首先要學(xué)會(huì)一個(gè)異步組件。
- 在setup函數(shù)總返回一個(gè)promise,就是一個(gè)異步組件。
- setup函數(shù)攜程async函數(shù),也是一個(gè)異步組件。
2.20-其他新的API
-
全新的全局API:
- createApp()
- defineProperty()
- defineComponent()
- nextTick()
-
將原來的全局API轉(zhuǎn)移到應(yīng)用對(duì)象:
- app.component()
- app.config()
- app.directive()
- app.mount()
- app.umount()
- app.use()
2.21-useSlots和useAttrs
useSlots 和 useAttrs 是真實(shí)的運(yùn)行時(shí)函數(shù),它會(huì)返回與 setupContext.slots 和 setupContext.attrs 等價(jià)的值,同樣也能在普通的組合式 API 中使用。
使用場(chǎng)景:父組件使用子組件的插槽
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;">我是父組件插槽--插入的內(nèi)容。。。</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("進(jìn)來了")
console.log(child.value.msg)
msg.value = child.value.msg;
})
</script>
三、路由
(1)userRout:獲得當(dāng)前路由對(duì)象。
(2)useRouter:獲得路由實(shí)例,可以進(jìn)行路由跳轉(zhuǎn)。
import {useRoute,useRouter} from "vue-router"
四、Vue3+TS實(shí)戰(zhàn)知識(shí)點(diǎn)
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)新建一個(gè)【http.ts】文件,寫一個(gè)簡(jiǎn)單的請(qǐng)求封裝。
//導(dǎo)入axios
import axios from "axios"
//axios.create創(chuàng)建一個(gè)axios實(shí)例
//我們給這個(gè)實(shí)例編寫配置,后端所有通過這個(gè)實(shí)例發(fā)送請(qǐng)求,都受這個(gè)配置約束。
const $http=axios.create({
baseURL:"http://jsonplaceholder.typicode.com/",
timeout:1000
});
// 添加請(qǐng)求攔截器
$http.interceptors.request.use(function (config) {
// 在發(fā)送請(qǐng)求之前做些什么
return config;
}, function (error) {
// 對(duì)請(qǐng)求錯(cuò)誤做些什么
return Promise.reject(error);
});
// 添加響應(yīng)攔截器
$http.interceptors.response.use(function (response) {
// 對(duì)響應(yīng)數(shù)據(jù)做點(diǎn)什么
return response.data;
}, function (error) {
// 對(duì)響應(yīng)錯(cuò)誤做點(diǎn)什么
return Promise.reject(error);
});
export default $http;
(3)新建一個(gè)【test.ts】文件進(jìn)行二次封裝。
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(){
//請(qǐng)求接口
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文件夾下新建一個(gè)【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({
// 導(dǎo)入模塊
modules: {
app,
settings,
user
}
})
(4)【app.ts】格式如下。
//【應(yīng)用程序模塊】
const app={
//單一狀態(tài)樹,UI可通過this.$store.state.app.*獲得數(shù)據(jù)
state: {
},
//對(duì)state數(shù)據(jù)進(jìn)行過濾后返回(可以認(rèn)為是 store 的計(jì)算屬性)
getters:{
},
// 唯一擁有更改內(nèi)存數(shù)據(jù)的接口,不可進(jìn)行異步操作
mutations: {
},
// 與mutation通訊,UI層寫入內(nèi)存數(shù)據(jù)的接口,可異步操作
actions: {
}
}
export default app;
1.4-引入.env
(1)創(chuàng)建文件xxx.env文件。
----------------【.env.development】---------------
#以下的值webpack會(huì)根據(jù)不同的環(huán)境取得不同的值,使用方法:process.env.*
#設(shè)定一個(gè)標(biāo)題,代表這個(gè)環(huán)境是development
NODE_ENV = 'development'
# Base URL
BASE_URL = 'http://xxx:7881'
# Base API
VUE_APP_BASE_API = 'http://xxx:7881'
----------------【.env.production】---------------
#以下的值webpack會(huì)根據(jù)不同的環(huán)境取得不同的值,使用方法:process.env.*
#設(shè)定一個(gè)標(biāo)題,代表這個(gè)環(huán)境是production
NODE_ENV = 'production'
# Base URL
BASE_URL = 'http://xxx:7881'
# Base API
VUE_APP_BASE_API = 'http://xxx:7881'
----------------【.env.staging】---------------
#以下的值webpack會(huì)根據(jù)不同的環(huán)境取得不同的值,使用方法:process.env.*
#設(shè)定一個(gè)標(biāo)題,代表這個(gè)環(huán)境是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
1.5-引入cookie
(1)安裝。
npm install vue-cookies --save
1.6-引入nprogress
(1)安裝。
npm install nprogress -S
npm i @types/nprogress-D
(2) 現(xiàn)在我們對(duì)NProgress進(jìn)行一下簡(jiǎn)單的封裝,首先我們?cè)凇綾ommon/utils/nporgress.ts】目錄下創(chuàng)建文件,然后引入NProgress和CSS樣式文件 。
(3)內(nèi)容如下。
import NProgress from 'nprogress' // 進(jìn)度條
import 'nprogress/nprogress.css' // 進(jìn)度條樣
//全局進(jìn)度條的配置
NProgress.configure({
easing: 'ease', // 動(dòng)畫方式
speed: 1000, // 遞增進(jìn)度條的速度
showSpinner: false, // 是否顯示加載ico
trickleSpeed: 200, // 自動(dòng)遞增間隔
minimum: 0.3, // 更改啟動(dòng)時(shí)使用的最小百分比
parent: 'body', //指定進(jìn)度條的父容器
})
//打開進(jìn)度條
export const start =()=>{
NProgress.start();
}
//關(guān)閉進(jìn)度條
export const close =()=>{
NProgress.done();
}
(4)在【router/index.ts】文件中使用
--引入文件
import { close, start } from '@/common/utils/nprogress'//進(jìn)度條
--使用
const router = createRouter({
routes,
history: createWebHistory(),
})
router.beforeEach((pre, next) => {
start()
})
router.afterEach(() => {
close()
})
1.7-引入Element Plus(按需自動(dòng)引入)
環(huán)境:
參考文獻(xiàn):https://www.yisu.com/zixun/723540.html
(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)然后執(zhí)行【npm run dev】報(bào)錯(cuò)了(如果報(bào)node-sass將這個(gè)卸載,安裝sass即可)。
解決: 卸載nodev12,更新到nodev16在重新構(gòu)建一下項(xiàng)目成功解決 了。
命令:
npm uninstall node-sass --dev-S
npm install sass --dev-S
1.8-引入sross-env環(huán)境變量配置
(1)安裝。
npm i --save-dev cross-env
npm install dotenv-cli --dev-s
(2)創(chuàng)建文件。
--【.env.dev.build】
#以下的值webpack會(huì)根據(jù)不同的環(huán)境取得不同的值,使用方法:process.env.*
#設(shè)定一個(gè)標(biāo)題,代表這個(gè)環(huán)境是development
NODE_ENV = production
# Base URL
BASE_URL = 'http://xxx.xxx.xxx:7881'
# Base API
VUE_APP_BASE_API = 'http://xxx.xxx.xxx:7990'
# 統(tǒng)一認(rèn)證中心配置
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會(huì)根據(jù)不同的環(huán)境取得不同的值,使用方法:process.env.*
#設(shè)定一個(gè)標(biāo)題,代表這個(gè)環(huán)境是development
NODE_ENV = development
# Base URL
BASE_URL = 'http://xxx.xxx.xxx:7881'
# Base API
VUE_APP_BASE_API = 'http://xxx.xxx.xxx:7990'
# 統(tǒng)一認(rèn)證中心配置
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)封裝單獨(dú)的ts文件。
import Cookies from 'js-cookie'
/*
封裝操作Cookie本地存儲(chǔ)的方法:
說明:主要用途有保存登錄信息。
存儲(chǔ)大?。?KB
*/
const cookies = {
/**
*設(shè)置Cookies
* @param {String} key 鍵
* @param {Object} value 值:存儲(chǔ)的值可能是數(shù)組/對(duì)象,不能直接存儲(chǔ),需要轉(zhuǎn)換 JSON.stringify()
* @param {Int} expiresTime 過期時(shí)間(單位:秒)
*/
set(key: string, value: any, expiresTime: number) {
expiresTime=arguments[2] ? arguments[2] : (60*60)*24;//默認(rèn)值為:24小時(shí)
let expires = new Date(new Date().getTime() * 1 + expiresTime * 1000);
return Cookies.set(key, value, {
expires: expires
});
},
/**
* 獲得Cookies
* @param {String} key 鍵
* @return {Object} 根據(jù)鍵取對(duì)應(yīng)的值
*/
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面包屑導(dǎo)航
(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()
);
},
// 跳轉(zhuǎn)路由
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();
})
// 監(jiān)聽路由變化
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請(qǐng)求【axios-mapper】
(1)安裝需要的依賴
npm install @types/qs
npm install qs
(2)創(chuàng)建【types.ts】
// 請(qǐng)求內(nèi)容類型
export enum ContentType {
form = 'application/x-www-form-urlencoded',
json = 'application/json; charset=utf-8',
multipart = 'multipart/form-data'
}
// 請(qǐng)求方式
export enum Method {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
PATCH = 'PATCH',
DELETE = 'DELETE'
}
// 網(wǎng)絡(luò)請(qǐng)求參數(shù)
export interface RequestParams {
[key: string]: any
}
(3)創(chuàng)建【convert-model.ts】文件。
// Json-Model相互轉(zhuǎn)換
export class ConvertModel {
/**
* @description: json轉(zhuǎn)model
* @param {string} json
* @return {*}
*/
public static jsonToModel<T>(json: string): T {
return JSON.parse(json) as T;
}
/**
* @description: model轉(zhuǎn)json
* @param {any} model
* @return {*}
*/
public static modelToJson(model: any): string {
return JSON.stringify(model);
}
}
(4)創(chuàng)建【index.ts】文件
import { RequestParams, Method, ContentType } from './types';
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
import { ConvertModel } from './convert-model';
// 導(dǎo)出所有的類型
export * from './types';
export interface HttpClientConfig extends AxiosRequestConfig {
defaultParams?: RequestParams;
}
// 導(dǎo)出默認(rèn)請(qǐng)求
export default class HttpClient {
private _defaultConfig: HttpClientConfig;
public httpClient: AxiosInstance;
// 構(gòu)造函數(shù)
constructor(options: HttpClientConfig = {}){
this.httpClient = axios.create(options);
this._defaultConfig = options;
}
/**
* @description: 封裝請(qǐng)求類
* @param {string} path 請(qǐng)求路徑
* @param {Method} method 請(qǐng)求方式(默認(rèn):GET)
* @param {RequestParams} params 參數(shù)
* @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
);
// 發(fā)送請(qǐng)求
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請(qǐng)求
get(url: string, params?: any) {
return this.httpClient.request({
url: url,
method: Method.GET,
params
});
};
// POST請(qǐng)求
post(url: string, data?: any) {
return this.httpClient.request({
url: url,
method: Method.POST,
data
});
};
// PUT請(qǐng)求
put(url: string, data?: any) {
return this.httpClient.request({
url: url,
method: Method.PUT,
data
});
};
// Delete請(qǐng)求
del (url: string, params?: any) {
return this.httpClient.request({
url: url,
method: Method.DELETE,
params
});
};
// Delete批量請(qǐng)求(Post方式傳參)
del_batch(url: string, data?: any) {
return this.httpClient.request({
url: url,
method: Method.DELETE,
data
});
}
}
(5)使用,封裝【https.ts】請(qǐng)求。
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 // 請(qǐng)求超時(shí)時(shí)間(默認(rèn):60秒)
};
const https = new HttpClient(config);
// 請(qǐng)求攔截器
https.httpClient.interceptors.request.use(
config => {
// 在發(fā)送請(qǐng)求之前做些什么...
console.log('https:請(qǐng)求攔截器...');
// // 如果存在Token,讓請(qǐng)求攜帶令牌。
// var curTime = new Date()
// var expiretime = new Date(Date.parse(storeTemp.state.user.tokenExpire))
// // 判斷是否存在token,如果存在的話,則每個(gè)http header都加上token
// if (storeTemp.state.user.token && (curTime < expiretime && storeTemp.state.user.tokenExpire)) {
// //['Authorization']是一個(gè)自定義頭密鑰,請(qǐng)根據(jù)實(shí)際情況進(jìn)行修改。
// config.headers['Authorization'] = 'Bearer ' + storeTemp.state.user.token;
// }
// ['Authorization']是一個(gè)自定義頭密鑰,請(qǐng)根據(jù)實(shí)際情況進(jìn)行修改。
// config.headers['Authorization'] = 'Bearer ' + store.state.user.kimiToken;
config.headers = {
Authorization: `Bearer ${getToken()}`
};
return config;
},
error => {
// 處理請(qǐng)求錯(cuò)誤...
return Promise.reject(error);
}
);
// 響應(yīng)攔截器
https.httpClient.interceptors.response.use(
/**
* 如果您想獲取http信息,如標(biāo)題或狀態(tài)
* 請(qǐng)返回response=>response
*/
/**
* 通過自定義代碼確定請(qǐng)求狀態(tài)
* 這里只是一個(gè)例子
* 您還可以通過HTTP狀態(tài)代碼來判斷狀態(tài)
*/
response => {
// 自定義錯(cuò)誤
// if(!response.data.success){
// Message({
// message: response.data.error.message,
// type: 'error',
// duration: 2 * 1000
// })
// }
console.log('https:響應(yīng)攔截器...');
const res = response;
return res;
},
error => {
let statusCode = 0;
try {
statusCode = error.response.status;
} catch (e) {
// 網(wǎng)絡(luò)請(qǐng)求錯(cuò)誤
if (error.toString().indexOf('Error: Network Error') !== -1) {
ElMessage({
message: '您的請(qǐng)求網(wǎng)絡(luò)發(fā)生錯(cuò)誤,請(qǐng)稍后重試!',
type: 'error',
duration: 2 * 1000
});
return Promise.reject(error);
}
}
// 超時(shí)請(qǐng)求處理
var originalRequest = error.config;
if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1 && !originalRequest._retry) {
ElMessage({
message: '您的請(qǐng)求超時(shí),請(qǐng)稍后重試!',
type: 'error',
duration: 2 * 1000
});
originalRequest._retry = true;
return Promise.reject(error);
}
// 根據(jù)狀態(tài)碼處理
if (statusCode) {
if (statusCode === 401) {
// 401:未登錄
ElMessage({
message: '您當(dāng)前未登錄,請(qǐng)先登錄!',
type: 'error',
duration: 2 * 1000
});
} else if (statusCode === 403) {
// 403:無權(quán)限
ElMessage({
message: '您訪問的權(quán)限等級(jí)不夠,拒絕訪問!',
type: 'error',
duration: 2 * 1000
});
} else if (statusCode === 429) {
// 429:IP限流
ElMessage({
message: '您刷新次數(shù)過多,請(qǐng)稍后重試!',
type: 'error',
duration: 2 * 1000
});
} else if (statusCode === 500) {
// 500:自動(dòng)錯(cuò)誤&&系統(tǒng)自定義錯(cuò)誤
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: '對(duì)不起,在處理您的請(qǐng)求期間,產(chǎn)生了一個(gè)服務(wù)器內(nèi)部錯(cuò)誤!',
type: 'error',
duration: 2 * 1000
});
}
} else {
// 其他
let errorMsg = '';
switch (statusCode) {
case 400:
errorMsg = '請(qǐng)求報(bào)文中存在語法錯(cuò)誤!';
break;
case 404:
errorMsg = '服務(wù)器找不到請(qǐng)求的接口!';
break;
case 405:
errorMsg = '請(qǐng)求類型出錯(cuò)!';
break;
case 408:
errorMsg = '請(qǐng)求超時(shí)!';
break;
case 415:
errorMsg = '請(qǐng)重新登錄!';
break;
case 501:
errorMsg = '服務(wù)未實(shí)現(xiàn)!';
break;
case 502:
errorMsg = '服務(wù)器作為網(wǎng)關(guān)或代理,從上游服務(wù)器收到無效響應(yīng)!';
break;
case 503:
errorMsg = '服務(wù)不可用!';
break;
case 504:
errorMsg = '服務(wù)器連接超時(shí)!';
break;
case 505:
errorMsg = 'HTTP版本不受支持!';
break;
default:
errorMsg = '其他錯(cuò)誤!';
}
ElMessage({
message: errorMsg,
type: 'error',
duration: 3 * 1000
});
}
} else {
ElMessage({
message: '您的接口請(qǐng)求失敗,請(qǐng)稍后重試!',
type: 'error',
duration: 2 * 1000
});
}
return Promise.reject(error);
}
);
export default https;
1.12-引入全局loading
(1)element-plus需要在【main.ts】中先單獨(dú)引入loading樣式。
// 引入loading樣式
import 'element-plus/theme-chalk/el-loading.css';
(2)新建【loading.ts】文件全局使用。
// 【全局 Loading】:以服務(wù)的方式調(diào)用的全屏 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)使用。文章來源:http://www.zghlxwxcb.cn/news/detail-646911.html
// 導(dǎo)入
import Loading from '@/utils/loading';
//啟用
const loadingInstance = loading('登錄中...');
//關(guān)閉
loadingInstance.close();
原創(chuàng)地址:https://www.cnblogs.com/kimiliucn/p/17605624.html文章來源地址http://www.zghlxwxcb.cn/news/detail-646911.html
到了這里,關(guān)于快速掌握Vue3:速成Vue3前端開發(fā)看這篇就夠啦的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!