小程序教程

【微信小程序开发系列文章二】视图层

字号+ 作者: 来源: 2016-11-23 09:49 我要评论( )

本帖最后由 斜月吟风 于 2016-10-31 11:54 编辑

上一篇中介绍过App这个函数,用来注册和建立整个app的基础主程序。
这一篇中要介绍另一个函数:Page,页面就是用它来创建的。 本文以一个小例子为线索来讲解。
1、页面文件
App跟Page的关系是,App在整个小程序中只能有一个,唯一的一个。但这个小程序中可以有多个页面,就是多个Page,这些页面相关的文件都放在根目录下的pages目录,每个页面主要由四类文件组成,分另以页面名命名,但不同的后缀的文件:xxx.js / xxx.wxml / xxx.wxss / xxx.json,如下图:
这个代码目录结构是我们这篇文章中要讲的一个小例子,pages/article 下面放的是文章相关的页面。文件夹article的名字你可以随便写,只要自己觉得目录结构合理就行,但同一个页面的四类文件,一定要用同一个名字,程序是通过这个名字来找到这个页面对应的各个相关文件的。这四个文件除了wxml之外都可以不是必须的,至少要有个wxml,想想就知道了,没有view层的代码哪知道页面要长什么样?js可以没有,就少些行为、数据、逻辑而已;wxss文件也可以没有,页面长得丑而已;json文件更可以没有(这个文件一般都没有),它的定义字段跟全局的app.json文件是一样的,它的作用是定义这页面的一些属性或者说配置,以覆盖全局定义的相关的属性。
文件夹的层次结构可以随便定义,甚至你想放到根目录其实都是可以的,只是同个页面相关文件要同名,且放同个目录。那么问题来了,程序怎么知道你的页面都放哪呢?
可能有读者已经回想起第一篇文章中讲到的 app.json 这个配置文件了。其中有一个pages的属性,它用来声明这个程序中到底有哪些页面的:
  1. {
  2.   "pages":[
  3.     "pages/index/index",
  4.     "pages/article/content"
  5.   ],
  6. ...
  7. }
复制代码

2、例子简介
下面我们先来总体分析一下这个小例子。上图:
借用36Kr网站的内容来做个很简单的小程序,新闻列表,点击查看内容。代码放在:https://github.com/jsongo/weapp-tutorial-2 这里。
本章的内容写死了数据,下一篇文章中我们会来介绍如何发起网络请求去取数据。
3、wxml代码分析
(1)相关组件
首先,这里会涉及到三种文件:wxml / wxss 和 js。index.wxml代码:
  1. <view class="container">
  2.   <scroll-view scroll-y="true" bindscrolltoupper="upper"
  3.     bindscrolltolower="lower" bindscroll="scroll" class="scroll-wrapper">
  4.     <view class="scroll-view-item" wx:for="{{news}}" data-id="{{item.id}}" catchtap="bindItemTap">
  5.       <image class="item-img" src="{{item.img}}"></image>
  6.       <view class="detail">
  7.         <text class="item-name">{{item.title}}</text>
  8.         <view class="tips">
  9.           <text class="item-info">文 / {{item.author}} {{item.time}}</text>
  10.           <text class="category">{{item.category}}</text>
  11.         </view>
  12.       </view>
  13.     </view>
  14.   </scroll-view>
  15. </view>
复制代码

view元素就相当于HTML中的<div>元素。重点介绍一下scroll-view组件,点击查看官网文档。它用来定义一个可滚动的视图区域,相当于ios开发中的UIScrollView。里面的内容如果超过这个区域的宽或高时,就会出现滚动条。上面定义的class为scroll-view-item的view就是一个36Kr的新闻项,一条新闻,布局很直观,左侧一个image,右侧一个新闻标题的<text>,加上补充信息。<text>相当于<p>标签,用来包裹文本内容。<image>就是图片元素,相当于HTML中的<img>标签。scroll-view有一些属性,重点介绍几个,它默认是不允许滚动的,用scroll-x来设置横向滚动(默认false),scroll-y设置竖直滚动(默认false)。bindscrolltoupper,这个属性用来设置滚动到顶部/左边时,触发 scrolltoupper 事件,用这个来做刷新。bindscrolltolower用来设置滚动到底部/右边时,触发 scrolltolower 事件,可以用来做加载更多的操作。本例子中没有用到这两个,不过加到代码中用来做使用说明。bindscroll用来绑定滚动时触发,这个可以用来做用户手势的判断等。其它属性,读者可以参数官方文档。
(2)数据绑定
数据绑定,在wxml文件里通过 两层大括号来定义,里面的内容会被解析成js代码,并把相关的变量绑定到相应的地方。
下面介绍下数据绑定。wxml代码就上面这么短,没有其它了,那它怎么做出整个完整的滚动列表这么多内容的?其实列表数据就定义在js文件里面:
  1. Page({
  2.   data: { // 当前页面的数据定义在这里
  3.     news: [
  4.       {
  5.         id: 1,
  6.         title: '苹果在华首家研发中心成立,能拯救大中华区的业绩吗?',
  7.         author: '卢晓明',
  8.         time: '12分钟前',
  9.         img: 'https://pic.36krcnd.com/avatar/201609/29013641/t87v7fekz7c3najy.jpg!feature',
  10.         category: '明星公司'
  11.       }
  12.     ]
  13.   },
  14.   ...
  15. })
