uniapp項目之小兔鮮兒小程序商城(二) 首頁的實現:自定義導航欄,輪撥圖,前臺分類,熱門推薦,猜你喜歡,下拉刷新,骨架屏

文章目錄

      • 零.首頁最終效果
      • 一.自定義導航欄
        • 1.新建`pages/index/components/CustomNavbar.vue`首頁子組件
        • 2.在首頁`pages/index/index.vue`中引入
        • 3.隱藏默認導航欄+修改標題顏色
        • 4.適配不同機型
          • 使用到了uniapp的一個api:獲取屏幕邊界到安全區域的距離
          • 在子組件中使用
      • 二.輪撥圖
        • 1.新建 `src/components/XtxSwiper.vue`全局子組件
        • 2.自動導入通用組件的步驟
        • 3.添加類型聲明
        • 4.輪撥圖的指示點
          • step1:獲取輪撥圖滾動時,當前圖片的索引
          • step2:提供類型聲明
          • step3:把拿到的下標更新給activeIndex
        • 5.獲取輪撥圖的數據
          • 5.1.封裝api
          • 5.2.頁面調用
        • 6.輪撥圖的數據類型
          • 6.1.定義輪撥圖的數據類型:`res.result`
          • 6.2.定義輪撥圖的數據類型:`bannerList`
        • 7.父傳子+動態渲染
      • 三.前臺分類
        • 1.組件封裝
          • 1.1.準備組件
          • 1.2.導入并使用組件
          • 1.3.設置首頁底色
        • 2.獲取數據
          • 2.1.封裝api
          • 2.2.頁面調用
          • 2.3.聲明類型
          • 2.4.父傳子+動態渲染
      • 四.熱門推薦
        • 1.組件封裝
          • 1.1.準備組件
          • 1.2.導入組件
        • 2.獲取數據
          • 2.1.封裝api
          • 2.2.頁面調用
          • 2.3.類型聲明
          • 2.4.父傳子+動態渲染
      • 五.猜你喜歡【難點】
        • 1.組件封裝
          • 1.1.準備組件
          • 1.2.直接使用
        • 2.定義組件類型
        • 3.添加滾動容器`scroll-view`
          • 3.1.滾動容器包裹需要的子組件
          • 3.2.設置滾動的高度
        • 4.獲取數據
          • 4.1.封裝api
          • 4.2.聲明類型
          • 4.3.列表數據的頁面調用
            • 4.3.1.為何不是在首頁中請求獲取數據然后父傳子?
            • 4.3.2.為啥要在組件掛載完畢時調用api?
            • 4.3.3.實現代碼
          • 4.4.列表數據的動態渲染
          • 4.5.什么時候以及如何調用分頁數據?
            • 4.5.1.加載分頁數據的時機以及如何實現
            • 4.5.2.注意事項
            • 4.5.3.實現步驟
          • 4.6.分頁數據的動態渲染
          • 4.6.1.聲明類型
            • 4.6.2.升級`getHomeGoodsGuessLikeAPI`接口
            • 4.6.3.調用子組件并傳入頁面參數
            • 4.6.4.什么時候退出分頁?
      • 六.優化:下拉刷新
        • 1.設置下拉刷新
        • 2.監聽到用戶的下拉行為后需要做些什么?
        • 3.下拉刷新時獲取猜你喜歡組件,獲取之后應該做什么?
      • 七.優化:骨架屏
          • 什么是骨架屏?
          • 如何編寫骨架屏文件?

零.首頁最終效果

在這里插入圖片描述

一.自定義導航欄

要求把默認的導航欄升級成自行以導航欄,并進行樣式適配,做成可復用的組件
在這里插入圖片描述

1.新建pages/index/components/CustomNavbar.vue首頁子組件

并復制靜態結構如下

