今天进入正题,看看下面效果的小程序是怎么实现的:
![](https://github.com/dongweiming/weapp-
zhihulive/raw/master/screenshot/zhihulive.png)
项目地址 https://github.com/dongweiming/weapp-zhihulive
PS: 本文是假设你已经看过微信小程序的官方文档、demo甚至已经动手写过小程序,否则建议先去翻翻再来看。
设计目录结构
我在上一节知乎Live全文搜索之微信小程序实战(一)/)介绍了组件化,今天就是要实施了。首先我们考虑一个只有index页面的小程序的目录结构是怎么样的:
1 |
|
pages目录下有个index目录,存放了名字叫做index,后缀为js/json/wxml/wxss的四个文件。这样做的好处是:
- index目录下存放了页面组件所需要的各种资源, 就近维护 。如果是React,还得通过使用各种loader,用import的方式来用,所以我喜欢小程序的处理方式。
- 当某天不再需要index这个页面,或者要替换成其他的组件,直接把index目录删掉/替换就完事了。
接着我们基于Live搜索,思考下如果页面变的复杂,重要元素多的场景: - 需要Live、User、Topic三大元素。
- 有些内容是可以重复被利用的,比如评分(就是大家熟悉的星星,5星满分,4.5星次之…)在Live详情页的效果比较大,而在搜索页由于区域小所以小了很多,但是本质上内容是一样的,只不过样式不同。
- 有些内容在不同页面重复出现,比如Live,在topic详情页、用户详情页、发现页都有,而且一样。
那么: - 评分是一个独立的区域,可以视作一个组件。
- 组件与组件之间应该可以 自由组合 ,所以组件的粒度要细,细到一个组件就是做一件事。
- 单个评分组件拿出来是无意义的,只有和Live信息汇合起来才是一个完整页面。
所以重新定义目录结构吧:
1 |
|
现在pages的每个子目录下后缀为js的文件就是页面逻辑。比如pages/users/users.js存放了/users/users的页面逻辑。
我新增了3个目录:
- utils。存放一些用得到的功能性的函数,如和后端通信的api.js,一会我们详细再看。
- images。存放公共的静态图片资源。
- components。组件目录,我们把抽象的元素都放在这里,比如评分是一个组件。组件目录下有这些文件:
1 |
|
widget是更小层级的组件,也是本项目最小的组件单元了。它会被其他的如user/live/hot引用,而user/live/hot最后又会被pages下对应的模板引用。
捋一个组件化的例子
首先感受一下发现页:
发现页是live的集合。每个live都是一个card,看上面的components目录,live有三种组件,发现页用的是user类型的。
pages/explore.wxml文件的内容是:
1 |
|
我简单介绍下这段模板的含义:
- import语句对于写Python,尤其是会Mako的同学非常好理解,就是引入文件。
- scroll-view是「可滚动视图区域」容器,小程序自带的组件,我们可以上下滑动。
- bindscrolltolower=”loadMore” 绑定了一个滚动到底部/右边就会触发的事件,会执行loadMore函数,之后会讲到。
- `` 表示一个for循环的块。
- wx:for-item=”live” 表示循环的每个元素赋值为live。
- `` 表示找名字叫做m-live的模板渲染,传入的参数是live,值为上面循环for-item指定的那个变量‘live’。
上面用import通过一个相对路径引入了../../components/live/live.wxml这个模板文件,它里面就会有这个叫做m-
live的模板,我们看看它的内容:
1 |
|
这个模板中还用到了import,引入了更小的组件单元rating.wxml,也用到了``这样的方式来调用rating.wxml中的名为m-
rating的模板。用这样的方式就实现了组件化,也保证了模板的整洁。
这个模板中还有个bindtap=”onViewTap”,相当于给这个div加了一个事件绑定,bindtap表示当用户点击该组件的时候会在该页面对应的Page中找到相应的事件处理函数onViewTap。
剩下的就是铺页面结构了,具体想展示什么数据,页面想设计成什么就铺成对应的view,有一点,标签你就当
理解好了。
数据绑定
现在大家对组件化的用法有了些了解,我们再看看视图和模板之间怎么做数据绑定的。
如果你熟悉jinja2或者Mustache,想必对于的语法很亲切。对,相当于将变量包起来。
那么其中的windowWidth、lives、windowHeight这些是哪里来的呢?
答案是「WXML 中的动态数据均来自对应 Page 的 data」,在这里就是pages/explore.js里面:
1 |
|
loadMore
实现一个简单的加载数据的逻辑,首先是在data下面初始化:
1 |
|
start/limit就是用来翻页的参数,后端拿到的结果合并到lives中,loading检查是不是已经请求过而减少后端压力:
1 |
|
有几点需要注意:
- 说一下
const self = this;
这样的用法的原因:this是一个特殊的对象,这个this对象是在运行时基于执行环境绑定的,即在全局对象中,this指向的是window对象;在自定义函数中,this对象指向的是调用这个函数的对象。常用的做法是在函数内赋值给一个私有的self(叫that或者其他也没有关系),这样就能保证在外部调用的时候使用的是函数对象了。 - setData就类似于React的setState。
- api.explore是封装好的api方法,后面会提到实现的原理。
小程序怎么和后端交互
我们后端提供JSON
API返回需要的数据,在小程序中如何来调用呢?小程序提供了wx.request这个方法来发起HTTP(S)请求。但是需要注意它不容许使用referer,所以图片不是直接从知乎获取,而是先下载到本地,再由本地来serve的。
我们基于wx.request封装一下:
1 |
|
由于这个项目都是一些获取资源的需求,一律用的GET,实现的就比较简单了。使用这个模块里面的那些函数就可以实现请求后端对应接口了。
组合这些页面
上面展示的是一个组件的实现。多个组件是路由的呢?在小程序中是靠全局配置文件app.json实现的:
1 |
|
其中:
- pages。设置页面路径,指定小程序由哪些页面组成。每一项代表对应页面的【路径+文件名】信息,数组的第一项代表小程序的初始页面。
- tabBar。设置底部tab的表现。
- window。设置页面路径。如状态栏、导航条、标题、窗口背景色等。
小程序的一些经验
上面就是我对小程序的玩法的理解了。说几个我开发中遇到的坑儿吧:事件冒泡
发现页的Live上有2种事件:点击头像会去用户详情页,其他位置会去Live详情页。事件绑定在组件上,触发事件就会执行逻辑层中对应的事件处理函数。想象一下,如果点击用户头像会发生什么:
- 进入用户详情页
- 然后再进入Live详情页
由于小程序的事件封装,我们不能使用e.stopPropagation()这样的方式阻止事件冒泡,所以在用户这个view上不能绑定bindtap,而是要用catchtap。在前面展示live.wxml的时候,你可以没注意到这点,我简化下:
1 |
|
绑定到同一个事件函数上:
1 |
|
抽象Page
我没有使用额外的辅助工具开发,不能使用继承的方式用Page:
1 |
|
这很不爽。你想啊,一些页面的LoadMore、Onload其实差不多。尤其是7天热门和30天热门这2个页面,不一样的只是调用了api.getHotByWeekly和api.getHotByMonthly,重复代码是不是有点不能忍?仔细看一下Page其实是接受了一个字典,每个其中的data和其他生命周期函数以及自定义方法都是键值对的方式传进去。那么这有点简单了,我写个函数:
1 |
|
需要注意一点,onChangeTab中对于Tab切换的时间的处理用到wx.navigateBack,我觉得这也是小程序实现的一个不好的地方,我需要特殊处理「这种打开了页面A(navigateTo),然后打开页面B(navigateTo),再用navigateTo就不能回到A了」的问题,得用navigateBack。
这样,调用的时候就简单了,比如7天的:
1 |
|
这是「知乎Live全文搜索的小程序」专题的最后一篇啦,感谢大家坚持阅读..
版权声明:本文由 董伟明 原创,未经作者授权禁止任何微信公众号和向掘金(juejin.im)转载,技术博客转载采用 保留署名-非商业性使用-禁止演绎 4.0-国际许可协议
python