复制代码

data字段,用来定义当前页面的所有的数据,可以理解为数据层,刚刚的wxml是view层,而Page({…})里的其它地方就是逻辑层。一个完整的MVC模型就很好理解了。
wxml里有个语法:wx:for,它会解析后面news这个数组,循环,一个个取出数据,并赋给item变量,里面就可以通过来引用这些数据了。所以代码写起来非常简洁。类似wx:for的还有wx:if等等,wx:if用来定义一个条件判断,如果成立,就会解析当前标签及其子标签。这里面除了以上提到的用法外,还可以做四则运算、三目运算、逻辑大小判断、字符串相加等,甚至可以定义成一个json对象,如:。还可以使用es6的语法,如 ,用…把myJson这个对象解包展开平铺到这个地方。相关的官方文档
如果要指定key和value的话,可以在wx:for 后加个wx:for-item指定value,用wx:for-index指定key或者说index。如果没指定,默认的value叫item,默认的key是index。
另外,微信文档上没提到,还有一个wx:for-items,跟wx:for一样。
有wx:if,那就应该有wx:else了。这里给个官方的例子,看下就明白了:
  1. <view wx:if="{{length > 5}}"> 1 </view>
  2. <view wx:elif="{{length > 2}}"> 2 </view>
  3. <view wx:else> 3 </view>
复制代码

文档除了介绍if之外,还讲了另一个类似功能的 hidden,使用:<view hidden=""> ... </view>当flag为true的时候,这个标签及其子标签会被隐藏,正好跟if相反,if是为true是解析、显示。另外hidden作用的表述跟上面说到wx:if时的表述是有区别的,wx:if是条件为真时才会去解析,而hidden是无论如何都会去解析代码,只是不显示这些标签而已。
<block>标签。这是一个不会显示出来的辅助标签,在wx:if时,如果你想作用于几个并列在一起的标签,那就可以用block把它们包起来。同样,如果你在用wx:for时,遇到没有外层标签的情况,只有几个标签并排着,这时也可以用block把它们包起来。
  1. <block wx:if="{{true}}">
  2.   <view> view1 </view>
  3.   <view> view2 </view>
  4. </block>
  5. <block wx:for="{{[1, 2, 3]}}">
  6.   <view> {{index}}: </view>
  7.   <view> {{item}} </view>
  8. </block>
复制代码

