Vue3+ts开发学习 · 小兔鲜儿3

黑马的视频学习

首页

组件封装

只有一个页面需要使用

以首页导航栏示例 /index/index.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<script setup lang="ts">
import CustomNavbar from './componets/CustomNavbar.vue';
</script>

<template>
  <!-- 自定义导航栏 -->
  <CustomNavbar />
</template>

<style lang="scss">
//
</style>

/index/components/CustomNavbar.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<script setup lang="ts">
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
</script>

<template>
  <view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 10 + 'px' }">
    <!-- 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;
    .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>

关键代码

1
2
3
4
5
6
7
8
<script setup lang="ts">
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
</script>

<template>
  <view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 10 + 'px' }">
...

需要多个页面使用【全局组件】

轮播图为例 /src/components/XtxSwiper.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<script setup lang="ts">
import { ref } from 'vue'

const activeIndex = ref(0)
</script>

<template>
  <view class="carousel">
    <swiper :circular="true" :autoplay="false" :interval="3000">
      <swiper-item>
        <navigator url="/pages/index/index" hover-class="none" class="navigator">
          <image
            mode="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">
          <image
            mode="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">
          <image
            mode="aspectFill"
            class="image"
            src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/slider_3.jpg"
          ></image>
        </navigator>
      </swiper-item>
    </swiper>
    <!-- 指示点 -->
    <view class="indicator">
      <text
        v-for="(item, index) in 3"
        :key="item"
        class="dot"
        :class="{ active: index === activeIndex }"
      ></text>
    </view>
  </view>
</template>

