写在前面
此帖承接 [从0 到1:初学者入门Demo],原计划内容是一个两个页面的小程序,现在暂时增加到三个页面,包括 [从0 到1:初学者入门Demo]篇中的欢迎页,该篇的内容列表页面,和下一篇的内容详情页面。
相信阅读过笔者 从0到1 内容的同学,已经了解了小程序从无到有的过程,那么现在我们就直接来内容。
初学者通过该帖将学习到
0、小程序的基本组件 view、image、text、swiper
1、小程序推荐的布局方式 flex (本帖子重点使用)
2、小程序的请求API wx:request
3、小程序的模板化编程 template
4、小程序的列表渲染
5、小程序升级更新之后的新内容
6、建立一个页面的全过程:先骨架(wxml),再穿衣服(wxss),最后搞个小动作(js)
7、其他更多内容... ...
8、体会前后端分离的开发过程:如何一步一步抽象 mock 数据,得到数据结构,并最终向服务器端获取对应结构的数据,直接用于视图层的渲染!
9、体会前后端分离的开发过程:如何一步一步抽象 mock 数据,得到数据结构,并最终向服务器端获取对应结构的数据,直接用于视图层的渲染!
10、体会前后端分离的开发过程:如何一步一步抽象 mock 数据,得到数据结构,并最终向服务器端获取对应结构的数据,直接用于视图层的渲染!
重要的事情说三遍!
展示效果
说明:和 从0到1 之中有些许的配色变化!
准备
为了更好更流畅的学习这部分内容,请确保已经阅读过先导篇: [从0 到1:初学者入门Demo]。本文将一改 从0 到1 中风格,不再细枝末节的阐述,只写重要内容,当然该填的坑依然会填。
在 从0 到1 中,笔者从0 开始创建了一个欢迎页的内容,作为大家在小程序编程生涯中的 hello world
小礼物。最终使用名为 toMina 的点击 tap 时间跳转到了 index 页面,index 展示的是一个下拉刷新和上拉加载更多的内容,这一部分我将暂时挖一个坑,在后续 从4到5 专题帖中更加贴合实际的实现这个效果,大家期待一下吧。
废话不说,看看本文将带你完成什么吧!
内容列表页
这是一个文字展示内容的列表页面,主要由三个个部分组成:导航栏、上方的轮播图(一个轮播图,3个图片循环轮播)、下方的文与字简介内容(6个不同文与字的简介)。
原型设计图展示 | 轮播图 | 文与字简介 |
---|---|---|
1、导航栏
戴个帽子(json):导航栏的背景色(#5B7896)和文字内容仅仅表现在当前页面中,因此设置在 index.json 文件中即可。自己动手完成吧。
2、轮播图实现
老规矩:先骨架(wxml),再穿衣服(wxss),最后搞个小动作(js),随便带个帽子(当前页面的json)
(1)构建 index.wxml 页面骨架,填充内容
index.wxml 中需要使用 swiper、view、image、text 组件,后三个组件已经在从0到1内容说明,不再赘述,直接开始 swiper 组件的说明(很重要的说明):
- a、swiper是一个轮播试图容器,其中只可以存放 swiper-item 组件,其余组件将会被自动删除;
- b、swiper-item 应该仅仅作为轮播的一个子容器使用,并继承 父容器的宽高设置。
- c、swiper 在新版本中已经支持 循环轮播了,只需要设置其属性 circular 为true即可;其实,还有一个隐藏的属性,官方文档没有,但是确实是有效果的:垂直轮播属性 vertical, 需要时,设置为true 即可。
说明:笔者迄今为止介绍的最详细的也就是 swiper 轮播容器组件了,跟着我的魔鬼的步伐一步一步实现。PS:最终的代码是和官方案例一样的,但是你将会知道那是怎么来的,不信的话,继续看下去!!!
PPS:基本属性和时间还是需要各位去看官文的 。哈哈
一个轮播图,3个图片循环轮播:
<!-- index.wxml -->
<view class="container">
<swiper class="post-swiper" circular="true" autoplay="true" indicator-dots="true">
<swiper-item>
<!-提示: 不要纠结 image 的高度和宽度,实际发时,这个是大家试出来的,或者是UI给的-->
<image src="/imgs/wx.png" style="height:400rpx;width:100%" bindtap="onPostTap"/>
</swiper-item>
<swiper-item>
<image src="/imgs/vr.png" style="height:400rpx;width:100%"/>
</swiper-item>
<swiper-item>
<image src="/imgs/iqiyi.png" style="height:400rpx;width:100%"/>
</swiper-item>
</swiper>
<view class="post-item-container">
<view class="post-item">
这里是一个文与字简介的内容
</view>
<view class="post-item">
这里是一个文与字简介的内容
</view>
<view class="post-item">
这里是一个文与字简介的内容
</view>
</view>
</view>
上述完成之后得到的效果图是:
(2)页面骨架穿上衣服 index.wxss ,定义样式
设置 swiper 的宽高和 image 的宽高一致,实际开发中宽高上可以有所差异,比如:如果设置了 swiper 的高度比 image 高的话,会发现 三个轮播点 向下走了一点。具体自己玩玩吧。试试吧,电脑又不会爆炸!
/* index.css */
.post-swiper{
height:400rpx;
width:100%;
}
/*关于post-item的样式设计只是为了让大家清晰看到是不同的view分割,可先不设定*/
.post-item{
margin-top: 20rpx;
background-color:aquamarine;
border: 1px solid red;
}
(3)搞点小动作 index.js
轮播图存在的意义:除了展示,当然必须具有点击事件,这基本是真理了。想象一下,轮播图只是轮播图的话,你能忍吗?
某宝的轮播图,点击去的商品详情页。设置我们轮播图的点击事件,点击之后到文与字详情页面(使用从0到1中提及的快速创建page页的四个组成文件的小技巧,创建文与字详请页面 pages/index/post-detail/post-detail
【文与字详情页的具体构建过程将在 《从2到3》 中完成】。
// pages/index/index.js
Page({
data:{},
onLoad:function(options){
// 页面初始化 options为页面跳转所带来的参数
},
onPostTap:function(event){
// 点击轮播图片之后后 跳转到 post-detail 详情页
wx.navigateTo({
url: 'post-detail/post-detail'
})
}
})
(4)如何确认点击的轮播图是哪一个轮播图呢?又怎么知道跳转到谁的详情页呢?
你的名字和名片:在一个交际晚会上,那么多人递名片,你怎么让别人知道你是谁,又怎么联系你呢?哼简单,让别人记住你的名字,当别人需要联系你的时候,通过名字找到你的名片即可!(忽略同名的情况,如果你叫张伟的话,... ...)
同样的,给每个轮播图添加一个“名字”,点击时通过“名字”确认发挥哪一个轮播图对应的详情页。在编程中确定唯一的标识,那就是 id 了。
非常重要的一点:小程序中,数据动态绑定是单向的,在逻辑层(js)中将需要绑定到的视图层(wxml)上的数据,通过 this.setData 设置到 data 中(PS:如果数据不需要传递到视图层的,建议不要设置到 data 中),视图层通过 {{}}
动态获取数据,实现局部渲染;那么问题来了,视图层(wxml)的数据变化怎么传递回逻辑层呢(js),答案是:通过事件监听。
这里介绍一种常用的小技巧,实现视图层向逻辑层的数据传递,特别适合点击到达详情页的场景中。步骤如下:
- 组件上 设置一个事件 (这是重要的前提)
- 需要传递到逻辑层的数据,将以
data-
开头的属性设置到已绑定事件的组件上(比如:data-post-id) - 逻辑层从 事件函数的 event 参数的 dataset 中获取视图层传递来的数据 (比如:postId)
官方 dataset的 说明:在组件中可以定义数据,这些数据将会通过事件传递给 逻辑层。 书写方式: 以data-开头,多个单词由连字符-链接,不能有大写(大写会自动转成小写)如data-element-type,最终在 event.target.dataset 中会将连字符转成驼峰elementType。(data-post-id => postId)
在 index.wxml 中,为 swiper-item 的 image 绑定点击事件 onPostTap ,并设置待传递到逻辑层的数据 data-post-id
<view class="container">
<swiper class="post-swiper" circular="true" autoplay="true" indicator-dots="true">
<swiper-item>
<!-提示: 不要纠结 image 的高度和宽度,实际发时,这个是大家试出来的,或者是UI给的-->
<image src="/imgs/wx.png" style="height:400rpx;width:100%" bindtap="onPostTap" data-post-id="3"/>
</swiper-item>
<swiper-item>
<image src="/imgs/vr.png" style="height:400rpx;width:100%" bindtap="onPostTap" data-post-id="4"/>
</swiper-item>
<swiper-item>
<image src="/imgs/iqiyi.png" style="height:400rpx;width:100%" bindtap="onPostTap" data-post-id="5"/>
</swiper-item>
</swiper>
<view class="post-item-container">
<view class="post-item">
这里是一个文与字简介的内容
</view>
<view class="post-item">
这里是一个文与字简介的内容
</view>
<view class="post-item">
这里是一个文与字简介的内容
</view>
</view>
</view>
在逻辑层 index.js 中,获取从视图层传递而来的数据 postId,并在跳转到详情页时,放在 url 中进行传递
// pages/index/index.js
Page({
data:{},
onLoad:function(options){
// 页面初始化 options为页面跳转所带来的参数
},
onPostTap:function(event){
// 点击轮播图片之后 跳转到 post-detail 详情页
// 通过传递而来的 postId 确定具体的详情页是哪一个
var postId = event.target.dataset.postId;
console.log(postId);
// 跳转到详情页时,携带 postId 参数
wx.navigateTo({
url: 'post-detail/post-detail?postId=' + postId
})
}
})
(5)为什么需要在跳转的 url 中传递 postId 到详情页呢?
你的名字和名片:当我需要联系你的时候,从大脑中提取了你的名字,然后用你的名字去找你的名片。
计算机中也是一样,需要拿到轮播图的“名字”,也就是 postId,近而 去数据库中找到轮播图的的详情内容,然后展示给用户。
(6)怎么获取到 跳转 url 中携带的参数呢?
细心一点的话会发现,自动创建的 js 文件中,onLoad 事件函数中,存在这样一句注释 :// 页面初始化 options为页面跳转所带来的参数
。这就是答案了!
// pages/index/post-detail/post-detail.js
Page({
data:{},
onLoad:function(options){
// 页面初始化 options为页面跳转所带来的参数
// 详情页的内容,将会在 《从2到到3》 中具体构建,这里只是为了展现一个完整的的 点击跳到详情页的过程
var postId = options.postId;
console.log("跳转所带来的参数 postId 是:"+options.postId)
}
})
注意:详情页的内容,将会在 《从2到到3》 中具体构建,这里只是为了展现一个完整的 点击跳到详情页的过程。
写在轮播图之后(非必读内容)
建议大家可以去看一下:官方文档的事件篇的内容:区分一下 target、currentTarget; 掌握一下 dataset、detail,这将是大家小程序开发中不可避免要经常使用的内容。
看完事件内容之后,笔者希望大家先停下,思考一下 onPostTap 事件是不是可以 绑定到 swiper-item ,又该怎么来实现上述的内容呢。
这将让你很好的理解 冒泡事件中 target、currentTarget 的区别! 问题很绕,如果暂时理解不了的话,就先放在那儿,回头再来看看吧
(7)轮播图数据的抽取:带你一步一步抽取数据结构
前后端分离开发,是在实践中非常重要的一个原则。前端设计时,使用静态数据填充展示,最终抽取数据的结构,近而确定从后端获取的数据的 json 格式。就实现了前后端的开发了哟,是不是很 easy 呢!
抽取数据之前,请大家先考虑一个问题,哪些数据是需要从后端动态获取的,对于静态的数据抽取不在我们这里的讨论范围之内,比如:笔者觉得不应该动态的定制 swiper 表现,那么 swiper 属性就是一种静态数据,可以直接定义在组件的行间,是否抽取到 逻辑层(js)的 data 中实现数据绑定,大家自定。
* 抽取过程(编程初学者,请细读。不仅是小程序,在今后的前后端开发中,都很有借鉴意义!
)
a、确定轮播图使用的数据(轮播图的动态数据主要集中在 image 组件中,一个是 src 、一个是待传递的 postId)
b、确定数据结构,因为单个轮播子项 的postId 和 image 的src 是一一对应的,而且 轮播图可以包含多个 轮播子项,我们将 轮播图的数据结构抽取成 一个对象数组,数组中的每个对象都包含两个属性 postId ,imgSrc 。比如 postId 为3 的轮播子项 image 的 src 是 /imgs/wx.png , 那么对象就是 {postId:3, imgSrc:"imgs/wx.png" }。对象数组譬如:[{ "postId": 3, "imgSrc": "/imgs/wx.png" }, { "postId": 4, "imgSrc": "/imgs/vr.png" }, { "postId": 5, "imgSrc": "/imgs/iqiyi.png" }]
c、确定数据的去向?之前我们说过,单向数据动态绑定时,建议仅仅将用于视图渲染的数据设置到 data 中,如果是仅在逻辑层使用的话, 可以设置到 this 的其他属性上。 很显然:抽取的数据适用于视图层(wxml)的轮播图的渲染,需要设置到 data 中
d、如果你手敲的代码,而不是直接复制我的代码的话,你将会发现,三个 swiper-item 内容的代码几乎是一样的,此时,我们需要使用列表渲染来简化 wxml 的代码编写(代码更简洁优美、代码量更少,小程序可是只有 1024 kb)【关于列表渲染的内容,比较简单,推荐大家直接阅读:官文内容-列表渲染,关于 wx:key 的疑问,可以直接在社区中搜索到答案。】
综上,我们最终抽取之后的代码:index.wxml、index.js 、index.wxss
<!-- index.wxml -->
<view class="container">
<!-- 利用属性值是字符串的特性,加上字符串1转换成boolean值是true,字符串0转换成boolean是false;根据需求可以设置boolean值属性的值为为1、0 -->
<swiper class="post-swiper" circular="1" autoplay="1" indicator-dots="1">
<block wx:for="{{swiperData}}" wx:key="*this">
<swiper-item>
<image src="{{item.imgSrc}}" bindtap="onPostTap" data-post-id="{{item.postId}}" />
</swiper-item>
</block>
</swiper>
<view class="post-item-container">
<view class="post-item">
这里是一个文与字简介的内容
</view>
<view class="post-item">
这里是一个文与字简介的内容
</view>
<view class="post-item">
这里是一个文与字简介的内容
</view>
</view>
</view>
// pages/index/index.js
Page({
data: {
// 抽取成一个键值对集合,每个元素的键是是 postId 值是是 image 的 src
swiperData: [
{
"postId": 3,
"imgSrc": "/imgs/wx.png"
},
{
"postId": 4,
"imgSrc": "/imgs/vr.png"
},
{
"postId": 5,
"imgSrc": "/imgs/iqiyi.png"
}
]
},
onLoad: function (options) {
// 页面初始化 options为页面跳转所带来的参数
},
onPostTap: function (event) {
// 点击轮播图片之后后 跳转到 post-detail 详情页
var postId = event.target.dataset.postId;
console.log(postId);
wx.navigateTo({
url: 'post-detail/post-detail?postId=' + postId
})
}
})
.post-swiper{
height:400rpx;
width:100%;
}
/*抽取 .post-swiper image 样式属性*/
.post-swiper image{
height:400rpx;
width:100%;
}
/*关于post-item的样式设计只是为了让大家清晰看到是不同的view分割,可先不设定*/
.post-item{
margin-top: 20rpx;
background-color:aquamarine;
border: 1px solid red;
}
某社区API中心使用
为了更加接近实际开发环境,用于渲染轮播图的数据 swiperData 应该从后端获取,这里我们借助社区的API中心作为后端服务器。
为了方便大家的学习使用,笔者提供了个人 API 中心给各位使用。大家克制哈,仅有1000次读,请不要post数据。(如果大家觉得可以的话,可以打赏一点积分给笔者)
学前准备:wx.request 接口使用说明
从后端请求数据的代码,需要更改三部分:data、onLoad、新增一个 getHttpData 的方法。
最终代码 index.js:
// pages/index/index.js
Page({
data: {
// 抽取成一个键值对集合,每个元素的键是是 postId 值是是 image 的 src
// 虽然,swiperData 从服务器端获取后 动态设置到了 data 中,可以不在此设置 空数组的初始化,但是建议这么做。(个人习惯)
swiperData: []
},
onLoad: function (options) {
// 页面初始化时,请求服务器,获取 swiperData ,用于渲染轮播图
// 为了避免 this 指向错误,截取this,赋值给 that
var that = this;
this.getHttpData("swiperData", "json", function (data) {
that.setData({ swiperData: data });
});
},
onPostTap: function (event) {
// 点击轮播图片之后后 跳转到 post-detail 详情页
var postId = event.target.dataset.postId;
console.log(postId);
wx.navigateTo({
url: 'post-detail/post-detail?postId=' + postId
})
},
// 从 wxappclub 的 api 中心获取数据的方法
// key 表示数据名称,_type 数据类型,callback 表示请求成功的回调函数
// 回调函数的的参数,用于携带请求得到的数据
getHttpData: function (key, _type, callback) {
wx.request({
url: 'https://api.wxappclub.com/get',
data: {
// 笔者的API中心,提供给各位使用
"appkey": "eaa7gcwem04j8sak7hm8g88mkftgb26h",
"key": key,
"type": _type
},
method: 'GET',
header: {
'content-type': 'json'
},
success: function (res) {
if (res.data.success) {
// console.log(res.data.result.value);
callback(res.data.result.value);
}
}
});
}
})
写在轮播图之后,文与字内容之前
上述我们基本完成了一个实际开发中的轮播内容。关于文与字内容的部分,笔者建议大家先自行开发。提示一下:
a、flex 的嵌套布局
b、内容数据的抽取和轮播图基本一样
c、服务器端的数据请求方法还是 getHttpData、用于渲染文与字简介内容(若没有社区API中心应用的话,自行解决吧,少年们)
d、依然需要列表渲染(笔者提供了6个文与字简介内容)
文与字简介内容列表
(1)分析原型图,拆分内容结构,构造骨架(wxml)
拆分结构,可将单个文与字简介内容分为三个部分:
-a、包含头像和发布日期的头部
-b、由文章标题、展示图、内容概述组成的主体部分
-c、尾部是分享数数和收藏数
结构分析之后得到骨架如下(主要是基础的view、image、text的嵌套布局):
<view class="container">
<!-- 省略了 swiper 内容 -->
<view class="post-item-container">
<!-- 各个文与字简介内容,基本结构一致,仅仅是灌入数据的不同,重点分析基本结构 -->
<!-- post-item的整体布局 开始(从这里开始的呢!) -->
<view class="post-item">
<!-- 头部:作者头像、发布日期 -->
<view class="post-avatar-date">
<image class="post-avatar" src="/imgs/avatar/1.png"></image>
<text class="post-date">Seq 12 2017</text>
</view>
<!-- 主体部分:标题、展示图片、内容概述 -->
<view class="post-article" bindtap="onPostTap" data-post-id="3">
<text class="post-title">这是标题</text>
<image class="post-coverImg" src="/imgs/post/crab.png" mode="aspectFill" />
<text class="post-content">这是内容概述</text>
</view>
<!-- 尾部:收藏图标、收藏数、分享图标、分享数 -->
<view class="post-collection-share">
<image src="/imgs/icon/comment.png" />
<text>985</text>
<image src="/imgs/icon/view.png" />
<text>211</text>
</view>
</view>
<!-- post-item的整体布局 结束-->
</view>
</view>
该步骤完成后,得到下图:
(2)页面骨架穿上衣服 welcome.wxss ,定义样式、布局
结构的合理拆分,可以大大提升页面搭建的速度,根据上述的分析,可以很简单的使用 flex 将各个部分进行布局,布局分析步骤依然是:先整体,再局部
:(flex布局的内容,请参看[CSS进阶系列一flex布局]
-a、整体来看,头部、主体部分、尾部是列布局(flex-direction: column;
)
-b、局部来看,头像、发布日期所在的头部使用的 行布局(flex-direction: row;
);主体部分使用的 列布局(flex-direction: row;
);尾部的话,依然是行布局(flex-direction: row;
)
注意:对于图标、图片宽高的设置,除了UI设计师提供之外,不二法门就是自己尝试,慢慢调。
/* 还记得 page 吗? 为了得到一种斑马线的效果。我们设置整体的背景色是是 灰色的*/
page {
background-color: #eee;
}
.post-swiper {
height: 400rpx;
width: 100%;
}
/*抽取 .post-swiper image 样式属性*/
.post-swiper image {
height: 400rpx;
width: 100%;
}
/* post-item 布局 开始 (从这里开始抽取哦)*/
.post-item {
background-color: #fff;
margin-top: 30rpx;
padding: 20rpx;
border-radius: 20rpx;
}
.post-avatar-date {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 10rpx;
}
.post-avatar {
height: 80rpx;
width: 80rpx;
margin-right: 20rpx;
}
.post-date {
font-size: 26rpx;
color: #666;
}
.post-title {
font-weight: 700;
font-size: 38rpx;
}
.post-coverImg {
width: 750rpx;
height: 210px;
margin-left: -20rpx;
margin-top: 20rpx;
}
.post-content {
font-size: 28rpx;
color: #333;
}
.post-collection-share {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 20rpx;
margin-left: 500rpx;
}
.post-collection-share image {
height: 24rpx;
width: 24rpx;
margin-left: 20rpx;
margin-right: 10rpx;
}
.post-collection-share text {
font-size: 26rpx;
color: #999;
}
/* post-item 布局 结束*/
该步骤完成后,得到下图(简直完美的不要不要的,有木有!):
(3)搞一个小动作 welcome.js
这里需要的就是从文与字简介内容到文与字详情页面的小动作,很熟悉有木有,还是那个轮播图片点击时间 onPostTap
,再在绑定事件的 组件上设置一个 data-post-id="{{postId}}"
就可以了。
实际上我已经偷偷的完成了,就在主体部分的最外层容器上,不信你看:<view class="post-article" bindtap="onPostTap" data-post-id="3">
(4)问题来了? 说好的是 6个文与字简介内容呢,现在只有一个呀!
开始分析时,我们曾说过,文与字简介内容具有相同的基本结构,只是灌入的数据不同。那么想要 6个文与字简介内容,岂不是易如反掌,直接复制粘贴,然后替换不同的数据不就可以了吗?
先不说。小程序仅仅有1024kb! 如果是 600 个、6000个文与字简介内容呢?
再者说,大量重复,代码不够简洁,这是不能忍的。
小程序视图层(Wxml)上,当遇到大量重复的代码时,我们要做的只有两件事情:(1)将重复代码抽取成一个模板单元;(2)使用列表渲染,根据传入的数据,渲染一定数量的模板单元即可。【类似逻辑层(js)方法的抽取,是不是一样的道理:当一段功能性代码总是被复制粘贴,我们要做的就是:取功能性代码,将变化的内容作为参数传入到方法内容。在需要使用时,传入具体的数据即可。】
写在文与字简介内容列表之后
从0到1 中,我们已经完成了一个 自定义按钮的抽取,道理一样,步骤一样,唯一不同的是:这里只是代码比较多而已。大家自己动手做一下吧!
PS:为了方便大家的模板抽取,笔者已经在代码中给了小提示了,细心的你,肯定可以发现的吧!
热门评论
2到3啥时候出?传数据到详情页怎么接收
期待你下一篇,谢谢分享
谢谢分享,对于我这个小程序的小白来说受益匪浅!!