第四章:代码修错与引入pinia进行状态管理
不影响运行的警告bug修复
解决项目启动警告
- 在进入消息组件的编写之前,我们可以发现一个问题
-
启动命令的时候会出现这个黄色警告
-
这个黄色警告的意思就是 defineProps组件被默认导入了,可以不需要手动导入,叫你去掉
-
不去掉也可也
- 搜索即可
defineEmits
defineProps
- 使用ctrl+c结束运行,然后再次进行编译,可以发现这次以及没有黄色报错
去除post多余的navigator
- 原本代码中,多写了一个navigator
<script setup>
const navigateToPostDetails = () => {
uni.navigateTo({
url: '/components/Post/PostDetails',
})
}
</script>
<template>
<view class="container" @click="navigateToPostDetails">
<!-- 评论组件 -->
<navigator url="/pages/index/Posts/PostDetails" class="reply-navigator">
<view class="reply">
<image class="reply-profile-image" src="/static/logo.png" />
<view class="reply-info">
<view class="reply-user">
<text class="reply-name">Qiuner</text>
<text class="reply-handle">22级 · 5天</text>
</view>
<text class="reply-text">
福信校园通是一个校园社交软件,在这里你可以分享你的想法、发现新事物、与志同道合的人互动。
</text>
</view>
</view>
</navigator>
<view class="post-image-container">
<view class="post-image" style="background-color: #1da1f2">
<!-- 蓝色背景替代图片部分 -->
</view>
</view>
<view class="post-footer"></view>
</view>
</template>
<style>
.container {
padding: 1rem;
margin-top: calc(env(safe-area-inset-top) + 2rem);
}
.reply {
display: flex;
padding: 1rem 0;
border-top: 0.0625rem solid #ddd;
}
.reply-profile-image {
width: 1.875rem;
height: 1.875rem;
border-radius: 50%;
}
.reply-info {
flex: 1;
margin-left: 0.625rem;
}
.reply-user {
display: flex;
align-items: center;
}
.reply-name {
font-weight: bold;
}
.reply-handle {
color: grey;
margin-left: 0.3125rem;
}
.reply-text {
margin-top: 0.3125rem;
}
.post-image-container {
display: flex;
justify-content: center;
}
.post-image {
width: 100%;
max-width: 100%;
height: 12rem;
border: 0.0625rem solid #ddd;
}
.post-footer {
color: grey;
}
.reply-navigator,
.post-image-navigator {
display: block;
}
</style>
解决组织页面出现错误问题
解决不是使用uniapp兼容图片加载
“Image is not defined in this environment” 表示 Image 对象在当前环境中不可用
改动前
// 预加载图片
function preloadImage(src: string) {
if (typeof Image !== 'undefined') {
const img = new Image()
img.src = src
} else {
console.warn('Image is not defined in this environment')
}
}
改动后
// 预加载图片
function preloadImage(src: string) {
uni.getImageInfo({
src,
success: () => console.log(`Image loaded: ${src}`),
fail: (err) => console.warn(`Failed to load image: ${src}`, err),
})
}
总体代码
<template>
<view class="container">
<!-- 页面其他内容 -->
<image class="add-button" :src="buttonImage" @click="handleClick"></image>
</view>
</template>
<script setup lang="ts">
import { ref, computed, nextTick } from 'vue'
// 定义响应式变量
const isAddButton1 = ref(true)
// 切换图片和跳转页面的方法
const handleClick = () => {
isAddButton1.value = !isAddButton1.value
// 如果切换后的状态是 AddButton1,进行页面跳转
if (isAddButton1.value) {
nextTick(() => {
setTimeout(() => {
uni.navigateTo({
url: '/pages/writing/writing',
fail: (err) => {
console.error('Navigation Error:', err)
},
})
}, 100) // 延迟时间可以根据实际需要调整
})
}
}
// 预加载图片
function preloadImage(src: string) {
uni.getImageInfo({
src,
success: () => {
// 成功时不进行任何操作
},
fail: (err) => {
console.warn(`Failed to load image: ${src}`, err)
},
})
}
// 预加载图片
preloadImage('/static/add/AddButton1.png')
preloadImage('/static/add/AddButton2.png')
// 图片路径的计算属性
const buttonImage = computed(() =>
isAddButton1.value ? '/static/add/AddButton1.png' : '/static/add/AddButton2.png',
)
</script>
<style scoped>
.container {
position: relative;
width: 100%;
height: 100vh;
background-color: #f0f0f0;
}
.add-button {
position: fixed;
right: 5%; /* 调整按钮的水平位置 */
bottom: 5%; /* 调整按钮的位置,使其在导航栏上方 */
width: 20vw; /* 相对屏幕宽度的按钮宽度 */
height: 20vw; /* 相对屏幕宽度的按钮高度 */
cursor: pointer;
}
</style>
页面状态缓存清除
- 现在页面还面对着以下问题
- 是的 在切换回页面的时候能够发现侧边栏目状态没有刷新
认识uniapp生命周期(重点)
- 想要解决这个问题我们需要先来认识一下uniapp的生命周期
- 生命周期,故名思意,一颗树的生命周期是从小种子开始,再发芽、长大、衰老、死亡。
- 也就是说一颗树的生命周期有
- 一:种子期
- 二:发芽期
- 三:长大期
- 四:衰老期
- 五:死亡期
- 你知道了在五分钟后,你面前的这颗树要进入发芽期,那你就可以对这个树进行操作。因为你知道了它的位置、时间。
- 你可以选择在这颗树进入发芽期的时候浇水,也可以什么都不做。但你不能在种子期之前对树进行操作,因为那个时候树还不存在,连种子都不是。
- 和树一样,页面和组件也存在着生命周期,也存在着操作,因此知道时间(处于什么生命周期)位置(处于哪里)后,就能对这个生命周期的页面进行操作。
- 下面是uniapp的生命周期
页面生命周期
钩子名称 | 触发时机 | 用途 |
---|---|---|
onLoad | 页面首次加载时 | 页面初始化操作,如获取数据、设置页面初始状态 |
onShow | 页面显示时(包括从其他页面返回时) | 页面显示时的操作,如更新数据、记录用户行为 |
onReady | 页面初次渲染完成时 | 页面渲染完成后的操作,如初始化插件、开始动画 |
onHide | 页面隐藏时(如切换到其他页面或应用被切换到后台) | 保存页面状态、停止动画、清理定时器 |
onUnload | 页面卸载时(如退出页面或页面被销毁) | 页面销毁前的清理操作,如释放资源、清除缓存 |
onTabItemTap | 用户点击 TabBar 的某一项时 | 处理 TabBar 项的点击事件 |
组件生命周期
钩子名称 | 触发时机 | 用途 |
---|---|---|
onBeforeMount | 组件挂载前 | 组件创建之前的准备工作 |
onMounted | 组件挂载后 | 组件挂载后的操作,如数据获取、DOM 操作 |
onBeforeUpdate | 组件数据更新前 | 数据更新之前进行操作 |
onUpdated | 组件数据更新后 | 数据更新之后执行操作,如操作新的 DOM 元素 |
onBeforeUnmount | 组件卸载前 | 组件卸载之前的清理工作 |
onUnmounted | 组件卸载后 | 组件卸载后的清理工作 |
解决页面状态缓存方案一:手动记录每个操作的元素,然后在退出的时候刷新状态
<script setup lang="ts">
import { ref, onShow } from 'vue'
import AddButton from '@/components/AddButton.vue'
import Posts from '@/components/Post/Post.vue'
import TabHeader from '@/components/HeadPortrait/TabHeader.vue'
import '@/styles/IndexAndMessage.css'
const currentTab = ref(0)
// 切换选项卡的方法
const changeTab = (index: number) => {
currentTab.value = index
}
// 监听滑动事件,实现选项卡动态切换
const onSwiperChange = (e: any) => {
currentTab.value = e.detail.current
}
// 重置状态到默认值
onShow(() => {
currentTab.value = 0
})
</script>
<template>
<TabHeader />
<!-- 顶部导航栏 -->
<view class="header">
<view class="tab-container">
<view class="tab" :class="{ active: currentTab === 0 }" @click="changeTab(0)">
<text>为您推荐</text>
</view>
<view class="tab" :class="{ active: currentTab === 1 }" @click="changeTab(1)">
<text>正在关注</text>
</view>
</view>
</view>
<!-- 滑动内容区 -->
<swiper :current="currentTab" @change="onSwiperChange" style="height: 100vh">
<!-- 为您推荐内容 -->
<swiper-item>
<view class="content">
<Posts />
</view>
</swiper-item>
<!-- 正在关注内容 -->
<swiper-item>
<view class="content">正在关注的内容</view>
</swiper-item>
</swiper>
<!-- 添加按钮 -->
<AddButton />
</template>
解决页面状态缓存方案二:使用不缓存页面的跳转
- 原本是用 uni.navigateTo({
- 换成 uni.redirectTo
解决页面状态缓存方案三:在 onHide
钩子中清除缓存,在 onShow
钩子中重置状态:
<script setup lang="ts">
import { ref } from 'vue'
import { onShow, onHide } from '@dcloudio/uni-app'
import AddButton from '@/components/AddButton.vue'
import Posts from '@/components/Post/Post.vue'
import TabHeader from '@/components/HeadPortrait/TabHeader.vue'
import '@/styles/IndexAndMessage.css'
const currentTab = ref(0)
// 切换选项卡的方法
const changeTab = (index: number) => {
currentTab.value = index
}
// 监听滑动事件,实现选项卡动态切换
const onSwiperChange = (e: any) => {
currentTab.value = e.detail.current
}
// 在页面隐藏时重置状态
onHide(() => {
uni.removeStorage({
key: 'currentTab',
})
})
// 在页面显示时恢复默认状态
onShow(() => {
uni.getStorage({
key: 'currentTab',
success: (res) => {
currentTab.value = res.data || 0
},
fail: () => {
currentTab.value = 0
},
})
})
</script>
<template>
<TabHeader />
<!-- 顶部导航栏 -->
<view class="header">
<view class="tab-container">
<view class="tab" :class="{ active: currentTab === 0 }" @click="changeTab(0)">
<text>为您推荐</text>
</view>
<view class="tab" :class="{ active: currentTab === 1 }" @click="changeTab(1)">
<text>正在关注</text>
</view>
</view>
</view>
<!-- 滑动内容区 -->
<swiper :current="currentTab" @change="onSwiperChange" style="height: 100vh">
<!-- 为您推荐内容 -->
<swiper-item>
<view class="content">
<Posts />
</view>
</swiper-item>
<!-- 正在关注内容 -->
<swiper-item>
<view class="content">正在关注的内容</view>
</swiper-item>
</swiper>
<!-- 添加按钮 -->
<AddButton />
</template>
解决页面状态缓存方案四:使用状态管理工具pinia
-
使用pinia的方法会比较繁琐,但pinia在后面中需要使用,因此我们可以先使用使用一些
-
使用pinia的步骤
安装
pnpm add pinia
配置
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')
创建实例
import { defineStore } from 'pinia'
export const useTabStore = defineStore('tab', {
state: () => ({
currentTab: 0 // 默认选中的标签页索引
}),
actions: {
setTab(index: number) {
this.currentTab = index
},
resetTab() {
this.currentTab = 0
}
}
})
在组件中使用……
- 可以看到我们再项目一开始就已经安装号了pinia,因此我们直接进入配置阶段
第二步:创建pinia实例并导出
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
// 创建pinia实例
const pinia = createPinia()
// 使用持久化插件
pinia.use(persist)
export default pinia
- 这里创建了pinia、使用插件简化操作然后导出
import { createSSRApp } from 'vue'
import App from './App.vue'
import pinia from './stores'
export function createApp() {
// vue实例
const app = createSSRApp(App)
// 使用pinia
app.use(pinia)
return {
app,
}
}
第三步:编写代码 将currentTab的值统一给pinia管理
// src/stores/indexStore.ts
import { defineStore } from 'pinia'
export const useIndexStore = defineStore('indexStore', {
state: () => ({
currentTab: 0, // 0 表示为您推荐,1 表示正在关注
}),
actions: {
changeTab(index: number) {
this.currentTab = index
},
resetTab() {
this.currentTab = 0 // 重置为默认状态
},
},
})
- 然后在项目中引用即可
第四步:在项目中使用
<script setup lang="ts">
import AddButton from '@/components/AddButton.vue'
import Posts from '@/components/Post/Post.vue'
import TabHeader from '@/components/HeadPortrait/TabHeader.vue'
import '@/styles/IndexAndMessage.css'
import type { SwiperChangeEvent } from '@/types/index'
import { onShow } from '@dcloudio/uni-app'
import { useIndexStore } from '@/stores/modules/indexStore'
const indexStore = useIndexStore()
onShow(() => {
indexStore.resetTab()
})
</script>
<template>
<TabHeader />
<!-- 顶部导航栏 -->
<view class="header">
<view class="tab-container">
<view
class="tab"
:class="{ active: indexStore.currentTab === 0 }"
@click="indexStore.changeTab(0)"
>
<text>为您推荐</text>
</view>
<view
class="tab"
:class="{ active: indexStore.currentTab === 1 }"
@click="indexStore.changeTab(1)"
>
<text>正在关注</text>
</view>
</view>
</view>
<!-- 滑动内容区 -->
<swiper
:current="indexStore.currentTab"
@change="(e: SwiperChangeEvent) => indexStore.changeTab(e.detail.current)"
style="height: 100vh"
>
<!-- 为您推荐内容 -->
<swiper-item>
<view class="content">
<Posts />
</view>
</swiper-item>
<!-- 正在关注内容 -->
<swiper-item>
<view class="content">正在关注的内容</view>
</swiper-item>
</swiper>
<!-- 添加按钮 -->
<AddButton />
</template>
- 需要注意的是,我们是一个TS类型的项目
@change="(e: SwiperChangeEvent) => indexStore.changeTab(e.detail.current)"
因此这样写代码而不是
@change="(e) => indexStore.changeTab(e.detail.current)"
-
这样写代码没有指定e的类型,因此无法通过编译
-
至于类型文件,我们不像之前一样卸载文件中,而是写在types文件夹中
// src/types/swiper.d.ts
export interface SwiperChangeEvent {
detail: {
current: number
}
}
最终效果
- 这样就能通过编译了,而且也实现了功能
在这里插入图片描述
在这里插入图片描述
用上pinia了吗?
- 但这里,使用了pinia了吗?
- pinia的作用是将数据持久化,那么数据持久化到了哪里?
验证数据持久化
- 观看以上动图,可以发现数据并没有存储进pinia中,因为我们只靠onShow方法进行了重置,并没有配置持久化的东西
如何持久化?
import { defineStore } from 'pinia'
export const useIndexAndMessageStore = defineStore('indexStoreAndMessage', {
state: () => ({
currentTab: 0, // 0 表示为您推荐,1 表示正在关注
}),
actions: {
changeTab(index: number) {
this.currentTab = index
},
resetTab() {
this.currentTab = 0 // 重置为默认状态
},
},
persist: {
// 调整为兼容多端的API
storage: {
getItem(key) {
return uni.getStorageSync(key)
},
setItem(key, value) {
uni.setStorageSync(key, value)
},
},
},
})
- 这样再次操作就能发现数据已经持久化成功
- 但这样将数据持久化后会有一个大的bug,会导致顶部导航栏一致反复横跳。
给消息界面、写作按钮、左侧弹出栏添加相同的处理
- 如动图所示,我们的代码目前还面对着以下问题
消息界面导航栏状态管理
- 因为这两边代码基本一致,所以我们直接进行更改之前的文件名,然后进行复制
index
<script setup lang="ts">
import AddButton from '@/components/AddButton.vue'
import Posts from '@/components/Post/Post.vue'
import TabHeader from '@/components/HeadPortrait/TabHeader.vue'
import '@/styles/IndexAndMessage.css'
import { onShow } from '@dcloudio/uni-app'
import { useIndexAndMessageStore } from '@/stores/modules/indexAndMessageStore'
import type { SwiperChangeEvent } from '@/types/indexAndMessageStore'
// 使用 Pinia Store
const indexAndMessageStore = useIndexAndMessageStore()
// 页面显示时重置 tab 状态
onShow(() => {
indexAndMessageStore.resetTab()
})
</script>
<template>
<TabHeader />
<!-- 顶部导航栏 -->
<view class="header">
<view class="tab-container">
<view
class="tab"
:class="{ active: indexAndMessageStore.currentTab === 0 }"
@click="indexAndMessageStore.changeTab(0)"
>
<text>为您推荐</text>
</view>
<view
class="tab"
:class="{ active: indexAndMessageStore.currentTab === 1 }"
@click="indexAndMessageStore.changeTab(1)"
>
<text>正在关注</text>
</view>
</view>
</view>
<!-- 滑动内容区 -->
<swiper
:current="indexAndMessageStore.currentTab"
@change="(e: SwiperChangeEvent) => indexAndMessageStore.changeTab(e.detail.current)"
style="height: 100vh"
>
<!-- 为您推荐内容 -->
<swiper-item>
<view class="content">
<Posts />
</view>
</swiper-item>
<!-- 正在关注内容 -->
<swiper-item>
<view class="content">正在关注的内容</view>
</swiper-item>
</swiper>
<!-- 添加按钮 -->
<AddButton />
</template>
<style lang="scss"></style>
message
<script setup lang="ts">
import AddButton from '@/components/AddButton.vue'
import TabHeader from '@/components/HeadPortrait/TabHeader.vue'
import '@/styles/IndexAndMessage.css'
import { useIndexAndMessageStore } from '@/stores/modules/indexAndMessageStore'
import { onShow } from '@dcloudio/uni-app'
import type { SwiperChangeEvent } from '@/types/indexAndMessageStore'
// 使用 Pinia Store
const indexAndMessageStore = useIndexAndMessageStore()
// 页面显示时重置 tab 状态
onShow(() => {
indexAndMessageStore.resetTab()
})
</script>
<template>
<TabHeader />
<!-- 顶部导航栏 -->
<view class="header">
<view class="tab-container">
<view
class="tab"
:class="{ active: indexAndMessageStore.currentTab === 0 }"
@click="indexAndMessageStore.changeTab(0)"
>
<text>全部</text>
</view>
<view
class="tab"
:class="{ active: indexAndMessageStore.currentTab === 1 }"
@click="indexAndMessageStore.changeTab(1)"
>
<text>点赞</text>
</view>
<view
class="tab"
:class="{ active: indexAndMessageStore.currentTab === 2 }"
@click="indexAndMessageStore.changeTab(2)"
>
<text>回复</text>
</view>
</view>
</view>
<!-- 滑动内容区 -->
<swiper
:current="indexAndMessageStore.currentTab"
@change="(e: SwiperChangeEvent) => indexAndMessageStore.changeTab(e.detail.current)"
style="height: 100vh"
>
<!-- 全部内容 -->
<swiper-item>
<view class="content">
<!-- 这里是“全部”的内容 -->
<p>这是全部</p>
</view>
</swiper-item>
<!-- 点赞内容 -->
<swiper-item>
<view class="content">
<p>这是点赞</p>
</view>
</swiper-item>
<!-- 回复内容 -->
<swiper-item>
<view class="content">
<p>这是回复</p>
</view>
</swiper-item>
</swiper>
<!-- 添加按钮 -->
<AddButton />
</template>
<style lang="scss"></style>
indexAndMessageStore
// src/stores/indexStore.ts
import { defineStore } from 'pinia'
export const useIndexAndMessageStore = defineStore('indexStoreAndMessage', {
state: () => ({
currentTab: 0, // 0 表示为您推荐,1 表示正在关注
}),
actions: {
changeTab(index: number) {
this.currentTab = index
},
resetTab() {
this.currentTab = 0 // 重置为默认状态
},
},
})
indexAndMessageStore.d
// src/types/swiper.d.ts
export interface SwiperChangeEvent {
detail: {
current: number
}
}
添加对写作按钮的处理
- 其实其他地方也是如此 只需要在onShow赋值为0,不过这里更多是是起教学作用,带大家认识到pinia进行状态管理,需要做什么。
你好,我是Qiuner. 为帮助别人少走弯路而写博客 这是我的 github https://github.com/Qiuner⭐ gitee https://gitee.com/Qiuner
标签:vue,const,修错,第四章,pinia,import,currentTab,页面 From: https://blog.csdn.net/qq_61654952/article/details/143671803