微信小程序开发02-小程序基本介绍

2018/07/30 · 基础技术 ·
小程序

原文出处: 叶小钗   

现有的基于微信平台的电商公众号大多采用内容+店铺的方式发布商品,店铺大多是接入有赞、微店等平台提供的服务,像逻辑思维公众号,山茶花公众号。

前言

前面我们研究了下微信小程序的执行流程,因为拿不到源码,只能算我们的猜想,我们需要更加了解小程序还需要做具体的项目,于是我们将原来那套还算复杂的业务拿出来:

【组件化开发】前端进阶篇之如何编写可维护可升级的代码(有些晦涩有些乱,但是对于整体了解小程序结构有帮助)

我们用小程序实现这里的代码,看看是个什么样的体验,另外我这里想保证代码最大程度重用,为后续一端代码四端运行做前驱探索。

威尼斯人线上娱乐 1

页面复杂度还是比较高的,包括了:

① 弹出层

② 页面跳转

③ 缓存

④ 数据请求

⑤ 列表页、滚动分页

⑥ ……

我相信完成了这个例子,我们对小程序业务代码怎么写会有比较好的了解,于是让我们开始今天的代码吧。

通过有情怀和有温度的文字达到与用户的情感共鸣,并引导用户消费是这类公众号在商业模式上的闭环。为了提高用户在浏览商品时的体验,提高产品的质感,我参照「iDaily
app的UI设计开发了这个微信小程序-好物杂志

小程序的布局

它很简单,「每日推荐」是同步公众号每天发布的商品,图片要足够优质,文字要足够精华。「往期好物」以timeline的形式展示过去每天的产品列表,不多,每天的商品不会多于六种。用最简单的设计带来良好的用户体验。

为什么不使用HTML&CSS

微信小程序这种平台型的超越Hybrid系统诞生还是有一些客观条件的,其中一个就是移动端的应用相对来说简单的多,想想PC负责的布局,如果要使用小程序实现,那么复杂度会提高很多。

小程序代码编写逻辑层依旧使用JS完成,但是结构层以及样式层推出了:

① WXML,Weixin Markup
Language,是微信设计的一套标签语言,与HTML类似,做过React&Vue的同学会非常熟悉

② WXSS,WeiXin Style
Sheets,是一套样式语言,用于定义样式,与CSS类似,一般认为是CSS的子集

因为小程序中UI组件都是Native实现,所以小程序直接手起刀落压根放弃让我们使用HTML容器,这样做我觉得有个好处是:

为了更好的限制,我之前也在做Hybrid乃至前端框架,一般来说我会限制到View级别的实习,要求必须按照我的规则做,但是因为入口为index.html文件,我甚至将全局控制器App的实例化放到了main.js里面,只提供了建议的做法,事实上HTML还是太过灵活,有些同事逐渐根本不按照我们的规则玩,他觉得他的做法更好,但是这样一来便会破坏了项目的总体性,后续的工程性的优化或者监控可能就不能帮助他了,从某个角度来说,我是认可小程序的做法的。

我们之前在这里研究过自定义标签的做法:从DOM操作看Vue&React的前端组件化,顺带补齐React的demo

<article class=”cm-page page-list” id=”main”> <div
class=”js_sort_wrapper sort-bar-wrapper”> <mySortBar
:entity=”sortEntity”></mySortBar> </div> <myList
:entity=”listEntity” :sort=”sort”></myList> </article>

1
2
3
4
5
6
<article class="cm-page page-list" id="main">
    <div class="js_sort_wrapper sort-bar-wrapper">
        <mySortBar :entity="sortEntity"></mySortBar>
    </div>
    <myList :entity="listEntity" :sort="sort"></myList>
</article>

从这个文章以及小程序的实现可以看出基本的概念:


标签的出现根本不是做标签用,而是为了让JS捕捉执行相关逻辑,最后生成真正的标签


为了做更好的限制,小程序根本不提供入口index.html文件了,所以这里的标签是用作JS做模板解析后生成Native能识别的代码,更具体点说是,Native实现了一个组件,组件有很多规则,可以使用JS去调用,正如我们这里的header组件调用逻辑(JS会设置Native的Header组件展示),这里如果不太清晰可以参考下这个文章:浅谈Hybrid技术的设计与实现第二弹

当然,小程序底层具体是不是这么做,我们不得而知,如果有小程序的同事,可以指导下:),至此,我觉得可以从技术层面说明为什么不直接使用HTML&CSS了:更好的业务限制

  • 方便JS解析模板被Native执行。

下面介绍我的开发过程,主要是小程序内置组件、数据绑定和相关API的使用。

小程序组件

