阶段四:UI优化与项目发布
1. 教学目标
- 掌握高级UI交互:学习使用
uni-popup实现弹窗,并通过props和emit实现父子组件间的交互。 - 提升用户体验(UX):掌握在
<scroll-view>中实现“下拉刷新”功能。 - 理解资源优化:(可选) 了解如何封装一个
SvgIcon组件,以更灵活地管理和使用矢量图标。 - 掌握项目交付:学习配置
manifest.json,执行打包命令,并了解将项目上传到微信开发者工具进行发布的完整流程。
2. 优化与发布流程图 (Mermaid)
3. 核心步骤详解
🧩 步骤一:实现生活指数详情弹窗
设计流程
目标:当用户点击首页的某个生活指数时,我们不应该跳转页面,而是弹出一个浮层来展示更详细的说明,这样交互更轻量。
设计决策:采用父子组件通信的方式。父组件 index.vue 负责管理弹窗的“是否显示”状态 (visible) 和“显示什么内容” (currentIndex)。当 LifeIndex.vue 被点击时,它只负责告诉父组件“我被点击了,相关数据是...”,而不关心弹窗如何显示。弹窗组件 IndexDetailPopup.vue 则更“单纯”,它只根据父组件给的 props 来决定自己是否显示、显示什么内容。
编码流程
- 创建弹窗组件: 创建
components/IndexDetailPopup.vue。内部使用uni-popup,并定义好visible和indexData两个 props,以及一个close事件。 - 监听Props变化: 在弹窗组件中,使用
watch监听props.visible的变化,当true时打开uni-popup,false时关闭。 - 父组件管理状态: 在
index.vue中,定义detailVisible和currentIndex两个ref变量。 - 连接父子:
- 在
index.vue的模板中,监听LifeIndex的@show-detail事件,触发一个方法来修改detailVisible和currentIndex。 - 将
detailVisible和currentIndex作为 props 传入IndexDetailPopup组件。 - 监听
IndexDetailPopup的@close事件,将detailVisible改为false。
- 在
点击展开/折叠 index.vue 与弹窗交互的相关代码
<template>
<!-- ... 其他组件 ... -->
<!-- 生活指数,监听子组件的 show-detail 事件 -->
<LifeIndex :index-data="lifeIndices" @show-detail="showIndexDetail"></LifeIndex>
<!-- 指数详情弹窗,传递 visible 状态并监听 close 事件 -->
<IndexDetailPopup :visible="detailVisible" :index-data="currentIndex" @close="closePopup"></IndexDetailPopup>
</template>
<script setup>
import { ref } from 'vue';
// ... 其他导入 ...
// 控制弹窗显隐的状态
const detailVisible = ref(false);
// 存储当前要展示的指数详情
const currentIndex = ref({});
// 显示指数详情(由 LifeIndex 组件触发)
const showIndexDetail = (index) => {
currentIndex.value = index;
detailVisible.value = true;
};
// 关闭弹窗(由 IndexDetailPopup 组件触发)
const closePopup = () => {
detailVisible.value = false;
};
</script>🚀 步骤二:实现下拉刷新功能
设计流程
目标:为用户提供一个主动、便捷的方式来刷新天气数据。
设计决策:不使用页面级的全局下拉刷新,因为它会作用于整个窗口。我们选择 scroll-view 组件内置的下拉刷新功能,这样可以更精细地控制刷新区域,且实现起来更简单,只需在 scroll-view 上配置几个属性并绑定一个事件即可。
编码流程
- 在
index.vue的模板中,找到最外层的<scroll-view>。 - 为其添加
refresher-enabled="true"来开启下拉刷新功能。 - 添加
@refresherrefresh="onRefresh"来绑定下拉被触发时要执行的方法。 - 添加
:refresher-triggered="refreshing",将其同一个名为refreshing的ref变量绑定。这个变量用来告诉scroll-view何时开始/结束刷新动画。 - 在脚本中,创建
refreshing = ref(false)。 - 创建
async方法onRefresh。在此方法中,先设置refreshing.value = true,然后await调用数据获取函数(如initData),数据返回后,再设置refreshing.value = false。
点击展开/折叠 index.vue 下拉刷新相关代码
<template>
<view class="weather-container">
<scroll-view
scroll-y
refresher-enabled="true"
@refresherrefresh="onRefresh"
:refresher-triggered="refreshing"
style="height: 100vh;"
>
<!-- 所有页面内容... -->
</scroll-view>
</view>
</template>
<script setup>
import { ref } from 'vue';
// ... 其他导入和 ref 定义 ...
// 刷新状态
const refreshing = ref(false);
// ... 其他函数 ...
// 下拉刷新事件处理
const onRefresh = async () => {
// 1. 显示刷新动画
refreshing.value = true;
// 2. 重新获取所有数据
await initData(); // 假设 initData() 是一个获取所有页面数据的函数
// 3. 数据获取完毕,隐藏刷新动画
refreshing.value = false;
};
</script>💻 步骤三:自定义SVG图标组件 (SvgIcon.vue) (可选)
设计流程
目标: 提供一个统一的、可复用的 SVG 图标组件,方便地控制图标的名称、大小和颜色,并且比 uni-icons 更灵活,可以加载我们自己的 SVG 文件。
设计决策: 组件本质上是一个 <image> 标签。它接收 name prop,并根据这个 name 动态地拼接出 /src/static/SvgIcon/ 目录下的对应 .svg 文件的路径。这样,我们只需要在 static 目录下放置 SVG 文件,就可以通过组件名来调用它们。
编码流程
- 创建
/src/components/SvgIcon/SvgIcon.vue文件。 - 在模板中,放置一个
<image>标签。 - 将其
src属性动态绑定为一个基于nameprop 的路径,例如:src="/src/static/SvgIcon/${name}.svg"。 - 添加
size和colorprops 以便从外部控制样式。
点击展开/折叠 SvgIcon.vue 源代码
<template>
<image
class="weather-icon"
:src="`/src/static/SvgIcon/${name}.svg`"
mode="widthFix"
:style="{ width: size + 'px', height: size + 'px', color: color }"
/>
</template>
<script setup>
import { defineProps } from 'vue'
// 定义组件属性,支持图标名、大小、颜色
const props = defineProps({
name: { // 图标名(对应SVG文件名:sunny、wind、rain等)
type: String,
required: true
},
size: { // 图标尺寸,默认24px
type: [Number, String],
default: 24
},
color: { // 单色图标颜色(需SVG的fill设为currentColor)
type: String,
default: ''
}
})
</script>
<style scoped>
.weather-icon {
height: auto;
vertical-align: middle;
}
</style>🚀 步骤四:项目打包与发布
设计流程
目标:将我们的源代码转换成微信小程序平台可识别、可运行的代码包,并将其上传发布。
设计决策:遵循 uni-app 的官方流程。首先配置 manifest.json 文件,这是 uni-app 项目的“身份证”,里面包含了小程序的 AppID 等关键信息。然后利用 npm 脚本执行 uni-app 提供的编译命令,将项目打包成 mp-weixin 平台的代码。最后一步是使用微信开发者工具这个“官方上传渠道”,来导入和上传我们的代码包。
编码流程
- 配置
manifest.json: 打开项目根目录的manifest.json文件,在“微信小程序配置”中,填入从微信公众平台申请到的 AppID。 - 执行打包命令: 在项目根目录下打开终端,执行
npm run build:mp-weixin。 - 导入微信开发者工具: 等待打包成功后,会在根目录生成
dist/build/mp-weixin文件夹。打开微信开发者工具,点击“导入”,项目目录选择这个mp-weixin文件夹。 - 上传版本: 在微信开发者工具中进行最后的预览和调试,确认无误后,点击右上角的“上传”按钮,填写版本信息即可上传。
上传成功后,即可登录 微信公众平台,在 “版本管理” 中看到你刚上传的版本,后续可提交审核或设为体验版。
4. 课程总结与拓展任务
拓展任务: 实现一个“骨架屏(Skeleton Screen)”加载效果。在 index.vue 中增加一个 isLoading 状态,在 initData 开始时设为 true,结束后设为 false。当 isLoading 为 true 时,用一些灰色的占位块来模拟 CurrentEnvironment 等组件的轮廓,为用户提供一个更平滑的加载预期。