Skip to content

阶段四:UI优化与项目发布

1. 教学目标

  • 掌握高级UI交互:学习使用 uni-popup 实现弹窗,并通过 propsemit 实现父子组件间的交互。
  • 提升用户体验(UX):掌握在 <scroll-view> 中实现“下拉刷新”功能。
  • 理解资源优化:(可选) 了解如何封装一个 SvgIcon 组件,以更灵活地管理和使用矢量图标。
  • 掌握项目交付:学习配置 manifest.json,执行打包命令,并了解将项目上传到微信开发者工具进行发布的完整流程。

2. 优化与发布流程图 (Mermaid)

3. 核心步骤详解

🧩 步骤一:实现生活指数详情弹窗

设计流程

目标:当用户点击首页的某个生活指数时,我们不应该跳转页面,而是弹出一个浮层来展示更详细的说明,这样交互更轻量。

设计决策:采用父子组件通信的方式。父组件 index.vue 负责管理弹窗的“是否显示”状态 (visible) 和“显示什么内容” (currentIndex)。当 LifeIndex.vue 被点击时,它只负责告诉父组件“我被点击了,相关数据是...”,而不关心弹窗如何显示。弹窗组件 IndexDetailPopup.vue 则更“单纯”,它只根据父组件给的 props 来决定自己是否显示、显示什么内容。

编码流程

  1. 创建弹窗组件: 创建 components/IndexDetailPopup.vue。内部使用 uni-popup,并定义好 visibleindexData 两个 props,以及一个 close 事件。
  2. 监听Props变化: 在弹窗组件中,使用 watch 监听 props.visible 的变化,当 true 时打开 uni-popupfalse 时关闭。
  3. 父组件管理状态: 在 index.vue 中,定义 detailVisiblecurrentIndex 两个 ref 变量。
  4. 连接父子:
    • index.vue 的模板中,监听 LifeIndex@show-detail 事件,触发一个方法来修改 detailVisiblecurrentIndex
    • detailVisiblecurrentIndex 作为 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 上配置几个属性并绑定一个事件即可。

编码流程

  1. index.vue 的模板中,找到最外层的 <scroll-view>
  2. 为其添加 refresher-enabled="true" 来开启下拉刷新功能。
  3. 添加 @refresherrefresh="onRefresh" 来绑定下拉被触发时要执行的方法。
  4. 添加 :refresher-triggered="refreshing",将其同一个名为 refreshingref 变量绑定。这个变量用来告诉 scroll-view 何时开始/结束刷新动画。
  5. 在脚本中,创建 refreshing = ref(false)
  6. 创建 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 文件,就可以通过组件名来调用它们。

编码流程

  1. 创建 /src/components/SvgIcon/SvgIcon.vue 文件。
  2. 在模板中,放置一个 <image> 标签。
  3. 将其 src 属性动态绑定为一个基于 name prop 的路径,例如 :src="/src/static/SvgIcon/${name}.svg"
  4. 添加 sizecolor props 以便从外部控制样式。
点击展开/折叠 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 平台的代码。最后一步是使用微信开发者工具这个“官方上传渠道”,来导入和上传我们的代码包。

编码流程

  1. 配置 manifest.json: 打开项目根目录的 manifest.json 文件,在“微信小程序配置”中,填入从微信公众平台申请到的 AppID
  2. 执行打包命令: 在项目根目录下打开终端,执行 npm run build:mp-weixin
  3. 导入微信开发者工具: 等待打包成功后,会在根目录生成 dist/build/mp-weixin 文件夹。打开微信开发者工具,点击“导入”,项目目录选择这个 mp-weixin 文件夹。
  4. 上传版本: 在微信开发者工具中进行最后的预览和调试,确认无误后,点击右上角的“上传”按钮,填写版本信息即可上传。

上传成功后,即可登录 微信公众平台,在 “版本管理” 中看到你刚上传的版本,后续可提交审核或设为体验版。

4. 课程总结与拓展任务

拓展任务: 实现一个“骨架屏(Skeleton Screen)”加载效果。在 index.vue 中增加一个 isLoading 状态,在 initData 开始时设为 true,结束后设为 false。当 isLoadingtrue 时,用一些灰色的占位块来模拟 CurrentEnvironment 等组件的轮廓,为用户提供一个更平滑的加载预期。