我们之前做Hybrid应用的时候,事实上只提供了一个真正具有结构的组件Header,其他loading类的提示组件都比较简单,而我们看看小程序提供了哪些组件呢:

小程序开发框架的目标是通过尽可能简单、高效的方式让开发者可以在微信中开发具有原生
APP 体验的服务。

容器类组件

view&scroll-view&swiper等作为容器组件存在,这里官方有基本介绍,我们这里看看其中一个即可:

威尼斯人线上娱乐 2

这里官方给了一个demo进行说明:

<view class=”section”> <view
class=”section__title”>flex-direction: row</view> <view
class=”flex-wrp” style=”flex-direction:row;”> <view
class=”flex-item bc_green”>1</view> <view class=”flex-item
bc_red”>2</view> <view class=”flex-item
bc_blue”>3</view> </view> </view> <view
class=”section”> <view class=”section__title”>flex-direction:
column</view> <view class=”flex-wrp” style=”height:
300px;flex-direction:column;”> <view class=”flex-item
bc_green”>1</view> <view class=”flex-item
bc_red”>2</view> <view class=”flex-item
bc_blue”>3</view> </view> </view>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<view class="section">
  <view class="section__title">flex-direction: row</view>
  <view class="flex-wrp" style="flex-direction:row;">
    <view class="flex-item bc_green">1</view>
    <view class="flex-item bc_red">2</view>
    <view class="flex-item bc_blue">3</view>
  </view>
</view>
<view class="section">
  <view class="section__title">flex-direction: column</view>
  <view class="flex-wrp" style="height: 300px;flex-direction:column;">
    <view class="flex-item bc_green">1</view>
    <view class="flex-item bc_red">2</view>
    <view class="flex-item bc_blue">3</view>
  </view>
</view>

@import “../lib/weui.wxss”; .page-section{ margin-bottom: 20rpx; }
.flex-wrp {display: flex;} .bc_green {background: green;width:100px;
height: 100px;} .bc_red {background: red;width:100px; height: 100px;}
.bc_blue {background: blue;width:100px; height: 100px;}

1
2
3
4
5
6
7
8
9
@import "../lib/weui.wxss";
 
.page-section{
  margin-bottom: 20rpx;
}
.flex-wrp {display: flex;}
.bc_green {background: green;width:100px; height: 100px;}
.bc_red {background: red;width:100px; height: 100px;}
.bc_blue {background: blue;width:100px; height: 100px;}

威尼斯人线上娱乐 3

可以将这个标签理解为div类组件。


swipe

一般来说,Native提供的轮播图体验要好得多,所以这里也提供了一个Native的组件:

<view class=”container”> <view class=”page-body”> <view
class=”page-section page-section-spacing swiper”> <swiper
indicator-dots=”{{indicatorDots}}” autoplay=”{{autoplay}}”
circular=”{{circular}}” vertical=”{{vertical}}” interval=”{{interval}}”
duration=”{{duration}}” previous-margin=”{{previousMargin}}px”
next-margin=”{{nextMargin}}px”> <block wx:for=”{{background}}”
wx:key=”*this”> <swiper-item> <view class=”swiper-item
{{item}}”></view> </swiper-item> </block>
</swiper> </view> <view class=”page-section”
style=”margin-top: 40rpx;margin-bottom: 0;”> <view
class=”weui-cells weui-cells_after-title”> <view class=”weui-cell
weui-cell_switch”> <view
class=”weui-cell__bd”>指示点</view> <view
class=”weui-cell__ft”> <switch checked=”{{indicatorDots}}”
bindchange=”changeProperty” data-property-name=”indicatorDots” />
</view> </view> <view class=”weui-cell
weui-cell_switch”> <view
class=”weui-cell__bd”>自动播放</view> <view
class=”weui-cell__ft”> <switch checked=”{{autoplay}}”
bindchange=”changeProperty” data-property-name=”autoplay” />
</view> </view> <view class=”weui-cell
weui-cell_switch”> <view
class=”weui-cell__bd”>衔接滑动</view> <view
class=”weui-cell__ft”> <switch checked=”{{circular}}”
bindchange=”changeProperty” data-property-name=”circular” />
</view> </view> <view class=”weui-cell
weui-cell_switch”> <view
class=”weui-cell__bd”>竖向</view> <view
class=”weui-cell__ft”> <switch checked=”{{vertical}}”
bindchange=”changeProperty” data-property-name=”vertical” />
</view> </view> </view> </view> <view
class=”page-section page-section-spacing”> <view
class=”page-section-title”>
<text>幻灯片切换时长(ms)</text> <text
class=”info”>{{duration}}</text> </view> <slider
value=”{{duration}}” min=”500″ max=”2000″ bindchange=”changeProperty”
data-property-name=”duration” /> <view
class=”page-section-title”>
<text>自动播放间隔时长(ms)</text> <text
class=”info”>{{interval}}</text> </view> <slider
value=”{{interval}}” min=”2000″ max=”10000″ bindchange=”changeProperty”
data-property-name=”interval” /> <view
class=”page-section-title”> <text>前边距(px)</text>
<text class=”info”>{{previousMargin}}</text> </view>
<slider value=”{{previousMargin}}” min=”0″ max=”50″
bindchange=”changeProperty” data-property-name=”previousMargin” />
<view class=”page-section-title”>
<text>后边距(px)</text> <text
class=”info”>{{nextMargin}}</text> </view> <slider
value=”{{nextMargin}}” min=”0″ max=”50″ bindchange=”changeProperty”
data-property-name=”nextMargin” /> </view> </view>
</view>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<view class="container">
  <view class="page-body">
    <view class="page-section page-section-spacing swiper">
      <swiper
        indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" circular="{{circular}}" vertical="{{vertical}}"
        interval="{{interval}}" duration="{{duration}}" previous-margin="{{previousMargin}}px" next-margin="{{nextMargin}}px">
        <block wx:for="{{background}}" wx:key="*this">
          <swiper-item>
            <view class="swiper-item {{item}}"></view>
          </swiper-item>
        </block>
      </swiper>
    </view>
    <view class="page-section" style="margin-top: 40rpx;margin-bottom: 0;">
      <view class="weui-cells weui-cells_after-title">
        <view class="weui-cell weui-cell_switch">
          <view class="weui-cell__bd">指示点</view>
          <view class="weui-cell__ft">
            <switch checked="{{indicatorDots}}" bindchange="changeProperty" data-property-name="indicatorDots" />
          </view>
        </view>
        <view class="weui-cell weui-cell_switch">
          <view class="weui-cell__bd">自动播放</view>
          <view class="weui-cell__ft">
            <switch checked="{{autoplay}}" bindchange="changeProperty" data-property-name="autoplay" />
          </view>
        </view>
        <view class="weui-cell weui-cell_switch">
          <view class="weui-cell__bd">衔接滑动</view>
          <view class="weui-cell__ft">
            <switch checked="{{circular}}" bindchange="changeProperty" data-property-name="circular" />
          </view>
        </view>
        <view class="weui-cell weui-cell_switch">
          <view class="weui-cell__bd">竖向</view>
          <view class="weui-cell__ft">
            <switch checked="{{vertical}}" bindchange="changeProperty" data-property-name="vertical" />
          </view>
        </view>
      </view>
    </view>
 
    <view class="page-section page-section-spacing">
      <view class="page-section-title">
        <text>幻灯片切换时长(ms)</text>
        <text class="info">{{duration}}</text>
      </view>
      <slider value="{{duration}}" min="500" max="2000" bindchange="changeProperty" data-property-name="duration" />
      <view class="page-section-title">
        <text>自动播放间隔时长(ms)</text>
        <text class="info">{{interval}}</text>
      </view>
      <slider value="{{interval}}" min="2000" max="10000" bindchange="changeProperty" data-property-name="interval" />
      <view class="page-section-title">
        <text>前边距(px)</text>
        <text class="info">{{previousMargin}}</text>
      </view>
      <slider value="{{previousMargin}}" min="0" max="50" bindchange="changeProperty" data-property-name="previousMargin" />
      <view class="page-section-title">
        <text>后边距(px)</text>
        <text class="info">{{nextMargin}}</text>
      </view>
      <slider value="{{nextMargin}}" min="0" max="50" bindchange="changeProperty" data-property-name="nextMargin" />
    </view>
  </view>
</view>

Page({ data: { background: [‘demo-text-1’, ‘demo-text-2’,
‘demo-text-3’], indicatorDots: true, vertical: false, autoplay: false,
circular: false, interval: 2000, duration: 500, previousMargin: 0,
nextMargin: 0 }, changeProperty: function (e) { var propertyName =
e.currentTarget.dataset.propertyName var newData = {}
newData[propertyName] = e.detail.value this.setData(newData) },
changeIndicatorDots: function (e) { this.setData({ indicatorDots:
!this.data.indicatorDots }) }, changeAutoplay: function (e) {
this.setData({ autoplay: !this.data.autoplay }) }, intervalChange:
function (e) { this.setData({ interval: e.detail.value }) },
durationChange: function (e) { this.setData({ duration: e.detail.value
}) } })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Page({
  data: {
    background: [‘demo-text-1’, ‘demo-text-2’, ‘demo-text-3’],
    indicatorDots: true,
    vertical: false,
    autoplay: false,
    circular: false,
    interval: 2000,
    duration: 500,
    previousMargin: 0,
    nextMargin: 0
  },
  changeProperty: function (e) {
    var propertyName = e.currentTarget.dataset.propertyName
    var newData = {}
    newData[propertyName] = e.detail.value
    this.setData(newData)
  },
  changeIndicatorDots: function (e) {
    this.setData({
      indicatorDots: !this.data.indicatorDots
    })
  },
  changeAutoplay: function (e) {
    this.setData({
      autoplay: !this.data.autoplay
    })
  },
  intervalChange: function (e) {
    this.setData({
      interval: e.detail.value
    })
  },
  durationChange: function (e) {
    this.setData({
      duration: e.detail.value
    })
  }
})