<style lang="scss">
:host {
  display: block;
  height: 280rpx;
}
/* 轮播图 */
.carousel {
  height: 100%;
  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>
  1. 关键代码
    自动导入设置【和uni-app自动导入设置类似】
    使用正则匹配方法
    pages.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
"easycom": {
    "autoscan": true,
    "custom": {
      // uni-ui 规则如下配置
      "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
      // 以Xtx 开头的组件,在components文件夹中查找引入
      "^Xtx(.*)": "@/components/Xtx$1.vue"
    }
  },
  "pages": 
  ...
  1. 对全局组件进行类型声明 模板代码
1
2
3
4
5
declare module '@vue/runtime-core' {
    export interface GlobalComponents {

    }
}

代码使用

1
2
3
4
5
6
7
import XtxSwiper from "./XtxSwiper.vue";

declare module '@vue/runtime-core' {
    export interface GlobalComponents {
        XtxSwiper: typeof XtxSwiper
    }
}

轮播图指示点高光跟随

原代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<script setup lang="ts">
import { ref } from 'vue'

const activeIndex = ref(0)
</script>

<template>
  ...
    <!-- 指示点 -->
    <view class="indicator">
      <text
        v-for="(item, index) in 3"
        :key="item"
        class="dot"
        :class="{ active: index === activeIndex }"
      ></text>
    </view>
  ...
<\template>
  1. 在swiper上绑定change事件
1
<swiper :circular="true" :autoplay="false" :interval="3000" @change="onChange">
  1. 获取事件数据,并发现detail里面的current随着变化滑动而变化数字。因此作为滑动指示点的高亮显示记号。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<script setup lang="ts">
import { ref } from 'vue'

const activeIndex = ref(0)

//  swiper 下标发生变化时触发
const onChange: UniHelper.SwiperOnChange = (ev) => {
  //不用?用!,是非空断言,主观上排除空的情况
  activeIndex.value = ev.detail!.current
}
</script>

ev的类型使用了UniHelper.SwiperOnChange

各模块加入到主页并渲染等步骤

  1. 在 /index/components 中保存对应模块的封装文件(.vue)
  2. 查看对应组件的AIP文档相关信息,在 /service/home.ts 中封装组件AIP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/**
 * 首页-热门推荐-小程序
 * GET
 * /home/hot/mutli
 */
export const getHomeHotAPI = () => {
    return http({
        method: 'GET',
        url: '/home/hot/mutli'
    })
}
  1. 在 index.vue 中的 template 标签里调用组件。并获取数据以及载入页面。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 获取热门推荐数据
const getHomeHotData = async () => {
  const res = await getHomeHotAPI()
}
...
onLoad(() => {
  getHomeBannerData()
  getHomeCategoryAPIData()
  getHomeHotData()
})
  1. 在 /types/home.d.ts 中定义数据类型
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/** 首页-热门推荐数据类型 */
export type HotItem = {
  /** 说明 */
  alt: string
  /** id */
  id: string
  /** 图片集合[ 图片路径 ] */
  pictures: string[]
  /** 跳转地址 */
  target: string
  /** 标题 */
  title: string
  /** 推荐类型 */
  type: string
}
  1. 此数据类型即为封装API中http的类型,但是http的内容是数组
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/**
 * 首页-热门推荐-小程序
 * GET
 * /home/hot/mutli
 */
export const getHomeHotAPI = () => {
    return http<HotItem>({
        method: 'GET',
        url: '/home/hot/mutli'
    })
}
  1. 在 index.vue 中得到具体的数据并把数据传到组件中
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<script>
// 获取热门推荐数据
const hotList = ref<HotItem[]>([])
const getHomeHotData = async () => {
  const res = await getHomeHotAPI()
  hotList = res.result
}
</script>
...
<template>
  <!-- 热门推荐 -->
  <HotPanel :list="hotList" />
</template>
  1. 在组件的vue文件中定义props接收数据
1
2
3
4
5
6
7
<script setup lang="ts">
...
// 定义props接收数据,一般是列表
defineProps<{
    list: HotItem[]
}>()
</script>
  1. 把组件中静态数据换成当前调用的数据(一般通过v-for循环)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<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" class="cards">
                <image v-for="src in item.pictures" :key="src" class="image" mode="aspectFit" :src="src">
                </image>
            </navigator>
        </view>
    </view>
</template>

自动导入公用组件

可以不用import导入就能使用,但是需要定义类型在 /types/conponents.d.ts

1
2
3
4
5
6
7
8
9
import XtxSwiper from "@/components/XtxSwiper.vue";
import XtxGuess from "@/components/XtxGuess.vue";

declare module '@vue/runtime-core' {
    export interface GlobalComponents {
        XtxSwiper: typeof XtxSwiper
        XtxGuess: typeof XtxGuess
    }
}

定义自动导入,在 /src/page.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "easycom": {
    "autoscan": true,
    "custom": {
      // uni-ui 规则如下配置
      "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
      // 以Xtx 开头的组件,在components文件夹中查找引入
      "^Xtx(.*)": "@/components/Xtx$1.vue"
    }
  },
  "pages": [...]
}

页面滚动设置

flexBox使用
scoll-view使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template>
  <!-- 自定义导航栏 -->
  <CustomNavbar />
  <scroll-view class="scroll-view" scroll-y>
    <!-- 自定义轮播图 -->
    <XtxSwiper :list="bannerList" />
    <!-- 分类面板 -->
    <CategoryPanel :list="categoryList" />
    <!-- 热门推荐 -->
    <HotPanel :list="hotList" />
    <!-- 猜你喜欢 -->
    <XtxGuess />
  </scroll-view>
</template>

<style lang="scss">
page {
  background-color: #f7f7f7;
  height: 100%;
  display: flex;
  flex-direction: column;
}

.scroll-view {
  flex: 1;
}
</style>

调用组件对应后端API

单个页面内部使用组件采用页面内部调用API

1
2
3
4
5
6
//页面加载
onLoad(() => {
  getHomeBannerData()
  getHomeCategoryAPIData()
  getHomeHotData()
})

多个页面内部都会用到的组件采用组件内部调用API

1
2
3
4
// 组件挂载完毕
onMounted(() => {
    getHomeGoodsGuessLikeAPI()
})
0%