(3)事件绑定
列表中每个item都有一个事件的绑定,catchtap=”bindItemTap”,绑定在用户点击时做页面的切换。
组件的事件有很多种,每个组件都有自己定义的组件,如<input>有input事件,<form>有submit事件等,详见各组件。不过有一些大部分组件都共同的事件,主要有:touchstart、touchmove、touchend、touchcancel、longtap、tap。前三个都很熟悉了,其它几个,touchcancel指的是用户的触摸事件被其它东西打断,比如突然弹窗,或者突然有个电话进来。longtap就是长按事件,微信官网解释是指超过350ms的touch。tap就类似于pc浏览器上的click点击事件。
这六个事件都是会冒泡的事件,如果没有js基础的朋友不太理解冒泡,这里帮你们找了些文章来理解这个
解析Javascript事件冒泡机制
javascript 的事件冒泡与捕获及冒泡优势
除了这六个事件之外 ,其它事件都是非冒泡事件。事件绑定的方式就是用bind或catch这两个词,再加上事件的名称(全小写)就可以了。如bindtap, catchtap。他们的差别是,bind允许冒泡传递,而catch会阻止冒泡,就是把所有的事件都变成非冒泡事件。每个事件的响应函数一般会有个event参数,事件对象,这个参数的event.targe指向触发事件的组件,里面有它的一些属性值,而event.currentTarget则指向当前事件所在的组件,currentTarget是因为事件冒泡机制而设置的,它指向事件当前指向的组件,不一定是我们设置bindtap属性的那个组件,而target就永远都是指向这个绑定的原组件。
接下来讲下MINA设置的一个很有意思的属性集,event.dataset,这是一个event.target指向的组件里定义的data-xxx的属性数据的json对象,可以用event.dataset.xxx来引用,比如最上面我们本文demo的wxml代码中,在catchtap绑定的事件的标签里,有一个data-id属性,那么可以用event.dataset.id来引用这个id值。如果data-xxx里有大写,这里都会转化成小写,如data-myDemo,这时要用event.dataset.mydemo来引用;如果是类似data-abc-def这种格式的属性,则会变成abcDef,-减号后面的第一个字母会被大写。事件相关的官方文档在这
所以,讲到这里,希望读者能再回去读一下最开始的那个index.wxml代码,会有很多收获的。
4、js代码分析
上面提到的data不是一个普通的字段,它和用户界面绑定,改变它的值,界面里相应的地方也会自动跟着变。这模式是不是跟react很像?目测微信小程序内部包装了react的核心算法和思想。data里的值 ,跟react中的state一样,不要去直接用等号给它赋值或改变它的值,直接修改它是无效的,要调用setData(newObj)来修改,才能触发绑定的界面更新。传入的参数,定义你要改变的那个字段就行,比如data为{a: 1, b: 2, c: 3},要修改a的值,可以这样调用:this.setData({a: 11}),这时界面中绑定a变量的值就会立马跟着变化。
react带来的一个很大的思想变革就是,不希望你通过id或class去取得或设置一个页面dom元素的值,这也是我很喜欢react的原因,其实做前端开发的朋友平时最痛苦的事情之一就是,获取dom节点然后做相应的界面更新,然后,当页面重构师把页面结构改掉的时候,前端开发的js代码就到处冒烟了。前端另一个比较推荐的框架是angular.js,它通过事件绑定的方式来减少dom操作,更新界面。(扯远了。)
MINA框架其实帮开发者做了非常多的事情,包括刚刚提到的数据绑定、界面更新的逻辑,还有页面切换的逻辑,它内部帮我们管理了整个小程序的页面路由(Page()函数什么时候调用?跳转的时候为什么不用new页面?),整个页面的生命周期(下面介绍)等等,开发者只需要把数据注入到框架中,并在相应的生命周期函数中填写相关的逻辑代码就可以了。
刚提到的页面切换方式,顺便介绍下链接跳转的方式,跟HTML不同的地方是,它不是用类似<a>标签来实现的,而是通过事件来做的。页面跳转,通过 wx.navigateTo 这个接口来实现,点击进入官网api的说明。navigateTo会保留当前页面的条件下,切换到新页面,可以用wx.navigateBack 返回。然而还有另一个跳转的api,wx.redirectTo,它则不保留当前页面,会关闭它,官网没有提供这个跳转方式返回的api,或许这时候,要想回到之前的这个页面,就得去新建了。
两个跳转函数的参数都是一个object,格式如下:
  1. {
  2.     url: '',
  3.     success: callback, // 可选
  4.     fail: callback, // 可选
  5.     complete: callback // 可选,无论调用成功失败,都会执行
  6. }
复制代码

一般带上一个url就可以了。url格式:’../article/content’ 是个相对地址,指向新页面的位置,注意不要写后缀。
5、Page函数
跟介绍App函数一样,先来理解一下Page的生命周期。
初始化数据->页面加载完onLoad  ->页面渲染完onReady->页面显示onShow  ->页面隐藏onHide->页面卸载onUnload
数据可以随便定义在Page({ … })里成为一个属性,类似于react的props,而data属性里定义的数据就要注意些,它类似于react的state,用来实际绑定界面数据用的,它是一个json格式的数据。
onLoad、onReady、onShow这几个回调的关系相对比较复杂,后面会有一篇文章专门用来分析程序生命周期的文章,这里就先不多说了。后面写完后,会把链接贴过来。
事件的绑定函数,也是定义在Page里,成为它的一个“成员函数”。比如,上面我用曾定义了一个catchtap=“bindItemTap”,这里的bindItemTap定义在这里:
  1. var app = getApp()
  2. Page({


  3.   //事件处理函数
  4.   bindItemTap: function(event) {
  5.     var id = event.currentTarget.dataset.id, // 当前id
  6.       article = null;
  7.     // 找出当时点击的那一项的详细信息
  8.     for(var d of this.data.news) {
  9.       if(d.id == id) {
  10.         article = d;
  11.         break;
  12.       }
  13.     }
  14.     console.log(article);
  15.     if(!article) {
  16.       console.log('系统出错');
  17.       return;
  18.     }
  19.     // 设置到全局变量中去,让下个页面可以访问
  20.     app.globalData.curArticle = article;
  21.     // 切换页面
  22.     wx.navigateTo({
  23.       url: '../article/content'
  24.     });
  25.   },

  26. })
复制代码

这样就可以了,微信会帮我们把这个函数跟我们在wxml里定义的绑定关联起来。
第二篇到这里就基本介绍完了,欢迎读者继续关注本系列文章的第三篇《【微信小程序开发系列文章三】数据层》

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • 微信小程序 轮播图 swiper图片组件

    微信小程序 轮播图 swiper图片组件

    2016-11-23 09:49

  • 微信小程序 开发 微信开发者工具 快捷键

    微信小程序 开发 微信开发者工具 快捷键

    2016-11-23 09:49

  • 微信小程序 页面跳转 传递参数

    微信小程序 页面跳转 传递参数

    2016-11-23 09:49

  • 微信小程序 如何获取时间

    微信小程序 如何获取时间

    2016-11-23 09:49

网友点评