有demo有代码,还是比较清晰。

小程序框架

小程序提供了自己的视图层描述语言 WXML 和 WXSS,以及基于 JavaScript
的逻辑层框架,每个页面开发包括三个部分:页面布局wxml,样式wxss和处理逻辑js。

movable-area

提供一个可以移动的区域,暂时没想到应用场景……

数据绑定

小程序使用Mustache愈发{{data}}的方式绑定视图层(View)和(App
Service)数据,当做数据修改的时候,只需要在逻辑层修改数据,视图层就会做相应的更新。
<pre>
<view class=”title-text”>
<text>{{title1}}</text>
</view>
</pre>

icon

图标,小程序这边还扩展了一下,给了很多默认的图标样式,能满足基本需求

首页的完整Demo如下

威尼斯人线上娱乐 4

今日推荐

text

文本

第一步 实现底部标签tabBar

小程序的底部tab栏在app.json文件配置
<pre>
“tabBar”: {
“color”: “#a9b7b7”,
“selectedColor”: “#e04ef3”,
“borderStyle”:”white”,
“list”: [
{
“selectedIconPath”: “img/Category.png”,
“iconPath”: “img/Category.png”,
“pagePath”: “pages/history/history”,
“text”: “往期好物”
},
{
“selectedIconPath”: “img/favorite.png”,
“iconPath”: “img/favorite.png”,
“pagePath”: “pages/index/index”,
“text”: “今日推荐”
}…
]
}
</pre>

rich-text

富文本,用于展示文章,支持HTML,这里的nodes属性建议使用数组,类型,还不如系统自己解析js算了,因为不会有人像这样写代码(nodes看上去很蠢):

JavaScript

Page({ data: { html: ‘<div class=”div_class” style=”line-height:
60px; color:
red;”>Hello World!</div><script>console.log(1)</script>’,
nodes: [{ name: ‘div’, attrs: { class: ‘div_class’, style:
‘line-height: 60px; color: red;’ }, children: [{ type: ‘text’, text:
‘Hello World!’ }] }] }, tap() { console.log(‘tap’) } })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Page({
  data: {
    html: ‘<div class="div_class" style="line-height: 60px; color: red;">Hello&nbsp;World!</div><script>console.log(1)</script>’,
    nodes: [{
      name: ‘div’,
      attrs: {
        class: ‘div_class’,
        style: ‘line-height: 60px; color: red;’
      },
      children: [{
        type: ‘text’,
        text: ‘Hello&nbsp;World!’
      }]
    }]
  },
  tap() {
    console.log(‘tap’)
  }
})
第二步 实现轮播图

「今日推荐」页面嵌套了两个<swiper>组件,swiper1用于多个商品切换,swiper2用于单个商品图片自动轮播。实现代码如下:
<pre>
<swiper indicator-dots=”{{indicatorDots}}” autoplay=”{{autoplay}}”
interval=”{{interval}}” duration=”{{duration}}” class=”swiper”>
<block wx:for=”{{banners1}}”>
<swiper-item>
<image src=”{{item}}” class=”slide-image”/>
</swiper-item>
</block>
</swiper>
<view class=”title-text”>
<text>{{title1}}</text>
</view>
<view class=”detail-text”>
{{detail1}}
</view>
</view>
</swiper-item>
</pre>

在js中设置<swiper>相关参数实现自动轮播

<pre>
indicatorDots:true,
autoplay:true,//开始自动切换
interval:6000,
duration:1000,
</pre>
由于微信严格限制小程序编译包大小,不能将大图片放到项目目录中,我们将大图片上传到了服务器,在js中绑定data,

<pre>
data:{
banners1:[
http://***.***.***.246/xcx2/img/xl.jpg‘,
http://***.***.***.246/xcx2/img/xl1.jpg‘,
http://***.***.\*\*\*.246/xcx2/img/xl2.jpg‘,
]
}
</pre>
然后使用
<pre>
wx:for
</pre>

方法将{{banners}}数组中的每一项渲染到swiper-item中。