<script setup lang="ts">
//
</script>
<template><view class="navbar"><!-- logo文字 --><view class="logo"><image class="logo-image" src="@/static/images/logo.png"></image><text class="logo-text">新鮮 · 親民 · 快捷</text></view><!-- 搜索條 --><view class="search"><text class="icon-search">搜索商品</text><text class="icon-scan"></text></view></view>
</template>
<style lang="scss">
/* 自定義導航條 */
.navbar {background-image: url(@/static/images/navigator_bg.png);background-size: cover;position: relative;display: flex;flex-direction: column;padding-top: 20px;.logo {display: flex;align-items: center;height: 64rpx;padding-left: 30rpx;padding-top: 20rpx;.logo-image {width: 166rpx;height: 39rpx;}.logo-text {flex: 1;line-height: 28rpx;color: #fff;margin: 2rpx 0 0 20rpx;padding-left: 20rpx;border-left: 1rpx solid #fff;font-size: 26rpx;}}.search {display: flex;align-items: center;justify-content: space-between;padding: 0 10rpx 0 26rpx;height: 64rpx;margin: 16rpx 20rpx;color: #fff;font-size: 28rpx;border-radius: 32rpx;background-color: rgba(255, 255, 255, 0.5);}.icon-search {&::before {margin-right: 10rpx;}}.icon-scan {font-size: 30rpx;padding: 15rpx;}
}
</style>
2.在首頁pages/index/index.vue中引入
<script setup lang="ts">
//引入子組件CustomNavBar
import CustomNavbar from './components/CustomNavbar.vue'
</script>
<template><!-- 使用子組件 --><CustomNavbar /><view class="index">index我是首頁</view>
</template>
3.隱藏默認導航欄+修改標題顏色
//pages.json{"path": "pages/index/index","style": {"navigationBarTitleText": "首頁","navigationStyle": "custom",//隱藏默認導航欄"navigationBarTextStyle": "white",//修改標題顏色}}
4.適配不同機型

對安全區域進行樣式適配

使用到了uniapp的一個api:獲取屏幕邊界到安全區域的距離
//獲取屏幕邊界到安全區域的距離
const { safeAreaInsets } = uni.getSystemInfoSync()
在子組件中使用
//使用uniapp的api,獲取屏幕邊界到安全區域的距離
const { safeAreaInsets } = uni.getSystemInfoSync()
......<!-- 把該距離變量動態綁定style,可以實現導航條跟隨屏幕劉海區域變化 --><view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }"><!-- logo文字 --></view>

二.輪撥圖

輪撥圖不僅在首頁中使用到,在商品分類頁中也有,因此也封裝成一個通用組件

1.新建 src/components/XtxSwiper.vue全局子組件

并準備靜態結構如下:

<script setup lang="ts">
import { ref } from 'vue'
const activeIndex = ref(0)
</script>
<template><vi ew class="carousel">
使用到了小程序的標簽:swiper<swiper :circular="true" :autoplay="false" :interval="3000"><swiper-item>點擊圖片跳轉:<navigator url="/pages/index/index" hover-class="none" class="navigator"><imagemode="aspectFill"class="image"src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/slider_1.jpg"></image></navigator></swiper-item><swiper-item><navigator url="/pages/index/index" hover-class="none" class="navigator"><imagemode="aspectFill"class="image"src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/slider_2.jpg"></image></navigator></swiper-item><swiper-item><navigator url="/pages/index/index" hover-class="none" class="navigator"><imagemode="aspectFill"class="image"src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/slider_3.jpg"></image></navigator></swiper-item></swiper><!-- 指示點(自定義) --><view class="indicator"><textv-for="(item, index) in 3":key="item"class="dot":class="{ active: index === activeIndex }"></text></view></view>
</template>
<style lang="scss">
/* 輪播圖 */
.carousel {height: 280rpx;position: relative;overflow: hidden;transform: translateY(0);background-color: #efefef;.indicator {position: absolute;left: 0;right: 0;bottom: 16rpx;display: flex;justify-content: center;.dot {width: 30rpx;height: 6rpx;margin: 0 8rpx;border-radius: 6rpx;background-color: rgba(255, 255, 255, 0.4);}.active {background-color: #fff;}}.navigator,.image {width: 100%;height: 100%;}
}
</style>
2.自動導入通用組件的步驟

(參考之前在pages.json中對uni-ui的配置)

//pages.json
{//組件自動導入"easycom": {//是否開啟自動掃描"autoscan": true,//以正則方式自定義組件的匹配規則(添加后需重啟服務器才能生效)"custom": {// 之前的:uni-ui 規則如下配置"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",//新增的:以Xtx開頭的組件"^xtx(.*)": "@/components/xtx$1.vue"}},"pages": [......],
}

驗證:在首頁中不導入直接使用輪撥圖組件

<script setup lang="ts">
//引入子組件CustomNavBar
import CustomNavbar from './components/CustomNavbar.vue'
</script>
<template><!-- 使用子組件 --><CustomNavbar /><!-- 不用導入,直接使用輪撥圖通用子組件 --><XtxSwiper /><view class="index">index我是首頁</view>
</template>
3.添加類型聲明

此時自動導入的 <XtxSwiper />和手動導入的<CustomNavbar />仍有區別----沒有類型聲明

知識點:為已有的js文件提供類型聲明,關鍵字declare

// src/types/components.d.ts//導入輪撥圖組件
import XtxSwiper from './XtxSwiper.vue'
//擴展全局組件類型,聲明全局組件的類型
declare module '@vue/runtime-core' {// 注意:此處更新了要寫成:declare module 'vue'export interface GlobalComponents {XtxSwiper: typeof XtxSwiper//typeof拿到組件的類型,然后賦值給全局組件類型}
}

注:declare module '@vue/runtime-core'應為declare module 'vue'

4.輪撥圖的指示點

此時的指示點僅是靜態結構

    <!-- 指示點(自定義) --><view class="indicator"><textv-for="(item, index) in 3":key="item"class="dot":class="{ active: index === activeIndex }"//動態綁定.active類實現高亮==>通過比較index===activeIndex></text></view>

最終目標是讓指示點跟著輪撥圖的切換而切換

step1:獲取輪撥圖滾動時,當前圖片的索引

uniapp官網>Swiper>@change事件中event.detail.current就是下標

//當輪撥圖滾動時觸發
function onChange(e) {//此時提示e為any類型==>缺少類型聲明console.log(e) //e.current為當前輪播圖的索引
}<!-- 指示點(自定義) --><view class="indicator"><textv-for="(item, index) in 3"@change='onChange':key="item"class="dot":class="{ active: index === activeIndex }"//動態綁定.active類實現高亮==>通過比較index===activeIndex></text></view>
step2:提供類型聲明
const activeIndex=ref(0)
const onChange: UniHelper.SwiperOnChange = (e) => {console.log(e) //e.current為當前輪播圖的索引 activeIndex.value=e.detail?.current
}
step3:把拿到的下標更新給activeIndex
activeIndex.value=e.detail?.current此時報錯:不能將"number|undefine"分配給"number"
因為detail后面加了可選符,當沒有時可能為undefined解決方式:把可選鏈調整為非空斷言
activeIndex.value=e.detail!.current
5.獲取輪撥圖的數據

當前輪撥圖的圖片使用的是靜態資源,現在優化為:從后臺獲取數據并動態渲染

5.1.封裝api

在這里插入圖片描述
新建services/home.js

import { http } from "@/utils/http"
const getHomeBannerAPI= (distributionSite=1)=>{//調用http中封裝的發起請求的函數(基于uni.request)return http({methods:'GET',url:'/home/banner',data:{distributionSite}})
}
5.2.頁面調用
//index.vue
import {getHomeBannerAPI} from '@/services/home.ts'
import {onLoad} from '@dcloundio/uni-app'
const bannerList=ref([])
//先封裝一個調用函數
const getHomeBannerData=async()=>{const res=await getHomeBannerAPI()const bannerList=res.result//缺乏類型聲明
}onLoad(()=>{//記得這個鉤子也需導入//頁面加載時調用該函數getHomeBannerData()
})

此時:
bannerList和res.result都缺乏類型聲明

6.輪撥圖的數據類型
6.1.定義輪撥圖的數據類型:res.result
  • 復制指定類型文件并粘貼到新建的types/home.d.ts如下:
/*首頁-廣告區域數據類型 */
export type BannerItem = {/** 跳轉鏈接 */hrefUrl: string/** id */id: string/** 圖片鏈接 */imgUrl: string/** 跳轉類型 */type: number
}

注:code,msg,result的類型不用再聲明了,已經有了
前面的代碼:

interface Data<T> {code: string //狀態碼:'1"msg: string //提示信息:'請求成功'result: T //核心數據類型:{{}.{},{},{},{}}
}

在這里插入圖片描述

  • service/home.ts中,導入types/home.d.ts中的BannerItem
//service/home.ts
import type {BannerItem} from '@/types/home'
import { http } from "@/utils/http"
const getHomeBannerAPI= (distributionSite=1)=>{//調用http中封裝的發起請求的函數(基于uni.request)return http<BannerItem[]>({//指定類型為一個對象數組methods:'GET',url:'/home/banner',data:{distributionSite}})
}
6.2.定義輪撥圖的數據類型:bannerList
//index.vue
import type {BannerItem} from '@/types/home'
const bannerList=ref<BannerItem[]>([])
7.父傳子+動態渲染

此時數據是在首頁中發起請求并獲取的,而輪撥圖是一個封裝子組件,因此需要父傳子

  • 父組件:pages/index/index.vue
<XtxSwiper :list='bannerList'/>
  • 子組件:src/components/XtxSwiper.vue
<script setup lang="ts">
import { ref } from 'vue'
import type { BannerItem } from '@/types/home'
// 子組件接收自定義屬性list
defineProps<{list: BannerItem[]
}>()
const activeIndex = ref(0)
//當輪撥圖滾動時觸發
const onChange: UniHelper.SwiperOnChange = (e) => {console.log(e) //e.current為當前輪播圖的索引activeIndex.value = e.detail!.current //非空斷言:回調參數是可選鏈式的
}
</script><template><view class="carousel"><swiper :circular="true" :autoplay="false" :interval="3000"><!-- 動態渲染:輪撥圖 --><swiper-item v-for="item in list" :key="item.id"><navigator url="/pages/index/index" hover-class="none" class="navigator"><image mode="aspectFill" class="image" :src="item.imgUrl"></image></navigator></swiper-item></swiper><!-- 指示點 --><view class="indicator"><!-- 動態渲染:指示點 --><textv-for="(item, index) in list":key="item.id"@change="onChange"class="dot":class="{ active: index === activeIndex }"></text></view></view>
</template>

三.前臺分類

在這里插入圖片描述

前臺分類也要封裝成獨立子組件,但是與輪撥圖不同的是,輪撥圖除了在首頁中使用之外,在分類頁中也用上,
但是前臺分類僅僅在首頁中用到,因此可以寫在index/components/CategoryPanel.vue中.(類似自定義導航欄)

1.組件封裝
1.1.準備組件

準備靜態結構如下:

<script setup lang="ts">
//
</script>
<template><view class="category"><navigator//導航鏈接class="category-item"hover-class="none"url="/pages/index/index"v-for="item in 10"//列表循環:key="item">內部結構:圖片+文本<imageclass="icon"src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/nav_icon_1.png"></image><text class="text">居家</text></navigator></view>
</template>
<style lang="scss">
/* 前臺類目 */
.category {margin: 20rpx 0 0;padding: 10rpx 0;display: flex;flex-wrap: wrap;min-height: 328rpx;.category-item {width: 150rpx;display: flex;justify-content: center;flex-direction: column;align-items: center;box-sizing: border-box;.icon {width: 100rpx;height: 100rpx;}.text {font-size: 26rpx;color: #666;}}
}
</style>
1.2.導入并使用組件
//index.vue
......
//導入分類面板組件
import CategoryPanel from './components/CategoryPanel.vue'<!-- 自定義導航欄 --><CustomNavbar /><!-- 輪撥圖通用子組件 --><XtxSwiper :list="bannerList" /><!-- 分類面板 --><CategoryPanel />
1.3.設置首頁底色
//index.vue
<style lang="scss">
//更改頁面的底色
page {background-color: #f5f5f5;
}
</style>
2.獲取數據

在這里插入圖片描述

2.1.封裝api
// services/home.ts
export const getHomeCategoryAPI = () => {return http({method: 'GET',url: '/home/category/mutli',})
}
2.2.頁面調用
//index.vueimport { getHomeBannerAPI, getHomeCategoryAPI } from '@/services/home.ts'
const categoryList = ref([])
// 獲取首頁分類數據
const getHomeCategoryData = async () => {const res = await getHomeCategoryAPI()categoryList.value = res.result
}
onLoad(() => {getHomeBannerData()getHomeCategoryData()
})

*此時categoryList.valueres.result都沒有類型聲明

2.3.聲明類型
  • 粘貼分類數據的聲明類型到已有的home.d.ts文件中
/** 首頁-前臺類目數據類型 */
export type CategoryItem = {/** 圖標路徑 */icon: string/** id */id: string/** 分類名稱 */name: string
}
  • res.result指定類型
// services/home.ts
import type {CategoryItem} from '@/types/home'export const getHomeCategoryAPI = () => {return http<CategoryItem[]>({method: 'GET',url: '/home/category/mutli',})
}
  • categoryList指定類型
import type {CategoryItem} from '@/types/home'
const categoryList = ref<CategoryItem[]>([])
// 獲取首頁分類數據
const getHomeCategoryData = async () => {const res = await getHomeCategoryAPI()categoryList.value = res.result
}
2.4.父傳子+動態渲染

父組件:index.vue

<CategoryPanel :list='categoryList' />

子組件:CategoryPanel.vue

<script setup lang="ts">
import type { CategoryItem } from '@/types/home'
// 定義 props 接收數據
defineProps<{list: CategoryItem[]
}>()
</script>
<template><view class="category"><navigatorclass="category-item"hover-class="none"url="/pages/index/index"v-for="item in list":key="item.id"><image class="icon" :src="item.icon"></image><text class="text">{{ item.name }}</text></navigator></view>
</template>

四.熱門推薦

在這里插入圖片描述
后端根據用戶的消費習慣等信息向用戶推薦的一系列商品,前端負責展示這些商品展示給用戶

1.組件封裝
1.1.準備組件

創建index/components/HotPanel.vue作為首頁的子組件,并準備靜態結構如下:

<script setup lang="ts">
//
</script>
<template><!-- 推薦專區 --><view class="panel hot"><view class="item" v-for="item in 4" :key="item"><view class="title"><text class="title-text">特惠推薦</text><text class="title-desc">精選全攻略</text></view><navigator hover-class="none" url="/pages/hot/hot" class="cards"><imageclass="image"mode="aspectFit"src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/goods_small_1.jpg"></image><imageclass="image"mode="aspectFit"src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/goods_small_2.jpg"></image></navigator></view></view>
</template>
<style lang="scss">
/* 熱門推薦 */
.hot {display: flex;flex-wrap: wrap;min-height: 508rpx;margin: 20rpx 20rpx 0;border-radius: 10rpx;background-color: #fff;.title {display: flex;align-items: center;padding: 24rpx 24rpx 0;font-size: 32rpx;color: #262626;position: relative;.title-desc {font-size: 24rpx;color: #7f7f7f;margin-left: 18rpx;}}.item {display: flex;flex-direction: column;width: 50%;height: 254rpx;border-right: 1rpx solid #eee;border-top: 1rpx solid #eee;.title {justify-content: start;}&:nth-child(2n) {border-right: 0 none;}&:nth-child(-n + 2) {border-top: 0 none;}.image {width: 150rpx;height: 150rpx;}}.cards {flex: 1;padding: 15rpx 20rpx;display: flex;justify-content: space-between;align-items: center;}
}
</style>
1.2.導入組件
//導入熱門推薦組件
import HotPanel from './components/HotPanel.vue'<!-- 熱門推薦 --><HotPanel :list="hotList" />
2.獲取數據

在這里插入圖片描述

2.1.封裝api
    <!-- 熱門推薦 --><HotPanel :list="hotList" />
2.2.頁面調用
//index.vueimport type { BannerItem, CategoryItem, HotItem } from '@/types/home'
import { getHomeBannerAPI, getHomeCategoryAPI, getHomeHotAPI } from '@/services/home.ts'
const hotList = ref<HotItem[]>([])
// 獲取首頁熱門推薦數據
const getHomeHotData = async () => {const res = await getHomeHotAPI()hotList.value = res.result
}
onLoad(() => {getHomeBannerData()getHomeCategoryData()getHomeHotData()
})

*此時hotList.valueres.result還沒有聲明類型

2.3.類型聲明
  • 粘貼類型聲明文件到home.d.ts
//types/home.d.ts/** 首頁-熱門推薦數據類型 */
export type HotItem = {/** 說明 */alt: string/** id */id: string/** 圖片集合[ 圖片路徑 ] */pictures: string[]/** 跳轉地址 */target: string/** 標題 */title: string/** 推薦類型 */type: string
}
  • res.result提供類型聲明
//home.ts
import type { BannerItem, CategoryItem, HotItem, GuessItem } from '@/types/home'const hotList = ref<HotItem[]>([])
// 獲取首頁熱門推薦數據
export const getHomeHotAPI = () => {return http<HotItem[]>({method: 'GET',url: '/home/hot/mutli',})
}
  • hotList提供類型聲明
//index.vue
import type { BannerItem, CategoryItem, HotItem } from '@/types/home'
const hotList = ref<HotItem[]>([])
2.4.父傳子+動態渲染

父組件:index.vue

  <!-- 熱門推薦 --><HotPanel :list="hotList" />

子組件:src\pages\index\components\HotPanel.vue

<script setup lang="ts">
import type { HotItem } from '@/types/home'
// 定義 props 接收數據
defineProps<{list: HotItem[]
}>()
</script>
<template><!-- 推薦專區 --><view class="panel hot"><view class="item" v-for="item in list" :key="item.id"><view class="title"><text class="title-text">{{ item.title }}</text><text class="title-desc">{{ item.alt }}</text></view><navigator hover-class="none" :url="`/pages/hot/hot?type=${item.type}`" class="cards">
<!-- 動態渲染:第二層v-for --><imagev-for="src in item.pictures":key="src"class="image"mode="aspectFit":src="src"></image></navigator></view></view>
</template>

五.猜你喜歡【難點】

后端根據用戶的瀏覽記錄等信息向用戶隨機推薦的一系列商品,前端負責把商品在多個頁面中展示。

猜你喜歡要封裝成全局通用子組件,因為多個頁面(購物車,結算頁,首頁)用到該組件

1.組件封裝
1.1.準備組件

新建src/components/XtxGuess.vue
并粘貼靜態結構代碼如下:

<script setup lang="ts">
//
</script>
<template><!-- 猜你喜歡 --><view class="caption"><text class="text">猜你喜歡</text></view><view class="guess"><navigatorclass="guess-item"v-for="item in 10":key="item":url="`/pages/goods/goods?id=4007498`"><imageclass="image"mode="aspectFill"src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/goods_big_1.jpg"></image><view class="name"> 德國THORE男表 超薄手表男士休閑簡約夜光石英防水直徑40毫米 </view><view class="price"><text class="small"></text><text>899.00</text></view></navigator></view><view class="loading-text"> 正在加載... </view>
</template>
<style lang="scss">
:host {display: block;
}
/* 分類標題 */
.caption {display: flex;justify-content: center;line-height: 1;padding: 36rpx 0 40rpx;font-size: 32rpx;color: #262626;.text {display: flex;justify-content: center;align-items: center;padding: 0 28rpx 0 30rpx;&::before,&::after {content: '';width: 20rpx;height: 20rpx;background-image: url(@/static/images/bubble.png);background-size: contain;margin: 0 10rpx;}}
}
/* 猜你喜歡 */
.guess {display: flex;flex-wrap: wrap;justify-content: space-between;padding: 0 20rpx;.guess-item {width: 345rpx;padding: 24rpx 20rpx 20rpx;margin-bottom: 20rpx;border-radius: 10rpx;overflow: hidden;background-color: #fff;}.image {width: 304rpx;height: 304rpx;}.name {height: 75rpx;margin: 10rpx 0;font-size: 26rpx;color: #262626;overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical;}.price {line-height: 1;padding-top: 4rpx;color: #cf4444;font-size: 26rpx;}.small {font-size: 80%;}
}
// 加載提示文字
.loading-text {text-align: center;font-size: 28rpx;color: #666;padding: 20rpx 0;
}
</style>
1.2.直接使用

類似輪撥圖組件,無需導入直接使用

//獲取猜你喜歡的組件實例
import type { XtxGuessInstance } from '@/types/component'
const guessRef = ref<XtxGuessInstance>()<!-- 猜你喜歡 -->
<XtxGuess />
2.定義組件類型
// types/components.d.ts
import XtxSwiper from '@/components/XtxSwiper.vue'
import XtxGuess from '@/components/XtxGuess.vue'
declare module 'vue' {export interface GlobalComponents {XtxSwiper: typeof XtxSwiperXtxGuess: typeof XtxGuess}
}
// 組件實例類型
export type XtxGuessInstance = InstanceType<typeof XtxGuess>
3.添加滾動容器scroll-view

在這里插入圖片描述

3.1.滾動容器包裹需要的子組件

使用滾動容器scroll-view把需要滾動的子組件占位符(除自定義導航欄都滾動)包起來

  <!-- 自定義導航欄 --><CustomNavbar /><scroll-view class='scroll-view' scroll-y><!-- 輪撥圖通用子組件 --><!-- 父傳子:自定義屬性為list --><XtxSwiper :list="bannerList" /><!-- 分類面板 --><CategoryPanel :list="categoryList" /><!-- 熱門推薦 --><HotPanel :list="hotList" /><!-- 猜你喜歡 --><XtxGuess /></scroll-view>
3.2.設置滾動的高度

在這里插入圖片描述

滾動的高度=頁面高度-自定義導航欄高度

  • 頁面高度:
page {//更改頁面的底色background-color: #f5f5f5;//設置頁面高度為100%height: 100%;// 設置彈性布局和排列方向display: flex;flex-direction: column;
}
  • 滾動高度:
.scroll-view{flex:1//height:0//若還滾動不了就加上這句
}
4.獲取數據

在這里插入圖片描述

4.1.封裝api
// src/services/home.ts
/**
* 猜你喜歡-小程序
*/
export const getHomeGoodsGuessLikeAPI = (data?: PageParams) => {return http<PageResult<GuessItem>>({method: 'GET',url: '/home/goods/guessLike',data,})
}
4.2.聲明類型

猜你喜歡后臺返回的數據類型中大致可以分為:列表數據,總條數,當前頁數,其中:

列表數據是根據調用的頁面而相應變化的,考慮將其抽離出來聲明為泛型數據
分頁數據在分頁功能中調用,不論哪個頁面調用,都是相同的數據類型,因此同樣可以抽離出來并聲明

  • 猜你喜歡的商品數據類型

放在已存在的src/types/home.d.ts 文件中

//src/types/home.d.ts /** 猜你喜歡-商品類型 */
export type GuessItem = {/** 商品描述 */desc: string/** 商品折扣 */discount: number/** id */id: string/** 商品名稱 */name: string/** 商品已下單數量 */orderNum: number/** 商品圖片 */picture: string/** 商品價格 */price: number
}
  • 分頁:分頁結果+分頁參數的數據類型

新建src/types/global.d.ts

/** 通用分頁結果類型 */
export type PageResult<T> = {/** 列表數據 */items: T[]/** 總條數 */counts: number/** 當前頁數 */page: number/** 總頁數 */pages: number/** 每頁條數 */pageSize: number
}/** 通用分頁參數類型 */
export type PageParams = {/** 頁碼:默認值為 1 */page?: number/** 頁大小:默認值為 10 */pageSize?: number
}
4.3.列表數據的頁面調用
4.3.1.為何不是在首頁中請求獲取數據然后父傳子?

因為這個組件多次被復用了,然后數據又都是一樣的,
所以放在組件內部中,可以一次請求就能完成數據的展示。

4.3.2.為啥要在組件掛載完畢時調用api?

使用組件生命周期鉤子而非頁面生命周期函數的原因是:僅調用一次,而不是在每個用到"猜你喜歡"功能的頁面中各調用一次
使用組件生命周期鉤子中的onMounted的原因是:等dom樹生成之后才能渲染,不然獲取到的數據沒有dom元素渲染

4.3.3.實現代碼
//XtxGuess.vue
import type {GuessItem} from '@/type/home.d.ts'
const guessList=ref<GuessItem[]>([])
//獲取猜你喜歡數據
const getHomeGoodsGuessLikeData=async()=>{const res=await getHomeGoodsGuessLikeAPI()guessList.value=res.result.items//為啥要加items:查看通用分頁結果類型
}
//組件掛載完畢
onMounted(){getHomeGoodsGuessLikeData()
}
4.4.列表數據的動態渲染

不用父傳子:用goodsList直接在子組件Xtxguess.vue中動態渲染

<!-- 猜你喜歡 --><view class="caption"><text class="text">猜你喜歡</text></view><view class="guess"><navigatorclass="guess-item"v-for="item in guessList":key="item.id":url="`/pages/goods/goods?id=4007498`"><imageclass="image"mode="aspectFill":src="item.picture"></image><view class="name">{{item.name}}</view><view class="price"><text class="small"></text><text>{{item.price}}</text></view></navigator></view>
4.5.什么時候以及如何調用分頁數據?

在這里插入圖片描述

4.5.1.加載分頁數據的時機以及如何實現

當滾動容器scroll-view滾動觸底的時候,才開始加載分頁數據

此時觸發"加載分頁數據"的事件是綁定在首頁的scroll-view上的,
數據的獲取和加載是在猜你喜歡子組件XtxGuess當中的,

為了實現這業務邏輯(父組件調用子組件的方法)–需要用到模板引用(ref標識),它可以獲取當前組件的DOM對象和其他組件的實例對象

4.5.2.注意事項

???? 1)當前是TypeScript項目,因此還需要指定組件實例的類型
???? 2)在setup語法糖是,所有子組件默認是封閉的,需要手動設置暴露子組件

4.5.3.實現步驟
  • step1:滾動容器綁定滾動觸底事件
  • step2:在事件中調用子組件獲取分頁數據的方法
// pages/index/index.vue
<script setup lang="ts">
import type { XtxGuessInstance } from '@/types/components'
import { ref } from 'vue'
// 獲取猜你喜歡組件實例
const guessRef = ref<XtxGuessInstance>()
// 滾動觸底事件
const onScrolltolower = () => {guessRef.value?.getMore()
}
</script>
<template><!-- 滾動容器 --><scroll-view scroll-y @scrolltolower="onScrolltolower">......<!-- 猜你喜歡 --><XtxGuess ref="guessRef" /></scroll-view>
</template>
  • step3:給ref指定類型

使用到了TS的方法InstanceType,用于獲取組件類型

//types/components.d.ts
//組件實例類型
export type XtxGuessInstance=InstanceType<typeof XtxGuess>
  • step4:暴露子組件的獲取數據的方法
//XtxGuess.vue
defineExpose({getMore:getHomeGoodsGuessLikeData//可以不暴露原有方法名,而是自定義
})
4.6.分頁數據的動態渲染

業務邏輯:
在已封裝的"獲取猜你喜歡"的接口api函數中getHomeGoodsGuessLikeAPI,把添加分類的參數添加進去并且為其指定類型,
然后調用該函數并傳參,獲得的分頁數據追加到guessList數組里面
最后對頁碼進行累加,目的是為下一次傳參時,可以傳入不同的數據,即下一頁的數據

4.6.1.聲明類型

(用到了通用分頁參數類型,上面已經聲明過)

4.6.2.升級getHomeGoodsGuessLikeAPI接口

在這里插入圖片描述

// src/services/home.ts
/**
* 猜你喜歡-小程序
*/
export const getHomeGoodsGuessLikeAPI = (data?: PageParams) => {return http<PageResult<GuessItem>>({method: 'GET',url: '/home/goods/guessLike',data,//導入并指定類型<==在子組件調用傳參的時候定義一個分頁參數pageParams})
}
4.6.3.調用子組件并傳入頁面參數
// 分頁參數
const pageParams: Required<PageParams> = {//ts的工具函數page: 1,pageSize: 10,
}
// 獲取猜你喜歡數據
const getHomeGoodsGuessLikeData = async () => {// 退出分頁判斷if (finish.value === true) {return uni.showToast({ icon: 'none', title: '沒有更多數據~' })}const res = await getHomeGoodsGuessLikeAPI(pageParams)// 數組追加guessList.value.push(...res.result.items)//數組追加到另一個數組:拓展運算符// 分頁條件if (pageParams.page < res.result.pages) {//要不要加value// 頁碼累加的條件是頁碼<總頁數pageParams.page++//當前是可選的,當沒有數據時會報錯,把其類型改為必選Required} else {finish.value = true//標記結束}
}
4.6.4.什么時候退出分頁?

數據總數和總頁數是有限的,
由此判斷:當頁碼小于總頁數時,可以繼續進行頁碼累加,否則標記結束

//已結束的標記
const finish=ref(false)替換"正在加載"的文字:
<view class="loading-text"> {{finish?"沒有更多數據~":"正在加載..."}} </view>

六.優化:下拉刷新

1.設置下拉刷新

下拉刷新使用到了uni-ui的scroll-view組件上的如下屬性:

  • 配置 refresher-enabled 屬性,開啟下拉刷新交互
  • 監聽 @refresherrefresh 事件,判斷用戶是否執行了下拉操作
  • 配置 refresher-triggered 屬性,關閉下拉狀態
//index .vue<!-- 滾動容器 -->
<scroll-viewrefresher-enabled@refresherrefresh="onRefresherrefresh":refresher-triggered="isTriggered"class="scroll-view"scroll-y
>
........
</scroll-view>
2.監聽到用戶的下拉行為后需要做些什么?
  • 刷新:輪撥圖,前臺分類,當前熱賣,猜你喜歡
  • 開啟和關閉下拉刷新的動畫
//index.vue//監聽用戶的下拉行為
const onRefreshrefresh = async () => {//開啟動畫isTriggered.value = true//先重置"猜你喜歡"組件的數據guessRef.value?.resetData()//刷新數據,重新獲取數據==>確保全部加載完畢后關閉動畫await Promise.all([getHomeBannerData(),getHomeCategoryData(),getHomeHotData(),guessRef.value?.getMore(), //再調用猜你喜歡的組件的獲取更多數據的方法])//關閉動畫isTriggered.value = false
}
3.下拉刷新時獲取猜你喜歡組件,獲取之后應該做什么?
  • 重置頁碼
  • 重置列表
  • 重置結束標記

(以上數據都是存在子組件中,首頁需要使用ref標識來調用)

  • 數據重置后再加載數據

子組件:XtxGuess.vue

const resetData = () => {
//重置數據
const resetData = () => {
pageParams.page = 1
guessList.value = []
finish.value = false
}
// 暴露方法
defineExpose({
resetData,
getMore: getHomeGoodsGuessLikeData,
})

父組件:index.vue

//index.vue//監聽用戶的下拉行為
const onRefreshrefresh = async () => {//開啟動畫isTriggered.value = true//先重置"猜你喜歡"組件的數據guessRef.value?.resetData()//刷新數據,重新獲取數據==>確保全部加載完畢后關閉動畫await Promise.all([getHomeBannerData(),getHomeCategoryData(),getHomeHotData(),guessRef.value?.getMore(), //再調用猜你喜歡的組件的獲取更多數據的方法])//關閉動畫isTriggered.value = false
}

*使用Promise.all的優勢是減少等待時間:
在這里插入圖片描述

七.優化:骨架屏

在這里插入圖片描述

什么是骨架屏?

骨架屏是頁面加載出來之前的空白頁面

骨架屏顯示的邏輯:數據是否在加載中

如何編寫骨架屏文件?
  • 微信開發者工具可以快速生成骨架屏的結構和樣式:
    微信開發者工具(模擬器)>(右上角)頁面信息>生成骨架屏>確認生成index.skeleton.wxmlindex.skeleton.wxss兩個文件

  • 找到這兩個文件,轉換為vue組件即可
    新建pages/index/components/PageSkeleton.vue

刪掉其他多余代碼只保留:輪撥圖,前臺分類,猜你喜歡
代碼略
  • 首頁調用子組件
<!-- 自定義導航欄 --><CustomNavbar /><scroll-view class='scroll-view' scroll-y><PageSkeleton v-if="true" /><template v-else><!-- 輪撥圖通用子組件 --><!-- 父傳子:自定義屬性為list --><XtxSwiper :list="bannerList" /><!-- 分類面板 --><CategoryPanel :list="categoryList" /><!-- 熱門推薦 --><HotPanel :list="hotList" /><!-- 猜你喜歡 --><XtxGuess /></template></scroll-view>
  • 判斷骨架屏的加載時機
const isLoading=ref(false)
onLoad(() => {isLoading.value=true//getHomeBannerData()//getHomeCategoryData()//getHomeHotData()Promise.all([getHomeBannerData()getHomeCategoryData()getHomeHotData()])isLoading.value=false  
})

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/84092.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/84092.shtml
英文地址,請注明出處:http://en.pswp.cn/web/84092.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RustDesk自建遠程服務器

目錄 服務端 環境linux 安裝 開放端口 客戶端配置 下載客戶端 安裝后配置網絡 參考&#xff1a;RustDesk自建遠程服務器_rustdesk自建服務器-CSDN博客 服務端 環境 linux 安裝 下載 wget https://github.com/rustdesk/rustdesk-server/releases/download/1.1.8-2/r…

【Axure高保真原型】圖片伸縮展示列表

今天和大家分享圖片伸縮展示列表的3個原型案例&#xff0c;模版都是用中繼器制作的&#xff0c;所以使用也很方便&#xff0c;在中繼器表格里導入對應的圖片&#xff0c;即可自動生成交互效果&#xff0c;具體效果可以點擊下方視頻觀看或打開下方預覽地址查看哦 【原型效果】 …

keil新建工程文件結構和每個文件的作用解析(標準庫版本)

通過網盤分享的文件:STM32工程模板 鏈接:https://pan.baidu.com/s/1YPFgXu1kwuwsCVxrXFSjZg?pwd=1111 提取碼: 1111 --來自百度網盤超級會員v5的分享 這個工程模版是來源于B站江科大的模版,每個人搭建工程文件結構不一樣,僅供參考。 工程文件目錄結構如圖所示 1、DebugC…

【AI論文】Saffron-1:LLM安全保證的推理縮放范例

摘要&#xff1a;現有的安全保證研究主要集中在培訓階段的協調&#xff0c;以向LLM灌輸安全行為。 然而&#xff0c;最近的研究表明這些方法容易受到各種越獄攻擊。 同時&#xff0c;推理擴展顯著提高了LLM推理能力&#xff0c;但在安全保證方面仍未得到探索。 為了解決這一差距…

LLM 支持的基于意圖的分類 網絡釣魚電子郵件

大家讀完覺得有幫助記得關注和點贊&#xff01;&#xff01;&#xff01; 抽象 網絡釣魚攻擊仍然是現代網絡安全的重大威脅&#xff0c;因為它們成功地欺騙了人類和旨在保護他們的防御機制。傳統的檢測系統主要關注用戶在收件箱中看不到的電子郵件元數據。此外&#xff0c;這些…

C++新特性技術發展路徑和時間

C 的新特性發展路徑和時間線是一個持續演進的過程。以下是一個概覽&#xff0c;涵蓋了主要的 C 標準及其關鍵特性&#xff0c;以及它們發布的時間&#xff1a; C 標準版本及發布時間線: C98 (ISO/IEC 14882:1998): 第一個正式的 C 標準。 發布時間: 1998年關鍵特性: 標準模板庫…

OpenAI 如何在激烈的AI人才爭奪戰中搶占先機?

在這個快速發展的人工智能時代&#xff0c;OpenAI 正處于一個至關重要的發展階段。隨著技術的不斷進步&#xff0c;人工智能行業的競爭日益激烈。如何在這場巨大的競爭中立于不敗之地&#xff0c;成為了每一個AI公司的核心挑戰。就在近日&#xff0c;OpenAI 的新招聘主管華金?…

【Java學習筆記】Java繪圖基礎

Java繪圖基礎 一、Java 坐標體系 1. 像素的概念 計算機在屏幕上顯示的內容都是由屏幕上的每一個像素組成的 例如&#xff0c;計算機顯示器的分辨率是 800600&#xff0c;表示計算機屏幕上的每一行由 800 個點組成&#xff0c;共有 600 行&#xff0c;整個計算機屏幕共有 480…

資深Java工程師的面試題目(一)基礎到高級概述

以下是幾道面向資深Java工程師的面試題目&#xff0c;涵蓋了從基礎知識到高級概念及參考答案&#xff1a; 1. Java內存模型和垃圾回收 問題: 請解釋一下Java的內存模型&#xff0c;并描述不同類型的內存區域。如何選擇適合特定應用需求的垃圾收集器&#xff1f;請比較幾種常…

Spring Retry:優雅地實現方法重試機制

前言 在實際的軟件開發中&#xff0c;尤其是在涉及網絡請求、數據庫操作或外部服務調用的場景下&#xff0c;我們常常會遇到一些臨時性故障&#xff08;Transient Failures&#xff09;&#xff0c;例如網絡波動、數據庫連接超時、第三方 API 暫時不可用等。面對這些問題&…

Mysql報錯

1.權限問題 MySQL 認證協議不兼容問題解決方案 這個錯誤表明您的 MySQL 客戶端與服務器要求的認證協議不兼容&#xff0c;通常發生在 MySQL 8.0 服務器與舊版客戶端之間。 nested exception is org.apache.ibatis.exceptions.PersistenceException: Error querying database. …

小米汽車5月交付量超過28000臺,與上月持平

6月1日&#xff0c;小米汽車公布5月交付數據&#xff0c;2025年5月&#xff0c;小米汽車交付量超過28000臺&#xff0c;4月官方披露的交付數據也為28000臺。 此外&#xff0c;小米汽車5月新增29家門店&#xff0c;全國82城已有298家門店&#xff1b;6月計劃新增37家門店&#x…

嚴格一致性模型

SC 的第二點約束 :store 必須被 所有(包括自身)執行流 同時看到 ,但是不要求寫操作“立即”對其他處理器可見&#xff1b;允許寫操作延遲一會兒被其他核觀察到。 而 嚴格一致性模型,包括1. store 必須被 所有(包括自身)執行流 同時看到2. 看到的時間 必須是 某個處理器完成寫操…

結合 STM32CubeMX 使用 FreeRTOS 實時操作系統

前言 在STM32CubeMX軟件出現以后&#xff0c;創建嵌入式項目變得簡潔了許多&#xff0c;開發者無需重復編寫MCU的外設初始化配置&#xff0c;只需在STM32CubeMX軟件中動動鼠標配置完畢&#xff0c;就可以自動生成基于HAL/LL庫的Keil項目文件&#xff0c;提高了開發效率。 最近想…

一致性框架:供應鏈分布式事務問題解決方案

來源&#xff1a;得物技術 一、前言 二、一致性理論基礎 1. 一致性模型概述 2. 最終一致性的必要性 三、供應鏈一致性框架總體架構 1. 一致性框架的核心功能 2. 一致性框架整體框架 3. 一致性框架整體流程 四、一致性框架實現原理 1. 核心組件設計 2. 異步執行實現原…

民國大模型:智能重構下的亂世覺醒與文明轉型

引言&#xff1a;當外灘鐘聲遇見生成式AI 在歷史博物館的數字化展廳中&#xff0c;一幅動態的《民國百景圖》正通過全息投影技術演繹十里洋場的繁華與滄桑。這個虛實交融的場景&#xff0c;恰似民國大模型技術的隱喻——以人工智能為紐帶&#xff0c;連接起北洋軍閥混戰與民族…

ROS2 筆記匯總(2) 通信接口

在 ROS 系統中&#xff0c;通信接口&#xff08;Interface&#xff09; 是節點之間傳遞信息的標準“語言協議”&#xff0c;確保了不同功能節點之間可以正確理解和使用彼此傳送的數據內容。我們可以將其理解為“數據結構格式定義”&#xff0c;貫穿于話題&#xff08;Topic&…

微信小程序:將搜索框和表格封裝成組件,頁面調用組件

一、實現效果 實現搜索框,表格和翻頁效果 二、組件實現 1、創建表格組件頁面 (1)創建文件 在文件根目錄(與pages同級)直接創建components文件夾,并創建表格的頁面common-table/index (2)視圖層 a、寫入表頭 循環由主頁面傳遞的columns,數據為字段名label,寬度為設置…

基于貝葉斯學習方法的塊稀疏信號壓縮感知算法

基于貝葉斯學習方法的塊稀疏信號壓縮感知算法 BSBL-FM-master/BSBL_BO.m , 15593 BSBL-FM-master/BSBL_FM.m , 12854 BSBL-FM-master/Phi.mat , 131256 BSBL-FM-master/README.md , 3954 BSBL-FM-master/demo.mat , 1610 BSBL-FM-master/demo_fecg.m , 1481 BSBL-FM-master/de…

【Python爬蟲】requests知識點講解

目錄 前言1. requests庫基礎1.1 安裝requests1.2 基本導入 2. HTTP請求方法2.1 GET請求2.2 POST請求2.3 其他HTTP方法 3. 請求頭設置3.1 User-Agent設置3.2 常用請求頭 4. 響應處理4.1 響應內容獲取4.2 響應狀態碼4.3 響應頭信息 5. 會話管理5.1 Session對象5.2 Cookie處理 6. …