今天进入正题,看看下面效果的小程序是怎么实现的:

项目地址 https://github.com/dongweiming/weapp-zhihulive

PS: 本文是假设你已经看过微信小程序的官方文档、demo 甚至已经动手写过小程序,否则建议先去翻翻再来看。

设计目录结构

我在上一节 知乎 Live 全文搜索之微信小程序实战(一) /) 介绍了组件化,今天就是要实施了。首先我们考虑一个只有 index 页面的小程序的目录结构是怎么样的:

├── app.js  // 全局的脚本文件
├── app.json  // 全局的配置文件
├── app.wxss  // 全局的样式文件
├── pages
│   ├── index
│   │   ├── index.js // 脚本文件
│   │   ├── index.json // 组件的配置文件
│   │   ├── index.wxml  // 页面结构文件
│   │   ├── rating.png  // 还有其他的图片..
│   │   └── index.wxss  // 样式表文件

pages 目录下有个 index 目录,存放了名字叫做 index,后缀为 js/json/wxml/wxss 的四个文件。这样做的好处是:

  1. index 目录下存放了页面组件所需要的各种资源, 就近维护 。如果是 React,还得通过使用各种 loader,用 import 的方式来用,所以我喜欢小程序的处理方式。
  2. 当某天不再需要 index 这个页面,或者要替换成其他的组件,直接把 index 目录删掉 / 替换就完事了。

接着我们基于 Live 搜索,思考下如果页面变的复杂,重要元素多的场景:

  1. 需要 Live、User、Topic 三大元素。
  2. 有些内容是可以重复被利用的,比如评分(就是大家熟悉的星星,5 星满分,4.5 星次之...)在 Live 详情页的效果比较大,而在搜索页由于区域小所以小了很多,但是本质上内容是一样的,只不过样式不同。
  3. 有些内容在不同页面重复出现,比如 Live,在 topic 详情页、用户详情页、发现页都有,而且一样。

那么:

  1. 评分是一个独立的区域,可以视作一个组件。
  2. 组件与组件之间应该可以 自由组合 ,所以组件的粒度要细,细到一个组件就是做一件事。
  3. 单个评分组件拿出来是无意义的,只有和 Live 信息汇合起来才是一个完整页面。

所以重新定义目录结构吧:

App
├── components
│   ├── hot
│   ├── live
│   ├── user
│   └── widget
├── images
│   └── rating
├── pages
│   ├── explore
│   ├── hot
│   ├── live
│   ├── search
│   ├── topic
│   └── users
└── utils

现在 pages 的每个子目录下后缀为 js 的文件就是页面逻辑。比如 pages/users/users.js 存放了 /users/users 的页面逻辑。

我新增了 3 个目录:

  1. utils。存放一些用得到的功能性的函数,如和后端通信的 api.js,一会我们详细再看。
  2. images。存放公共的静态图片资源。
  3. components。组件目录,我们把抽象的元素都放在这里,比如评分是一个组件。组件目录下有这些文件:
components/
├── hot
│   ├── hot.wxml
│   └── hot.wxss
├── live
│   ├── live.wxml
│   ├── live.wxss
│   ├── live_middle.wxml
│   ├── live_middle.wxss
│   ├── live_small.wxml
│   └── live_small.wxss
├── user
│   ├── user.wxml
│   ├── user.wxss
│   ├── user_small.wxml
│   └── user_small.wxss
└── widget
    ├── rating.wxml
    └── rating.wxss

widget 是更小层级的组件,也是本项目最小的组件单元了。它会被其他的如 user/live/hot 引用,而 user/live/hot 最后又会被 pages 下对应的模板引用。

捋一个组件化的例子

首先感受一下发现页:

发现页是 live 的集合。每个 live 都是一个 card,看上面的 components 目录,live 有三种组件,发现页用的是 user 类型的。

pages/explore.wxml 文件的内容是:

{% raw %}
<import src="../../components/live/live.wxml" />
<scroll-view class="list" style="height: {{ windowHeight }}px; width: {{ windowWidth }}px;" scroll-y="true" bindscrolltolower="loadMore" lower-threshold="800">
  <block wx:for="{{ lives }}" wx:for-item="live" wx:key="live.id">
    <template is="m-live" data="{{live: live}}" />
  </block>
  <view class="loading">
    正在加载...
  </view>
</scroll-view>
{% endraw %}

我简单介绍下这段模板的含义:

  1. import 语句对于写 Python,尤其是会 Mako 的同学非常好理解,就是引入文件。
  2. scroll-view 是「可滚动视图区域」容器,小程序自带的组件,我们可以上下滑动。
  3. bindscrolltolower="loadMore" 绑定了一个滚动到底部 / 右边就会触发的事件,会执行 loadMore 函数,之后会讲到。
  4. {% raw %}<block wx:for="{{lives}}">{% endraw %} 表示一个 for 循环的块。
  5. wx:for-item="live" 表示循环的每个元素赋值为 live。
  6. {% raw %}