手记

从 1 到 2 :小程序阅读列表页

从 1 到 2 :小程序阅读列表页

写在前面

此帖承接 [从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"  bindtap="onPostTap"/>
      </swiper-item>
      <swiper-item>
          <image src="/imgs/vr.png" />
      </swiper-item>
      <swiper-item>
          <image src="/imgs/iqiyi.png" />
      </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"  bindtap="onPostTap" data-post-id="3"/>
      </swiper-item>
      <swiper-item>
          <image src="/imgs/vr.png"  bindtap="onPostTap" data-post-id="4"/>
      </swiper-item>
      <swiper-item>
          <image src="/imgs/iqiyi.png"  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个文与字简介内容)


文与字简介内容列表

注意:所用到的图片素材,在 百度公共盘中。失效的话,也可使用 百度私密盘; 密码:gcv1

(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:为了方便大家的模板抽取,笔者已经在代码中给了小提示了,细心的你,肯定可以发现的吧!

说好的不啰嗦,还是写了 快 13000 字了。宝宝心里苦,辛苦宝宝的可以,打赏、专栏点赞等等,当然你也可以看了就走,如果你忍心的话,,,,

12人推荐
随时随地看视频
慕课网APP

热门评论

2到3啥时候出?传数据到详情页怎么接收

期待你下一篇,谢谢分享

谢谢分享,对于我这个小程序的小白来说受益匪浅!!

查看全部评论