一、uni-ui网络请求封装(通过uni.request({})发起网络请求)
uniapp.dcloud.net.cn/api/request…
1. utils工具文件夹中创建一个request.js文件,用于封装网络请求
request.js文件
/**
* 封装网络请求
* {
* url:''
* method:'get',
* data:,
*
* }
*/
export const baseUrl = 'http://localhost:8089'
export const axiosInstance = options => {
return new Promise((resolve, reject) => {
uni.request({
url: baseUrl + options.url,
method: options.method,
data: options.data,
success: res => {
resolve(res.data)
},
fail:error=>{
reject(error)
}
})
})
}
2. utils同级目录创建一个api文件夹(集中管理接口),api文件夹下创建js文件,用于封装项目中所有用到的请求接口
js文件
import { axiosInstance } from '../utils/request.js'
/**
* 商品列表
*/
export const RequestGoodsList = (pageNo,pageSize=10)=>{
return axiosInstance({
url:'/api/shop',
method:'get',
data:{
pageNo,
pageSize
}
})
}
/**
* 轮播
*/
export const RequestBanner = ()=>{
return axiosInstance({
url:'/api/banner',
method:'get'
})
}
/**
* 商品详情
*/
export const RequesetGoodsDetail = (id)=>{
return axiosInstance({
url:'/api/shop/find',
method:'get',
data:{
id
}
})
}
3. 页面中使用,页面vue文件中引入接口,调用即可
<template>
<view class="g-container">
详情页
<view class="g-goods-wrapper">
<image :src="goods?.picture" ></image>
<view class="">{{goods?.product}}</view>
</view>
</view>
</template>
<!--
第一次进入组件时, goods为null,调用其属性报错
-->
<script setup>
import { ref } from "vue"
import { onLoad } from '@dcloudio/uni-app'
import { RequesetGoodsDetail } from '../../api/goods.js'
// let goods = ref()
let goods = ref({picture:'',product:''})
onLoad((options)=>{
getDetail()
})
/**
* 商品详情
**/
const getDetail = async()=>{
const data = await RequesetGoodsDetail(45)
const { resultCode,resultInfo} = data
if(resultCode === 1){
goods.value = resultInfo
console.log('resultInfo ',resultInfo);
}
}
</script>
<style lang="scss"></style>
二、uni-ui拦截器
uniapp.dcloud.net.cn/api/interce…
uni.addInterceptor(STRING, OBJECT)添加拦截器
uni.removeInterceptor(STRING)删除拦截器
参数说明:
STRING:需要拦截的api名称,如:uni.addInterceptor('request', OBJECT) ,将拦截 uni.request()
OBJECT:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| invoke | Function | 否 | 拦截前触发 |
| returnValue | Function | 否 | 方法调用后触发,处理返回值 |
| success | Function | 否 | 成功回调拦截 |
| fail | Function | 否 | 失败回调拦截 |
| complete | Function | 否 | 完成回调拦截 |
1.拦截路由跳转,实现统一登录认证(方法一)
1>. 在utlis文件夹中新建一个interceptor.js文件,用于封装拦截器
/**
* 统一登录认证, 拦截路由跳转
* 重构优化封装
* 业务 统一登录认证
* 1. 创建白名单,不需要登录认证的路由直接放行
* 2. token登录认证
* 3. 放行
*/
export const routerInterceptor = () => {
// 1. 路由白名单
const whileRouters = ['navigateTo', 'navigateBack', 'redirectTo', 'switchTab']
//不需要认证的页面白名单
const whites = ['/', '/pages/login/login', '/pages/index/index', '/pages/category/category', '/pages/detail/detail']
//遍历路由白名单
whileRouters.forEach(rouerApi => {
uni.addInterceptor(rouerApi, {
invoke(e) {//拦截前触发
// "url": "/pages/detail/detail?id=1001"
let url = e.url
if (url.indexOf('?') !== 1) {
url = url.split('?')[0]
}
const isOk = whites.some(item => item === url)
if (isOk) {
return true // 直接放行
}
// 2. token登录认证
let token = uni.getStorageSync('TOKEN')
if (token) {
return true // 直接放行
}
console.log('>>>>>11111 不放行 重定向到登录界面 ');
uni.showToast({
title: '请先登录',
icon: 'none'
})
// 3. 不放行 重定向到登录界面
uni.navigateTo({
url: '/pages/login/login'
})
return false // 结束不放行
},
success(args) {// 成功响应拦截
console.log('success >>>> ', args);
},
fail(err) {// 失败响应拦截
console.log('interceptor-fail', err)
},
complete(res) {//完成回调拦截
console.log('interceptor-complete', res)
}
})
})
}
2>. 在App.vue根组件中引入并加载拦截器
<script>
import { routerInterceptor } from './utils/interceptor.js'
export default {
onLaunch: function() {
console.log('App Launch')
// 加载拦截器
routerInterceptor()
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style>
/*每个页面公共css */
</style>
2.网络拦截器,请求/响应拦截器
1>. login.vue文件中登录保存token,uni.setStorageSync(‘TOKEN’,token)
2>. interceptor.js文件
/**
* 网络拦截
*/
export const requestInterceptor = () => {
// 网络请求拦截
uni.addInterceptor('request', {
invoke(args) {//拦截前触发
// 请求拦截器,将token放到请求头里
const token = uni.getStorageSync('TOKEN')
if(token){
// args.header['Authorization'] = token
args.header = {
Authorization:token
}
}
},
success(args) {// 成功响应拦截
// 请求成功后,修改code值为1
args.data.code = 1
},
fail(err) {
// 失败响应拦截
console.log('interceptor-fail', err)
},
complete(res) {//完成回调拦截
console.log('interceptor-complete', res)
}
})
}
3>. 在App.vue根组件中引入并加载拦截器
<script>
import { requestInterceptor } from './utils/interceptor.js'
export default {
onLaunch: function() {
console.log('App Launch')
// 加载拦截器
requestInterceptor()
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style>
/*每个页面公共css */
</style>
三、uView2.x网络请求与拦截器封装
1.request.js文件中
// 此vm参数为页面的实例,可以通过它引用vuex中的变量
module.exports = (vm) => {
// 初始化请求配置
uni.$u.http.setConfig((config) => {
/* config 为默认全局配置*/
config.baseURL = 'https://www.example.com'; /* 根域名 */
return config
})
// 请求拦截
uni.$u.http.interceptors.request.use((config) => { // 可使用async await 做异步操作
// 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{}
config.data = config.data || {}
// 根据custom参数中配置的是否需要token,添加对应的请求头
if(config?.custom?.auth) {
// 可以在此通过vm引用vuex中的变量,具体值在vm.$store.state中
config.header.token = vm.$store.state.userInfo.token
}
return config
}, config => { // 可使用async await 做异步操作
return Promise.reject(config)
})
// 响应拦截
uni.$u.http.interceptors.response.use((response) => { /* 对响应成功做点什么 可使用async await 做异步操作*/
const data = response.data
return data.data === undefined ? {} : data.data
}, (response) => {
// 对响应错误做点什么 (statusCode !== 200)
return Promise.reject(response)
})
}
2.api集中管理,api文件夹下创建.js文件
const http = uni.$u.http
// post请求,获取菜单
export const postMenu = (params, config = {}) => http.post('/ebapi/public_api/index', params, config)
// get请求,获取菜单,注意:get请求的配置等,都在第二个参数中
export const getMenu = (data) => http.get('/ebapi/public_api/index', {params:data})
3.引用配置
在main.js中引用request.js
import App from './App'
import * as Pinia from 'pinia';
// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
import uView from "uview-ui";
Vue.use(uView);//引入并使用uView的JS库
// 引入请求封装,将app参数传递到配置中
require('/util/request.js')(app)
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
app.use(Pinia.createPinia()); // 集成状态管理pinia
return {
app,
Pinia, // 此处必须将 Pinia 返回
}
}
// #endif
4.页面中使用,页面vue文件中引入接口,调用即可
import { postMenu, getMenu } from '/config/api.js';
// 发出post,假设需要带上token
postMenu({ custom: { auth: true }}).then(() => {}).catch(() =>{})
// await等待,注意与async结合使用
await postMenu({ custom: { auth: true }})
// get请求
getMenu({ custom: { auth: true }}).then(() => {}).catch(() =>{})
// 也可以直接通过uni.$u.post发出请求,注意此处需要写上接口地址
uni.$u.http.post('/common/menu', { custom: { auth: true }}).then(() => {}).catch(() =>{})
四、实现统一登录认证(方法二)
uni-app不自带导航守卫,实现方法有两种:
1.使用拦截器自行封装,具体如上第二条。
2.使用插件。
①安装:
npm install uni-simple-router
npm install uni-read-pages
②在项目根目录新建vue.config.js文件
const TransformPages=require('uni-read-pages')
const {webpack} = new TransformPages()
module.exports ={
configureWebpack:{
plugins:[
new webpack.DefinePlugin({
ROUTES:webpack.DefinePlugin.runtimeValue(()=>{
const tfPages = newTransformPages({
includes:['path','name",'aliasPath']
});
return JSON.stringify(tfPages.routes)
},true )
})
]
}
}
③项目根目录创建router.js文件
import {RouterMount,createRouter} from 'uni-simple-router';
const router = createRouter({
platform: process.env.VUE_APP_PLATFORM,
routes: [
//进入购物车之前判断登录状态,如果是未登录跳转到首页
{
path:"/pages/cart/cart",
beforeEnter:(to,from,next)=>{
let status = store?.state?.user?.status;
if( !status ){
console.log('跳转到登录页");
next('/pages/login/login");
}
next();
},
...ROUTES
]
});
//全局路由前置守卫
router.beforeEach((to, from, next) => {
next();
});
export {
router,
RouterMount
}
④在 main.js 引入 router.js
import App from './App'
import * as Pinia from 'pinia';
import {router,RouterMount} from './router.js' //路径换成自己的
Vue.use(router)
// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
app.use(Pinia.createPinia()); // 集成状态管理pinia
return {
app,
Pinia, // 此处必须将 Pinia 返回
}
}
// #endif
//v1.3.5起 H5端 你应该去除原有的app.$mount();使用路由自带的渲染方式
// #ifdef H5
RouterMount(app,router,'#app')
// #endif
// #ifndef H5
app.$mount(); //为了兼容小程序及app端必须这样写才有效果
// #endif