主题
滚动容器与页面交互(三)
本页对应 滚动容器与页面交互,共 8 段视频,约 1 小时 15 分。
学习目标
- 能够在响应拦截器的失败回调中识别状态码 401,并实现清除登录态、跳转登录页的重新登录逻辑。
- 能够使用 Pinia 定义并持久化
redirectUrl与openType,保存登录前页面路径及跳转方式。 - 能够通过页面栈获取当前页面完整路径(含参数),判断是否为 tabBar 页面,并选择
switchTab或redirectTo完成登录成功跳转。 - 能够配置接口请求白名单,使登录、验证码等接口不携带 token,避免 401 循环跳转。
- 能够封装
scroll-page组件,设置页面高度与安全区样式,并支持自定义底部边框线。 - 能够为
scroll-page组件接入下拉刷新与上拉加载事件,并通过自定义事件向父组件传递事件对象。
知识点
一、在响应拦截器里识别401
为什么需要:本地保存的 token 有有效期,过期后服务端会认为未登录。前后端约定:没有 token 或 token 失效时返回 401。
怎么想:401 是服务端响应结果,应该在响应拦截器的“失败回调”里判断,而不是请求拦截器或成功回调。
怎么做:在 request.js 的响应拦截器中打印错误并判断状态码,命中 401 就执行重新登录。
js
service.interceptors.response.use(
(res) => res.data,
(error) => {
console.log(error)
if (error.response.status === 401) {
relogin()
}
return Promise.reject(error)
}
)
function relogin() {
uni.redirectTo({ url: '/pages/login/login' })
}易错点:不要把 401 判断写进成功回调;跳转地址前的斜杠不要丢;调试时可故意把 token 改错,观察控制台是否打印 401。
二、记录登录后要返回的页面
为什么需要:用户未登录访问某页被切到登录页,登录成功后应回到原页面。
怎么想:转写稿里演示过,若用地址拼接 redirectUrl,原地址本身已有 ? 时,小程序会过滤掉第二个问号后的参数;H5 可能正常,小程序不兼容。因此改用 Pinia 存。
怎么做:在用户 store 中增加 redirectUrl 和 openType,默认回首页,首页通常是 tabBar 页面,所以默认 switchTab;同时做持久化。
js
const redirectUrl = ref('/pages/index/index')
const openType = ref('switchTab')易错点:默认地址要写完整;持久化数组里写字符串,不要写变量;openType 要记录跳转方式,不能只记地址。
三、获取当前页面路径与打开方式
为什么需要:跳转前必须知道“当前在哪”和“目标是不是 tabBar 页面”。
怎么想:小程序用 getCurrentPages() 取页面栈,数组最后一项是当前页;fullPath 比 route 更完整,带参数。tabBar 页面最多 5 个,可用路径数组判断。
怎么做:取当前页,判断 route 是否在 tabBar 列表,决定 openType,再把 fullPath 存入 store。
js
const pages = getCurrentPages()
const current = pages[pages.length - 1]
const redirectUrl = current.fullPath
const tabBarList = [
'/pages/index/index',
'/pages/my/my'
]
const openType = tabBarList.includes(current.route)
? 'switchTab'
: 'navigateTo'易错点:判断 tabBar 用 route,保存地址用 fullPath;验证码、登录接口若也触发 401,可能把登录页自身存进去,后面要用白名单解决。
四、登录成功后按记录跳转
为什么需要:登录成功不能永远写死跳首页,要回到刚才记录的页面。
怎么想:openType 是变量,不能写 uni.openType,要用中括号动态取方法。
怎么做:登录提交成功后,从 store 取 openType 和 redirectUrl 跳转。
js
const userStore = useUserStore()
uni[userStore.openType]({
url: userStore.redirectUrl
})易错点:动态属性用 uni[userStore.openType];普通属性才用点;redirectUrl 要带斜杠;测试前清空本地脏数据。
五、接口请求白名单
为什么需要:登录、验证码接口本身是为了拿 token,不应携带旧 token。否则旧 token 失效时,验证码请求也返回 401,又跳登录页,形成“登录页跳登录页”的死循环。
怎么想:在请求拦截器里判断当前接口是否属于白名单,不在白名单才加 token。
怎么做:定义白名单数组,用 includes 判断;先打印 config.url 确认路径。
js
const whiteList = ['/user/code', '/user/login', '/user/login/password']
if (!whiteList.includes(config.url)) {
config.header.Authorization = 'Bearer ' + token
}易错点:白名单路径必须和实际接口一致;不要靠响应拦截器兜底;验证码接口成功且未带 token,才说明白名单生效。
六、scroll-page 组件封装要点
为什么需要:统一处理页面滚动、安全区、tabBar 顶部边框线、下拉刷新和上拉加载。
怎么想:本质是二次封装 scroll-view。高度取 windowHeight,底部安全区用样式适配,边框线用 border-bottom,事件用 defineEmits 中转。
怎么做:
html
<scroll-view
scroll-y
:style="{ height: windowHeight + 'px' }"
:refresher-enabled="refresh"
:refresher-triggered="triggered"
@scrolltolower="onScrollToLower"
>
<slot />
</scroll-view>js
const windowHeight = uni.getSystemInfoSync().windowHeight
const emits = defineEmits(['scrolltolower', 'refresherrefresh'])安全区可加 padding-bottom: env(safe-area-inset-bottom);;边框线可传 borderStyle,为 false 时不显示。
易错点:高度忘加 px;加边框后要 box-sizing: border-box,否则盒子变高出现滚动条;测试用的固定高度、背景色要删除;真机才能更好验证底部安全区。
先纠正一个词:本节讲的是“路径参数”
为什么需要:登录成功后要回到登录前的页面,就必须把“原页面路径”和“打开方式”记下来。怎么想:转写稿里的“中径参数”应理解为“路径参数”,不是新概念,而是登录回跳链路中的两个 Pinia 数据。怎么做:在用户 store 中增加 redirectURL 和 openType,前者默认指向首页,后者默认用 switchTab。易错点:不要继续只靠地址栏问号传参,小程序里地址再带参数时第二个问号后的内容可能被过滤。
401 要在响应拦截器的失败回调里处理
为什么需要:本地有 token 不代表 token 永远有效,后端约定 token 不存在或失效时返回 401。怎么想:401 是响应结果,应该在响应拦截器里判断,而不是请求拦截器。怎么做:在响应拦截器的第二个回调中打印 error,故意把本地 token 改错,刷新页面观察控制台,确认 statusCode 为 401;随后调用重新登录方法跳登录页。易错点:把 401 判断写进成功回调,或者只看 data 不看状态码,都会导致失效逻辑不触发。核心形态是:
js
if (error.statusCode === 401) {
relogin()
}记录登录前的路径与打开方式
为什么需要:用户从首页跳到登录页,登录成功后要回到首页;若原页面带参数,也要尽量保留。怎么想:路径放 redirectURL,跳转方式放 openType,并做持久化,防止刷新丢失。怎么做:在 Pinia 中追加两个 ref 数据,默认值分别指向首页和 switchTab;把它们加入持久化数组。易错点:只记路径不记打开方式;或持久化时把变量名直接写进数组而不是字符串,导致恢复失败。
获取当前页面路径:页面栈最后一项与 fullPath
为什么需要:跳转前必须知道“当前在哪”。怎么想:小程序没有 Vue 那样方便的路由对象,但可以用页面栈。怎么做:取 getCurrentPages() 的最后一个单元,打印后使用 $page.fullPath,它比 route 更完整,能带参数。调试时可在 401 分支里打印当前页对象,确认 fullPath 是否包含查询参数。易错点:用 route 丢掉参数;把页面栈顺序理解反;在非页面环境里取页面栈。
js
const current = getCurrentPages().at(-1)
const fullPath = current.$page.fullPath判断 tabBar 页面,决定 switchTab 还是 redirectTo
为什么需要:tabBar 页面只能用 switchTab,普通页面用 redirectTo 一类跳转。怎么想:把 tabBar 页面路径列成数组,看当前 route 是否在数组里。怎么做:若命中,openType 记为 switchTab;否则记为普通跳转方式;再把 redirectURL、openType 写入 Pinia。易错点:不区分页面类型直接跳转;路径少了前导斜杠;判断时拿 fullPath 和只含路径的数组比较,导致带参数页面误判。
登录成功后的跳转与验证
为什么需要:登录成功后不能永远写死跳首页,要回到记录的原页面。怎么想:Pinia 里已经存了地址和打开方式,用变量访问 uni 上的方法。怎么做:登录提交成功后执行 uni[userStore.openType]({ url: userStore.redirectURL })。验证时先清空本地存储,从首页触发登录,确认默认回首页;再模拟 token 失效,确认回登录页后重新登录能回原页。易错点:把 openType 写死;对象属性用点访问变量名;忘记清脏数据导致测试结果不可信。
白名单:解决验证码接口 401 死循环
为什么需要:调试时发现获取验证码也带了错误 token,于是又返回 401,又跳登录页,形成自己跳自己。怎么想:登录、验证码、密码登录这类接口本来就不该受 token 限制。怎么做:在请求拦截器里定义 whiteList,用 includes(config.url) 判断;只有“有 token 且不在白名单”时才加 token 头。易错点:白名单路径与实际请求路径不一致;把白名单判断写在加 token 之后;忘记验证码接口也会触发响应拦截器。
js
if (token && !whiteList.includes(config.url)) {
// 追加 token 头
}scroll-page 组件:高度、安全区与边框
为什么需要:页面要滚动、适配安全区、自定义 tabBar 顶部边框线,还要支持下拉刷新和上拉加载。怎么想:内部仍用 scroll-view 二次封装,外层盒子高度等于页面可用高度。怎么做:用 API 取 windowHeight,给组件根节点设置高度;底部安全区用样式处理;边框线通过 borderStyle 属性传入,组件内绑定样式。调试时给 border-bottom 加粗后出现多余滚动条,用 box-sizing: border-box 修复。易错点:高度漏单位;把边框宽度算到高度外;把测试用的固定高度留在组件里。
下拉刷新与上拉加载事件中转
为什么需要:原生 scroll-view 支持下拉刷新和滚动到底事件,但要让使用组件的页面也能监听。怎么想:组件内开启 refresher-enabled,用 refresher-triggered 控制状态;滚动到底用 scrolltolower。怎么做:通过 defineEmits 声明事件,在原生事件触发时 emit 出去,并把事件对象传给父组件。易错点:只写原生事件不 emit;emit 事件名与父组件监听名不一致;测试完不删除临时高度和测试事件。
本节要解决的问题:登录后回到原页面
在优医咨询项目里,响应拦截器一旦发现接口返回 401,就说明 token 不存在或已经失效,需要重新登录。这里有一个体验问题:用户可能是在“我的”页面或其他页面触发 401 的,重新登录成功后,不能一律跳回首页,而应尽量回到原来页面。要做到这一点,就必须在跳登录页之前,把“当前页面路径”和“页面打开方式”记下来。
为什么需要?因为登录页本身不知道用户从哪来;等登录成功再取页面栈,原页面已经丢了。怎么想?在响应拦截器的 401 处理逻辑里记录,而不是在登录页记录。怎么做?先用 getCurrentPages() 取页面栈,最后一个单元就是当前页,再取 fullPath 存入 Pinia。易错点:不要等到登录成功后才获取当前页,那时页面栈已经变化。
获取当前页面栈与 fullPath
小程序没有 Vue Router 那样方便的路由对象,但可以通过 getCurrentPages() 拿到页面栈数组。数组最后一项是当前页。当前页上有 route 和 fullPath 两个常用属性:route 更偏页面路径,fullPath 会带查询参数,更完整。转写稿里通过打印 currentPage 验证了这一点:fullPath 比单纯路径多了参数部分,因此存跳转地址时应优先用 fullPath。
js
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
console.log('currentPage', currentPage)
const fullPath = currentPage.fullPath易错点:页面栈可能为空,实际项目里要判空;route 通常不带前导斜杠,而 fullPath 带斜杠和参数,比较时不要混用。
判断 tabBar 页面并确定 openType
为什么要判断?小程序里 tabBar 页面只能用 switchTab 跳转,普通页面才用 redirectTo 或 navigateTo。如果统一用普通跳转,tabBar 页面会跳不过去。怎么想?tabBar 页面数量有限,把项目 app.json 中 tabBar.list 的 pagePath 整理成数组,判断当前页面 route 是否在数组里。怎么做?用 includes 判断,并记录 openType。
js
const currentRoute = '/' + currentPage.route
const tabBarPages = [
// 按项目 app.json 中 tabBar.list 的 pagePath 填写,记得统一加前导斜杠
]
const isTabBar = tabBarPages.includes(currentRoute)
const userStore = useUserStore()
userStore.redirectURL = fullPath
userStore.openType = isTabBar ? 'switchTab' : 'redirectTo'易错点:判断 tabBar 时建议用 route 转成统一格式,不要直接用带参数的 fullPath 去 includes,否则可能因参数导致匹配失败。
把路径和打开方式存入 Pinia
为什么放 Pinia?因为响应拦截器、登录页等多个位置都要用,且要跨页面保存。怎么想?在用户 store 里增加 redirectURL 和 openType 两个数据,并做持久化,防止刷新丢失。转写稿里默认值设置为首页路径和 switchTab,表示没有记录时登录成功默认回首页。
js
import { ref } from 'vue'
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', () => {
const redirectURL = ref('/pages/index/index') // 以项目实际首页路径为准
const openType = ref('switchTab')
// 按项目已有持久化方式,把 redirectURL、openType 加入
return { redirectURL, openType }
})易错点:持久化时要把这两个字段加入存储配置;默认值要和项目实际首页、tabBar 配置对应。
登录成功后按记录跳转
登录提交成功后,根据 openType 动态调用 uni 的跳转方法,并传入 redirectURL。转写稿强调这里要用中括号取动态方法,不能写成 uni.openType。
js
const userStore = useUserStore()
uni[userStore.openType]({
url: userStore.redirectURL
})易错点:openType 是变量,必须用 uni[userStore.openType];如果是 switchTab,URL 参数不能被当作普通 query 传递,带参场景要另行处理。调试时还发现:登录、验证码等接口若带着错误 token 请求,会再次返回 401 并跳登录页,形成自己跳自己的循环,因此需要接口白名单,把登录相关接口排除在 token 校验之外。这样,获取页面路径、存 Pinia、登录后跳回原页面的链路才算完整。
代码示例
示例一:请求/响应拦截器(401 检测 + 接口白名单)
js
// utils/request.js
import { reLogin } from './auth'
const BASE_URL = 'https://api-xxx.example.com'
// 接口白名单:这些接口不受 token 限制(登录相关接口本身就是为了拿 token)
const whiteList = ['/login', '/login/password', '/code']
// ===== 请求拦截器 =====
const requestInterceptor = (config) => {
const token = uni.getStorageSync('token')
// 有 token 且不在白名单中,才把 token 带过去
if (token && !whiteList.includes(config.url)) {
config.header.Authorization = token
}
return config
}
// ===== 响应拦截器:成功回调 =====
const responseSuccess = (response) => response.data
// ===== 响应拦截器:失败回调 =====
const responseError = (error) => {
console.log('error', error)
// 401 是前后端约定好的「未登录 / token 失效」状态码
if (error.statusCode === 401) {
reLogin()
}
}
const request = (options) => {
const config = requestInterceptor({ header: {}, ...options })
return new Promise((resolve, reject) => {
uni.request({
url: BASE_URL + config.url,
method: config.method || 'GET',
data: config.data || {},
header: config.header,
success: (res) => {
if (res.statusCode === 200) {
resolve(responseSuccess(res))
} else {
// 非 200 视为请求失败,交给响应拦截器的失败回调
responseError(res)
reject(res)
}
},
fail: (err) => {
responseError(err)
reject(err)
}
})
})
}
export default request要点与易错点
statusCode === 401要写在响应拦截器的第二个(失败)回调里,因为 401 本身就代表请求已经失败。- 白名单必须先判断再带 token:登录/验证码接口如果也带上了失效 token,就会返回 401 → 跳登录页 → 又发验证码请求 → 又 401,形成自己跳自己的死循环。
- 白名单判断用
!whiteList.includes(config.url)取反,语义是「不是白名单才加 token」。 config.url是相对路径,要和白名单里的字符串完全一致(含前导/),否则匹配不上。
示例二:pinia 存跳转地址 + 获取页面路径 + 登录成功跳转
js
// stores/user.js
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useUserStore = defineStore('user', () => {
const token = ref('')
// 登录成功后要跳转的地址,默认首页
const redirectUrl = ref('/pages/index/index')
// 跳转方式:tabBar 页用 switchTab,普通页用 navigateTo
const openType = ref('switchTab')
return { token, redirectUrl, openType }
}, {
persist: {
// 字符串数组,刷新页面后数据不丢
paths: ['token', 'redirectUrl', 'openType']
}
})js
// utils/auth.js
import { useUserStore } from '@/stores/user'
// tabBar 页面路径(最多 5 个)
const tabBarList = [
'/pages/index/index',
'/pages/my/my'
]
export const reLogin = () => {
// 页面栈的最后一个单元就是当前页
const pageStack = getCurrentPages()
const currentPage = pageStack[pageStack.length - 1]
console.log('currentPage', currentPage)
const userStore = useUserStore()
// fullPath 才是带参数的完整路径
userStore.redirectUrl = currentPage.fullPath
// 判断是不是 tabBar 页面,决定跳转方式
userStore.openType = tabBarList.includes(currentPage.route)
? 'switchTab'
: 'navigateTo'
uni.redirectTo({ url: '/pages/login/login' })
}js
// pages/login/login.vue —— 登录成功、数据存好之后
const userStore = useUserStore()
// openType 是变量,方法名必须用中括号取
uni[userStore.openType]({
url: userStore.redirectUrl
})要点与易错点
- 参数不要拼在地址栏传(
?redirectUrl=xxx?a=b会出现两个问号,小程序会把第二个问号后的参数过滤掉),要放进 pinia。 - 取路径用
currentPage.fullPath(带参数),不要用route(只有部分路径、不带参数)。 - 判断 tabBar 页面:拿
currentPage.route去tabBarList里includes,命中就用switchTab,否则navigateTo。 uni[userStore.openType]中括号语法只能用于变量方法名,写成uni.userStore.openType是错的。- 三个字段都要写进
persist.paths数组,且必须是字符串,否则刷新页面后数据丢失。 - 跳转地址前导斜杠
/pages/...不能丢。
示例三:scroll-page 组件封装
vue
<!-- components/scroll-page/scroll-page.vue -->
<template>
<scroll-view
class="scroll-page"
:scroll-y="true"
:style="[{ height: windowHeight + 'px' }, { borderBottom: borderStyle }]"
:refresher-enabled="refresherEnabled"
:refresher-triggered="refresherTriggered"
@scrolltolower="onScrollToLower"
@refresherrefresh="onRefresherRefresh"
>
<view class="scroll-page-content">
<slot />
</view>
</scroll-view>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({
// 可以是字符串(如 '1px solid #eee'),也可以是 false(不要边框线)
borderStyle: {
type: [String, Boolean],
default: '1px solid #eee'
},
// 是否启用下拉刷新
refresherEnabled: {
type: Boolean,
default: false
},
// 关闭下拉刷新动画
refresherTriggered: {
type: Boolean,
default: false
}
})
// 自定义事件,把原生 scroll-view 的事件中转出去
const emits = defineEmits(['scrolltolower', 'refresherrefresh'])
// 页面高度,让组件和屏幕一样高
const { windowHeight } = uni.getSystemInfoSync()
const onScrollToLower = (e) => {
emits('scrolltolower', e)
}
const onRefresherRefresh = (e) => {
emits('refresherrefresh', e)
}
</script>
<style lang="scss" scoped>
.scroll-page {
// 加了边框线后不撑大盒子,否则会出现多余的滚动条
box-sizing: border-box;
}
.scroll-page-content {
// 适配底部安全区(刘海屏 / 小黑条)
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
</style>要点与易错点
- 本质是对原生
scroll-view的二次包装,scroll-y加上即可垂直滚动。 - 高度用
uni.getSystemInfoSync()的windowHeight,拼接时单位px不能丢。 - 加
border-bottom后必须配box-sizing: border-box,否则边框宽度会被算进高度,出现多余滚动条。 - 安全区适配写在内层包裹
slot的view上,写在外层盒子会让整块内容都缩进。 - 原生事件(
scrolltolower、refresherrefresh)要通过defineEmits重新抛出,emits('xxx', e)里事件对象也要传,否则父组件拿不到参数。 - 下拉刷新需要
refresher-enabled开启、refresher-triggered关闭动画,两者配合使用。 - 调试时临时写死的高度(如
1000px)和背景色,测完一定要删掉,否则组件高度被固定死。
常见错误
401 判断写在响应拦截器的成功回调里,永远不触发
- 现象:手动把 token 改错,接口明明返回 401,但页面毫无反应,控制台也没打印。
- 原因:401 属于请求失败,走的是拦截器第二个回调(error 分支),成功回调里拿不到。
- 修法:把
console.log(error)和状态码判断整体搬到(error) => {}里,从error.response.status(或error.status,看封装层)取状态码,等于 401 时执行重新登录。
白名单缺失,登录页自己跳自己,形成死循环
- 现象:token 失效后刷新首页 → 跳到登录页 → 点"获取验证码" → 页面又被刷回登录页,手机号和验证码被清空,怎么都登不进去。
- 原因:验证码/登录接口本身也带着那个失效 token 发出去,同样返回 401,于是又触发一次跳转登录页。
- 修法:定义接口白名单数组(获取验证码、账号登录、密码登录),在请求拦截器里判断:只有在"有 token 且不在白名单"时才往 header 里塞 token;同时 401 跳转逻辑保持不动。
回跳地址用地址栏传参,参数被吃掉
- 现象:登录成功后回到首页,地址上
?a=b这类参数没了,或只回到一个光秃秃的路径。 - 原因:小程序里 URL 里出现第二个
?时,后面的参数会被过滤(H5 无此问题,所以本地测不出来)。 - 修法:不再用
?redirectUrl=xxx传,改为把回跳地址和打开方式放进 Pinia(redirectURL+openType),并加入持久化列表,防止刷新丢失。
- 现象:登录成功后回到首页,地址上
取路径用了
root而不是fullPath- 现象:回跳后页面参数丢失,或回跳地址和当初进入的地址对不上。
- 原因:
root只有路径部分,不带 query;fullPath才是带参数的完整路径。 - 修法:取页面栈最后一项后,用
fullPath存回跳地址;root只用来和白名单/tabBar路径数组做比对判断打开方式。
动态调用写成
uni.openType/uni.redirectURL- 现象:控制台报 "xxx is not a function",或跳转根本不执行。
- 原因:方法名和地址都是变量,用点语法访问等于去找一个叫
openType的固定属性。 - 修法:变量作属性名一律用中括号:
uni[openType]({ url: redirectURL });同时确认openType的值是switchTab/navigateTo/redirectTo这类真实方法名。
scroll-page 加了边框线后整页多出一条滚动条
- 现象:给组件加
border-bottom后,页面出现竖向滚动条,内容被顶出去一点点。 - 原因:高度已经按
windowHeight写死,边框宽度被额外算进盒子尺寸,总高超出屏幕。 - 修法:给滚动容器加
box-sizing: border-box;边框线用 props(borderStyle,支持boolean关闭)传入,不要写死颜色和宽度。
- 现象:给组件加
组件里遗留测试代码
- 现象:页面怎么都滚不动,或高度永远是 1000px。
- 原因:调试上拉加载时临时写死的
height: 1000px、调试用的背景色没删。 - 修法:验证完立即删除;高度始终由
uni.getSystemInfoSync().windowHeight动态给,背景色只在调试时临时加。
练习
复现并修复 401:把本地 token 手动改成错误值 → 刷新首页 → 确认跳到登录页;在拦截器里打印出状态码来源,说明它为什么在 error 分支。再把 401 判断临时挪到成功回调里,观察"不生效"的具体表现并复原。
补一条白名单:新增一个"忘记密码-发送短信"接口,把它加入白名单。用错误 token 触发该接口,在网络面板确认请求头里没有 token;再用同样方式请求一个普通业务接口,确认 token 仍在。给出判断条件的完整写法。
深链回跳验证:从首页带参数进入(如
/pages/index/index?name=xxx),让 token 失效触发跳登录,登录成功后检查:①是否回到带参数的完整地址;②openType是否为switchTab;③刷新页面后 Pinia 里两个值是否还在。若参数丢了,定位是哪一步写错了。tabBar 判定:分别从 tabBar 页面和普通页面进入,打印两次的
root与openType,说明判定依据;再故意把路径数组里某一项写错一个字符,观察跳转方式变成什么、会报什么错。扩展 scroll-page:把
scrolltolower和下拉刷新两个原生事件用defineEmits抛给父组件,在"我的"页面接收并打印事件对象;同时给组件传一个蓝色borderStyle,验证既不出现多余滚动条,边框颜色也可自定义。
课堂画面






原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 优医咨询-权限验证检测状401 | 07:04 |
| 2 | 优医咨询-初始页面中径参数 | 06:42 |
| 3 | 优医咨询-获取页面路径 | 11:29 |
| 4 | 优医咨询-登录成功跳转 | 10:30 |
| 5 | 优医咨询-接口请求白名单 | 08:59 |
| 6 | 优医咨询-scroll-page组件介绍 | 06:54 |
| 7 | 优医咨询-scroll-page高度和安全区 | 10:58 |
| 8 | 优医咨询-scroll-page边框线和事件 | 12:23 |
小结
- 状态码 401 是前后端约定好的"未登录/登录失效"信号,处理位置只能是响应拦截器的失败回调。
- 权限校验必须配白名单,否则登录相关接口自身返回 401 会把用户困在登录页,形成死循环。
- 回跳地址不要走地址栏传参,小程序对第二个
?不兼容;用状态管理(+持久化)承载redirectURL与openType。 - 当前页路径从页面栈最后一项取,且要用
fullPath(带参数);root只用于判断是不是 tabBar 页面。 - tabBar 页面必须用
switchTab,普通页面用navigateTo/redirectTo,所以打开方式必须随地址一起被记录。 - 变量作为方法名/属性名时只能用中括号访问,这是本模块跳转能跑通的关键语法点。
- scroll-page 的本质是
scroll-view的二次封装,价值在于:统一处理刘海屏安全区、隐藏滚动条、自定义 tabBar 边框线、复用下拉刷新与上拉加载。 - 组件高度动态给、边框宽度用
border-box兜住、调试代码及时清理——这三点决定组件是否会"莫名其妙多出滚动条"。