前文:
微信小程序学习之旅–第一个页面的制作
微信小程序学习之旅–零基础制作自己的小程序–第二个页面的制作
微信小程序学习之旅–完善pages页面–字体图标,数据绑定,条件/列表渲染,事件/catch/bind以及路由的学习
微信小程序学习之旅–零基础制作自己的小程序-完成文章详情页–自定义属性/页面通信/缓存机制/异步API/async/await
微信小程序学习之旅–零基础制作自己的小程序-文章详情页音乐播放功能的实现-背景音频/全局App/页面的bug修改和优化
微信小程序入门(六)-自定义组件
完善文章轮播图
轮播图文章跳转
前面我们的轮播图只能简单的动起来。
现在打算实现点击轮播图上的图片,跳转到对应的文章详情页去。只要在图片上绑定文章的id号即可。用到的函数还是我们之前用来进行文章详情页跳转的函数。
生成tabBar选项卡
配置生成
小程序提供的有tabBar的选项卡。我们只需要在app.json中配置这个选项,即可生成。
"tabBar": {
"list": [
{
// tabbar的标题
"text": "阅读",
// 点击这个bar后去往的页面路径(必填)
"pagePath": "pages/posts/posts"
},
{
"text": "电影",
"pagePath": "pages/movies/movies"
}
]
}
tabBar的其他配置
"tabBar": {
// 设置边框线的颜色为白色 这样舒服点
"borderStyle": "white",
// bar的位置 可以在顶端
"position": "bottom",
// bar未选中时文字的颜色
"color": "#999",
// 选中当前bar,文字的颜色
"selectedColor": "#333",
"list": [
{
"text": "阅读",
"pagePath": "pages/posts/posts",
// 未选中时的图标
"iconPath": "/images/tab/yuedu.png",
// 选中时的图标
"selectedIconPath": "/images/tab/yuedu_hl.png"
},
{
"text": "电影",
"pagePath": "pages/movies/movies",
"iconPath": "/images/tab/dianying.png",
"selectedIconPath": "/images/tab/dianying_hl.png"
}
]
}
页面跳转
如果想要让一个普通页面,跳转到一个带有bar选项卡的页面,我们之前的逻辑是不行的了。
回顾一下,之前我们在这的跳转方式是点击按钮,进行页面的重定向,跳转到文章页面。
这种方式现在是不能满足我们的要求了。
switchTab方法的使用
使用switchTab方法,就可以从普通页面跳转到选项卡页面。
自定义组件
要说精髓,小程序的精髓应该就是在自定义组件这里了。
做一个自己的自定义组件
新建一个自定义组件
右键点击post文件夹,然后点新建Component,就可以建好自定义组件的文件了。
一般来说,我们习惯性的让自定义组件的文件名都是index。
这里我们就先做一个文章的自定义组件,重构一下之前的那个文章页面。
注册自定义组件
很明显,就跟我们前面使用第三方的自定义组件库一样,使用组件之前都是需要进行自定义的。
组件的自定义属性
常规写法
在我们组件的js文件的properties属性上,我们可以进行自定义属性的注册。到时候使用组件的时候就可以给该属性赋值,就和使用image标签的时候,给src属性赋值类似的操作。
/**
* 组件的属性列表
*/
properties: {
// 自定义一个属性 属性名称
text: {
// 属性值的类型
type: String,
// 属性的默认值
value: "我是默认值"
}
}
如果不给属性赋值,那么就是我们定义好的该属性的默认值
简写(语法糖)
很多时候,我们觉得上面那种定义属性的方式比较麻烦。那么可以采取下面这种方式。在没有默认值的情况下很适用。
**这种方式定义的属性,如果属性没有传参,那么默认值就是当前属性的类型的默认值。**比如Number的默认值是0。String的默认值是空字符串
/**
* 组件的属性列表
*/
properties: {
// 自定义一个属性 属性名称 类型
text: String
}
以前,普通的页面只有data里面的变量才能做数据绑定(setData定义的数据最后也会出现在data里面),但是,在自定义组件里,组件的属性properties和data里面出现的变量,都可以进行数据绑定。
自定义属性可以接收一个Object对象
前面,我们进行文章列表的遍历,来渲染整个文章页面(除去轮播图)。
我们可以发现,每篇文章的结构实际上都是相同的。唯一不同的,只是数据而已。
所以我们可以考虑用自定义组件来代替这个文章的结构,将结构隐藏到组件中,使页面看起来更加清爽,而且还可以达到复用的效果。
封装文章组件结构/样式
我们将文章的结构和样式,都直接封装到这个自定义组件里面。
结构
结构还会进行二次更改。
<!--components/post/index.wxml-->
<view data-post-id="{{item.postId}}" class="post-container" bind:tap="onGoToDetail">
<!-- 第一部分 作者 日期 -->
<view class="post-author-date">
<!-- 头像 -->
<image class="post-author" src="{{item.avatar}}"></image>
<text class="post-date">{{item.date}}</text>
</view>
<!-- 第二部分 文章标题 -->
<text class="post-title" user-select="true">{{item.title}}</text>
<!-- 第三部分 -->
<image class="post-image" src="{{item.imgSrc}}"></image>
<!-- 第四部分 文章内容 -->
<!-- user-select 方便用户复制 -->
<text class="post-content">{{item.content}}</text>
<!-- 第五部分 -->
<view class="post-like">
<l-icon color="#666" size="28" name="favor" class="post-like-image" />
<text class="post-like-font">{{item.collection}}</text>
<l-icon color="#666" size="32" name="eye" class="post-like-image" />
<text class="post-like-font">{{item.reading}}</text>
</view>
</view>
样式
/* components/post/index.wxss */
/* 文章样式 */
.post-container {
/* 弹性布局 */
display: flex;
flex-direction: column;
margin-top: 20rpx;
margin-bottom: 40rpx;
background-color: #fff;
/* 上下边框线 */
border-top: 1rpx solid #ededed;
border-bottom: 1rpx solid #ededed;
padding-bottom: 10rpx;
}
/* 作者 日期样式 */
.post-author-date {
margin: 10rpx 0 20rpx 10rpx;
display: flex;
flex-direction: row;
/* flex 布局居中方案 */
align-items: center;
}
.post-author {
width: 60rpx;
height: 60rpx;
/* 头像和文字居中 */
/* vertical-align: middle; */
}
.post-date {
margin-left: 20rpx;
font-size:26rpx;
/* vertical-align: middle; */
}
/* 文章标题 */
.post-title{
font-size:34rpx;
font-weight: 700;
margin-bottom: 20rpx;
margin-left: 20rpx;
color:#333;
}
/* 文章主图 */
.post-image{
width: 100%;
height: 340rpx;
margin-bottom: 30rpx;
}
/* 文章内容 */
.post-content{
color:#666;
font-size: 28rpx;
margin-bottom: 20rpx;
margin-left: 10rpx;
margin-right: 10rpx;
line-height: 40rpx;
letter-spacing: 2rpx;
text-indent: 2em;
}
/* 文章模拟阅读量 */
.post-like{
display: flex;
flex-direction: row;
margin-left: 26rpx;
align-items: center;
}
.post-like-image{
/* height: 32rpx;
width: 32rpx; */
margin-right: 16rpx;
}
.post-like-font{
margin-right: 40rpx;
font-size: 26rpx;
}
自定义属性:Object类型
很明显,我们一篇文章会用到很多数据,所有使用文本进行传递是不可靠的。
这里采用传递文章对象,这个对象里面包含所有的当前文章的信息。
那么我们就需要定义一个属性,且该属性的类型是对象类型。
/**
* 组件的属性列表
*/
properties: {
// 自定义一个属性 属性名称
postData:Object
},
组件结构调整
将进行数据绑定的地方进行修改。
<!--components/post/index.wxml-->
<view data-post-id="{{postData.postId}}" class="post-container" bind:tap="onGoToDetail">
<!-- 第一部分 作者 日期 -->
<view class="post-author-date">
<!-- 头像 -->
<image class="post-author" src="{{postData.avatar}}"></image>
<text class="post-date">{{postData.date}}</text>
</view>
<!-- 第二部分 文章标题 -->
<text class="post-title" user-select="true">{{postData.title}}</text>
<!-- 第三部分 -->
<image class="post-image" src="{{postData.imgSrc}}"></image>
<!-- 第四部分 文章内容 -->
<!-- user-select 方便用户复制 -->
<text class="post-content">{{postData.content}}</text>
<!-- 第五部分 -->
<view class="post-like">
<l-icon color="#666" size="28" name="favor" class="post-like-image" />
<text class="post-like-font">{{postData.collection}}</text>
<l-icon color="#666" size="32" name="eye" class="post-like-image" />
<text class="post-like-font">{{postData.reading}}</text>
</view>
</view>
使用文章自定义组件
<block wx:for="{{posts}}" wx:key="postId" wx:for-item="post">
<post post-data="{{post}}"></post>
</block>
可以看见,文章被成功的渲染出来了。
自定义组件的事件
这里,我们简单的把当时进行点击文章,调整到对应的文章详情页的方法代码,抽离到自定义组件中。
组件使用到的放到,都需要在methods属性里面进行定义。
自定义组件-movie-list
这里将自定义一个展示电影列表的自定义组件。
组件建立好了以后就在电影页面movies里面进行组件的注册。
测试:发现正常引入
组件的嵌套
我们可以发现,每个电影实际上也是结构相同,只是数据不同而已。所以我们可以把电影也抽成一个组件。
电影组件-movie
组件的制作
我们前面说过,开发页面的时候,可以先用假数据模拟,然后等静态页面制作好了以后,再将假数据替换为我们实际从服务器请求的数据。
组件的开发也是一样,先用假数据,然后在替换为实际使用组件时,外界传递进来的数据。
在movie-list组件内部,我们会使用 movie组件,那么这就构成了组件之间的相互嵌套。
组件嵌套的使用
注册
在movie-list组件内组成movie组件
使用movie组件
在组件内部使用其他组件,也是和普通标签一样使用。
movie组件的制作
静态模板的制作
这一步,比较简单,没什么好说的。
<!--components/movie/index.wxml-->
<!-- 电影容器 -->
<view class="container">
<!-- 电影海报 -->
<image class="poster" src="/images/img1.jpg"></image>
<!-- 电影标题 -->
<text class="title">小米手机哈哈哈哈哈哈哈</text>
</view>
静态模板的css
/* components/movie/index.wxss */
/* 容器 */
.container {
display: flex;
width: 200rpx;
flex-direction: column;
}
/* 电影海报 */
.poster {
width: 100%;
height: 270rpx;
margin-bottom: 22rpx;
}
/* 标题 */
.title {
/* 标题字数过长 多出的隐藏 */
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
word-break: break-all;
}
静态模板效果
使用lin-ui的评分组件
这个评分的组件,我们自己制作的话是比较麻烦的,所以直接使用第三方提供好的。(如果想要自己实现其实也不是很难,只实现整数性质的平分还是比较容易的。)
l-rate评分组件的注册
使用l-rate继续完成movie组件
<view class="rate-container">
<!-- 评分 -->
<!-- score属性 显示点亮多少星星 可以是小数 -->
<!-- 星星的大小 size 进行设置 -->
<!-- 使用count属性可以修改星星的数量(默认是五个星) -->
<!-- 设置禁用点击 disabled="true" -->
<!-- 使用active-color属性 可以修改点亮的星星的颜色 -->
<!-- 使用 inActive-color 属性 可以修改未选中的星星颜色 -->
<l-rate disabled="{{true}}" score="3.6" size="22" />
<!-- 显示评分 -->
<text class="score">3.6</text>
</view>
/* 评分容器 */
.rate-container{
margin-top: 6rpx;
display: flex;
flex-direction: row;
/* 基线对齐 */
align-items: baseline;
}
.score{
margin-left: 16rpx;
font-size: 24rpx;
}
movie-list组件的制作
通过开始的图片我们看见了,一个movie-list组件里面是有三个电影组件的。
所以接下来我们要利用制作好的movie组件来制作我们的movie-list组件了。
<!--components/movie-list/index.wxml-->
<view class="container">
<view class="title-container">
<text>正在热映</text>
<text class="more-text">更多 ></text>
</view>
<!-- 组件的嵌套 -->
<view class="movie-container">
<movie />
<movie />
<movie />
</view>
</view>
/* components/movie-list/index.wxss */
/* 组件容器 */
.container {
padding: 36rpx 28rpx;
}
/* 标题容器 */
.title-container {
display: flex;
flex-direction: row;
/* 主轴排列方式 两端对齐*/
justify-content: space-between;
margin-bottom: 26rpx;
}
.more-text{
color:#1f4ba5;
}
/* 海报组件容器 */
.movie-container {
display: flex;
flex-direction: row;
justify-content: space-between;
}
静态的制作都是挺简单的,先用死数据,制作好样式,后期在替换就完事。
使用多个movie-list组件
效果看起来也还可以。但是样式还需要进行调整。
设置组件的样式
我们在使用movie-list组件的时候,感觉这个样式并不完美,想要给这个组件加一些样式,但是,遇见了一些问题,就是给这个组件设置的样式都不生效。
直接用组件的名称设置样式,或者给组件添加class类名设置样式,都不生效。
这是为什么呢?
解决movie-list样式不生效
想要解决样式不生效的问题,我们这里需要使用组件的外部样式类。
之所以使用外部样式,是因为我们使用的组件,并不知道我们实际上会设置什么样式,所以定义好类名交给我们外部进行设置,到时候通过外部样式的入口进行传递给组件引用,完成样式的设置。
外部样式类的使用也比较简单。
很明显,可以看见第一个设置外部样式的颜色发生了改变。
这种直接给自定义组件设置样式不生效的问题,是由小程序内部的机制引起的。这时候我们就需要考虑使用外部样式类了。
movie-list组件的优化
动态标题
前面我们将所有的数据都是按照死数据来定死的。实际上这肯定是不合理的。这里进行标题的动态赋值。
我们在组件的js文件中设置这个属性。让外界动态的传参。
/**
* 组件的属性列表
*/
properties: {
title:String
},
小程序请求服务端数据
发起请求wx.request方法
常规用法,请求地址加上回调函数
// 请求服务器数据 获取正在热映的电影
wx.request({
// 请求地址
url: 'url',
// 请求方式 默认就是get请求
method:"GET",
success(res){
console.log(res);
}
})
获取请求数据
// 别忘了获取全局app
const app = getApp()
/**
* 页面的初始数据
*/
data: {
// 正在热映的电影数据
inTheaters:[],
// 即将上映的电影数据
comingSoon:[],
// top250
top250:[]
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 请求服务器数据 获取正在热映的电影
wx.request({
// 请求地址
url: app.gBaseUrl+'in_theaters',
method: "GET",
// 参数 在get请求中为查询字符串
data: {
// 电影的起始索引
start: 0,
// 电影的总条数
count: 3
},
success:(res) =>{ // 使用箭头函数解决this指向问题
// 电影数据
console.log(res.data.subjects);
this.setData({
inTheaters:res.data.subjects
})
}
});
// 即将上映的数据
wx.request({
// 请求地址
url: app.gBaseUrl+'coming_soon',
method: "GET",
data: {
start: 0,
count: 3
},
success:(res) =>{
// console.log(res);
this.setData({
comingSoon:res.data.subjects
})
}
});
// top250
wx.request({
// 请求地址
url: app.gBaseUrl+'top250',
method: "GET",
data: {
start: 0,
count: 3
},
success:(res) =>{
this.setData({
top250:res.data.subjects
})
}
})
},
将数据绑定到movie-list组件
<!--pages/movies/movies.wxml-->
<movie-list movies="{{inTheaters}}" title="正在热映" l-class="movie-list" />
<movie-list movies="{{comingSoon}}" title="正在上映" l-class="movie-list" />
<movie-list movies="{{top250}}" title="豆瓣Top250" l-class="movie-list" />
movie-list组件数据的替换
现在,数据已经传递进来了,我们要做的就是动态替换实际的电影列表数据,并将每个电影的数据传递给电影组件。
movie组件的数据替换
接下来将movie组件的死数据替换为真实数据即可。
效果
有些图片加载不出来,和星星显示有误很正常,毕竟不是我们自己写的服务端数据。肯定会出现数据图片等有问题的错误。