布局组件使用 - 让页面排版更美观
📐 好的布局是用户体验的基础,今天我们来学习如何用布局组件打造美观的页面
1. scroll-view - 滚动视图
scroll-view
是最常用的布局组件,用于创建可滚动的区域:
垂直滚动
vue
<template>
<view class="container">
<scroll-view
scroll-y
class="scroll-area"
@scrolltoupper="onScrollToUpper"
@scrolltolower="onScrollToLower"
@scroll="onScroll"
>
<view
v-for="item in list"
:key="item.id"
class="list-item"
>
<text>\{\{ item.title \}\}</text>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
list: []
}
},
onLoad() {
this.loadData()
},
methods: {
loadData() {
// 模拟数据
for (let i = 1; i <= 50; i++) {
this.list.push({
id: i,
title: `列表项 \${i}`
})
}
},
onScrollToUpper() {
console.log('滚动到顶部')
// 下拉刷新
this.refreshData()
},
onScrollToLower() {
console.log('滚动到底部')
// 加载更多
this.loadMore()
},
onScroll(e) {
console.log('滚动中:', e.detail.scrollTop)
},
refreshData() {
// 刷新数据
uni.showToast({
title: '刷新成功',
icon: 'success'
})
},
loadMore() {
// 加载更多数据
const currentLength = this.list.length
for (let i = 1; i <= 10; i++) {
this.list.push({
id: currentLength + i,
title: `新加载项 \${currentLength + i}`
})
}
}
}
}
</script>
<style>
.container {
height: 100vh;
}
.scroll-area {
height: 100%;
padding: 20px;
}
.list-item {
padding: 15px;
margin-bottom: 10px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
</style>
水平滚动
vue
<template>
<view class="horizontal-scroll-demo">
<scroll-view scroll-x class="scroll-x">
<view class="scroll-content">
<view
v-for="item in categories"
:key="item.id"
class="category-item"
:class="{ active: selectedCategory === item.id }"
@click="selectCategory(item.id)"
>
<text>\{\{ item.name \}\}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
selectedCategory: 1,
categories: [
{ id: 1, name: '推荐' },
{ id: 2, name: '热门' },
{ id: 3, name: '科技' },
{ id: 4, name: '娱乐' },
{ id: 5, name: '体育' },
{ id: 6, name: '财经' },
{ id: 7, name: '军事' },
{ id: 8, name: '国际' }
]
}
},
methods: {
selectCategory(id) {
this.selectedCategory = id
console.log('选择分类:', id)
}
}
}
</script>
<style>
.scroll-x {
white-space: nowrap;
padding: 10px 0;
}
.scroll-content {
display: flex;
padding: 0 15px;
}
.category-item {
flex-shrink: 0;
padding: 8px 16px;
margin-right: 15px;
background: #f5f5f5;
border-radius: 20px;
text-align: center;
}
.category-item.active {
background: #007aff;
color: white;
}
</style>
2. swiper - 轮播组件
swiper
用于创建轮播图和滑动切换效果:
基础轮播图
vue
<template>
<view class="swiper-demo">
<swiper
class="swiper"
indicator-dots
autoplay
interval="3000"
duration="500"
circular
@change="onSwiperChange"
>
<swiper-item
v-for="(banner, index) in banners"
:key="index"
>
<image
:src="banner.image"
class="swiper-image"
mode="aspectFill"
@click="onBannerClick(banner)"
/>
<view class="banner-content">
<text class="banner-title">\{\{ banner.title \}\}</text>
<text class="banner-desc">\{\{ banner.description \}\}</text>
</view>
</swiper-item>
</swiper>
<!-- 自定义指示器 -->
<view class="custom-dots">
<view
v-for="(banner, index) in banners"
:key="index"
class="dot"
:class="{ active: currentIndex === index }"
></view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
currentIndex: 0,
banners: [
{
id: 1,
title: '春季新品上市',
description: '全场8折优惠',
image: '/static/banner1.jpg',
url: '/pages/promotion/promotion'
},
{
id: 2,
title: '限时秒杀',
description: '每日10点开抢',
image: '/static/banner2.jpg',
url: '/pages/seckill/seckill'
},
{
id: 3,
title: '会员专享',
description: '专属福利等你来',
image: '/static/banner3.jpg',
url: '/pages/member/member'
}
]
}
},
methods: {
onSwiperChange(e) {
this.currentIndex = e.detail.current
},
onBannerClick(banner) {
uni.navigateTo({
url: banner.url
})
}
}
}
</script>
<style>
.swiper {
height: 200px;
border-radius: 12px;
overflow: hidden;
}
.swiper-image {
width: 100%;
height: 100%;
}
.banner-content {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(transparent, rgba(0,0,0,0.6));
padding: 20px;
color: white;
}
.banner-title {
font-size: 18px;
font-weight: bold;
display: block;
margin-bottom: 5px;
}
.banner-desc {
font-size: 14px;
opacity: 0.9;
}
.custom-dots {
display: flex;
justify-content: center;
margin-top: 10px;
}
.dot {
width: 8px;
height: 8px;
border-radius: 4px;
background: #ddd;
margin: 0 4px;
transition: all 0.3s;
}
.dot.active {
background: #007aff;
width: 20px;
}
</style>
3. Flex布局实战
常用布局模式
vue
<template>
<view class="layout-demo">
<!-- 水平居中 -->
<view class="section">
<text class="section-title">水平居中</text>
<view class="flex-center-h">
<view class="box">居中内容</view>
</view>
</view>
<!-- 垂直居中 -->
<view class="section">
<text class="section-title">垂直居中</text>
<view class="flex-center-v">
<view class="box">居中内容</view>
</view>
</view>
<!-- 完全居中 -->
<view class="section">
<text class="section-title">完全居中</text>
<view class="flex-center">
<view class="box">居中内容</view>
</view>
</view>
<!-- 两端对齐 -->
<view class="section">
<text class="section-title">两端对齐</text>
<view class="flex-between">
<view class="box">左侧</view>
<view class="box">右侧</view>
</view>
</view>
<!-- 等分布局 -->
<view class="section">
<text class="section-title">等分布局</text>
<view class="flex-equal">
<view class="box flex-1">1</view>
<view class="box flex-1">2</view>
<view class="box flex-1">3</view>
</view>
</view>
<!-- 网格布局 -->
<view class="section">
<text class="section-title">网格布局</text>
<view class="grid">
<view
v-for="i in 9"
:key="i"
class="grid-item"
>
<text>\{\{ i \}\}</text>
</view>
</view>
</view>
</view>
</template>
<style>
.layout-demo {
padding: 20px;
}
.section {
margin-bottom: 30px;
}
.section-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
display: block;
}
/* 水平居中 */
.flex-center-h {
display: flex;
justify-content: center;
height: 60px;
background: #f5f5f5;
}
/* 垂直居中 */
.flex-center-v {
display: flex;
align-items: center;
height: 60px;
background: #f5f5f5;
}
/* 完全居中 */
.flex-center {
display: flex;
justify-content: center;
align-items: center;
height: 80px;
background: #f5f5f5;
}
/* 两端对齐 */
.flex-between {
display: flex;
justify-content: space-between;
align-items: center;
height: 60px;
background: #f5f5f5;
padding: 0 15px;
}
/* 等分布局 */
.flex-equal {
display: flex;
gap: 10px;
}
.flex-1 {
flex: 1;
}
/* 网格布局 */
.grid {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.grid-item {
width: calc(33.333% - 7px);
height: 60px;
background: #007aff;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
}
.box {
background: #007aff;
color: white;
padding: 10px 15px;
border-radius: 6px;
text-align: center;
}
</style>
4. 实战案例:商品卡片布局
vue
<template>
<view class="product-layout">
<!-- 轮播图 -->
<swiper class="banner-swiper" indicator-dots autoplay>
<swiper-item v-for="banner in banners" :key="banner.id">
<image :src="banner.image" class="banner-image" mode="aspectFill" />
</swiper-item>
</swiper>
<!-- 分类导航 -->
<scroll-view scroll-x class="category-nav">
<view class="category-list">
<view
v-for="category in categories"
:key="category.id"
class="category-item"
@click="selectCategory(category.id)"
>
<image :src="category.icon" class="category-icon" />
<text class="category-name">\{\{ category.name \}\}</text>
</view>
</view>
</scroll-view>
<!-- 商品列表 -->
<scroll-view scroll-y class="product-list" @scrolltolower="loadMore">
<view class="product-grid">
<view
v-for="product in products"
:key="product.id"
class="product-card"
@click="goToDetail(product.id)"
>
<image :src="product.image" class="product-image" mode="aspectFill" />
<view class="product-info">
<text class="product-name">\{\{ product.name \}\}</text>
<view class="product-price-row">
<text class="product-price">¥\{\{ product.price \}\}</text>
<text class="product-sales">已售\{\{ product.sales \}\}</text>
</view>
</view>
</view>
</view>
<!-- 加载更多 -->
<view v-if="loading" class="loading">
<text>加载中...</text>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
loading: false,
banners: [
{ id: 1, image: '/static/banner1.jpg' },
{ id: 2, image: '/static/banner2.jpg' },
{ id: 3, image: '/static/banner3.jpg' }
],
categories: [
{ id: 1, name: '手机', icon: '/static/phone.png' },
{ id: 2, name: '电脑', icon: '/static/computer.png' },
{ id: 3, name: '家电', icon: '/static/appliance.png' },
{ id: 4, name: '服装', icon: '/static/clothes.png' },
{ id: 5, name: '美妆', icon: '/static/beauty.png' }
],
products: []
}
},
onLoad() {
this.loadProducts()
},
methods: {
loadProducts() {
// 模拟商品数据
for (let i = 1; i <= 20; i++) {
this.products.push({
id: i,
name: `商品名称 \${i}`,
price: (Math.random() * 1000 + 100).toFixed(2),
sales: Math.floor(Math.random() * 1000),
image: `/static/product\${i % 5 + 1}.jpg`
})
}
},
selectCategory(id) {
console.log('选择分类:', id)
// 根据分类筛选商品
},
goToDetail(id) {
uni.navigateTo({
url: `/pages/product-detail/product-detail?id=\${id}`
})
},
loadMore() {
if (this.loading) return
this.loading = true
// 模拟加载更多
setTimeout(() => {
const currentLength = this.products.length
for (let i = 1; i <= 10; i++) {
this.products.push({
id: currentLength + i,
name: `商品名称 \${currentLength + i}`,
price: (Math.random() * 1000 + 100).toFixed(2),
sales: Math.floor(Math.random() * 1000),
image: `/static/product\${(currentLength + i) % 5 + 1}.jpg`
})
}
this.loading = false
}, 1000)
}
}
}
</script>
<style>
.product-layout {
height: 100vh;
display: flex;
flex-direction: column;
}
.banner-swiper {
height: 180px;
flex-shrink: 0;
}
.banner-image {
width: 100%;
height: 100%;
}
.category-nav {
flex-shrink: 0;
background: white;
padding: 15px 0;
}
.category-list {
display: flex;
padding: 0 15px;
}
.category-item {
flex-shrink: 0;
margin-right: 25px;
text-align: center;
}
.category-icon {
width: 40px;
height: 40px;
margin-bottom: 5px;
}
.category-name {
font-size: 12px;
color: #666;
}
.product-list {
flex: 1;
background: #f5f5f5;
}
.product-grid {
display: flex;
flex-wrap: wrap;
padding: 10px;
gap: 10px;
}
.product-card {
width: calc(50% - 5px);
background: white;
border-radius: 8px;
overflow: hidden;
}
.product-image {
width: 100%;
height: 120px;
}
.product-info {
padding: 10px;
}
.product-name {
font-size: 14px;
color: #333;
display: block;
margin-bottom: 8px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.product-price-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.product-price {
color: #ff6b6b;
font-size: 16px;
font-weight: bold;
}
.product-sales {
font-size: 12px;
color: #999;
}
.loading {
text-align: center;
padding: 20px;
color: #999;
}
</style>
小结
今天我们学习了:
- ✅
scroll-view
- 创建可滚动区域 - ✅
swiper
- 实现轮播效果 - ✅ Flex布局的各种应用场景
- ✅ 实战案例:商品页面布局
布局要点:
- 合理使用滚动组件提升用户体验
- 灵活运用Flex布局实现各种排版
- 注意性能优化,避免过度嵌套
- 保持布局的一致性和美观性
下一篇预告
下一篇我们将学习《样式和CSS - 让你的小程序更好看》,深入学习UniApp的样式系统。
好的布局是用户体验的基础,掌握了这些布局技巧,你就能创造出美观实用的页面!