关于微信小程序的基础开发与创建

1-1小程序介绍及安装

开发环境搭建
  • 工具下载

微信web开发者工具

推荐下载稳定版,并根据自己的电脑选择下载对应的版本进行安装

  • 注册账号

微信公众平台

点击小程序图标,注册小程序开发者账号

创建第一个实验项目
  • 小程序后台

完成注册后,即可登陆小程序后台,在自己的小程序后台对小程序进行开发和管理。

  • 微信web开发者工具

登陆微信web开发者工具

点击右边图片中的加号,即可开始创建小程序

新建项目

如果不使用测试号,也可直接使用自己的AppID

点击“新建”按扭,进入编辑页面

创建第一个实验项目

创建完项目,可以返回主页面对项目进行预览和管理

逻辑层初步介绍

除了视图层(即我们所看到的页面),小程序逻辑层各部分的功能如下:

  • index.js 逻辑代码
  • index.json 页面配置文件
  • index.wxml 页面结构
  • index.wxss 表示index.wxml结构的样式

app.js app.json app wxss的作用与index是类似的,但它们作用于全局

1-2小程序框架及文件介绍

小程序框架介绍

小程序框架的核心是一个响应的数据绑定系统。整个系统分为两块,视图层和逻辑层。

视图层 (view)
  • 内容展示
  • 文本样式

视图层由WXML与WXSS编写。

将逻辑层的数据反应成视图,同时将视图层的事件发送给逻辑层。

WXML(WeiXin Markup language)用于描述页面的结构。

WXSS(WeiXin Style Sheet)用于描述页面的样式。

组件(Component)是视图的基本组成单元。

逻辑层(app service)
  • 业务逻辑
  • 数据处理

小程序开发框架的逻辑层是由JavaScript编写。

逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈。

每个页面有独立的作用域,并提供模块化能力。

小程序文件介绍

小程序文件包含一个描述整体程序的app和多个描述各自页面的page.

页面

一个页面由四个文件组成,如下所示:

  • index(默认)

小程序注册及配置
  • 入口文件
  • 配置文件
  • 全局样式

app.js文件,用来定义小程序的全局数据和函数,控制、监听小程序的全生命周期。在这里可以见到的全局函数有onlaunch(监听小程序初始化),onshow(监听小程序显示),onhide(监听小程序隐藏)等。app.js中还可以定义一些全局变量,其他页面引用app.js文件后就可以直接使用这个文件中的函数和变量。

app.json是配置文件,可以配置以下信息:页面路径,窗口信息,标签导航,网络超时等。

app.wxss,类似于css,用于配置各种样式

project.config.json用于保存配置信息。

sitemap.json用于控制小程序是否能够被搜索到。

小程序的执行流程
小程序注册及配置
  • 注册应用 app.js
  • 全局配置 app.json
  • 设置样式 app.wxss
  • 工具配置 project.config.json
页面渲染执行 - app.json
  • 主页面(index)
  • 从页面(index2)
  • 日志信息(logs)
json文件介绍
特点
  • 轻量级的数据交换格式
  • 直观理解
  • 便于修改
结构
  • 文件数据被包裹在一个大括号中 {}
  • 通过键值对(key-value)的方式来表达数据

1-3配置文件和顶部导航栏

app.json

app.json是全局配置文件,我们进入到app.json看一下。

pages

试着对pages进行一些修改,进行编译

可以看到pages下面多了一个test目录。

实际上,pages接受一个数组,每一项都是字符串,来指定小程序由哪些页面组成。每一项代表对应页面的【路径+文件名】信息,数组的第一项代表小程序的初始页面。小程序中新增/减少页面,都需要对pages数组进行修改。

文件名不需要写文件后缀,因为框架会自动去寻找路径下.json、.js、.wxml、.wxss四个文件进行整合。

window

用于定义页面的背景颜色,字体样式,文字颜色等等。

enablePullDownRefresh

:设置是否进行下拉刷新

backgroundColor

: 设置下拉背景颜色,颜色为HEXcolor编码

backgroundTextStyle

:设置下拉进度条的颜色,它只有两种选择,一种是light,一种是dark

navigationBarBackgroundColor

:设置导航栏背景颜色,颜色为HEXcolor编码

navigationBarTitleText

:设置导航栏文本

navigationBarTextStyle

:设置导航栏文本颜色,同样地,颜色为HEXcolor编码

2-1、2-2小程序wxml基本语法

小程序开发语言

  • wxml语法
  • wxss语法
  • js语法
wxml语法

WXML(WeiXin Markup Language),它与HTML有很多相似之处。它是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。

什么是组件:
  • 组件是视图层的基本组成单元
  • 组件自带一些功能与微信风格的样式

一个组件通常包括

开始标签

 和

结束标签

属性

用来修饰这个组件,

内容

在两个标签之内。

基本写法

写注释

<!--注释-->

 (快捷方式:ctrl+/)

写标签

<标签名 属性名1=“属性值1”……>……</标签名>

所有元素都必须闭合标签

<text>hello world</text>

1.标签必须使用小写

2.不可以含有中文

常用的属性类型
体会text与view组件的区别

{{ }}的作用
  • {{ }}中的内容为动态数据,若内容为数字与字符串相加,则会默认数字为字符串格式。详见数据绑定部分内容。
基础语法介绍
  • 数据绑定

wxml中的动态数据均来自对应page的data,数据绑定使用 Mustache 语法(双大括号)将变量包起来

列表渲染

在组件上使用 

wx:for

 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。

默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item

使用 

wx:for-item

  •  可以指定数组当前元素的变量名,

使用 

wx:for-index

 可以指定数组当前下标的变量名

条件渲染

在框架中,使用 

wx:if=""

 来判断是否需要渲染该代码块:

<view wx:if="{{condition}}"> True </view>

在下图中,使用条件渲染并设置了false,即该条件后的语句均不起作用,因此该语句后面的文本并没有显示出来

另外,使用

hidden

也可以不显示文本或组件。

wx:if

vs 

hidden

因为 wx:if 之中的模板也可能包含数据绑定,所以当 wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。

同时 wx:if 也是惰性的,如果在初始渲染条件为 false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。

相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。

一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好。

也可以用 

wx:elif

 和 

wx:else

 来添加一个 else 块:

<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>

2-3 小程序WXSS基本语法

尺寸单位(px和rpx)

px(pixel):物理像素,设备的实际分辨率,也叫设备像素或绝对像素,用 px 表示一个像素点单位。

rpx(responsive pixel): 逻辑分辨率,可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素

注意: 在较小的屏幕上不可避免的会有一些毛刺,开发时尽量避免这种情况

样式设置
内联样式

框架组件上支持使用 style、class 属性来控制组件的样式。

style:静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,请尽量避免将静态的样式写进 style 中,以免影响渲染速度。

<view style="color:{{color}};" />

class:用于指定样式规则,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上.,样式类名之间用空格分隔

<view class="normal_view" />

一个简单的例子:

除了以上这些简单的例子,wxss样式属性主要包括以下几类:

  1. wxss display (显示)
  2. wxss position(定位)
  3. wxss float(浮动)
  4. wxss background(背景)
  5. wxss border (边框)
  6. wxss outline(轮廓)
  7. wxss text(文本属性)
  8. wxss font(字体属性)
  9. wxss margin(外边距)
  10. wxss padding(填充)

w3cschool      https://www.w3cschool.cn/css/css-tutorial.html

盒子模型

盒子模型来自于CSS,它是CSS布局的基础,CSS假定每个元素都会生成一个或多个矩形框,每个元素框中心都有一个内容区(content),这个内容区周围有内边距(padding)、边框(border)和外边距(margin),这些项默认宽度为0,这个矩形框就是常说的盒子模型。

一个简单的例子:

4-1小程序流程及生命周期函数导航栏

小程序运行机制

整个小程序框架系统分为两部分:逻辑层(App Service)和 视图层(View)。

每个小程序都需要在 app.js 中调用 App 方法注册小程序示例,绑定生命周期回调函数、错误监听和页面不存在监听函数等。

App(Object object)

注册小程序。接受一个 Object 参数,其指定小程序的生命周期回调等。

App() 必须在 app.js 中调用,必须调用且只能调用一次。不然会出现无法预期的后果。

// app.js

App({

    onLaunch:function(){……

    },

    globalData:{……

    }

})

//app.json

{

    "pages":[

        "pages/index/index",

        "pages/logs/logs"

        ],

}

//index.js

//获取应用实例

const app=getApp()

Page({

    data:{……

    },

//事件处理函数

    bindViewTap:function(){……

    },

    onload:function(){……

    },

    getUserInfo:function(e){……

    }

})

参数
小程序加载及生命周期
几个核心生命周期函数

页面主要的生命周期

onLoad

页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。

onShow

页面显示/切入前台时触发。

onReady

页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。

onHide

页面隐藏/切入后台时触发。

onUnload

页面卸载时触发。

下部导航栏

tabBar

将图标的图片文件夹放入项目文件夹中

4-2组件概述-视图容器

小程序的组件

小程序的界面是由一系列组件构成的,小程序基础序提供了一组基础组件来满足开发者的需求。它包括以下几种组件:

视图容器

基础内容

表单组件

导航

媒体组件

地图

画布

视图容器

视图容器就是将页面分割为独立的,不同的部分。视图容器共包含:

view(视图容器)属性说明:

例:

4-3弹性布局

页面内容的弹性布局

布局的传统解决方案,基于盒状模型,它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。

2009年,W3C提出了一种新的方案—-Flex布局,可以简便、完整、响应式地实现各种页面布局。

采用Flex布局的元素,称为Flex容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为Flex项目(flex item),简称”项目”。

容器的属性
  • flex-direction
  • flex-wrap
  • flex-flow
  • justify-content
  • align-items
  • align-content
flex-direction

决定主轴的方向(即项目的排列方向)。

  • row(默认值):主轴为水平方向,起点在左端。
  • row-reverse:主轴为水平方向,起点在右端。
  • column:主轴为垂直方向,起点在上沿。
  • column-reverse:主轴为垂直方向,起点在下沿。
flex-wrap

默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。

它可能取三个值。

  • nowrap(默认):不换行。
  • wrap:换行,第一行在上方。
  • wrap-reverse:换行,第一行在下方。
flex-flow

flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。

justify-content属性

justify-content属性定义了项目在主轴上的对齐方式。

它可能取5个值,具体对齐方式与轴的方向有关。下面假设主轴为从左到右。

  • flex-start(默认值):左对齐
  • flex-end:右对齐
  • center: 居中
  • space-between:两端对齐,项目之间的间隔都相等。
  • space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。
align-items属性

align-items属性定义项目在交叉轴上如何对齐。

它可能取5个值。具体的对齐方式与交叉轴的方向有关,下面假设交叉轴从上到下。

  • flex-start:交叉轴的起点对齐。
  • flex-end:交叉轴的终点对齐。
  • center:交叉轴的中点对齐。
  • baseline: 项目的第一行文字的基线对齐。
  • stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。
align-content属性

align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

该属性可能取6个值。

  • flex-start:与交叉轴的起点对齐。
  • flex-end:与交叉轴的终点对齐。
  • center:与交叉轴的中点对齐。
  • space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
  • space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
  • stretch(默认值):轴线占满整个交叉轴。

更多内容请参考:flex布局语法教程

案例

/*index.wxss*/

.box{

    display:inlineflex;

    flex-direction:row;

    justify-content:space-around;

    flex-wrap:wrap;

    align-items:center

    /*align-content:flex-start;*/

}

.box_2{

    display:flex;

    background-color:greenyellow;

}

<view> 顶部</view>

<view class='box'>

  <!-- <view class='box_2'>

    <view class='box_3'>

      1-XXX

    </view>

    <view class='box_3'>

      1-YYY

    </view>

    <view class='box_3'>

      1-ZZZ

    </view>

  </view>

  <view class='box_2'>

    <view class='box_3'>

      2-XXX

    </view>

    <view class='box_3'>

      2-YYY

    </view>

    <view class='box_3'>

      2-ZZZ

    </view>

  </view>

  <view class='box_2'>

    <view class='box_3'>

      3-XXX

    </view>

    <view class='box_3'>

      3-YYY

    </view>

    <view class='box_3'>

      3-ZZZ

    </view>

  </view> -->

  <view class='box_2'>

    <view class='box_3'>



      <text>\n\n4-X</text>



    </view>

    <view class='box_3'>

      <text>\n4-Y\n</text>





    </view>

    <view class='box_3'>



      <text>4-Z\n\n</text>



    </view>

    </view>

  <view class='box_2'> 

    <view class='box_3'>

      5-XX

      <text>\n</text>

      <text>\n</text>

    </view>

    <view class='box_3'>

      5-YY

      <text>\n</text>

      <text>\n</text>

    </view>

    <view class='box_3'>

      5-ZZ

      <text>\n</text>

      <text>\n</text>

    </view>

  </view>

    <view class='box_2'> 

    <view class='box_3'>

      <text>\n</text>

      <text>\n</text>

      <text>\n\n6-X</text> 

    </view>

    <view class='box_3'>

      <text>\n</text>

      <text>\n</text>

      <text>\n\n6-Y</text>    

    </view>

    <view class='box_3'>

4-4轮播组件

swiper组件

滑块视图容器。其中只可放置swiper-item组件,否则会导致未定义的行为。

swiper-item

仅可放置在swiper组件中,宽高自动设置为100%。

案例

//index.js

Page({

    data:{

        background:['a','b','c']

},

//index.wxml

.si{

    display:block;

    height:150px;

}

.a{

    background-color:yellow;    

}

.b{

    background-color:red;

}

.c{

    background-color:blue;

}

//index.wxss

<view>

    <swiper autoplay="true" interval="2000"

    indicator-dots="true">

    <block wx:for="{{background}}" wx:key='swip'>

        <swiper-item>

            <view class='si {{itme}}'></view>

        </swiper-item>

    </block>

    </swiper-item>

</view> 

4-5 基础内容组件

小程序基础内容组件包含图标icon,进度条progress,富文本rich-text,和文本text等组件。

  • icon
  • progress
  • rich-text
  • text
icon

图标。组件属性的长度单位默认为px,2.4.0起支持传入单位(rpx/px)

progress

进度条。组件属性的长度单位默认为px,2.4.0起支持传入单位(rpx/px)。

rich-text

副文本

text

文本

4-6 表单组件

button

按钮

示例代码:

.button-hover {

  background-color: red;

}

.other-button-hover {

  background-color: blue;

}

<button type="default" size="{{defaultSize}}" loading="{{loading}}" plain="{{plain}}"

  disabled="{{disabled}}" bindtap="default" hover-class="other-button-hover"> default </button>

<button type="primary" size="{{primarySize}}" loading="{{loading}}" plain="{{plain}}"

  disabled="{{disabled}}" bindtap="primary"> primary </button>

<button type="warn" size="{{warnSize}}" loading="{{loading}}" plain="{{plain}}"

  disabled="{{disabled}}" bindtap="warn"> warn </button>

<button bindtap="setDisabled">点击设置以上按钮disabled属性</button>

<button bindtap="setPlain">点击设置以上按钮plain属性</button>

<button bindtap="setLoading">点击设置以上按钮loading属性</button>

<button open-type="contact">进入客服会话</button>

<button open-type="getUserInfo" lang="zh_CN" bindgetuserinfo="onGotUserInfo">获取用户信息</button>

<button open-type="openSetting">打开授权设置页</button>

var types = ['default', 'primary', 'warn']

var pageObject = {

  data: {

    defaultSize: 'default',

    primarySize: 'default',

    warnSize: 'default',

    disabled: false,

    plain: false,

    loading: false

  },

  setDisabled: function(e) {

    this.setData({

      disabled: !this.data.disabled

    })

  },

  setPlain: function(e) {

    this.setData({

      plain: !this.data.plain

    })

  },

  setLoading: function(e) {

    this.setData({

      loading: !this.data.loading

    })

  },

  onGotUserInfo: function(e) {

    console.log(e.detail.errMsg)

    console.log(e.detail.userInfo)

    console.log(e.detail.rawData)

  },

}



for (var i = 0; i < types.length; ++i) {

  (function(type) {

    pageObject[type] = function(e) {

      var key = type + 'Size'

      var changedData = {}

      changedData[key] =

        this.data[key] === 'default' ? 'mini' : 'default'

      this.setData(changedData)

    }

  })(types[i])

}



Page(pageObject)

checkbox

多选项目。

示例代码:

<checkbox-group bindchange="checkboxChange">

  <label class="checkbox" wx:for="{{items}}">

    <checkbox value="{{item.name}}" checked="{{item.checked}}"/>{{item.value}}

  </label>

</checkbox-group>

Page({

  data: {

    items: [

      {name: 'USA', value: '美国'},

      {name: 'CHN', value: '中国', checked: 'true'},

      {name: 'BRA', value: '巴西'},

      {name: 'JPN', value: '日本'},

      {name: 'ENG', value: '英国'},

      {name: 'TUR', value: '法国'},

    ]

  },

  checkboxChange: function(e) {

    console.log('checkbox发生change事件,携带value值为:', e.detail.value)

  }

})

.checkbox{

    display:flex;

    flex-direction:row-reverse;

}

editor

富文本编辑器,可以对图片、文字进行编辑。

编辑器导出内容支持带标签的 html和纯文本的 text,编辑器内部采用 delta 格式进行存储。

通过setContents接口设置内容时,解析插入的 html 可能会由于一些非法标签导致解析错误,建议开发者在小程序内使用时通过 delta 进行插入。

富文本组件内部引入了一些基本的样式使得内容可以正确的展示,开发时可以进行覆盖。需要注意的是,在其它组件或环境中使用富文本组件导出的html时,需要额外引入这段样式,并维护的结构。

图片控件仅初始化时设置有效。

示例代码:

<editor

  id="editor"

  class="ql-container"

  placeholder="{{placeholder}}"

  showImgSize

  showImgToolbar

  showImgResize

  bindstatuschange="onStatusChange"

  read-only="{{readOnly}}"

  bindready="onEditorReady">

</editor>

Page({

  readOnlyChange() {

    this.setData({

      readOnly: !this.data.readOnly

    })

  },

  onEditorReady() {

    const that = this

    wx.createSelectorQuery().select('#editor').context(function (res) {

      that.editorCtx = res.context

    }).exec()

  },

  format(e) {

    let { name, value } = e.target.dataset

    if (!name) return

    // console.log('format', name, value)

    this.editorCtx.format(name, value)

  },

})

form

表单。将组件内的用户输入的switch input checkbox slider radio picker 提交。

当点击 form 表单中 form-type 为 submit 的 button 组件时,会将表单组件中的 value 值进行提交,需要在表单组件中加上 name 来作为 key。

示例代码:

<form bindsubmit="formSubmit" bindreset="formReset">

  <view class="section section_gap">

    <view class="section__title">switch</view>

    <switch name="switch"/>

  </view>

  <view class="section section_gap">

    <view class="section__title">slider</view>

    <slider name="slider" show-value ></slider>

  </view>



  <view class="section">

    <view class="section__title">input</view>

    <input name="input" placeholder="please input here" />

  </view>

  <view class="section section_gap">

    <view class="section__title">radio</view>

    <radio-group name="radio-group">

      <label><radio value="radio1"/>radio1</label>

      <label><radio value="radio2"/>radio2</label>

    </radio-group>

  </view>

  <view class="section section_gap">

    <view class="section__title">checkbox</view>

    <checkbox-group name="checkbox">

      <label><checkbox value="checkbox1"/>checkbox1</label>

      <label><checkbox value="checkbox2"/>checkbox2</label>

    </checkbox-group>

  </view>

  <view class="btn-area">

    <button form-type="submit">Submit</button>

    <button form-type="reset">Reset</button>

  </view>

</form>

Page({

  formSubmit: function(e) {

    console.log('form发生了submit事件,携带数据为:', e.detail.value)

  },

  formReset: function() {

    console.log('form发生了reset事件')

  }

})

input

输入框。该组件是原生组件,使用时请注意相关限制。

示例代码:

<!--input.wxml-->

<view class="section">

  <input placeholder="这是一个可以自动聚焦的input" auto-focus/>

</view>

<view class="section">

  <input placeholder="这个只有在按钮点击的时候才聚焦" focus="{{focus}}" />

  <view class="btn-area">

    <button bindtap="bindButtonTap">使得输入框获取焦点</button>

  </view>

</view>

<view class="section">

  <input  maxlength="10" placeholder="最大输入长度10" />

</view>

<view class="section">

  <view class="section__title">你输入的是:{{inputValue}}</view>

  <input  bindinput="bindKeyInput" placeholder="输入同步到view中"/>

</view>

<view class="section">

  <input  bindinput="bindReplaceInput" placeholder="连续的两个1会变成2" />

</view>

<view class="section">

  <input password type="number" />

</view>

<view class="section">

  <input password type="text" />

</view>

<view class="section">

  <input type="digit" placeholder="带小数点的数字键盘"/>

</view>

<view class="section">

  <input type="idcard" placeholder="身份证输入键盘" />

</view>

<view class="section">

  <input placeholder-style="color:red" placeholder="占位符字体是红色的" />

</view>

//input.js

Page({

  data: {

    focus: false,

    inputValue: ''

  },

  bindButtonTap: function() {

    this.setData({

      focus: true

    })

  },

  bindKeyInput: function(e) {

    this.setData({

      inputValue: e.detail.value

    })

  },

  bindReplaceInput: function(e) {

    var value = e.detail.value

    var pos = e.detail.cursor

    if(pos != -1){

      //光标在中间

      var left = e.detail.value.slice(0,pos)

      //计算光标的位置

      pos = left.replace(/11/g,'2').length

    }



    //直接返回对象,可以对输入进行过滤处理,同时可以控制光标的位置

    return {

      value: value.replace(/11/g,'2'),

      cursor: pos

    }



    //或者直接返回字符串,光标在最后边

    //return value.replace(/11/g,'2'),

  }

})

label

用来改进表单组件的可用性。

使用for属性找到对应的id,或者将控件放在该标签下,当点击时,就会触发对应的控件。 for优先级高于内部控件,内部有多个控件的时候默认触发第一个控件。 目前可以绑定的控件有:button, checkbox, radio, switch。

示例代码:

<view class="section section_gap">

<view class="section__title">表单组件在label内</view>

<checkbox-group class="group" bindchange="checkboxChange">

  <view class="label-1" wx:for="{{checkboxItems}}">

    <label>

      <checkbox hidden value="{{item.name}}" checked="{{item.checked}}"></checkbox>

      <view class="label-1__icon">

        <view class="label-1__icon-checked" style="opacity:{{item.checked ? 1: 0}}"></view>

      </view>

      <text class="label-1__text">{{item.value}}</text>

    </label>

  </view>

</checkbox-group>

</view>



<view class="section section_gap">

<view class="section__title">label用for标识表单组件</view>

<radio-group class="group" bindchange="radioChange">

  <view class="label-2" wx:for="{{radioItems}}">

    <radio id="{{item.name}}" hidden value="{{item.name}}" checked="{{item.checked}}"></radio>

    <view class="label-2__icon">

      <view class="label-2__icon-checked" style="opacity:{{item.checked ? 1: 0}}"></view>

    </view>

    <label class="label-2__text" for="{{item.name}}"><text>{{item.name}}</text></label>

  </view>

</radio-group>

</view>

Page({

  data: {

    checkboxItems: [

      {name: 'USA', value: '美国'},

      {name: 'CHN', value: '中国', checked: 'true'},

      {name: 'BRA', value: '巴西'},

      {name: 'JPN', value: '日本', checked: 'true'},

      {name: 'ENG', value: '英国'},

      {name: 'TUR', value: '法国'},

    ],

    radioItems: [

      {name: 'USA', value: '美国'},

      {name: 'CHN', value: '中国', checked: 'true'},

      {name: 'BRA', value: '巴西'},

      {name: 'JPN', value: '日本'},

      {name: 'ENG', value: '英国'},

      {name: 'TUR', value: '法国'},

    ],

    hidden: false

  },

  checkboxChange: function(e) {

    var checked = e.detail.value

    var changed = {}

    for (var i = 0; i < this.data.checkboxItems.length; i ++) {

      if (checked.indexOf(this.data.checkboxItems[i].name) !== -1) {

        changed['checkboxItems['+i+'].checked'] = true

      } else {

        changed['checkboxItems['+i+'].checked'] = false

      }

    }

    this.setData(changed)

  },

  radioChange: function(e) {

    var checked = e.detail.value

    var changed = {}

    for (var i = 0; i < this.data.radioItems.length; i ++) {

      if (checked.indexOf(this.data.radioItems[i].name) !== -1) {

        changed['radioItems['+i+'].checked'] = true

      } else {

        changed['radioItems['+i+'].checked'] = false

      }

    }

    this.setData(changed)

  }

})

.label-1, .label-2{

    margin-bottom: 15px;

}

.label-1__text, .label-2__text {

    display: inline-block;

    vertical-align: middle;

}



.label-1__icon {

    position: relative;

    margin-right: 10px;

    display: inline-block;

    vertical-align: middle;

    width: 18px;

    height: 18px;

    background: #fcfff4;

}



.label-1__icon-checked {

    position: absolute;

    top: 3px;

    left: 3px;

    width: 12px;

    height: 12px;

    background: #1aad19;

}



.label-2__icon {

    position: relative;

    display: inline-block;

    vertical-align: middle;

    margin-right: 10px;

    width: 18px;

    height: 18px;

    background: #fcfff4;

    border-radius: 50px;

}



.label-2__icon-checked {

    position: absolute;

    left: 3px;

    top: 3px;

    width: 12px;

    height: 12px;

    background: #1aad19;

    border-radius: 50%;

}



.label-4_text{

    text-align: center;

    margin-top: 15px;

}

picker

从底部弹起的滚动选择器。

示例代码:

<view class="section">

  <view class="section__title">普通选择器</view>

  <picker bindchange="bindPickerChange" value="{{index}}" range="{{array}}">

    <view class="picker">

      当前选择:{{array[index]}}

    </view>

  </picker>

</view>

<view class="section">

  <view class="section__title">多列选择器</view>

  <picker mode="multiSelector" bindchange="bindMultiPickerChange" bindcolumnchange="bindMultiPickerColumnChange" value="{{multiIndex}}" range="{{multiArray}}">

    <view class="picker">

      当前选择:{{multiArray[0][multiIndex[0]]}},{{multiArray[1][multiIndex[1]]}},{{multiArray[2][multiIndex[2]]}}

    </view>

  </picker>

</view>

<view class="section">

  <view class="section__title">时间选择器</view>

  <picker mode="time" value="{{time}}" start="09:01" end="21:01" bindchange="bindTimeChange">

    <view class="picker">

      当前选择: {{time}}

    </view>

  </picker>

</view>



<view class="section">

  <view class="section__title">日期选择器</view>

  <picker mode="date" value="{{date}}" start="2015-09-01" end="2017-09-01" bindchange="bindDateChange">

    <view class="picker">

      当前选择: {{date}}

    </view>

  </picker>

</view>

<view class="section">

  <view class="section__title">省市区选择器</view>

  <picker mode="region" bindchange="bindRegionChange" value="{{region}}" custom-item="{{customItem}}">

    <view class="picker">

      当前选择:{{region[0]}},{{region[1]}},{{region[2]}}

    </view>

  </picker>

</view>

Page({

  data: {

    array: ['美国', '中国', '巴西', '日本'],

    objectArray: [

      {

        id: 0,

        name: '美国'

      },

      {

        id: 1,

        name: '中国'

      },

      {

        id: 2,

        name: '巴西'

      },

      {

        id: 3,

        name: '日本'

      }

    ],

    index: 0,

    multiArray: [['无脊柱动物', '脊柱动物'], ['扁性动物', '线形动物', '环节动物', '软体动物', '节肢动物'], ['猪肉绦虫', '吸血虫']],

    objectMultiArray: [

      [

        {

          id: 0,

          name: '无脊柱动物'

        },

        {

          id: 1,

          name: '脊柱动物'

        }

      ], [

        {

          id: 0,

          name: '扁性动物'

        },

        {

          id: 1,

          name: '线形动物'

        },

        {

          id: 2,

          name: '环节动物'

        },

        {

          id: 3,

          name: '软体动物'

        },

        {

          id: 3,

          name: '节肢动物'

        }

      ], [

        {

          id: 0,

          name: '猪肉绦虫'

        },

        {

          id: 1,

          name: '吸血虫'

        }

      ]

    ],

    multiIndex: [0, 0, 0],

    date: '2016-09-01',

    time: '12:01',

    region: ['广东省', '广州市', '海珠区'],

    customItem: '全部'

  },

  bindPickerChange: function(e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      index: e.detail.value

    })

  },

  bindMultiPickerChange: function (e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      multiIndex: e.detail.value

    })

  },

  bindMultiPickerColumnChange: function (e) {

    console.log('修改的列为', e.detail.column, ',值为', e.detail.value);

    var data = {

      multiArray: this.data.multiArray,

      multiIndex: this.data.multiIndex

    };

    data.multiIndex[e.detail.column] = e.detail.value;

    switch (e.detail.column) {

      case 0:

        switch (data.multiIndex[0]) {

          case 0:

            data.multiArray[1] = ['扁性动物', '线形动物', '环节动物', '软体动物', '节肢动物'];

            data.multiArray[2] = ['猪肉绦虫', '吸血虫'];

            break;

          case 1:

            data.multiArray[1] = ['鱼', '两栖动物', '爬行动物'];

            data.multiArray[2] = ['鲫鱼', '带鱼'];

            break;

        }

        data.multiIndex[1] = 0;

        data.multiIndex[2] = 0;

        break;

      case 1:

        switch (data.multiIndex[0]) {

          case 0:

            switch (data.multiIndex[1]) {

              case 0:

                data.multiArray[2] = ['猪肉绦虫', '吸血虫'];

                break;

              case 1:

                data.multiArray[2] = ['蛔虫'];

                break;

              case 2:

                data.multiArray[2] = ['蚂蚁', '蚂蟥'];

                break;

              case 3:

                data.multiArray[2] = ['河蚌', '蜗牛', '蛞蝓'];

                break;

              case 4:

                data.multiArray[2] = ['昆虫', '甲壳动物', '蛛形动物', '多足动物'];

                break;

            }

            break;

          case 1:

            switch (data.multiIndex[1]) {

              case 0:

                data.multiArray[2] = ['鲫鱼', '带鱼'];

                break;

              case 1:

                data.multiArray[2] = ['青蛙', '娃娃鱼'];

                break;

              case 2:

                data.multiArray[2] = ['蜥蜴', '龟', '壁虎'];

                break;

            }

            break;

        }

        data.multiIndex[2] = 0;

        break;

    }

    console.log(data.multiIndex);

    this.setData(data);

  },

  bindDateChange: function(e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      date: e.detail.value

    })

  },

  bindTimeChange: function(e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      time: e.detail.value

    })

  },

  bindRegionChange: function (e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      region: e.detail.value

    })

  }

})

radio

单选项目。

示例代码:

<radio-group class="radio-group" bindchange="radioChange">

  <label class="radio" wx:for="{{items}}">

    <radio value="{{item.name}}" checked="{{item.checked}}"/>{{item.value}}

  </label>

</radio-group>

Page({

  data: {

    items: [

      {name: 'USA', value: '美国'},

      {name: 'CHN', value: '中国', checked: 'true'},

      {name: 'BRA', value: '巴西'},

      {name: 'JPN', value: '日本'},

      {name: 'ENG', value: '英国'},

      {name: 'TUR', value: '法国'},

    ]

  },

  radioChange: function(e) {

    console.log('radio发生change事件,携带value值为:', e.detail.value)

  }

})

slider

滑动选择器。

示例代码:

<view class="section section_gap">

  <text class="section__title">设置step</text>

  <view class="body-view">

    <slider bindchange="slider2change" step="5"/>

  </view>

</view>



<view class="section section_gap">

  <text class="section__title">显示当前value</text>

  <view class="body-view">

    <slider bindchange="slider3change" show-value/>

  </view>

</view>



<view class="section section_gap">

  <text class="section__title">设置最小/最大值</text>

  <view class="body-view">

    <slider bindchange="slider4change" min="50" max="200" show-value/>

  </view>

</view>

var pageData = {}

for (var i = 1; i < 5; i++) {

  (function (index) {

    pageData['slider' + index + 'change'] = function(e) {

      console.log('slider' + 'index' + '发生 change 事件,携带值为', e.detail.value)

    }

  })(i)

}

Page(pageData)

switch

开关选择器。

示例代码:

<view class="body-view">

    <switch checked bindchange="switch1Change"/>

    <switch bindchange="switch2Change"/>

</view>

Page({

  switch1Change: function (e){

    console.log('switch1 发生 change 事件,携带值为', e.detail.value)

  },

  switch2Change: function (e){

    console.log('switch2 发生 change 事件,携带值为', e.detail.value)

  }

})

textarea

多行输入框。该组件是原生组件,使用时请注意相关限制。

示例代码:

<view class="section">

  <textarea bindblur="bindTextAreaBlur" auto-height placeholder="自动变高" />

</view>

<view class="section">

  <textarea placeholder="placeholder颜色是红色的" placeholder-style="color:red;"  />

</view>

<view class="section">

  <textarea placeholder="这是一个可以自动聚焦的textarea" auto-focus />

</view>

<view class="section">

  <textarea placeholder="这个只有在按钮点击的时候才聚焦" focus="{{focus}}" />

  <view class="btn-area">

    <button bindtap="bindButtonTap">使得输入框获取焦点</button>

  </view>

</view>

<view class="section">

  <form bindsubmit="bindFormSubmit">

    <textarea placeholder="form 中的 textarea" name="textarea"/>

    <button form-type="submit"> 提交 </button>

  </form>

</view>

//textarea.js

Page({

  data: {

    height: 20,

    focus: false

  },

  bindButtonTap: function() {

    this.setData({

      focus: true

    })

  },

  bindTextAreaBlur: function(e) {

    console.log(e.detail.value)

  },

  bindFormSubmit: function(e) {

    console.log(e.detail.value.textarea)

  }

})

4-7 其他组件

导航组件

navigator

页面链接。

示例代码:

.navigator-hover {

  color:blue;

}

.other-navigator-hover {

  color:red;

}

<!-- sample.wxml -->

<view class="btn-area">

  <navigator url="/page/navigate/navigate?title=navigate" hover-class="navigator-hover">跳转到新页面</navigator>

  <navigator url="../../redirect/redirect/redirect?title=redirect" open-type="redirect" hover-class="other-navigator-hover">在当前页打开</navigator>

  <navigator url="/page/index/index" open-type="switchTab" hover-class="other-navigator-hover">切换 Tab</navigator>

  <navigator target="miniProgram" open-type="navigate" app-id="" path="" extra-data="" version="release">打开绑定的小程序</navigator>

</view>

<!-- navigator.wxml -->

<view style="text-align:center"> {{title}} </view>

<view> 点击左上角返回回到之前页面 </view>

<!-- redirect.wxml -->

<view style="text-align:center"> {{title}} </view>

<view> 点击左上角返回回到上级页面 </view>

Page({

  onLoad: function(options) {

    this.setData({

      title: options.title

    })

  }

})

媒体组件

image

图片。支持JPG、PNG、SVG格式,2.3.0 起支持云文件ID。

示例代码:

<view class="page">

  <view class="page__hd">

    <text class="page__title">image</text>

    <text class="page__desc">图片</text>

  </view>

  <view class="page__bd">

    <view class="section section_gap" wx:for="{{array}}" wx:for-item="item">

      <view class="section__title">{{item.text}}</view>

      <view class="section__ctn">

        <image style="width: 200px; height: 200px; background-color: #eeeeee;" mode="{{item.mode}}" src="{{src}}"></image>

      </view>

    </view>

  </view>

</view>

Page({

  data: {

    array: [{

      mode: 'scaleToFill',

      text: 'scaleToFill:不保持纵横比缩放图片,使图片完全适应'

    }, {

      mode: 'aspectFit',

      text: 'aspectFit:保持纵横比缩放图片,使图片的长边能完全显示出来'

    }, {

      mode: 'aspectFill',

      text: 'aspectFill:保持纵横比缩放图片,只保证图片的短边能完全显示出来'

    }, {

      mode: 'top',

      text: 'top:不缩放图片,只显示图片的顶部区域'

    }, {

      mode: 'bottom',

      text: 'bottom:不缩放图片,只显示图片的底部区域'

    }, {

      mode: 'center',

      text: 'center:不缩放图片,只显示图片的中间区域'

    }, {

      mode: 'left',

      text: 'left:不缩放图片,只显示图片的左边区域'

    }, {

      mode: 'right',

      text: 'right:不缩放图片,只显示图片的右边边区域'

    }, {

      mode: 'top left',

      text: 'top left:不缩放图片,只显示图片的左上边区域'

    }, {

      mode: 'top right',

      text: 'top right:不缩放图片,只显示图片的右上边区域'

    }, {

      mode: 'bottom left',

      text: 'bottom left:不缩放图片,只显示图片的左下边区域'

    }, {

      mode: 'bottom right',

      text: 'bottom right:不缩放图片,只显示图片的右下边区域'

    }],

    src: '../resources/cat.jpg'

  },

  imageError: function(e) {

    console.log('image3发生error事件,携带值为', e.detail.errMsg)

  }

})

4-8 商城内容展示

示例代码

// index.js

const app = getApp()



Page({

  data: {

    imgUrls:[

      "https://img.youpin.mi-img.com/youpinoper/1002265792a851c83054da07959d976c.jpg?id=&w=1080&h=450",

      "https://img.youpin.mi-img.com/youpinoper/f62e9e59b21f97f81ed3ab567f0e4b08.jpg?id=&w=1080&h=450",

      "https://img.youpin.mi-img.com/youpinoper/0bb6ad5d5c85b03534b460c297c6b85a.jpeg?id=&w=1080&h=450"

    ],



    category:[

      { url:"https://img.youpin.mi-img.com/shopmain/b68323e900ffbb4835dd9e6a15d10b77.png?w=800&h=800",

      tag:'小米众筹'},

            {

              url: "https://img.youpin.mi-img.com/800_pic/27a789a428038214a8dda98f97d5fe4c.png",

        tag: '限时抢购'

      },

            {

              url: "https://img.youpin.mi-img.com/800_pic/c13ae49b289b269d9a39496e3e31708d.png",

        tag: '热销榜单'

      },

            {

              url: "https://img.youpin.mi-img.com/800_pic/da003d715c9e832b2c8e62402e48bfa0.png",

        tag: '随便逛逛'

      }

    ]

  },



})


<!-- index.wxml -->

<!-- 商城首页 -->

<swiper indicator-dots="true" autoplay="true" interval="2000">

  <block wx:for="{{imgUrls}}" wx:key='imgs'>

  <swiper-item>

    <image src='{{item}}' mode="widthFix"></image>

  </swiper-item>

  </block>

</swiper>

<!-- 分类推荐 -->

<view class="category">

  <block wx:for="{{category}}" wx:key="types">

  <view class="category-item">

    <image src='{{item.url}}' class='category-img'></image>

    <text>{{item.tag}}</text>

    </view>

  </block>

</view>

<!-- 今日特价 -->

<view class="separate"></view>

<view class="discount">

<view class="tag">

  <view class="tag-insider"></view>

  <view>今日特价</view></view>

  <view>更多>></view>

</view>

/* index.wxss */

.category{

  display: flex;

  justify-content: space-around

}

.category-item{

  display: flex;

  flex-direction: column;

  align-items: center;

  padding: 20rpx;



}

.category-img{

  background-color: #C5C1C0;

  width: 100rpx;

  height: 100rpx;

  border-radius: 30%;

}

.category-item text{

  padding-top: 10rpx;

  font-size: 25rpx;

}

.separate{

  height: 10rpx;

  background-color: #F3F3F3;



}

.discount{

  display: flex;

  justify-content: space-between;

  padding: 0 10px;

  font-size: 30rpx;

}

.tag{

  display: flex;

  color: red;

}

.tag-insider{

  margin-right: 10rpx;

  margin-top: 5rpx;

  width: 10rpx;

  height: 30rpx;

  background-color:#fea96a;

}


效果

5-1、5-2小程序API介绍

API(Application Programming Interface)
  • 由小程序开发框架提供的预先定义的函数
  • 可直接按需调用功能,无需获取源码或理解内部细节
小程序API分类
  • 网络 媒体 文件 数据缓存 画布
  • 位置 设备 开放接口等
小程序API的类型
事件监听

约定以 

on

开头的 API 用来监听某个事件是否触发,如:

wx.onSocketOpen

wx.onCompassChange

 等。

这类 API 接受一个回调函数作为参数,当事件触发时会调用这个回调函数,并将相关数据以参数形式传入。

代码示例:

wx.onCompassChange(function (res) {

  console.log(res.direction)

})

数据操作

数据的读取及写入 

读取:

wx.get***

写入:

wx.set***

同步-可由函数返回值获取结果

约定以 

Sync

 结尾的 API 都是同步 API, 如

wx.setStorageSync

wx.getSystemInfoSync

 等。此外,也有一些其他的同步 API,如 

wx.createWorker

wx.getBackgroundAudioManager

 等

同步 API 的执行结果可以通过函数返回值直接获取,如果执行出错会抛出异常。

代码示例:

try {

  wx.setStorageSync('key', 'value')

} catch (e) {

  console.error(e)

}

异步

大多数 API 都是异步 API,如 

wx.request

wx.login

 等。这类 API 接口通常都接受一个 Object 类型的参数,这个参数都支持按需指定以下字段来接收接口调用结果:

三个回调函数 

success

fail

complete

原理:回调函数的核心是委托制 

  适用场景:在特定条件满足时执行一个任务 

  使用:

  • 定义一个函数将要执行的任务与一特定事件建立关联
  • 当该函数关联的事件触发的时候,函数将被执行(回调机制)

优点:程序的任务执行变为异步,无需一直等待任务执行完成

success

fail

complete

 函数调用时会传入一个 Object 类型参数,包含以下字段:

异步 API 的执行结果需要通过 Object 类型的参数中传入的对应回调函数获取。部分异步 API 也会有返回值,可以用来实现更丰富的功能,如 

wx.request

wx.connectSocket

 等。

代码示例:

wx.login({

  success(res) {

    console.log(res.code)

  }

})

5-3结合回调函数操作API

例一:

我们先来看媒体中的获取图片信息的函数:

wx.getImageInfo

(Object object)

该函数的作用是获取图片信息。网络图片需先配置download域名才能生效。

函数参数:

object.success 回调函数参数:

res.orientation 的合法值:

代码示例:

//index.js

const app=getApp()

Page({

    data:{

        src:'../../imgs/pic.jpg'

        },

    getImgInfo:function(){

        wx.getImageInfo({

            src:this.data.src,

            success(res)  {  //回调函数

                console.log(res)

            },

            fail(res){

                console.log(res)

            },

            complete(res){

                console.log('一定会执行')

            }

        })

        }   

    })

//index.wxml

<view>下面是一个获取图片信息的按钮</view>

<button bindtap='getImgInfo'>点一下</button>

运行

输出图片信息:

例二:

再来看一下界面交互中的

wx.showToast

函数 

该函数的作用是显示消息提示框

函数参数:

代码示例:

//index.js

const app = getApp()



Page({

  data: {

    src:'../../imgs/pic.jpg'

  },

  //事件触发函数

  showImgInfo:function(text){

    wx.showToast({

      title: text,

      icon:'none'

    })

  },



  getImgInfo:function(){

    var that=this

    wx.getImageInfo({

      src: this.data.src,

      success(res) {  // 回调函数 委托 执行者变化

        that.showImgInfo('路径->'+res.path+'\n尺寸'+res.width+'X'+res.height)

      },

      fail(res){

        console.log(res)

      },

      complete(res){

        console.log('一定会执行',res)

      }

    })

  }



})


运行,获得图片信息,并在交互界面看到所输出的图片信息

5-4音频API

InnerAudioContext

函数

InnerAudioContext 实例,可通过 wx.createInnerAudioContext 接口获取实例。

属性:

tring src

音频资源的地址,用于直接播放。2.2.3 开始支持云文件ID

number startTime

开始播放的位置(单位:s),默认为 0

boolean autoplay

是否自动开始播放,默认为 false

boolean loop

是否循环播放,默认为 false

boolean obeyMuteSwitch

是否遵循系统静音开关,默认为 true。当此参数为 false 时,即使用户打开了静音开关,也能继续发出声音。从 2.3.0 版本开始此参数不生效,使用 

wx.setInnerAudioOption

 接口统一设置。

number volume

基础库 1.9.90 开始支持,低版本需做兼容处理。

音量

范围 0~1。默认为 1

number duration

当前音频的长度(单位 s)。只有在当前有合法的 src 时返回(只读)

number currentTime

当前音频的播放位置(单位 s)。只有在当前有合法的 src 时返回,时间保留小数点后 6 位(只读)

boolean paused

当前是是否暂停或停止状态(只读)

number buffered

音频缓冲的时间点,仅保证当前播放时间点到此时间点内容已缓冲(只读

代码示例:

//index.js

Page({



  /**

   * 页面的初始数据

   */

  data: {

    src: 'http://ws.stream.qqmusic.qq.com/M500001VfvsJ21xFqb.mp3?guid=ffffffff82def4af4b12b3cd9337d5e7&uin=346897220&vkey=6292F51E1E384E061FF02C31F716658E5C81F5594D561F2E88B854E81CAAB7806D5E4F103E55D33C16F3FAC506D1AB172DE8600B37E43FAD&fromtag=46',

    poster: '../../imgs/pic.jpg',

    name: '普通disco',

    author: '佚名',

    src2:'../../music/light_emotion.mp3',

    src3: 'http://sc1.111ttt.cn:8282/2018/1/03m/13/396131229550.m4a?tflag=1546606800&pin=97bb2268ae26c20fe093fd5b0f04be80#.mp3',

  },

  audioPlay() {

    this.audioData.play()

    console.log('播放')

  },

  audioPause() {

    this.audioData.pause()

    console.log('暂停')

  },

  audioPlayBack() {

    this.audioData.seek(this.audioData.currentTime-3)

  },

  audioStop() {

    // this.audioData.stop()

  },

  audioStart() {

  },



  /**

   * 生命周期函数--监听页面加载

   */

  onLoad: function(options) {

    this.audioData =wx.createInnerAudioContext()

    this.audioData.src = this.data.src

    this.audioData.autoplay = false

    console.log(this.audioData)

  },



  /**

   * 生命周期函数--监听页面初次渲染完成

   */

  onReady: function() {

    // this.audioData = wx.createAudioContext('myAudio')



  },



  /**

   * 生命周期函数--监听页面显示

   */

  onShow: function() {



  },



  /**

   * 生命周期函数--监听页面隐藏

   */

  onHide: function() {



  },



  /**

   * 生命周期函数--监听页面卸载

   */

  onUnload: function() {



  },



  /**

   * 页面相关事件处理函数--监听用户下拉动作

   */

  onPullDownRefresh: function() {



  },



  /**

   * 页面上拉触底事件的处理函数

   */

  onReachBottom: function() {



  },



  /**

   * 用户点击右上角分享

   */

  onShareAppMessage: function() {



  }

})


//index.wxml

<!--pages/index2/index2.wxml-->

<audio poster="{{poster}}" name="{{name}}" author="{{author}}" src="{{src3}}" id="myAudio" controls loop></audio>



<button bindtap='audioPlay'>播放</button>

<button bindtap='audioPause'>暂停</button>

<button bindtap='audioPlayBack'>回放测试</button>

<button bindtap='audioStop'>结束</button>

<button bindtap='audioStart'>回到开头</button>

值得注意的是,

<audio/>

组件不再维护,可以使用能力更强的 

wx.createInnerAudioContext

接口.

5-5位置API

首先我们看一下

wx.getLocation

函数

作用:获取当前的地理位置、速度。当用户离开小程序后,此接口无法调用。

参数:

object.success 回调函数

代码示例:

//index.js

Page({



  /**

   * 页面的初始数据

   */

  data: {

    demo_la: 48.1545,

    demo_lo: 82.3555907,

    latitude: 0,

    longitude: 0,

    begin_la: 0,

    begin_lo: 0,

    interval: 0,

    speed: 0,

    accuracy: 0,

    markers: [{

      iconPath: "../../imgs/pic.jpg",

      id: 0,

      latitude: 39.9205,

      longitude: 116.4605,

      width: 30,

      height: 30

    }]



  },

  openLoc() {

    this.mapCtx.includePoints({

      padding: [10],

      points: [{

        latitude: 23.10229,

        longitude: 113.3345211,

      }, {

        latitude: 23.00229,

        longitude: 113.3345211,

      }]

    })

  },



  geoInfo() {

    var that = this

    wx.getLocation({

      type: 'gcj02',

      scale:18,

      success(res) {



        that.setData({

          latitude: res.latitude,

          longitude: res.longitude,

          speed: res.speed,

          accuracy: res.accuracy,

        })

      }

    })

    console.log(this.data.latitude, this.data.longitude)

  },



  startRun() {

    var that = this

    clearInterval(this.data.interval)



    wx.getLocation({

      type: 'gcj02',

      success(res) {

        var la = res.latitude

        var lo = res.longitude

        that.setData({

          begin_la: la,

          begin_lo: lo,

          'markers[1]': {

            iconPath: "../../imgs/red.png",

            id: 1,

            latitude: la,

            longitude: lo,

            width: 20,

            height: 20

          }

        })

      }

    })



    var interval = setInterval(() => {

      this.running()

    }, 1000)





  },

  stopRun() {

    var that = this

    clearInterval(this.data.interval)

    wx.getLocation({

      type: 'gcj02',

      success(res) {

        var la = res.latitude

        var lo = res.longitude

        that.setData({

          latitude: la,

          longitude: lo,

          'markers[2]': {

            iconPath: "../../imgs/yellow.png",

            id: 2,

            latitude: la,

            longitude: lo,

            width: 20,

            height: 20

          }

        })

      }

    })

    console.log('开始位置', this.data.begin_la, this.data.begin_lo)

    console.log('结束位置', this.data.latitude, this.data.longitude)

    console.log(this.data)

  },

  running() {

    this.data.latitude += 0.0002

    this.data.longitude += 0.0002

  },

  center() {

    this.mapCtx.getCenterLocation({

      success: function (res) {

        console.log(res.latitude,res.longitude)

      }

    })



  },

  trans() {

    this.mapCtx.translateMarker({

      markerId: 0,

      autoRotate: false,

      duration: 3000,

      destination: {

        latitude: 40.30229,

        longitude: 116.7545211,

      },

      animationEnd() {

        console.log('animation end')

      }

    })

  },

  onReady: function () {

    this.mapCtx = wx.createMapContext('myMap')

  },

})

//index.wxml

<view class='top'>

  <button bindtap='geoInfo'>位置</button>

  <button bindtap='openLoc'>打开</button>

  <button bindtap='center'>定心</button>

  <button bindtap='trans'>动</button>

</view>



<map id='myMap' style="width:100%; height:350px" longitude='{{longitude}}' latitude='{{latitude}}' markers='{{markers}}' polyline="{{polyline}}" show-location subkey='IWLBZ-CK6R4-YXDUO-DQCXK-J5H4Z-DSBCI'>

  <cover-view calss='cover'>

    经度:{{longitude}} 纬度:{{latitude}} 速度:{{speed}}

  </cover-view>

</map>



<button bindtap='startRun'>开始</button>

<button bindtap='stopRun'>结束</button>

5-6网络请求原理

网络请求过程

小程序访问服务器,经过TCP握手后建立起TCP连接,然后小程序向服务器发送HTTP请求,即发送一个request请求去获取客户器上的数据,服务器把response文件对象发送回给小程序。

数据结构

请求 

HTTP请求格式如下所示四部分组成,分别是请求行、请求头、空行、消息体,每部分内容占一行。

<request-line>

<general-headers>

 
  
  1. <request-headers>

<entity-headers>

<empty-line>

[message-body]

请求行:由三部分组成:分别是请求方法(GET/POST/DELETE/PUT/HEAD)、URI路径、HTTP版本号。

主体:客户端发给服务端的请求数据,这部分数据并不是每个请求必须的

返回数据 

服务器接收处理完请求后返回一个HTTP响应消息给客户端。HTTP响应消息的格式包括:状态行、响应头、空行、消息体。每部分内容占一行。 

<status-line>

<general-headers>

<response-headers>

<entity-headers>

<empty-line>

<[message-body]>

状态行:有HTTP协议版本号,状态码和状态说明三部分构成。

响应头:用于说明数据的一些信息,比如数据类型、内容长度等键值对。 

空行。

消息体:服务端返回给客户端的HTML文本内容。或者其他格式的数据,比如:视频流、图片或者音频数据。

豆瓣示例

<general-headers>

<response-headers>

<request-headers>

小程序网络请求API

wx.request

函数

作用:发起HTTPS 网络请求

参数:

代码示例:

//index.js

Page({



  /**

   * 页面的初始数据

   */

  data: {

    url:'https://easy-mock.com/mock/5cd4d8aa4993c3317af392a5/example/test',

    url_2:'https://easy-mock.com/mock/5cd4d8aa4993c3317af392a5/example/douban'

  },



  getData:function(){

    wx.request({

      url: this.data.url_2, 

      header: {

        'content-type': 'application/json' // 默认值

      },

      success(res) {

        console.log(res.data)

      }

    })

  },



  /**

   * 生命周期函数--监听页面加载

   */

  onLoad: function (options) {



  },



  /**

   * 生命周期函数--监听页面初次渲染完成

   */

  onReady: function () {



  },



  /**

   * 生命周期函数--监听页面显示

   */

  onShow: function () {



  },



  /**

   * 生命周期函数--监听页面隐藏

   */

  onHide: function () {



  },



  /**

   * 生命周期函数--监听页面卸载

   */

  onUnload: function () {



  },



  /**

   * 页面相关事件处理函数--监听用户下拉动作

   */

  onPullDownRefresh: function () {



  },



  /**

   * 页面上拉触底事件的处理函数

   */

  onReachBottom: function () {



  },



  /**

   * 用户点击右上角分享

   */

  onShareAppMessage: function () {



  }

})

//index.wxml

<button bindtap='getData'>请求模拟数据</button>

6-1 云开发介绍

介绍

开发者可以使用云开发开发微信小程序、小游戏,无需搭建服务器,即可使用云端能力。

云开发为开发者提供完整的原生云端支持和微信服务支持,弱化后端和运维概念,无需搭建服务器,使用平台提供的 API 进行核心业务开发,即可实现快速上线和迭代,同时这一能力,同开发者已经使用的云服务相互兼容,并不互斥。

云开发提供了几大基础能力支持:

步骤

可以按如下步骤快速开始使用云开发。

  1. 新建云开发模板
  2. 开通云开发
  3. 体验小程序
  4. 查看控制台
1. 新建云开发模板

新建项目选择一个空目录,填入 AppID(使用云开发能力必须填写 AppID),勾选创建 “云开发 QuickStart 项目”,点击创建即可得到一个展示云开发基础能力的示例小程序。该小程序与普通 QuickStart 小程序有以下不同需注意:

  1. 无游客模式、也不可以使用 测试号
  2. project.config.json 中增加了字段 cloudfunctionRoot 用于指定存放云函数的目录
  3. cloudfunctionRoot 指定的目录有特殊的图标
  4. 云开发能力从基础库 2.2.3 开始支持(覆盖率 97.3%,查看兼容性问题)

从基础库 2.4.1 开始,在小程序插件中可以使用云开发,插件中使用云开发时,使用的是插件方的云资源而非宿主的云资源,在使用方式上与在小程序中使用无异。

2. 开通云开发、创建环境

创建了第一个云开发小程序后,在使用云开发能力之前需要先开通云开发。在开发者工具工具栏左侧,点击 “云开发” 按钮即可打开云控制台、根据提示开通云开发、创建云环境。默认配额下可以创建两个环境,各个环境相互隔离,每个环境都包含独立的数据库实例、存储空间、云函数配置等资源。每个环境都有唯一的环境 ID 标识,初始创建的环境自动成为默认环境。

注:AppID 首次开通云环境后,需等待大约 10 分钟方可正常使用云 API,在此期间官方后台服务正在做准备服务,如尝试在小程序中调用云 API 则会报 cloud init error:{ errMsg: “invalid scope” } 的错误

3. 体验小程序

开通创建环境后,即可以开始在模拟器上操作小程序体验云开发提供的部分基础能力演示。

4. 查看控制台

云开发控制台是管理云开发资源的地方,控制台提供以下能力:

  1. 运营分析:查看云开发监控、配额使用量、用户访问情况
  2. 数据库:管理数据库,可查看、增加、更新、查找、删除数据、管理索引、管理数据库访问权限等
  3. 存储管理:查看和管理存储空间
  4. 云函数:查看云函数列表、配置、日志

数据库

云开发提供了一个 JSON 数据库,顾名思义,数据库中的每条记录都是一个 JSON 格式的对象。一个数据库可以有多个集合(相当于关系型数据中的表),集合可看做一个 JSON 数组,数组中的每个对象就是一条记录,记录的格式是 JSON 对象。

关系型数据库和 JSON 数据库的概念对应关系如下表:

以下是一个示例的集合数据,假设我们有一个 books 集合存放了图书记录,其中有两本书:

[

  {

    "_id": "Wzh76lk5_O_dt0vO",

    "title": "The Catcher in the Rye",

    "author": "J. D. Salinger",

    "characters": [

      "Holden Caulfield",

      "Stradlater",

      "Mr. Antolini"

    ],

    "publishInfo": {

      "year": 1951,

      "country": "United States"

    }

  },

  {

    "_id": "Wzia0lk5_O_dt0vR",

    "_openid": "ohl4L0Rnhq7vmmbT_DaNQa4ePaz0",

    "title": "The Lady of the Camellias",

    "author": "Alexandre Dumas fils",

    "characters": [

      "Marguerite Gautier",

      "Armand Duval",

      "Prudence",

      "Count de Varville"

    ],

    "publishInfo": {

      "year": 1848,

      "country": "France"

    }

  }

]

在图书信息中,我们用 title, author 来记录图书标题和作者,用 characters 数组来记录书中的主要人物,用 publishInfo 来记录图书的出版信息。在其中我们可以看到,字段既可以是字符串或数字,还可以是对象或数组,就是一个 JSON 对象。

每条记录都有一个 _id 字段用以唯一标志一条记录、一个 _openid 字段用以标志记录的创建者,即小程序的用户。需要特别注意的是,在管理端(控制台和云函数)中创建的不会有 _openid 字段,因为这是属于管理员创建的记录。开发者可以自定义 _id,但不可自定义和修改 _openid 。_openid 是在文档创建时由系统根据小程序用户默认创建的,开发者可使用其来标识和定位文档。

数据库 API 分为小程序端和服务端两部分,小程序端 API 拥有严格的调用权限控制,开发者可在小程序内直接调用 API 进行非敏感数据的操作。对于有更高安全要求的数据,可在云函数内通过服务端 API 进行操作。云函数的环境是与客户端完全隔离的,在云函数上可以私密且安全的操作数据库。

数据库 API 包含增删改查的能力,使用 API 操作数据库只需三步:获取数据库引用、构造查询/更新条件、发出请求。以下是一个在小程序中查询数据库的发表于美国的图书记录的例子:

// 1. 获取数据库引用

const db = wx.cloud.database()

// 2. 构造查询语句

// collection 方法获取一个集合的引用

// where 方法传入一个对象,数据库返回集合中字段等于指定值的 JSON 文档。API 也支持高级的查询条件(比如大于、小于、in 等),具体见文档查看支持列表

// get 方法会触发网络请求,往数据库取数据

db.collection('books').where({

  publishInfo: {

    country: 'United States'

  }

}).get({

  success: function(res) {

  // 输出 [{ "title": "The Catcher in the Rye", ... }]

  console.log(res)

 }

})

存储

云开发提供了一块存储空间,提供了上传文件到云端、带权限管理的云端下载能力,开发者可以在小程序端和云函数端通过 API 使用云存储功能。

在小程序端可以分别调用 wx.cloud.uploadFile 和 wx.cloud.downloadFile 完成上传和下载云文件操作。下面简单的几行代码,即可实现在小程序内让用户选择一张图片,然后上传到云端管理的功能:

// 让用户选择一张图片

wx.chooseImage({

  success: chooseResult => {

    // 将图片上传至云存储空间

    wx.cloud.uploadFile({

      // 指定上传到的云路径

      cloudPath: 'my-photo.png',

      // 指定要上传的文件的小程序临时文件路径

      filePath: chooseResult.tempFilePaths[0],

      // 成功回调

      success: res => {

        console.log('上传成功', res)

      },

    })

  },

})

上传完成后可在控制台中看到刚上传的图片。

云函数

云函数是一段运行在云端的代码,无需管理服务器,在开发工具内编写、一键上传部署即可运行后端代码。

小程序内提供了专门用于云函数调用的 API。开发者可以在云函数内使用 wx-server-sdk 提供的 getWXContext 方法获取到每次调用的上下文(appid、openid 等),无需维护复杂的鉴权机制,即可获取天然可信任的用户登录态(openid)。

比如我们如下定义一个云函数,命名为 add ,功能是将传入的两个参数 a 和 b 相加:

// index.js 是入口文件,云函数被调用时会执行该文件导出的 main 方法

// event 包含了调用端(小程序端)调用该函数时传过来的参数,同时还包含了可以通过 getWXContext 方法获取的用户登录态 `openId` 和小程序 `appId` 信息

const cloud = require('wx-server-sdk')

exports.main = (event, context) => {

  let { userInfo, a, b} = event

  let { OPENID, APPID } = cloud.getWXContext() // 这里获取到的 openId 和 appId 是可信的

  let sum = a + b



  return {

    OPENID,

    APPID,

    sum

  }

}

在开发者工具中上传部署云函数后,我们在小程序中可以这么调用:

wx.cloud.callFunction({

  // 需调用的云函数名

  name: 'add',

  // 传给云函数的参数

  data: {

    a: 12,

    b: 19,

  },

  // 成功回调

  complete: console.log

})

// 当然 promise 方式也是支持的

wx.cloud.callFunction({

  name: 'add',

  data: {

    a: 12,

    b: 19

  }

}).then(console.log)

如需在云函数中操作数据库、管理云文件、调用其他云函数等操作,可使用官方提供的 npm 包 wx-server-sdk 进行操作。

6-2 云开发的同步与异步

同步与异步

同步 synchronous

- 程序正常按照顺序依次执行

- 亲力亲为 需要等待当前任务完成才执行下一个

异步 asynchronous

- 将事情交给其他函数去做

- 甩手掌柜 可能马上就完成也可能需要一段时间

- Promise

pending(进行中)

resolved(已完成)

rejected (已拒绝)

举个例子:

”同步“就好比:你去外地上学(人生地不熟),突然生活费不够了;此时你决定打电话回家,通知家里转生活费过来,可是当你拨出电话时,对方一直处于待接听状态(即:打不通,联系不上),为了拿到生活费,你就不停的oncall、等待,最终可能不能及时要到生活费,导致你今天要做的事都没有完成,而白白花掉了时间。

“异步”就是:在你打完电话发现没人接听时,猜想:对方可能在忙,暂时无法接听电话,所以你发了一条短信(或者语音留言,亦或是其他的方式)通知对方后便忙其他要紧的事了;这时你就不需要持续不断的拨打电话,还可以做其他事情;待一定时间后,对方看到你的留言便回复响应你,当然对方可能转钱也可能不转钱。但是整个一天下来,你还做了很多事情。 或者说你找室友临时借了一笔钱,又开始happy的上学时光了。

Promise()

var promise = new Promise(function(resolve, reject) {

 if (/* 异步操作成功 */){

 resolve(value);

 } else {

 reject(error);

 }

});



promise.then(function(value) {

 // success

}, function(value) {

 // failure

});

Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 方法和 reject 方法。

  • 如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从「未完成」变为「成功」(即从 pending 变为 resolved);
  • 如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从「未完成」变为「失败」(即从 pending 变为 rejected)。

async函数

正常执行

  • 包含函数语句、函数表达式、lambda表达式
  • 返回Promise对象 不阻塞

await

  • 暂停当前async执行,等待Promise处理完成
  • 正常(处理结果作为await表达式值)
  • 异常 抛出异常原因

特点

  • 优化处理then(解决多层回调问题)逻辑链 异步变同步
  • 异常用catch直接捕捉

1.async函数返回的是一个Promise对象。Async函数(包含函数语句、函数表达式、Lambda表达式)会返回一个Prom

1-1小程序介绍及安装

开发环境搭建
● 工具下载

微信web开发者工具

推荐下载稳定版,并根据自己的电脑选择下载对应的版本进行安装

● 注册账号

微信公众平台

点击小程序图标,注册小程序开发者账号

创建第一个实验项目
● 小程序后台

完成注册后,即可登陆小程序后台,在自己的小程序后台对小程序进行开发和管理。

● 微信web开发者工具

登陆微信web开发者工具

点击右边图片中的加号,即可开始创建小程序

新建项目

如果不使用测试号,也可直接使用自己的AppID

点击“新建”按扭,进入编辑页面

创建第一个实验项目

创建完项目,可以返回主页面对项目进行预览和管理

逻辑层初步介绍

除了视图层(即我们所看到的页面),小程序逻辑层各部分的功能如下:

● index.js 逻辑代码

● index.json 页面配置文件

● index.wxml 页面结构

● index.wxss 表示index.wxml结构的样式

app.js app.json app wxss的作用与index是类似的,但它们作用于全局

1-2小程序框架及文件介绍

小程序框架介绍

小程序框架的核心是一个响应的数据绑定系统。整个系统分为两块,视图层和逻辑层。

视图层 (view)

● 内容展示

● 文本样式

视图层由WXML与WXSS编写。

将逻辑层的数据反应成视图,同时将视图层的事件发送给逻辑层。

WXML(WeiXin Markup language)用于描述页面的结构。

WXSS(WeiXin Style Sheet)用于描述页面的样式。

组件(Component)是视图的基本组成单元。

逻辑层(app service)

● 业务逻辑

● 数据处理

小程序开发框架的逻辑层是由JavaScript编写。

逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈。

每个页面有独立的作用域,并提供模块化能力。

小程序文件介绍

小程序文件包含一个描述整体程序的app和多个描述各自页面的page.

页面

一个页面由四个文件组成,如下所示:

● index(默认)

小程序注册及配置

● 入口文件

● 配置文件

● 全局样式

app.js文件,用来定义小程序的全局数据和函数,控制、监听小程序的全生命周期。在这里可以见到的全局函数有onlaunch(监听小程序初始化),onshow(监听小程序显示),onhide(监听小程序隐藏)等。app.js中还可以定义一些全局变量,其他页面引用app.js文件后就可以直接使用这个文件中的函数和变量。

app.json是配置文件,可以配置以下信息:页面路径,窗口信息,标签导航,网络超时等。

app.wxss,类似于css,用于配置各种样式

project.config.json用于保存配置信息。

sitemap.json用于控制小程序是否能够被搜索到。

小程序的执行流程
小程序注册及配置

● 注册应用 app.js

● 全局配置 app.json

● 设置样式 app.wxss

● 工具配置 project.config.json

页面渲染执行 - app.json

● 主页面(index)

● 从页面(index2)

● 日志信息(logs)

json文件介绍
特点

● 轻量级的数据交换格式

● 直观理解

● 便于修改

结构

● 文件数据被包裹在一个大括号中 {}

● 通过键值对(key-value)的方式来表达数据

1-3配置文件和顶部导航栏

app.json

app.json是全局配置文件,我们进入到app.json看一下。

pages

试着对pages进行一些修改,进行编译

可以看到pages下面多了一个test目录。

实际上,pages接受一个数组,每一项都是字符串,来指定小程序由哪些页面组成。每一项代表对应页面的【路径+文件名】信息,数组的第一项代表小程序的初始页面。小程序中新增/减少页面,都需要对pages数组进行修改。

文件名不需要写文件后缀,因为框架会自动去寻找路径下.json、.js、.wxml、.wxss四个文件进行整合。

window

用于定义页面的背景颜色,字体样式,文字颜色等等。

enablePullDownRefresh

:设置是否进行下拉刷新

backgroundColor

: 设置下拉背景颜色,颜色为HEXcolor编码

backgroundTextStyle

:设置下拉进度条的颜色,它只有两种选择,一种是light,一种是dark

navigationBarBackgroundColor

:设置导航栏背景颜色,颜色为HEXcolor编码

navigationBarTitleText

:设置导航栏文本

navigationBarTextStyle

:设置导航栏文本颜色,同样地,颜色为HEXcolor编码

2-1、2-2小程序wxml基本语法

小程序开发语言

● wxml语法
● wxss语法
● js语法
wxml语法

WXML(WeiXin Markup Language),它与HTML有很多相似之处。它是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。

什么是组件:

■ 组件是视图层的基本组成单元

■ 组件自带一些功能与微信风格的样式

一个组件通常包括

开始标签

 和

结束标签

属性

用来修饰这个组件,

内容

在两个标签之内。

基本写法

写注释

<!--注释-->

 (快捷方式:ctrl+/)

写标签

<标签名 属性名1=“属性值1”……>……</标签名>

所有元素都必须闭合标签

<text>hello world</text>

1.标签必须使用小写

2.不可以含有中文

常用的属性类型
体会text与view组件的区别

{{ }}的作用

● {{ }}中的内容为动态数据,若内容为数字与字符串相加,则会默认数字为字符串格式。详见数据绑定部分内容。

基础语法介绍
● 数据绑定

wxml中的动态数据均来自对应page的data,数据绑定使用 Mustache 语法(双大括号)将变量包起来

列表渲染

在组件上使用 

wx:for

 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。

默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item

使用 

wx:for-item

●  可以指定数组当前元素的变量名,

使用 

wx:for-index

 可以指定数组当前下标的变量名

条件渲染

在框架中,使用 

wx:if=""

 来判断是否需要渲染该代码块:

<view wx:if="{{condition}}"> True </view>

在下图中,使用条件渲染并设置了false,即该条件后的语句均不起作用,因此该语句后面的文本并没有显示出来

另外,使用

hidden

也可以不显示文本或组件。

wx:if

vs 

hidden

因为 wx:if 之中的模板也可能包含数据绑定,所以当 wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。

同时 wx:if 也是惰性的,如果在初始渲染条件为 false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。

相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。

一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好。

也可以用 

wx:elif

 和 

wx:else

 来添加一个 else 块:

<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>

2-3 小程序WXSS基本语法

尺寸单位(px和rpx)

px(pixel):物理像素,设备的实际分辨率,也叫设备像素或绝对像素,用 px 表示一个像素点单位。

rpx(responsive pixel): 逻辑分辨率,可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素

注意: 在较小的屏幕上不可避免的会有一些毛刺,开发时尽量避免这种情况

样式设置
内联样式

框架组件上支持使用 style、class 属性来控制组件的样式。

style:静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,请尽量避免将静态的样式写进 style 中,以免影响渲染速度。

<view style="color:{{color}};" />

class:用于指定样式规则,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上.,样式类名之间用空格分隔

<view class="normal_view" />

一个简单的例子:

除了以上这些简单的例子,wxss样式属性主要包括以下几类:

1.  wxss display (显示)

2.  wxss position(定位)

3.  wxss float(浮动)

4.  wxss background(背景)

5.  wxss border (边框)

6.  wxss outline(轮廓)

7.  wxss text(文本属性)

8.  wxss font(字体属性)

9.  wxss margin(外边距)

10.  wxss padding(填充)

w3cschool      https://www.w3cschool.cn/css/css-tutorial.html

盒子模型

盒子模型来自于CSS,它是CSS布局的基础,CSS假定每个元素都会生成一个或多个矩形框,每个元素框中心都有一个内容区(content),这个内容区周围有内边距(padding)、边框(border)和外边距(margin),这些项默认宽度为0,这个矩形框就是常说的盒子模型。

一个简单的例子:

4-1小程序流程及生命周期函数导航栏

小程序运行机制

整个小程序框架系统分为两部分:逻辑层(App Service)和 视图层(View)。

每个小程序都需要在 app.js 中调用 App 方法注册小程序示例,绑定生命周期回调函数、错误监听和页面不存在监听函数等。

App(Object object)

注册小程序。接受一个 Object 参数,其指定小程序的生命周期回调等。

App() 必须在 app.js 中调用,必须调用且只能调用一次。不然会出现无法预期的后果。

// app.js

App({

    onLaunch:function(){……

    },

    globalData:{……

    }

})

//app.json

{

    "pages":[

        "pages/index/index",

        "pages/logs/logs"

        ],

}

//index.js

//获取应用实例

const app=getApp()

Page({

    data:{……

    },

//事件处理函数

    bindViewTap:function(){……

    },

    onload:function(){……

    },

    getUserInfo:function(e){……

    }

})

参数
小程序加载及生命周期
几个核心生命周期函数

页面主要的生命周期

onLoad

页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。

onShow

页面显示/切入前台时触发。

onReady

页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。

onHide

页面隐藏/切入后台时触发。

onUnload

页面卸载时触发。

下部导航栏

tabBar

将图标的图片文件夹放入项目文件夹中

4-2组件概述-视图容器

小程序的组件

小程序的界面是由一系列组件构成的,小程序基础序提供了一组基础组件来满足开发者的需求。它包括以下几种组件:

视图容器

基础内容

表单组件

导航

媒体组件

地图

画布

视图容器

视图容器就是将页面分割为独立的,不同的部分。视图容器共包含:

view(视图容器)属性说明:

例:

4-3弹性布局

页面内容的弹性布局

布局的传统解决方案,基于盒状模型,它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。

2009年,W3C提出了一种新的方案—-Flex布局,可以简便、完整、响应式地实现各种页面布局。

采用Flex布局的元素,称为Flex容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为Flex项目(flex item),简称”项目”。

容器的属性

● flex-direction

● flex-wrap

● flex-flow

● justify-content

● align-items

● align-content

flex-direction

决定主轴的方向(即项目的排列方向)。

● row(默认值):主轴为水平方向,起点在左端。

● row-reverse:主轴为水平方向,起点在右端。

● column:主轴为垂直方向,起点在上沿。

● column-reverse:主轴为垂直方向,起点在下沿。

flex-wrap

默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。

它可能取三个值。

● nowrap(默认):不换行。

● wrap:换行,第一行在上方。

● wrap-reverse:换行,第一行在下方。

flex-flow

flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。

justify-content属性

justify-content属性定义了项目在主轴上的对齐方式。

它可能取5个值,具体对齐方式与轴的方向有关。下面假设主轴为从左到右。

● flex-start(默认值):左对齐

● flex-end:右对齐

● center: 居中

● space-between:两端对齐,项目之间的间隔都相等。

● space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。

align-items属性

align-items属性定义项目在交叉轴上如何对齐。

它可能取5个值。具体的对齐方式与交叉轴的方向有关,下面假设交叉轴从上到下。

● flex-start:交叉轴的起点对齐。

● flex-end:交叉轴的终点对齐。

● center:交叉轴的中点对齐。

● baseline: 项目的第一行文字的基线对齐。

● stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。

align-content属性

align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

该属性可能取6个值。

● flex-start:与交叉轴的起点对齐。

● flex-end:与交叉轴的终点对齐。

● center:与交叉轴的中点对齐。

● space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。

● space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。

● stretch(默认值):轴线占满整个交叉轴。

更多内容请参考:flex布局语法教程

案例

/*index.wxss*/

.box{

    display:inlineflex;

    flex-direction:row;

    justify-content:space-around;

    flex-wrap:wrap;

    align-items:center

    /*align-content:flex-start;*/

}

.box_2{

    display:flex;

    background-color:greenyellow;

}

<view> 顶部</view>

<view class='box'>

  <!-- <view class='box_2'>

    <view class='box_3'>

      1-XXX

    </view>

    <view class='box_3'>

      1-YYY

    </view>

    <view class='box_3'>

      1-ZZZ

    </view>

  </view>

  <view class='box_2'>

    <view class='box_3'>

      2-XXX

    </view>

    <view class='box_3'>

      2-YYY

    </view>

    <view class='box_3'>

      2-ZZZ

    </view>

  </view>

  <view class='box_2'>

    <view class='box_3'>

      3-XXX

    </view>

    <view class='box_3'>

      3-YYY

    </view>

    <view class='box_3'>

      3-ZZZ

    </view>

  </view> -->

  <view class='box_2'>

    <view class='box_3'>



      <text>\n\n4-X</text>



    </view>

    <view class='box_3'>

      <text>\n4-Y\n</text>





    </view>

    <view class='box_3'>



      <text>4-Z\n\n</text>



    </view>

    </view>

  <view class='box_2'> 

    <view class='box_3'>

      5-XX

      <text>\n</text>

      <text>\n</text>

    </view>

    <view class='box_3'>

      5-YY

      <text>\n</text>

      <text>\n</text>

    </view>

    <view class='box_3'>

      5-ZZ

      <text>\n</text>

      <text>\n</text>

    </view>

  </view>

    <view class='box_2'> 

    <view class='box_3'>

      <text>\n</text>

      <text>\n</text>

      <text>\n\n6-X</text> 

    </view>

    <view class='box_3'>

      <text>\n</text>

      <text>\n</text>

      <text>\n\n6-Y</text>    

    </view>

    <view class='box_3'>

4-4轮播组件

swiper组件

滑块视图容器。其中只可放置swiper-item组件,否则会导致未定义的行为。

swiper-item

仅可放置在swiper组件中,宽高自动设置为100%。

案例

//index.js

Page({

    data:{

        background:['a','b','c']

},

//index.wxml

.si{

    display:block;

    height:150px;

}

.a{

    background-color:yellow;    

}

.b{

    background-color:red;

}

.c{

    background-color:blue;

}

//index.wxss

<view>

    <swiper autoplay="true" interval="2000"

    indicator-dots="true">

    <block wx:for="{{background}}" wx:key='swip'>

        <swiper-item>

            <view class='si {{itme}}'></view>

        </swiper-item>

    </block>

    </swiper-item>

</view> 

4-5 基础内容组件

小程序基础内容组件包含图标icon,进度条progress,富文本rich-text,和文本text等组件。

● icon
● progress
● rich-text
● text
icon

图标。组件属性的长度单位默认为px,2.4.0起支持传入单位(rpx/px)

progress

进度条。组件属性的长度单位默认为px,2.4.0起支持传入单位(rpx/px)。

rich-text

副文本

text

文本

4-6 表单组件

button

按钮

示例代码:

.button-hover {

  background-color: red;

}

.other-button-hover {

  background-color: blue;

}

<button type="default" size="{{defaultSize}}" loading="{{loading}}" plain="{{plain}}"

  disabled="{{disabled}}" bindtap="default" hover-class="other-button-hover"> default </button>

<button type="primary" size="{{primarySize}}" loading="{{loading}}" plain="{{plain}}"

  disabled="{{disabled}}" bindtap="primary"> primary </button>

<button type="warn" size="{{warnSize}}" loading="{{loading}}" plain="{{plain}}"

  disabled="{{disabled}}" bindtap="warn"> warn </button>

<button bindtap="setDisabled">点击设置以上按钮disabled属性</button>

<button bindtap="setPlain">点击设置以上按钮plain属性</button>

<button bindtap="setLoading">点击设置以上按钮loading属性</button>

<button open-type="contact">进入客服会话</button>

<button open-type="getUserInfo" lang="zh_CN" bindgetuserinfo="onGotUserInfo">获取用户信息</button>

<button open-type="openSetting">打开授权设置页</button>

var types = ['default', 'primary', 'warn']

var pageObject = {

  data: {

    defaultSize: 'default',

    primarySize: 'default',

    warnSize: 'default',

    disabled: false,

    plain: false,

    loading: false

  },

  setDisabled: function(e) {

    this.setData({

      disabled: !this.data.disabled

    })

  },

  setPlain: function(e) {

    this.setData({

      plain: !this.data.plain

    })

  },

  setLoading: function(e) {

    this.setData({

      loading: !this.data.loading

    })

  },

  onGotUserInfo: function(e) {

    console.log(e.detail.errMsg)

    console.log(e.detail.userInfo)

    console.log(e.detail.rawData)

  },

}



for (var i = 0; i < types.length; ++i) {

  (function(type) {

    pageObject[type] = function(e) {

      var key = type + 'Size'

      var changedData = {}

      changedData[key] =

        this.data[key] === 'default' ? 'mini' : 'default'

      this.setData(changedData)

    }

  })(types[i])

}



Page(pageObject)

checkbox

多选项目。

示例代码:

<checkbox-group bindchange="checkboxChange">

  <label class="checkbox" wx:for="{{items}}">

    <checkbox value="{{item.name}}" checked="{{item.checked}}"/>{{item.value}}

  </label>

</checkbox-group>

Page({

  data: {

    items: [

      {name: 'USA', value: '美国'},

      {name: 'CHN', value: '中国', checked: 'true'},

      {name: 'BRA', value: '巴西'},

      {name: 'JPN', value: '日本'},

      {name: 'ENG', value: '英国'},

      {name: 'TUR', value: '法国'},

    ]

  },

  checkboxChange: function(e) {

    console.log('checkbox发生change事件,携带value值为:', e.detail.value)

  }

})

.checkbox{

    display:flex;

    flex-direction:row-reverse;

}

editor

富文本编辑器,可以对图片、文字进行编辑。

编辑器导出内容支持带标签的 html和纯文本的 text,编辑器内部采用 delta 格式进行存储。

通过setContents接口设置内容时,解析插入的 html 可能会由于一些非法标签导致解析错误,建议开发者在小程序内使用时通过 delta 进行插入。

富文本组件内部引入了一些基本的样式使得内容可以正确的展示,开发时可以进行覆盖。需要注意的是,在其它组件或环境中使用富文本组件导出的html时,需要额外引入这段样式,并维护的结构。

图片控件仅初始化时设置有效。

示例代码:

<editor

  id="editor"

  class="ql-container"

  placeholder="{{placeholder}}"

  showImgSize

  showImgToolbar

  showImgResize

  bindstatuschange="onStatusChange"

  read-only="{{readOnly}}"

  bindready="onEditorReady">

</editor>

Page({

  readOnlyChange() {

    this.setData({

      readOnly: !this.data.readOnly

    })

  },

  onEditorReady() {

    const that = this

    wx.createSelectorQuery().select('#editor').context(function (res) {

      that.editorCtx = res.context

    }).exec()

  },

  format(e) {

    let { name, value } = e.target.dataset

    if (!name) return

    // console.log('format', name, value)

    this.editorCtx.format(name, value)

  },

})

form

表单。将组件内的用户输入的switch input checkbox slider radio picker 提交。

当点击 form 表单中 form-type 为 submit 的 button 组件时,会将表单组件中的 value 值进行提交,需要在表单组件中加上 name 来作为 key。

示例代码:

<form bindsubmit="formSubmit" bindreset="formReset">

  <view class="section section_gap">

    <view class="section__title">switch</view>

    <switch name="switch"/>

  </view>

  <view class="section section_gap">

    <view class="section__title">slider</view>

    <slider name="slider" show-value ></slider>

  </view>



  <view class="section">

    <view class="section__title">input</view>

    <input name="input" placeholder="please input here" />

  </view>

  <view class="section section_gap">

    <view class="section__title">radio</view>

    <radio-group name="radio-group">

      <label><radio value="radio1"/>radio1</label>

      <label><radio value="radio2"/>radio2</label>

    </radio-group>

  </view>

  <view class="section section_gap">

    <view class="section__title">checkbox</view>

    <checkbox-group name="checkbox">

      <label><checkbox value="checkbox1"/>checkbox1</label>

      <label><checkbox value="checkbox2"/>checkbox2</label>

    </checkbox-group>

  </view>

  <view class="btn-area">

    <button form-type="submit">Submit</button>

    <button form-type="reset">Reset</button>

  </view>

</form>

Page({

  formSubmit: function(e) {

    console.log('form发生了submit事件,携带数据为:', e.detail.value)

  },

  formReset: function() {

    console.log('form发生了reset事件')

  }

})

input

输入框。该组件是原生组件,使用时请注意相关限制。

示例代码:

<!--input.wxml-->

<view class="section">

  <input placeholder="这是一个可以自动聚焦的input" auto-focus/>

</view>

<view class="section">

  <input placeholder="这个只有在按钮点击的时候才聚焦" focus="{{focus}}" />

  <view class="btn-area">

    <button bindtap="bindButtonTap">使得输入框获取焦点</button>

  </view>

</view>

<view class="section">

  <input  maxlength="10" placeholder="最大输入长度10" />

</view>

<view class="section">

  <view class="section__title">你输入的是:{{inputValue}}</view>

  <input  bindinput="bindKeyInput" placeholder="输入同步到view中"/>

</view>

<view class="section">

  <input  bindinput="bindReplaceInput" placeholder="连续的两个1会变成2" />

</view>

<view class="section">

  <input password type="number" />

</view>

<view class="section">

  <input password type="text" />

</view>

<view class="section">

  <input type="digit" placeholder="带小数点的数字键盘"/>

</view>

<view class="section">

  <input type="idcard" placeholder="身份证输入键盘" />

</view>

<view class="section">

  <input placeholder-style="color:red" placeholder="占位符字体是红色的" />

</view>

//input.js

Page({

  data: {

    focus: false,

    inputValue: ''

  },

  bindButtonTap: function() {

    this.setData({

      focus: true

    })

  },

  bindKeyInput: function(e) {

    this.setData({

      inputValue: e.detail.value

    })

  },

  bindReplaceInput: function(e) {

    var value = e.detail.value

    var pos = e.detail.cursor

    if(pos != -1){

      //光标在中间

      var left = e.detail.value.slice(0,pos)

      //计算光标的位置

      pos = left.replace(/11/g,'2').length

    }



    //直接返回对象,可以对输入进行过滤处理,同时可以控制光标的位置

    return {

      value: value.replace(/11/g,'2'),

      cursor: pos

    }



    //或者直接返回字符串,光标在最后边

    //return value.replace(/11/g,'2'),

  }

})

label

用来改进表单组件的可用性。

使用for属性找到对应的id,或者将控件放在该标签下,当点击时,就会触发对应的控件。 for优先级高于内部控件,内部有多个控件的时候默认触发第一个控件。 目前可以绑定的控件有:button, checkbox, radio, switch。

示例代码:

<view class="section section_gap">

<view class="section__title">表单组件在label内</view>

<checkbox-group class="group" bindchange="checkboxChange">

  <view class="label-1" wx:for="{{checkboxItems}}">

    <label>

      <checkbox hidden value="{{item.name}}" checked="{{item.checked}}"></checkbox>

      <view class="label-1__icon">

        <view class="label-1__icon-checked" style="opacity:{{item.checked ? 1: 0}}"></view>

      </view>

      <text class="label-1__text">{{item.value}}</text>

    </label>

  </view>

</checkbox-group>

</view>



<view class="section section_gap">

<view class="section__title">label用for标识表单组件</view>

<radio-group class="group" bindchange="radioChange">

  <view class="label-2" wx:for="{{radioItems}}">

    <radio id="{{item.name}}" hidden value="{{item.name}}" checked="{{item.checked}}"></radio>

    <view class="label-2__icon">

      <view class="label-2__icon-checked" style="opacity:{{item.checked ? 1: 0}}"></view>

    </view>

    <label class="label-2__text" for="{{item.name}}"><text>{{item.name}}</text></label>

  </view>

</radio-group>

</view>

Page({

  data: {

    checkboxItems: [

      {name: 'USA', value: '美国'},

      {name: 'CHN', value: '中国', checked: 'true'},

      {name: 'BRA', value: '巴西'},

      {name: 'JPN', value: '日本', checked: 'true'},

      {name: 'ENG', value: '英国'},

      {name: 'TUR', value: '法国'},

    ],

    radioItems: [

      {name: 'USA', value: '美国'},

      {name: 'CHN', value: '中国', checked: 'true'},

      {name: 'BRA', value: '巴西'},

      {name: 'JPN', value: '日本'},

      {name: 'ENG', value: '英国'},

      {name: 'TUR', value: '法国'},

    ],

    hidden: false

  },

  checkboxChange: function(e) {

    var checked = e.detail.value

    var changed = {}

    for (var i = 0; i < this.data.checkboxItems.length; i ++) {

      if (checked.indexOf(this.data.checkboxItems[i].name) !== -1) {

        changed['checkboxItems['+i+'].checked'] = true

      } else {

        changed['checkboxItems['+i+'].checked'] = false

      }

    }

    this.setData(changed)

  },

  radioChange: function(e) {

    var checked = e.detail.value

    var changed = {}

    for (var i = 0; i < this.data.radioItems.length; i ++) {

      if (checked.indexOf(this.data.radioItems[i].name) !== -1) {

        changed['radioItems['+i+'].checked'] = true

      } else {

        changed['radioItems['+i+'].checked'] = false

      }

    }

    this.setData(changed)

  }

})

.label-1, .label-2{

    margin-bottom: 15px;

}

.label-1__text, .label-2__text {

    display: inline-block;

    vertical-align: middle;

}



.label-1__icon {

    position: relative;

    margin-right: 10px;

    display: inline-block;

    vertical-align: middle;

    width: 18px;

    height: 18px;

    background: #fcfff4;

}



.label-1__icon-checked {

    position: absolute;

    top: 3px;

    left: 3px;

    width: 12px;

    height: 12px;

    background: #1aad19;

}



.label-2__icon {

    position: relative;

    display: inline-block;

    vertical-align: middle;

    margin-right: 10px;

    width: 18px;

    height: 18px;

    background: #fcfff4;

    border-radius: 50px;

}



.label-2__icon-checked {

    position: absolute;

    left: 3px;

    top: 3px;

    width: 12px;

    height: 12px;

    background: #1aad19;

    border-radius: 50%;

}



.label-4_text{

    text-align: center;

    margin-top: 15px;

}

picker

从底部弹起的滚动选择器。

示例代码:

<view class="section">

  <view class="section__title">普通选择器</view>

  <picker bindchange="bindPickerChange" value="{{index}}" range="{{array}}">

    <view class="picker">

      当前选择:{{array[index]}}

    </view>

  </picker>

</view>

<view class="section">

  <view class="section__title">多列选择器</view>

  <picker mode="multiSelector" bindchange="bindMultiPickerChange" bindcolumnchange="bindMultiPickerColumnChange" value="{{multiIndex}}" range="{{multiArray}}">

    <view class="picker">

      当前选择:{{multiArray[0][multiIndex[0]]}},{{multiArray[1][multiIndex[1]]}},{{multiArray[2][multiIndex[2]]}}

    </view>

  </picker>

</view>

<view class="section">

  <view class="section__title">时间选择器</view>

  <picker mode="time" value="{{time}}" start="09:01" end="21:01" bindchange="bindTimeChange">

    <view class="picker">

      当前选择: {{time}}

    </view>

  </picker>

</view>



<view class="section">

  <view class="section__title">日期选择器</view>

  <picker mode="date" value="{{date}}" start="2015-09-01" end="2017-09-01" bindchange="bindDateChange">

    <view class="picker">

      当前选择: {{date}}

    </view>

  </picker>

</view>

<view class="section">

  <view class="section__title">省市区选择器</view>

  <picker mode="region" bindchange="bindRegionChange" value="{{region}}" custom-item="{{customItem}}">

    <view class="picker">

      当前选择:{{region[0]}},{{region[1]}},{{region[2]}}

    </view>

  </picker>

</view>

Page({

  data: {

    array: ['美国', '中国', '巴西', '日本'],

    objectArray: [

      {

        id: 0,

        name: '美国'

      },

      {

        id: 1,

        name: '中国'

      },

      {

        id: 2,

        name: '巴西'

      },

      {

        id: 3,

        name: '日本'

      }

    ],

    index: 0,

    multiArray: [['无脊柱动物', '脊柱动物'], ['扁性动物', '线形动物', '环节动物', '软体动物', '节肢动物'], ['猪肉绦虫', '吸血虫']],

    objectMultiArray: [

      [

        {

          id: 0,

          name: '无脊柱动物'

        },

        {

          id: 1,

          name: '脊柱动物'

        }

      ], [

        {

          id: 0,

          name: '扁性动物'

        },

        {

          id: 1,

          name: '线形动物'

        },

        {

          id: 2,

          name: '环节动物'

        },

        {

          id: 3,

          name: '软体动物'

        },

        {

          id: 3,

          name: '节肢动物'

        }

      ], [

        {

          id: 0,

          name: '猪肉绦虫'

        },

        {

          id: 1,

          name: '吸血虫'

        }

      ]

    ],

    multiIndex: [0, 0, 0],

    date: '2016-09-01',

    time: '12:01',

    region: ['广东省', '广州市', '海珠区'],

    customItem: '全部'

  },

  bindPickerChange: function(e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      index: e.detail.value

    })

  },

  bindMultiPickerChange: function (e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      multiIndex: e.detail.value

    })

  },

  bindMultiPickerColumnChange: function (e) {

    console.log('修改的列为', e.detail.column, ',值为', e.detail.value);

    var data = {

      multiArray: this.data.multiArray,

      multiIndex: this.data.multiIndex

    };

    data.multiIndex[e.detail.column] = e.detail.value;

    switch (e.detail.column) {

      case 0:

        switch (data.multiIndex[0]) {

          case 0:

            data.multiArray[1] = ['扁性动物', '线形动物', '环节动物', '软体动物', '节肢动物'];

            data.multiArray[2] = ['猪肉绦虫', '吸血虫'];

            break;

          case 1:

            data.multiArray[1] = ['鱼', '两栖动物', '爬行动物'];

            data.multiArray[2] = ['鲫鱼', '带鱼'];

            break;

        }

        data.multiIndex[1] = 0;

        data.multiIndex[2] = 0;

        break;

      case 1:

        switch (data.multiIndex[0]) {

          case 0:

            switch (data.multiIndex[1]) {

              case 0:

                data.multiArray[2] = ['猪肉绦虫', '吸血虫'];

                break;

              case 1:

                data.multiArray[2] = ['蛔虫'];

                break;

              case 2:

                data.multiArray[2] = ['蚂蚁', '蚂蟥'];

                break;

              case 3:

                data.multiArray[2] = ['河蚌', '蜗牛', '蛞蝓'];

                break;

              case 4:

                data.multiArray[2] = ['昆虫', '甲壳动物', '蛛形动物', '多足动物'];

                break;

            }

            break;

          case 1:

            switch (data.multiIndex[1]) {

              case 0:

                data.multiArray[2] = ['鲫鱼', '带鱼'];

                break;

              case 1:

                data.multiArray[2] = ['青蛙', '娃娃鱼'];

                break;

              case 2:

                data.multiArray[2] = ['蜥蜴', '龟', '壁虎'];

                break;

            }

            break;

        }

        data.multiIndex[2] = 0;

        break;

    }

    console.log(data.multiIndex);

    this.setData(data);

  },

  bindDateChange: function(e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      date: e.detail.value

    })

  },

  bindTimeChange: function(e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      time: e.detail.value

    })

  },

  bindRegionChange: function (e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      region: e.detail.value

    })

  }

})

radio

单选项目。

示例代码:

<radio-group class="radio-group" bindchange="radioChange">

  <label class="radio" wx:for="{{items}}">

    <radio value="{{item.name}}" checked="{{item.checked}}"/>{{item.value}}

  </label>

</radio-group>

Page({

  data: {

    items: [

      {name: 'USA', value: '美国'},

      {name: 'CHN', value: '中国', checked: 'true'},

      {name: 'BRA', value: '巴西'},

      {name: 'JPN', value: '日本'},

      {name: 'ENG', value: '英国'},

      {name: 'TUR', value: '法国'},

    ]

  },

  radioChange: function(e) {

    console.log('radio发生change事件,携带value值为:', e.detail.value)

  }

})

slider

滑动选择器。

示例代码:

<view class="section section_gap">

  <text class="section__title">设置step</text>

  <view class="body-view">

    <slider bindchange="slider2change" step="5"/>

  </view>

</view>



<view class="section section_gap">

  <text class="section__title">显示当前value</text>

  <view class="body-view">

    <slider bindchange="slider3change" show-value/>

  </view>

</view>



<view class="section section_gap">

  <text class="section__title">设置最小/最大值</text>

  <view class="body-view">

    <slider bindchange="slider4change" min="50" max="200" show-value/>

  </view>

</view>

var pageData = {}

for (var i = 1; i < 5; i++) {

  (function (index) {

    pageData['slider' + index + 'change'] = function(e) {

      console.log('slider' + 'index' + '发生 change 事件,携带值为', e.detail.value)

    }

  })(i)

}

Page(pageData)

switch

开关选择器。

示例代码:

<view class="body-view">

    <switch checked bindchange="switch1Change"/>

    <switch bindchange="switch2Change"/>

</view>

Page({

  switch1Change: function (e){

    console.log('switch1 发生 change 事件,携带值为', e.detail.value)

  },

  switch2Change: function (e){

    console.log('switch2 发生 change 事件,携带值为', e.detail.value)

  }

})

textarea

多行输入框。该组件是原生组件,使用时请注意相关限制。

示例代码:

<view class="section">

  <textarea bindblur="bindTextAreaBlur" auto-height placeholder="自动变高" />

</view>

<view class="section">

  <textarea placeholder="placeholder颜色是红色的" placeholder-style="color:red;"  />

</view>

<view class="section">

  <textarea placeholder="这是一个可以自动聚焦的textarea" auto-focus />

</view>

<view class="section">

  <textarea placeholder="这个只有在按钮点击的时候才聚焦" focus="{{focus}}" />

  <view class="btn-area">

    <button bindtap="bindButtonTap">使得输入框获取焦点</button>

  </view>

</view>

<view class="section">

  <form bindsubmit="bindFormSubmit">

    <textarea placeholder="form 中的 textarea" name="textarea"/>

    <button form-type="submit"> 提交 </button>

  </form>

</view>

//textarea.js

Page({

  data: {

    height: 20,

    focus: false

  },

  bindButtonTap: function() {

    this.setData({

      focus: true

    })

  },

  bindTextAreaBlur: function(e) {

    console.log(e.detail.value)

  },

  bindFormSubmit: function(e) {

    console.log(e.detail.value.textarea)

  }

})

4-7 其他组件

导航组件

navigator

页面链接。

示例代码:

.navigator-hover {

  color:blue;

}

.other-navigator-hover {

  color:red;

}

<!-- sample.wxml -->

<view class="btn-area">

  <navigator url="/page/navigate/navigate?title=navigate" hover-class="navigator-hover">跳转到新页面</navigator>

  <navigator url="../../redirect/redirect/redirect?title=redirect" open-type="redirect" hover-class="other-navigator-hover">在当前页打开</navigator>

  <navigator url="/page/index/index" open-type="switchTab" hover-class="other-navigator-hover">切换 Tab</navigator>

  <navigator target="miniProgram" open-type="navigate" app-id="" path="" extra-data="" version="release">打开绑定的小程序</navigator>

</view>

<!-- navigator.wxml -->

<view style="text-align:center"> {{title}} </view>

<view> 点击左上角返回回到之前页面 </view>

<!-- redirect.wxml -->

<view style="text-align:center"> {{title}} </view>

<view> 点击左上角返回回到上级页面 </view>

Page({

  onLoad: function(options) {

    this.setData({

      title: options.title

    })

  }

})

媒体组件

image

图片。支持JPG、PNG、SVG格式,2.3.0 起支持云文件ID。

示例代码:

<view class="page">

  <view class="page__hd">

    <text class="page__title">image</text>

    <text class="page__desc">图片</text>

  </view>

  <view class="page__bd">

    <view class="section section_gap" wx:for="{{array}}" wx:for-item="item">

      <view class="section__title">{{item.text}}</view>

      <view class="section__ctn">

        <image style="width: 200px; height: 200px; background-color: #eeeeee;" mode="{{item.mode}}" src="{{src}}"></image>

      </view>

    </view>

  </view>

</view>

Page({

  data: {

    array: [{

      mode: 'scaleToFill',

      text: 'scaleToFill:不保持纵横比缩放图片,使图片完全适应'

    }, {

      mode: 'aspectFit',

      text: 'aspectFit:保持纵横比缩放图片,使图片的长边能完全显示出来'

    }, {

      mode: 'aspectFill',

      text: 'aspectFill:保持纵横比缩放图片,只保证图片的短边能完全显示出来'

    }, {

      mode: 'top',

      text: 'top:不缩放图片,只显示图片的顶部区域'

    }, {

      mode: 'bottom',

      text: 'bottom:不缩放图片,只显示图片的底部区域'

    }, {

      mode: 'center',

      text: 'center:不缩放图片,只显示图片的中间区域'

    }, {

      mode: 'left',

      text: 'left:不缩放图片,只显示图片的左边区域'

    }, {

      mode: 'right',

      text: 'right:不缩放图片,只显示图片的右边边区域'

    }, {

      mode: 'top left',

      text: 'top left:不缩放图片,只显示图片的左上边区域'

    }, {

      mode: 'top right',

      text: 'top right:不缩放图片,只显示图片的右上边区域'

    }, {

      mode: 'bottom left',

      text: 'bottom left:不缩放图片,只显示图片的左下边区域'

    }, {

      mode: 'bottom right',

      text: 'bottom right:不缩放图片,只显示图片的右下边区域'

    }],

    src: '../resources/cat.jpg'

  },

  imageError: function(e) {

    console.log('image3发生error事件,携带值为', e.detail.errMsg)

  }

})

4-8 商城内容展示

示例代码

// index.js

const app = getApp()



Page({

  data: {

    imgUrls:[

      "https://img.youpin.mi-img.com/youpinoper/1002265792a851c83054da07959d976c.jpg?id=&w=1080&h=450",

      "https://img.youpin.mi-img.com/youpinoper/f62e9e59b21f97f81ed3ab567f0e4b08.jpg?id=&w=1080&h=450",

      "https://img.youpin.mi-img.com/youpinoper/0bb6ad5d5c85b03534b460c297c6b85a.jpeg?id=&w=1080&h=450"

    ],



    category:[

      { url:"https://img.youpin.mi-img.com/shopmain/b68323e900ffbb4835dd9e6a15d10b77.png?w=800&h=800",

      tag:'小米众筹'},

            {

              url: "https://img.youpin.mi-img.com/800_pic/27a789a428038214a8dda98f97d5fe4c.png",

        tag: '限时抢购'

      },

            {

              url: "https://img.youpin.mi-img.com/800_pic/c13ae49b289b269d9a39496e3e31708d.png",

        tag: '热销榜单'

      },

            {

              url: "https://img.youpin.mi-img.com/800_pic/da003d715c9e832b2c8e62402e48bfa0.png",

        tag: '随便逛逛'

      }

    ]

  },



})


<!-- index.wxml -->

<!-- 商城首页 -->

<swiper indicator-dots="true" autoplay="true" interval="2000">

  <block wx:for="{{imgUrls}}" wx:key='imgs'>

  <swiper-item>

    <image src='{{item}}' mode="widthFix"></image>

  </swiper-item>

  </block>

</swiper>

<!-- 分类推荐 -->

<view class="category">

  <block wx:for="{{category}}" wx:key="types">

  <view class="category-item">

    <image src='{{item.url}}' class='category-img'></image>

    <text>{{item.tag}}</text>

    </view>

  </block>

</view>

<!-- 今日特价 -->

<view class="separate"></view>

<view class="discount">

<view class="tag">

  <view class="tag-insider"></view>

  <view>今日特价</view></view>

  <view>更多>></view>

</view>

/* index.wxss */

.category{

  display: flex;

  justify-content: space-around

}

.category-item{

  display: flex;

  flex-direction: column;

  align-items: center;

  padding: 20rpx;



}

.category-img{

  background-color: #C5C1C0;

  width: 100rpx;

  height: 100rpx;

  border-radius: 30%;

}

.category-item text{

  padding-top: 10rpx;

  font-size: 25rpx;

}

.separate{

  height: 10rpx;

  background-color: #F3F3F3;



}

.discount{

  display: flex;

  justify-content: space-between;

  padding: 0 10px;

  font-size: 30rpx;

}

.tag{

  display: flex;

  color: red;

}

.tag-insider{

  margin-right: 10rpx;

  margin-top: 5rpx;

  width: 10rpx;

  height: 30rpx;

  background-color:#fea96a;

}


效果

5-1、5-2小程序API介绍

API(Application Programming Interface)

● 由小程序开发框架提供的预先定义的函数

● 可直接按需调用功能,无需获取源码或理解内部细节

小程序API分类

● 网络 媒体 文件 数据缓存 画布

● 位置 设备 开放接口等

小程序API的类型
事件监听

约定以 

on

开头的 API 用来监听某个事件是否触发,如:

wx.onSocketOpen

wx.onCompassChange

 等。

这类 API 接受一个回调函数作为参数,当事件触发时会调用这个回调函数,并将相关数据以参数形式传入。

代码示例:

wx.onCompassChange(function (res) {

  console.log(res.direction)

})

数据操作

数据的读取及写入 

读取:

wx.get***

写入:

wx.set***

同步-可由函数返回值获取结果

约定以 

Sync

 结尾的 API 都是同步 API, 如

wx.setStorageSync

wx.getSystemInfoSync

 等。此外,也有一些其他的同步 API,如 

wx.createWorker

wx.getBackgroundAudioManager

 等

同步 API 的执行结果可以通过函数返回值直接获取,如果执行出错会抛出异常。

代码示例:

try {

  wx.setStorageSync('key', 'value')

} catch (e) {

  console.error(e)

}

异步

大多数 API 都是异步 API,如 

wx.request

wx.login

 等。这类 API 接口通常都接受一个 Object 类型的参数,这个参数都支持按需指定以下字段来接收接口调用结果:

三个回调函数 

success

fail

complete

原理:回调函数的核心是委托制 

  适用场景:在特定条件满足时执行一个任务 

  使用:

● 定义一个函数将要执行的任务与一特定事件建立关联

● 当该函数关联的事件触发的时候,函数将被执行(回调机制)

优点:程序的任务执行变为异步,无需一直等待任务执行完成

success

fail

complete

 函数调用时会传入一个 Object 类型参数,包含以下字段:

异步 API 的执行结果需要通过 Object 类型的参数中传入的对应回调函数获取。部分异步 API 也会有返回值,可以用来实现更丰富的功能,如 

wx.request

wx.connectSocket

 等。

代码示例:

wx.login({

  success(res) {

    console.log(res.code)

  }

})

5-3结合回调函数操作API

例一:

我们先来看媒体中的获取图片信息的函数:

wx.getImageInfo

(Object object)

该函数的作用是获取图片信息。网络图片需先配置download域名才能生效。

函数参数:

object.success 回调函数参数:

res.orientation 的合法值:

代码示例:

//index.js

const app=getApp()

Page({

    data:{

        src:'../../imgs/pic.jpg'

        },

    getImgInfo:function(){

        wx.getImageInfo({

            src:this.data.src,

            success(res)  {  //回调函数

                console.log(res)

            },

            fail(res){

                console.log(res)

            },

            complete(res){

                console.log('一定会执行')

            }

        })

        }   

    })

//index.wxml

<view>下面是一个获取图片信息的按钮</view>

<button bindtap='getImgInfo'>点一下</button>

运行

输出图片信息:

例二:

再来看一下界面交互中的

wx.showToast

函数 

该函数的作用是显示消息提示框

函数参数:

代码示例:

//index.js

const app = getApp()



Page({

  data: {

    src:'../../imgs/pic.jpg'

  },

  //事件触发函数

  showImgInfo:function(text){

    wx.showToast({

      title: text,

      icon:'none'

    })

  },



  getImgInfo:function(){

    var that=this

    wx.getImageInfo({

      src: this.data.src,

      success(res) {  // 回调函数 委托 执行者变化

        that.showImgInfo('路径->'+res.path+'\n尺寸'+res.width+'X'+res.height)

      },

      fail(res){

        console.log(res)

      },

      complete(res){

        console.log('一定会执行',res)

      }

    })

  }



})


运行,获得图片信息,并在交互界面看到所输出的图片信息

5-4音频API

InnerAudioContext

函数

InnerAudioContext 实例,可通过 wx.createInnerAudioContext 接口获取实例。

属性:

tring src

音频资源的地址,用于直接播放。2.2.3 开始支持云文件ID

number startTime

开始播放的位置(单位:s),默认为 0

boolean autoplay

是否自动开始播放,默认为 false

boolean loop

是否循环播放,默认为 false

boolean obeyMuteSwitch

是否遵循系统静音开关,默认为 true。当此参数为 false 时,即使用户打开了静音开关,也能继续发出声音。从 2.3.0 版本开始此参数不生效,使用 

wx.setInnerAudioOption

 接口统一设置。

number volume

基础库 1.9.90 开始支持,低版本需做兼容处理。

音量

范围 0~1。默认为 1

number duration

当前音频的长度(单位 s)。只有在当前有合法的 src 时返回(只读)

number currentTime

当前音频的播放位置(单位 s)。只有在当前有合法的 src 时返回,时间保留小数点后 6 位(只读)

boolean paused

当前是是否暂停或停止状态(只读)

number buffered

音频缓冲的时间点,仅保证当前播放时间点到此时间点内容已缓冲(只读

代码示例:

//index.js

Page({



  /**

   * 页面的初始数据

   */

  data: {

    src: 'http://ws.stream.qqmusic.qq.com/M500001VfvsJ21xFqb.mp3?guid=ffffffff82def4af4b12b3cd9337d5e7&uin=346897220&vkey=6292F51E1E384E061FF02C31F716658E5C81F5594D561F2E88B854E81CAAB7806D5E4F103E55D33C16F3FAC506D1AB172DE8600B37E43FAD&fromtag=46',

    poster: '../../imgs/pic.jpg',

    name: '普通disco',

    author: '佚名',

    src2:'../../music/light_emotion.mp3',

    src3: 'http://sc1.111ttt.cn:8282/2018/1/03m/13/396131229550.m4a?tflag=1546606800&pin=97bb2268ae26c20fe093fd5b0f04be80#.mp3',

  },

  audioPlay() {

    this.audioData.play()

    console.log('播放')

  },

  audioPause() {

    this.audioData.pause()

    console.log('暂停')

  },

  audioPlayBack() {

    this.audioData.seek(this.audioData.currentTime-3)

  },

  audioStop() {

    // this.audioData.stop()

  },

  audioStart() {

  },



  /**

   * 生命周期函数--监听页面加载

   */

  onLoad: function(options) {

    this.audioData =wx.createInnerAudioContext()

    this.audioData.src = this.data.src

    this.audioData.autoplay = false

    console.log(this.audioData)

  },



  /**

   * 生命周期函数--监听页面初次渲染完成

   */

  onReady: function() {

    // this.audioData = wx.createAudioContext('myAudio')



  },



  /**

   * 生命周期函数--监听页面显示

   */

  onShow: function() {



  },



  /**

   * 生命周期函数--监听页面隐藏

   */

  onHide: function() {



  },



  /**

   * 生命周期函数--监听页面卸载

   */

  onUnload: function() {



  },



  /**

   * 页面相关事件处理函数--监听用户下拉动作

   */

  onPullDownRefresh: function() {



  },



  /**

   * 页面上拉触底事件的处理函数

   */

  onReachBottom: function() {



  },



  /**

   * 用户点击右上角分享

   */

  onShareAppMessage: function() {



  }

})


//index.wxml

<!--pages/index2/index2.wxml-->

<audio poster="{{poster}}" name="{{name}}" author="{{author}}" src="{{src3}}" id="myAudio" controls loop></audio>



<button bindtap='audioPlay'>播放</button>

<button bindtap='audioPause'>暂停</button>

<button bindtap='audioPlayBack'>回放测试</button>

<button bindtap='audioStop'>结束</button>

<button bindtap='audioStart'>回到开头</button>

值得注意的是,

<audio/>

组件不再维护,可以使用能力更强的 

wx.createInnerAudioContext

接口.

5-5位置API

首先我们看一下

wx.getLocation

函数

作用:获取当前的地理位置、速度。当用户离开小程序后,此接口无法调用。

参数:

object.success 回调函数

代码示例:

//index.js

Page({



  /**

   * 页面的初始数据

   */

  data: {

    demo_la: 48.1545,

    demo_lo: 82.3555907,

    latitude: 0,

    longitude: 0,

    begin_la: 0,

    begin_lo: 0,

    interval: 0,

    speed: 0,

    accuracy: 0,

    markers: [{

      iconPath: "../../imgs/pic.jpg",

      id: 0,

      latitude: 39.9205,

      longitude: 116.4605,

      width: 30,

      height: 30

    }]



  },

  openLoc() {

    this.mapCtx.includePoints({

      padding: [10],

      points: [{

        latitude: 23.10229,

        longitude: 113.3345211,

      }, {

        latitude: 23.00229,

        longitude: 113.3345211,

      }]

    })

  },



  geoInfo() {

    var that = this

    wx.getLocation({

      type: 'gcj02',

      scale:18,

      success(res) {



        that.setData({

          latitude: res.latitude,

          longitude: res.longitude,

          speed: res.speed,

          accuracy: res.accuracy,

        })

      }

    })

    console.log(this.data.latitude, this.data.longitude)

  },



  startRun() {

    var that = this

    clearInterval(this.data.interval)



    wx.getLocation({

      type: 'gcj02',

      success(res) {

        var la = res.latitude

        var lo = res.longitude

        that.setData({

          begin_la: la,

          begin_lo: lo,

          'markers[1]': {

            iconPath: "../../imgs/red.png",

            id: 1,

            latitude: la,

            longitude: lo,

            width: 20,

            height: 20

          }

        })

      }

    })



    var interval = setInterval(() => {

      this.running()

    }, 1000)





  },

  stopRun() {

    var that = this

    clearInterval(this.data.interval)

    wx.getLocation({

      type: 'gcj02',

      success(res) {

        var la = res.latitude

        var lo = res.longitude

        that.setData({

          latitude: la,

          longitude: lo,

          'markers[2]': {

            iconPath: "../../imgs/yellow.png",

            id: 2,

            latitude: la,

            longitude: lo,

            width: 20,

            height: 20

          }

        })

      }

    })

    console.log('开始位置', this.data.begin_la, this.data.begin_lo)

    console.log('结束位置', this.data.latitude, this.data.longitude)

    console.log(this.data)

  },

  running() {

    this.data.latitude += 0.0002

    this.data.longitude += 0.0002

  },

  center() {

    this.mapCtx.getCenterLocation({

      success: function (res) {

        console.log(res.latitude,res.longitude)

      }

    })



  },

  trans() {

    this.mapCtx.translateMarker({

      markerId: 0,

      autoRotate: false,

      duration: 3000,

      destination: {

        latitude: 40.30229,

        longitude: 116.7545211,

      },

      animationEnd() {

        console.log('animation end')

      }

    })

  },

  onReady: function () {

    this.mapCtx = wx.createMapContext('myMap')

  },

})

//index.wxml

<view class='top'>

  <button bindtap='geoInfo'>位置</button>

  <button bindtap='openLoc'>打开</button>

  <button bindtap='center'>定心</button>

  <button bindtap='trans'>动</button>

</view>



<map id='myMap' style="width:100%; height:350px" longitude='{{longitude}}' latitude='{{latitude}}' markers='{{markers}}' polyline="{{polyline}}" show-location subkey='IWLBZ-CK6R4-YXDUO-DQCXK-J5H4Z-DSBCI'>

  <cover-view calss='cover'>

    经度:{{longitude}} 纬度:{{latitude}} 速度:{{speed}}

  </cover-view>

</map>



<button bindtap='startRun'>开始</button>

<button bindtap='stopRun'>结束</button>

5-6网络请求原理

网络请求过程

小程序访问服务器,经过TCP握手后建立起TCP连接,然后小程序向服务器发送HTTP请求,即发送一个request请求去获取客户器上的数据,服务器把response文件对象发送回给小程序。

数据结构

请求 

HTTP请求格式如下所示四部分组成,分别是请求行、请求头、空行、消息体,每部分内容占一行。

<request-line>

<general-headers>

 
    

1.  <request-headers>

2. 

<entity-headers>

<empty-line>

[message-body]

请求行:由三部分组成:分别是请求方法(GET/POST/DELETE/PUT/HEAD)、URI路径、HTTP版本号。

主体:客户端发给服务端的请求数据,这部分数据并不是每个请求必须的

返回数据 

服务器接收处理完请求后返回一个HTTP响应消息给客户端。HTTP响应消息的格式包括:状态行、响应头、空行、消息体。每部分内容占一行。 

<status-line>

<general-headers>

<response-headers>

<entity-headers>

<empty-line>

<[message-body]>

状态行:有HTTP协议版本号,状态码和状态说明三部分构成。

响应头:用于说明数据的一些信息,比如数据类型、内容长度等键值对。 

空行。

消息体:服务端返回给客户端的HTML文本内容。或者其他格式的数据,比如:视频流、图片或者音频数据。

豆瓣示例

<general-headers>

<response-headers>

<request-headers>

小程序网络请求API

wx.request

函数

作用:发起HTTPS 网络请求

参数:

代码示例:

//index.js

Page({



  /**

   * 页面的初始数据

   */

  data: {

    url:'https://easy-mock.com/mock/5cd4d8aa4993c3317af392a5/example/test',

    url_2:'https://easy-mock.com/mock/5cd4d8aa4993c3317af392a5/example/douban'

  },



  getData:function(){

    wx.request({

      url: this.data.url_2, 

      header: {

        'content-type': 'application/json' // 默认值

      },

      success(res) {

        console.log(res.data)

      }

    })

  },



  /**

   * 生命周期函数--监听页面加载

   */

  onLoad: function (options) {



  },



  /**

   * 生命周期函数--监听页面初次渲染完成

   */

  onReady: function () {



  },



  /**

   * 生命周期函数--监听页面显示

   */

  onShow: function () {



  },



  /**

   * 生命周期函数--监听页面隐藏

   */

  onHide: function () {



  },



  /**

   * 生命周期函数--监听页面卸载

   */

  onUnload: function () {



  },



  /**

   * 页面相关事件处理函数--监听用户下拉动作

   */

  onPullDownRefresh: function () {



  },



  /**

   * 页面上拉触底事件的处理函数

   */

  onReachBottom: function () {



  },



  /**

   * 用户点击右上角分享

   */

  onShareAppMessage: function () {



  }

})

//index.wxml

<button bindtap='getData'>请求模拟数据</button>

6-1 云开发介绍

介绍

开发者可以使用云开发开发微信小程序、小游戏,无需搭建服务器,即可使用云端能力。

云开发为开发者提供完整的原生云端支持和微信服务支持,弱化后端和运维概念,无需搭建服务器,使用平台提供的 API 进行核心业务开发,即可实现快速上线和迭代,同时这一能力,同开发者已经使用的云服务相互兼容,并不互斥。

云开发提供了几大基础能力支持:

步骤

可以按如下步骤快速开始使用云开发。

1.  新建云开发模板

2.  开通云开发

3.  体验小程序

4.  查看控制台

1. 新建云开发模板

新建项目选择一个空目录,填入 AppID(使用云开发能力必须填写 AppID),勾选创建 “云开发 QuickStart 项目”,点击创建即可得到一个展示云开发基础能力的示例小程序。该小程序与普通 QuickStart 小程序有以下不同需注意:

1.  无游客模式、也不可以使用 测试号

2.  project.config.json 中增加了字段 cloudfunctionRoot 用于指定存放云函数的目录

3.  cloudfunctionRoot 指定的目录有特殊的图标

4.  云开发能力从基础库 2.2.3 开始支持(覆盖率 97.3%,查看兼容性问题)

从基础库 2.4.1 开始,在小程序插件中可以使用云开发,插件中使用云开发时,使用的是插件方的云资源而非宿主的云资源,在使用方式上与在小程序中使用无异。

2. 开通云开发、创建环境

创建了第一个云开发小程序后,在使用云开发能力之前需要先开通云开发。在开发者工具工具栏左侧,点击 “云开发” 按钮即可打开云控制台、根据提示开通云开发、创建云环境。默认配额下可以创建两个环境,各个环境相互隔离,每个环境都包含独立的数据库实例、存储空间、云函数配置等资源。每个环境都有唯一的环境 ID 标识,初始创建的环境自动成为默认环境。

注:AppID 首次开通云环境后,需等待大约 10 分钟方可正常使用云 API,在此期间官方后台服务正在做准备服务,如尝试在小程序中调用云 API 则会报 cloud init error:{ errMsg: “invalid scope” } 的错误

3. 体验小程序

开通创建环境后,即可以开始在模拟器上操作小程序体验云开发提供的部分基础能力演示。

4. 查看控制台

云开发控制台是管理云开发资源的地方,控制台提供以下能力:

1.  运营分析:查看云开发监控、配额使用量、用户访问情况

2.  数据库:管理数据库,可查看、增加、更新、查找、删除数据、管理索引、管理数据库访问权限等

3.  存储管理:查看和管理存储空间

4.  云函数:查看云函数列表、配置、日志

数据库

云开发提供了一个 JSON 数据库,顾名思义,数据库中的每条记录都是一个 JSON 格式的对象。一个数据库可以有多个集合(相当于关系型数据中的表),集合可看做一个 JSON 数组,数组中的每个对象就是一条记录,记录的格式是 JSON 对象。

关系型数据库和 JSON 数据库的概念对应关系如下表:

以下是一个示例的集合数据,假设我们有一个 books 集合存放了图书记录,其中有两本书:

[

  {

    "_id": "Wzh76lk5_O_dt0vO",

    "title": "The Catcher in the Rye",

    "author": "J. D. Salinger",

    "characters": [

      "Holden Caulfield",

      "Stradlater",

      "Mr. Antolini"

    ],

    "publishInfo": {

      "year": 1951,

      "country": "United States"

    }

  },

  {

    "_id": "Wzia0lk5_O_dt0vR",

    "_openid": "ohl4L0Rnhq7vmmbT_DaNQa4ePaz0",

    "title": "The Lady of the Camellias",

    "author": "Alexandre Dumas fils",

    "characters": [

      "Marguerite Gautier",

      "Armand Duval",

      "Prudence",

      "Count de Varville"

    ],

    "publishInfo": {

      "year": 1848,

      "country": "France"

    }

  }

]

在图书信息中,我们用 title, author 来记录图书标题和作者,用 characters 数组来记录书中的主要人物,用 publishInfo 来记录图书的出版信息。在其中我们可以看到,字段既可以是字符串或数字,还可以是对象或数组,就是一个 JSON 对象。

每条记录都有一个 _id 字段用以唯一标志一条记录、一个 _openid 字段用以标志记录的创建者,即小程序的用户。需要特别注意的是,在管理端(控制台和云函数)中创建的不会有 _openid 字段,因为这是属于管理员创建的记录。开发者可以自定义 _id,但不可自定义和修改 _openid 。_openid 是在文档创建时由系统根据小程序用户默认创建的,开发者可使用其来标识和定位文档。

数据库 API 分为小程序端和服务端两部分,小程序端 API 拥有严格的调用权限控制,开发者可在小程序内直接调用 API 进行非敏感数据的操作。对于有更高安全要求的数据,可在云函数内通过服务端 API 进行操作。云函数的环境是与客户端完全隔离的,在云函数上可以私密且安全的操作数据库。

数据库 API 包含增删改查的能力,使用 API 操作数据库只需三步:获取数据库引用、构造查询/更新条件、发出请求。以下是一个在小程序中查询数据库的发表于美国的图书记录的例子:

// 1. 获取数据库引用

const db = wx.cloud.database()

// 2. 构造查询语句

// collection 方法获取一个集合的引用

// where 方法传入一个对象,数据库返回集合中字段等于指定值的 JSON 文档。API 也支持高级的查询条件(比如大于、小于、in 等),具体见文档查看支持列表

// get 方法会触发网络请求,往数据库取数据

db.collection('books').where({

  publishInfo: {

    country: 'United States'

  }

}).get({

  success: function(res) {

  // 输出 [{ "title": "The Catcher in the Rye", ... }]

  console.log(res)

 }

})

存储

云开发提供了一块存储空间,提供了上传文件到云端、带权限管理的云端下载能力,开发者可以在小程序端和云函数端通过 API 使用云存储功能。

在小程序端可以分别调用 wx.cloud.uploadFile 和 wx.cloud.downloadFile 完成上传和下载云文件操作。下面简单的几行代码,即可实现在小程序内让用户选择一张图片,然后上传到云端管理的功能:

// 让用户选择一张图片

wx.chooseImage({

  success: chooseResult => {

    // 将图片上传至云存储空间

    wx.cloud.uploadFile({

      // 指定上传到的云路径

      cloudPath: 'my-photo.png',

      // 指定要上传的文件的小程序临时文件路径

      filePath: chooseResult.tempFilePaths[0],

      // 成功回调

      success: res => {

        console.log('上传成功', res)

      },

    })

  },

})

上传完成后可在控制台中看到刚上传的图片。

云函数

云函数是一段运行在云端的代码,无需管理服务器,在开发工具内编写、一键上传部署即可运行后端代码。

小程序内提供了专门用于云函数调用的 API。开发者可以在云函数内使用 wx-server-sdk 提供的 getWXContext 方法获取到每次调用的上下文(appid、openid 等),无需维护复杂的鉴权机制,即可获取天然可信任的用户登录态(openid)。

比如我们如下定义一个云函数,命名为 add ,功能是将传入的两个参数 a 和 b 相加:

// index.js 是入口文件,云函数被调用时会执行该文件导出的 main 方法

// event 包含了调用端(小程序端)调用该函数时传过来的参数,同时还包含了可以通过 getWXContext 方法获取的用户登录态 `openId` 和小程序 `appId` 信息

const cloud = require('wx-server-sdk')

exports.main = (event, context) => {

  let { userInfo, a, b} = event

  let { OPENID, APPID } = cloud.getWXContext() // 这里获取到的 openId 和 appId 是可信的

  let sum = a + b



  return {

    OPENID,

    APPID,

    sum

  }

}

在开发者工具中上传部署云函数后,我们在小程序中可以这么调用:

wx.cloud.callFunction({

  // 需调用的云函数名

  name: 'add',

  // 传给云函数的参数

  data: {

    a: 12,

    b: 19,

  },

  // 成功回调

  complete: console.log

})

// 当然 promise 方式也是支持的

wx.cloud.callFunction({

  name: 'add',

  data: {

    a: 12,

    b: 19

  }

}).then(console.log)

如需在云函数中操作数据库、管理云文件、调用其他云函数等操作,可使用官方提供的 npm 包 wx-server-sdk 进行操作。

6-2 云开发的同步与异步

同步与异步

同步 synchronous

- 程序正常按照顺序依次执行

- 亲力亲为 需要等待当前任务完成才执行下一个

异步 asynchronous

- 将事情交给其他函数去做

- 甩手掌柜 可能马上就完成也可能需要一段时间

- Promise

pending(进行中)

resolved(已完成)

rejected (已拒绝)

举个例子:

”同步“就好比:你去外地上学(人生地不熟),突然生活费不够了;此时你决定打电话回家,通知家里转生活费过来,可是当你拨出电话时,对方一直处于待接听状态(即:打不通,联系不上),为了拿到生活费,你就不停的oncall、等待,最终可能不能及时要到生活费,导致你今天要做的事都没有完成,而白白花掉了时间。

“异步”就是:在你打完电话发现没人接听时,猜想:对方可能在忙,暂时无法接听电话,所以你发了一条短信(或者语音留言,亦或是其他的方式)通知对方后便忙其他要紧的事了;这时你就不需要持续不断的拨打电话,还可以做其他事情;待一定时间后,对方看到你的留言便回复响应你,当然对方可能转钱也可能不转钱。但是整个一天下来,你还做了很多事情。 或者说你找室友临时借了一笔钱,又开始happy的上学时光了。

Promise()

var promise = new Promise(function(resolve, reject) {

 if (/* 异步操作成功 */){

 resolve(value);

 } else {

 reject(error);

 }

});



promise.then(function(value) {

 // success

}, function(value) {

 // failure

});

Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 方法和 reject 方法。

● 如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从「未完成」变为「成功」(即从 pending 变为 resolved);

● 如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从「未完成」变为「失败」(即从 pending 变为 rejected)。

async函数

正常执行

● 包含函数语句、函数表达式、lambda表达式

● 返回Promise对象 不阻塞

await

● 暂停当前async执行,等待Promise处理完成

● 正常(处理结果作为await表达式值)

● 异常 抛出异常原因

特点

● 优化处理then(解决多层回调问题)逻辑链 异步变同步

● 异常用catch直接捕捉

1.async函数返回的是一个Promise对象。Async函数(包含函数语句、函数表达式、Lambda表达式)会返回一个Promise对象,如果在函数中return一个直接量,async会把这个直接量通过Promise.resolve()封装成Promise对象。

2.async函数返回的是一个Promise对象,所以在最外层不能用await获取其返回值的情况下,我们当然应该用原来的方式:th

1-1小程序介绍及安装

开发环境搭建
  • 工具下载

微信web开发者工具

推荐下载稳定版,并根据自己的电脑选择下载对应的版本进行安装

  • 注册账号

微信公众平台

点击小程序图标,注册小程序开发者账号

创建第一个实验项目
  • 小程序后台

完成注册后,即可登陆小程序后台,在自己的小程序后台对小程序进行开发和管理。

  • 微信web开发者工具

登陆微信web开发者工具

点击右边图片中的加号,即可开始创建小程序

新建项目

如果不使用测试号,也可直接使用自己的AppID

点击“新建”按扭,进入编辑页面

创建第一个实验项目

创建完项目,可以返回主页面对项目进行预览和管理

逻辑层初步介绍

除了视图层(即我们所看到的页面),小程序逻辑层各部分的功能如下:

  • index.js 逻辑代码
  • index.json 页面配置文件
  • index.wxml 页面结构
  • index.wxss 表示index.wxml结构的样式

app.js app.json app wxss的作用与index是类似的,但它们作用于全局

1-2小程序框架及文件介绍

小程序框架介绍

小程序框架的核心是一个响应的数据绑定系统。整个系统分为两块,视图层和逻辑层。

视图层 (view)
  • 内容展示
  • 文本样式

视图层由WXML与WXSS编写。

将逻辑层的数据反应成视图,同时将视图层的事件发送给逻辑层。

WXML(WeiXin Markup language)用于描述页面的结构。

WXSS(WeiXin Style Sheet)用于描述页面的样式。

组件(Component)是视图的基本组成单元。

逻辑层(app service)
  • 业务逻辑
  • 数据处理

小程序开发框架的逻辑层是由JavaScript编写。

逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈。

每个页面有独立的作用域,并提供模块化能力。

小程序文件介绍

小程序文件包含一个描述整体程序的app和多个描述各自页面的page.

页面

一个页面由四个文件组成,如下所示:

  • index(默认)

小程序注册及配置
  • 入口文件
  • 配置文件
  • 全局样式

app.js文件,用来定义小程序的全局数据和函数,控制、监听小程序的全生命周期。在这里可以见到的全局函数有onlaunch(监听小程序初始化),onshow(监听小程序显示),onhide(监听小程序隐藏)等。app.js中还可以定义一些全局变量,其他页面引用app.js文件后就可以直接使用这个文件中的函数和变量。

app.json是配置文件,可以配置以下信息:页面路径,窗口信息,标签导航,网络超时等。

app.wxss,类似于css,用于配置各种样式

project.config.json用于保存配置信息。

sitemap.json用于控制小程序是否能够被搜索到。

小程序的执行流程
小程序注册及配置
  • 注册应用 app.js
  • 全局配置 app.json
  • 设置样式 app.wxss
  • 工具配置 project.config.json
页面渲染执行 - app.json
  • 主页面(index)
  • 从页面(index2)
  • 日志信息(logs)
json文件介绍
特点
  • 轻量级的数据交换格式
  • 直观理解
  • 便于修改
结构
  • 文件数据被包裹在一个大括号中 {}
  • 通过键值对(key-value)的方式来表达数据

1-3配置文件和顶部导航栏

app.json

app.json是全局配置文件,我们进入到app.json看一下。

pages

试着对pages进行一些修改,进行编译

可以看到pages下面多了一个test目录。

实际上,pages接受一个数组,每一项都是字符串,来指定小程序由哪些页面组成。每一项代表对应页面的【路径+文件名】信息,数组的第一项代表小程序的初始页面。小程序中新增/减少页面,都需要对pages数组进行修改。

文件名不需要写文件后缀,因为框架会自动去寻找路径下.json、.js、.wxml、.wxss四个文件进行整合。

window

用于定义页面的背景颜色,字体样式,文字颜色等等。

enablePullDownRefresh

:设置是否进行下拉刷新

backgroundColor

: 设置下拉背景颜色,颜色为HEXcolor编码

backgroundTextStyle

:设置下拉进度条的颜色,它只有两种选择,一种是light,一种是dark

navigationBarBackgroundColor

:设置导航栏背景颜色,颜色为HEXcolor编码

navigationBarTitleText

:设置导航栏文本

navigationBarTextStyle

:设置导航栏文本颜色,同样地,颜色为HEXcolor编码

2-1、2-2小程序wxml基本语法

小程序开发语言

  • wxml语法
  • wxss语法
  • js语法
wxml语法

WXML(WeiXin Markup Language),它与HTML有很多相似之处。它是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。

什么是组件:
  • 组件是视图层的基本组成单元
  • 组件自带一些功能与微信风格的样式

一个组件通常包括

开始标签

 和

结束标签

属性

用来修饰这个组件,

内容

在两个标签之内。

基本写法

写注释

<!--注释-->

 (快捷方式:ctrl+/)

写标签

<标签名 属性名1=“属性值1”……>……</标签名>

所有元素都必须闭合标签

<text>hello world</text>

1.标签必须使用小写

2.不可以含有中文

常用的属性类型
体会text与view组件的区别

{{ }}的作用
  • {{ }}中的内容为动态数据,若内容为数字与字符串相加,则会默认数字为字符串格式。详见数据绑定部分内容。
基础语法介绍
  • 数据绑定

wxml中的动态数据均来自对应page的data,数据绑定使用 Mustache 语法(双大括号)将变量包起来

列表渲染

在组件上使用 

wx:for

 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。

默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item

使用 

wx:for-item

  •  可以指定数组当前元素的变量名,

使用 

wx:for-index

 可以指定数组当前下标的变量名

条件渲染

在框架中,使用 

wx:if=""

 来判断是否需要渲染该代码块:

<view wx:if="{{condition}}"> True </view>

在下图中,使用条件渲染并设置了false,即该条件后的语句均不起作用,因此该语句后面的文本并没有显示出来

另外,使用

hidden

也可以不显示文本或组件。

wx:if

vs 

hidden

因为 wx:if 之中的模板也可能包含数据绑定,所以当 wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。

同时 wx:if 也是惰性的,如果在初始渲染条件为 false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。

相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。

一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好。

也可以用 

wx:elif

 和 

wx:else

 来添加一个 else 块:

<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>

2-3 小程序WXSS基本语法

尺寸单位(px和rpx)

px(pixel):物理像素,设备的实际分辨率,也叫设备像素或绝对像素,用 px 表示一个像素点单位。

rpx(responsive pixel): 逻辑分辨率,可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素

注意: 在较小的屏幕上不可避免的会有一些毛刺,开发时尽量避免这种情况

样式设置
内联样式

框架组件上支持使用 style、class 属性来控制组件的样式。

style:静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,请尽量避免将静态的样式写进 style 中,以免影响渲染速度。

<view style="color:{{color}};" />

class:用于指定样式规则,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上.,样式类名之间用空格分隔

<view class="normal_view" />

一个简单的例子:

除了以上这些简单的例子,wxss样式属性主要包括以下几类:

  1. wxss display (显示)
  2. wxss position(定位)
  3. wxss float(浮动)
  4. wxss background(背景)
  5. wxss border (边框)
  6. wxss outline(轮廓)
  7. wxss text(文本属性)
  8. wxss font(字体属性)
  9. wxss margin(外边距)
  10. wxss padding(填充)

w3cschool      https://www.w3cschool.cn/css/css-tutorial.html

盒子模型

盒子模型来自于CSS,它是CSS布局的基础,CSS假定每个元素都会生成一个或多个矩形框,每个元素框中心都有一个内容区(content),这个内容区周围有内边距(padding)、边框(border)和外边距(margin),这些项默认宽度为0,这个矩形框就是常说的盒子模型。

一个简单的例子:

4-1小程序流程及生命周期函数导航栏

小程序运行机制

整个小程序框架系统分为两部分:逻辑层(App Service)和 视图层(View)。

每个小程序都需要在 app.js 中调用 App 方法注册小程序示例,绑定生命周期回调函数、错误监听和页面不存在监听函数等。

App(Object object)

注册小程序。接受一个 Object 参数,其指定小程序的生命周期回调等。

App() 必须在 app.js 中调用,必须调用且只能调用一次。不然会出现无法预期的后果。

// app.js

App({

    onLaunch:function(){……

    },

    globalData:{……

    }

})

//app.json

{

    "pages":[

        "pages/index/index",

        "pages/logs/logs"

        ],

}

//index.js

//获取应用实例

const app=getApp()

Page({

    data:{……

    },

//事件处理函数

    bindViewTap:function(){……

    },

    onload:function(){……

    },

    getUserInfo:function(e){……

    }

})

参数
小程序加载及生命周期
几个核心生命周期函数

页面主要的生命周期

onLoad

页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。

onShow

页面显示/切入前台时触发。

onReady

页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。

onHide

页面隐藏/切入后台时触发。

onUnload

页面卸载时触发。

下部导航栏

tabBar

将图标的图片文件夹放入项目文件夹中

4-2组件概述-视图容器

小程序的组件

小程序的界面是由一系列组件构成的,小程序基础序提供了一组基础组件来满足开发者的需求。它包括以下几种组件:

视图容器

基础内容

表单组件

导航

媒体组件

地图

画布

视图容器

视图容器就是将页面分割为独立的,不同的部分。视图容器共包含:

view(视图容器)属性说明:

例:

4-3弹性布局

页面内容的弹性布局

布局的传统解决方案,基于盒状模型,它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。

2009年,W3C提出了一种新的方案—-Flex布局,可以简便、完整、响应式地实现各种页面布局。

采用Flex布局的元素,称为Flex容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为Flex项目(flex item),简称”项目”。

容器的属性
  • flex-direction
  • flex-wrap
  • flex-flow
  • justify-content
  • align-items
  • align-content
flex-direction

决定主轴的方向(即项目的排列方向)。

  • row(默认值):主轴为水平方向,起点在左端。
  • row-reverse:主轴为水平方向,起点在右端。
  • column:主轴为垂直方向,起点在上沿。
  • column-reverse:主轴为垂直方向,起点在下沿。
flex-wrap

默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。

它可能取三个值。

  • nowrap(默认):不换行。
  • wrap:换行,第一行在上方。
  • wrap-reverse:换行,第一行在下方。
flex-flow

flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。

justify-content属性

justify-content属性定义了项目在主轴上的对齐方式。

它可能取5个值,具体对齐方式与轴的方向有关。下面假设主轴为从左到右。

  • flex-start(默认值):左对齐
  • flex-end:右对齐
  • center: 居中
  • space-between:两端对齐,项目之间的间隔都相等。
  • space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。
align-items属性

align-items属性定义项目在交叉轴上如何对齐。

它可能取5个值。具体的对齐方式与交叉轴的方向有关,下面假设交叉轴从上到下。

  • flex-start:交叉轴的起点对齐。
  • flex-end:交叉轴的终点对齐。
  • center:交叉轴的中点对齐。
  • baseline: 项目的第一行文字的基线对齐。
  • stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。
align-content属性

align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

该属性可能取6个值。

  • flex-start:与交叉轴的起点对齐。
  • flex-end:与交叉轴的终点对齐。
  • center:与交叉轴的中点对齐。
  • space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
  • space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
  • stretch(默认值):轴线占满整个交叉轴。

更多内容请参考:flex布局语法教程

案例

/*index.wxss*/

.box{

    display:inlineflex;

    flex-direction:row;

    justify-content:space-around;

    flex-wrap:wrap;

    align-items:center

    /*align-content:flex-start;*/

}

.box_2{

    display:flex;

    background-color:greenyellow;

}

<view> 顶部</view>

<view class='box'>

  <!-- <view class='box_2'>

    <view class='box_3'>

      1-XXX

    </view>

    <view class='box_3'>

      1-YYY

    </view>

    <view class='box_3'>

      1-ZZZ

    </view>

  </view>

  <view class='box_2'>

    <view class='box_3'>

      2-XXX

    </view>

    <view class='box_3'>

      2-YYY

    </view>

    <view class='box_3'>

      2-ZZZ

    </view>

  </view>

  <view class='box_2'>

    <view class='box_3'>

      3-XXX

    </view>

    <view class='box_3'>

      3-YYY

    </view>

    <view class='box_3'>

      3-ZZZ

    </view>

  </view> -->

  <view class='box_2'>

    <view class='box_3'>



      <text>\n\n4-X</text>



    </view>

    <view class='box_3'>

      <text>\n4-Y\n</text>





    </view>

    <view class='box_3'>



      <text>4-Z\n\n</text>



    </view>

    </view>

  <view class='box_2'> 

    <view class='box_3'>

      5-XX

      <text>\n</text>

      <text>\n</text>

    </view>

    <view class='box_3'>

      5-YY

      <text>\n</text>

      <text>\n</text>

    </view>

    <view class='box_3'>

      5-ZZ

      <text>\n</text>

      <text>\n</text>

    </view>

  </view>

    <view class='box_2'> 

    <view class='box_3'>

      <text>\n</text>

      <text>\n</text>

      <text>\n\n6-X</text> 

    </view>

    <view class='box_3'>

      <text>\n</text>

      <text>\n</text>

      <text>\n\n6-Y</text>    

    </view>

    <view class='box_3'>

4-4轮播组件

swiper组件

滑块视图容器。其中只可放置swiper-item组件,否则会导致未定义的行为。

swiper-item

仅可放置在swiper组件中,宽高自动设置为100%。

案例

//index.js

Page({

    data:{

        background:['a','b','c']

},

//index.wxml

.si{

    display:block;

    height:150px;

}

.a{

    background-color:yellow;    

}

.b{

    background-color:red;

}

.c{

    background-color:blue;

}

//index.wxss

<view>

    <swiper autoplay="true" interval="2000"

    indicator-dots="true">

    <block wx:for="{{background}}" wx:key='swip'>

        <swiper-item>

            <view class='si {{itme}}'></view>

        </swiper-item>

    </block>

    </swiper-item>

</view> 

4-5 基础内容组件

小程序基础内容组件包含图标icon,进度条progress,富文本rich-text,和文本text等组件。

  • icon
  • progress
  • rich-text
  • text
icon

图标。组件属性的长度单位默认为px,2.4.0起支持传入单位(rpx/px)

progress

进度条。组件属性的长度单位默认为px,2.4.0起支持传入单位(rpx/px)。

rich-text

副文本

text

文本

4-6 表单组件

button

按钮

示例代码:

.button-hover {

  background-color: red;

}

.other-button-hover {

  background-color: blue;

}

<button type="default" size="{{defaultSize}}" loading="{{loading}}" plain="{{plain}}"

  disabled="{{disabled}}" bindtap="default" hover-class="other-button-hover"> default </button>

<button type="primary" size="{{primarySize}}" loading="{{loading}}" plain="{{plain}}"

  disabled="{{disabled}}" bindtap="primary"> primary </button>

<button type="warn" size="{{warnSize}}" loading="{{loading}}" plain="{{plain}}"

  disabled="{{disabled}}" bindtap="warn"> warn </button>

<button bindtap="setDisabled">点击设置以上按钮disabled属性</button>

<button bindtap="setPlain">点击设置以上按钮plain属性</button>

<button bindtap="setLoading">点击设置以上按钮loading属性</button>

<button open-type="contact">进入客服会话</button>

<button open-type="getUserInfo" lang="zh_CN" bindgetuserinfo="onGotUserInfo">获取用户信息</button>

<button open-type="openSetting">打开授权设置页</button>

var types = ['default', 'primary', 'warn']

var pageObject = {

  data: {

    defaultSize: 'default',

    primarySize: 'default',

    warnSize: 'default',

    disabled: false,

    plain: false,

    loading: false

  },

  setDisabled: function(e) {

    this.setData({

      disabled: !this.data.disabled

    })

  },

  setPlain: function(e) {

    this.setData({

      plain: !this.data.plain

    })

  },

  setLoading: function(e) {

    this.setData({

      loading: !this.data.loading

    })

  },

  onGotUserInfo: function(e) {

    console.log(e.detail.errMsg)

    console.log(e.detail.userInfo)

    console.log(e.detail.rawData)

  },

}



for (var i = 0; i < types.length; ++i) {

  (function(type) {

    pageObject[type] = function(e) {

      var key = type + 'Size'

      var changedData = {}

      changedData[key] =

        this.data[key] === 'default' ? 'mini' : 'default'

      this.setData(changedData)

    }

  })(types[i])

}



Page(pageObject)

checkbox

多选项目。

示例代码:

<checkbox-group bindchange="checkboxChange">

  <label class="checkbox" wx:for="{{items}}">

    <checkbox value="{{item.name}}" checked="{{item.checked}}"/>{{item.value}}

  </label>

</checkbox-group>

Page({

  data: {

    items: [

      {name: 'USA', value: '美国'},

      {name: 'CHN', value: '中国', checked: 'true'},

      {name: 'BRA', value: '巴西'},

      {name: 'JPN', value: '日本'},

      {name: 'ENG', value: '英国'},

      {name: 'TUR', value: '法国'},

    ]

  },

  checkboxChange: function(e) {

    console.log('checkbox发生change事件,携带value值为:', e.detail.value)

  }

})

.checkbox{

    display:flex;

    flex-direction:row-reverse;

}

editor

富文本编辑器,可以对图片、文字进行编辑。

编辑器导出内容支持带标签的 html和纯文本的 text,编辑器内部采用 delta 格式进行存储。

通过setContents接口设置内容时,解析插入的 html 可能会由于一些非法标签导致解析错误,建议开发者在小程序内使用时通过 delta 进行插入。

富文本组件内部引入了一些基本的样式使得内容可以正确的展示,开发时可以进行覆盖。需要注意的是,在其它组件或环境中使用富文本组件导出的html时,需要额外引入这段样式,并维护的结构。

图片控件仅初始化时设置有效。

示例代码:

<editor

  id="editor"

  class="ql-container"

  placeholder="{{placeholder}}"

  showImgSize

  showImgToolbar

  showImgResize

  bindstatuschange="onStatusChange"

  read-only="{{readOnly}}"

  bindready="onEditorReady">

</editor>

Page({

  readOnlyChange() {

    this.setData({

      readOnly: !this.data.readOnly

    })

  },

  onEditorReady() {

    const that = this

    wx.createSelectorQuery().select('#editor').context(function (res) {

      that.editorCtx = res.context

    }).exec()

  },

  format(e) {

    let { name, value } = e.target.dataset

    if (!name) return

    // console.log('format', name, value)

    this.editorCtx.format(name, value)

  },

})

form

表单。将组件内的用户输入的switch input checkbox slider radio picker 提交。

当点击 form 表单中 form-type 为 submit 的 button 组件时,会将表单组件中的 value 值进行提交,需要在表单组件中加上 name 来作为 key。

示例代码:

<form bindsubmit="formSubmit" bindreset="formReset">

  <view class="section section_gap">

    <view class="section__title">switch</view>

    <switch name="switch"/>

  </view>

  <view class="section section_gap">

    <view class="section__title">slider</view>

    <slider name="slider" show-value ></slider>

  </view>



  <view class="section">

    <view class="section__title">input</view>

    <input name="input" placeholder="please input here" />

  </view>

  <view class="section section_gap">

    <view class="section__title">radio</view>

    <radio-group name="radio-group">

      <label><radio value="radio1"/>radio1</label>

      <label><radio value="radio2"/>radio2</label>

    </radio-group>

  </view>

  <view class="section section_gap">

    <view class="section__title">checkbox</view>

    <checkbox-group name="checkbox">

      <label><checkbox value="checkbox1"/>checkbox1</label>

      <label><checkbox value="checkbox2"/>checkbox2</label>

    </checkbox-group>

  </view>

  <view class="btn-area">

    <button form-type="submit">Submit</button>

    <button form-type="reset">Reset</button>

  </view>

</form>

Page({

  formSubmit: function(e) {

    console.log('form发生了submit事件,携带数据为:', e.detail.value)

  },

  formReset: function() {

    console.log('form发生了reset事件')

  }

})

input

输入框。该组件是原生组件,使用时请注意相关限制。

示例代码:

<!--input.wxml-->

<view class="section">

  <input placeholder="这是一个可以自动聚焦的input" auto-focus/>

</view>

<view class="section">

  <input placeholder="这个只有在按钮点击的时候才聚焦" focus="{{focus}}" />

  <view class="btn-area">

    <button bindtap="bindButtonTap">使得输入框获取焦点</button>

  </view>

</view>

<view class="section">

  <input  maxlength="10" placeholder="最大输入长度10" />

</view>

<view class="section">

  <view class="section__title">你输入的是:{{inputValue}}</view>

  <input  bindinput="bindKeyInput" placeholder="输入同步到view中"/>

</view>

<view class="section">

  <input  bindinput="bindReplaceInput" placeholder="连续的两个1会变成2" />

</view>

<view class="section">

  <input password type="number" />

</view>

<view class="section">

  <input password type="text" />

</view>

<view class="section">

  <input type="digit" placeholder="带小数点的数字键盘"/>

</view>

<view class="section">

  <input type="idcard" placeholder="身份证输入键盘" />

</view>

<view class="section">

  <input placeholder-style="color:red" placeholder="占位符字体是红色的" />

</view>

//input.js

Page({

  data: {

    focus: false,

    inputValue: ''

  },

  bindButtonTap: function() {

    this.setData({

      focus: true

    })

  },

  bindKeyInput: function(e) {

    this.setData({

      inputValue: e.detail.value

    })

  },

  bindReplaceInput: function(e) {

    var value = e.detail.value

    var pos = e.detail.cursor

    if(pos != -1){

      //光标在中间

      var left = e.detail.value.slice(0,pos)

      //计算光标的位置

      pos = left.replace(/11/g,'2').length

    }



    //直接返回对象,可以对输入进行过滤处理,同时可以控制光标的位置

    return {

      value: value.replace(/11/g,'2'),

      cursor: pos

    }



    //或者直接返回字符串,光标在最后边

    //return value.replace(/11/g,'2'),

  }

})

label

用来改进表单组件的可用性。

使用for属性找到对应的id,或者将控件放在该标签下,当点击时,就会触发对应的控件。 for优先级高于内部控件,内部有多个控件的时候默认触发第一个控件。 目前可以绑定的控件有:button, checkbox, radio, switch。

示例代码:

<view class="section section_gap">

<view class="section__title">表单组件在label内</view>

<checkbox-group class="group" bindchange="checkboxChange">

  <view class="label-1" wx:for="{{checkboxItems}}">

    <label>

      <checkbox hidden value="{{item.name}}" checked="{{item.checked}}"></checkbox>

      <view class="label-1__icon">

        <view class="label-1__icon-checked" style="opacity:{{item.checked ? 1: 0}}"></view>

      </view>

      <text class="label-1__text">{{item.value}}</text>

    </label>

  </view>

</checkbox-group>

</view>



<view class="section section_gap">

<view class="section__title">label用for标识表单组件</view>

<radio-group class="group" bindchange="radioChange">

  <view class="label-2" wx:for="{{radioItems}}">

    <radio id="{{item.name}}" hidden value="{{item.name}}" checked="{{item.checked}}"></radio>

    <view class="label-2__icon">

      <view class="label-2__icon-checked" style="opacity:{{item.checked ? 1: 0}}"></view>

    </view>

    <label class="label-2__text" for="{{item.name}}"><text>{{item.name}}</text></label>

  </view>

</radio-group>

</view>

Page({

  data: {

    checkboxItems: [

      {name: 'USA', value: '美国'},

      {name: 'CHN', value: '中国', checked: 'true'},

      {name: 'BRA', value: '巴西'},

      {name: 'JPN', value: '日本', checked: 'true'},

      {name: 'ENG', value: '英国'},

      {name: 'TUR', value: '法国'},

    ],

    radioItems: [

      {name: 'USA', value: '美国'},

      {name: 'CHN', value: '中国', checked: 'true'},

      {name: 'BRA', value: '巴西'},

      {name: 'JPN', value: '日本'},

      {name: 'ENG', value: '英国'},

      {name: 'TUR', value: '法国'},

    ],

    hidden: false

  },

  checkboxChange: function(e) {

    var checked = e.detail.value

    var changed = {}

    for (var i = 0; i < this.data.checkboxItems.length; i ++) {

      if (checked.indexOf(this.data.checkboxItems[i].name) !== -1) {

        changed['checkboxItems['+i+'].checked'] = true

      } else {

        changed['checkboxItems['+i+'].checked'] = false

      }

    }

    this.setData(changed)

  },

  radioChange: function(e) {

    var checked = e.detail.value

    var changed = {}

    for (var i = 0; i < this.data.radioItems.length; i ++) {

      if (checked.indexOf(this.data.radioItems[i].name) !== -1) {

        changed['radioItems['+i+'].checked'] = true

      } else {

        changed['radioItems['+i+'].checked'] = false

      }

    }

    this.setData(changed)

  }

})

.label-1, .label-2{

    margin-bottom: 15px;

}

.label-1__text, .label-2__text {

    display: inline-block;

    vertical-align: middle;

}



.label-1__icon {

    position: relative;

    margin-right: 10px;

    display: inline-block;

    vertical-align: middle;

    width: 18px;

    height: 18px;

    background: #fcfff4;

}



.label-1__icon-checked {

    position: absolute;

    top: 3px;

    left: 3px;

    width: 12px;

    height: 12px;

    background: #1aad19;

}



.label-2__icon {

    position: relative;

    display: inline-block;

    vertical-align: middle;

    margin-right: 10px;

    width: 18px;

    height: 18px;

    background: #fcfff4;

    border-radius: 50px;

}



.label-2__icon-checked {

    position: absolute;

    left: 3px;

    top: 3px;

    width: 12px;

    height: 12px;

    background: #1aad19;

    border-radius: 50%;

}



.label-4_text{

    text-align: center;

    margin-top: 15px;

}

picker

从底部弹起的滚动选择器。

示例代码:

<view class="section">

  <view class="section__title">普通选择器</view>

  <picker bindchange="bindPickerChange" value="{{index}}" range="{{array}}">

    <view class="picker">

      当前选择:{{array[index]}}

    </view>

  </picker>

</view>

<view class="section">

  <view class="section__title">多列选择器</view>

  <picker mode="multiSelector" bindchange="bindMultiPickerChange" bindcolumnchange="bindMultiPickerColumnChange" value="{{multiIndex}}" range="{{multiArray}}">

    <view class="picker">

      当前选择:{{multiArray[0][multiIndex[0]]}},{{multiArray[1][multiIndex[1]]}},{{multiArray[2][multiIndex[2]]}}

    </view>

  </picker>

</view>

<view class="section">

  <view class="section__title">时间选择器</view>

  <picker mode="time" value="{{time}}" start="09:01" end="21:01" bindchange="bindTimeChange">

    <view class="picker">

      当前选择: {{time}}

    </view>

  </picker>

</view>



<view class="section">

  <view class="section__title">日期选择器</view>

  <picker mode="date" value="{{date}}" start="2015-09-01" end="2017-09-01" bindchange="bindDateChange">

    <view class="picker">

      当前选择: {{date}}

    </view>

  </picker>

</view>

<view class="section">

  <view class="section__title">省市区选择器</view>

  <picker mode="region" bindchange="bindRegionChange" value="{{region}}" custom-item="{{customItem}}">

    <view class="picker">

      当前选择:{{region[0]}},{{region[1]}},{{region[2]}}

    </view>

  </picker>

</view>

Page({

  data: {

    array: ['美国', '中国', '巴西', '日本'],

    objectArray: [

      {

        id: 0,

        name: '美国'

      },

      {

        id: 1,

        name: '中国'

      },

      {

        id: 2,

        name: '巴西'

      },

      {

        id: 3,

        name: '日本'

      }

    ],

    index: 0,

    multiArray: [['无脊柱动物', '脊柱动物'], ['扁性动物', '线形动物', '环节动物', '软体动物', '节肢动物'], ['猪肉绦虫', '吸血虫']],

    objectMultiArray: [

      [

        {

          id: 0,

          name: '无脊柱动物'

        },

        {

          id: 1,

          name: '脊柱动物'

        }

      ], [

        {

          id: 0,

          name: '扁性动物'

        },

        {

          id: 1,

          name: '线形动物'

        },

        {

          id: 2,

          name: '环节动物'

        },

        {

          id: 3,

          name: '软体动物'

        },

        {

          id: 3,

          name: '节肢动物'

        }

      ], [

        {

          id: 0,

          name: '猪肉绦虫'

        },

        {

          id: 1,

          name: '吸血虫'

        }

      ]

    ],

    multiIndex: [0, 0, 0],

    date: '2016-09-01',

    time: '12:01',

    region: ['广东省', '广州市', '海珠区'],

    customItem: '全部'

  },

  bindPickerChange: function(e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      index: e.detail.value

    })

  },

  bindMultiPickerChange: function (e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      multiIndex: e.detail.value

    })

  },

  bindMultiPickerColumnChange: function (e) {

    console.log('修改的列为', e.detail.column, ',值为', e.detail.value);

    var data = {

      multiArray: this.data.multiArray,

      multiIndex: this.data.multiIndex

    };

    data.multiIndex[e.detail.column] = e.detail.value;

    switch (e.detail.column) {

      case 0:

        switch (data.multiIndex[0]) {

          case 0:

            data.multiArray[1] = ['扁性动物', '线形动物', '环节动物', '软体动物', '节肢动物'];

            data.multiArray[2] = ['猪肉绦虫', '吸血虫'];

            break;

          case 1:

            data.multiArray[1] = ['鱼', '两栖动物', '爬行动物'];

            data.multiArray[2] = ['鲫鱼', '带鱼'];

            break;

        }

        data.multiIndex[1] = 0;

        data.multiIndex[2] = 0;

        break;

      case 1:

        switch (data.multiIndex[0]) {

          case 0:

            switch (data.multiIndex[1]) {

              case 0:

                data.multiArray[2] = ['猪肉绦虫', '吸血虫'];

                break;

              case 1:

                data.multiArray[2] = ['蛔虫'];

                break;

              case 2:

                data.multiArray[2] = ['蚂蚁', '蚂蟥'];

                break;

              case 3:

                data.multiArray[2] = ['河蚌', '蜗牛', '蛞蝓'];

                break;

              case 4:

                data.multiArray[2] = ['昆虫', '甲壳动物', '蛛形动物', '多足动物'];

                break;

            }

            break;

          case 1:

            switch (data.multiIndex[1]) {

              case 0:

                data.multiArray[2] = ['鲫鱼', '带鱼'];

                break;

              case 1:

                data.multiArray[2] = ['青蛙', '娃娃鱼'];

                break;

              case 2:

                data.multiArray[2] = ['蜥蜴', '龟', '壁虎'];

                break;

            }

            break;

        }

        data.multiIndex[2] = 0;

        break;

    }

    console.log(data.multiIndex);

    this.setData(data);

  },

  bindDateChange: function(e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      date: e.detail.value

    })

  },

  bindTimeChange: function(e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      time: e.detail.value

    })

  },

  bindRegionChange: function (e) {

    console.log('picker发送选择改变,携带值为', e.detail.value)

    this.setData({

      region: e.detail.value

    })

  }

})

radio

单选项目。

示例代码:

<radio-group class="radio-group" bindchange="radioChange">

  <label class="radio" wx:for="{{items}}">

    <radio value="{{item.name}}" checked="{{item.checked}}"/>{{item.value}}

  </label>

</radio-group>

Page({

  data: {

    items: [

      {name: 'USA', value: '美国'},

      {name: 'CHN', value: '中国', checked: 'true'},

      {name: 'BRA', value: '巴西'},

      {name: 'JPN', value: '日本'},

      {name: 'ENG', value: '英国'},

      {name: 'TUR', value: '法国'},

    ]

  },

  radioChange: function(e) {

    console.log('radio发生change事件,携带value值为:', e.detail.value)

  }

})

slider

滑动选择器。

示例代码:

<view class="section section_gap">

  <text class="section__title">设置step</text>

  <view class="body-view">

    <slider bindchange="slider2change" step="5"/>

  </view>

</view>



<view class="section section_gap">

  <text class="section__title">显示当前value</text>

  <view class="body-view">

    <slider bindchange="slider3change" show-value/>

  </view>

</view>



<view class="section section_gap">

  <text class="section__title">设置最小/最大值</text>

  <view class="body-view">

    <slider bindchange="slider4change" min="50" max="200" show-value/>

  </view>

</view>

var pageData = {}

for (var i = 1; i < 5; i++) {

  (function (index) {

    pageData['slider' + index + 'change'] = function(e) {

      console.log('slider' + 'index' + '发生 change 事件,携带值为', e.detail.value)

    }

  })(i)

}

Page(pageData)

switch

开关选择器。

示例代码:

<view class="body-view">

    <switch checked bindchange="switch1Change"/>

    <switch bindchange="switch2Change"/>

</view>

Page({

  switch1Change: function (e){

    console.log('switch1 发生 change 事件,携带值为', e.detail.value)

  },

  switch2Change: function (e){

    console.log('switch2 发生 change 事件,携带值为', e.detail.value)

  }

})

textarea

多行输入框。该组件是原生组件,使用时请注意相关限制。

示例代码:

<view class="section">

  <textarea bindblur="bindTextAreaBlur" auto-height placeholder="自动变高" />

</view>

<view class="section">

  <textarea placeholder="placeholder颜色是红色的" placeholder-style="color:red;"  />

</view>

<view class="section">

  <textarea placeholder="这是一个可以自动聚焦的textarea" auto-focus />

</view>

<view class="section">

  <textarea placeholder="这个只有在按钮点击的时候才聚焦" focus="{{focus}}" />

  <view class="btn-area">

    <button bindtap="bindButtonTap">使得输入框获取焦点</button>

  </view>

</view>

<view class="section">

  <form bindsubmit="bindFormSubmit">

    <textarea placeholder="form 中的 textarea" name="textarea"/>

    <button form-type="submit"> 提交 </button>

  </form>

</view>

//textarea.js

Page({

  data: {

    height: 20,

    focus: false

  },

  bindButtonTap: function() {

    this.setData({

      focus: true

    })

  },

  bindTextAreaBlur: function(e) {

    console.log(e.detail.value)

  },

  bindFormSubmit: function(e) {

    console.log(e.detail.value.textarea)

  }

})

4-7 其他组件

导航组件

navigator

页面链接。

示例代码:

.navigator-hover {

  color:blue;

}

.other-navigator-hover {

  color:red;

}

<!-- sample.wxml -->

<view class="btn-area">

  <navigator url="/page/navigate/navigate?title=navigate" hover-class="navigator-hover">跳转到新页面</navigator>

  <navigator url="../../redirect/redirect/redirect?title=redirect" open-type="redirect" hover-class="other-navigator-hover">在当前页打开</navigator>

  <navigator url="/page/index/index" open-type="switchTab" hover-class="other-navigator-hover">切换 Tab</navigator>

  <navigator target="miniProgram" open-type="navigate" app-id="" path="" extra-data="" version="release">打开绑定的小程序</navigator>

</view>

<!-- navigator.wxml -->

<view style="text-align:center"> {{title}} </view>

<view> 点击左上角返回回到之前页面 </view>

<!-- redirect.wxml -->

<view style="text-align:center"> {{title}} </view>

<view> 点击左上角返回回到上级页面 </view>

Page({

  onLoad: function(options) {

    this.setData({

      title: options.title

    })

  }

})

媒体组件

image

图片。支持JPG、PNG、SVG格式,2.3.0 起支持云文件ID。

示例代码:

<view class="page">

  <view class="page__hd">

    <text class="page__title">image</text>

    <text class="page__desc">图片</text>

  </view>

  <view class="page__bd">

    <view class="section section_gap" wx:for="{{array}}" wx:for-item="item">

      <view class="section__title">{{item.text}}</view>

      <view class="section__ctn">

        <image style="width: 200px; height: 200px; background-color: #eeeeee;" mode="{{item.mode}}" src="{{src}}"></image>

      </view>

    </view>

  </view>

</view>

Page({

  data: {

    array: [{

      mode: 'scaleToFill',

      text: 'scaleToFill:不保持纵横比缩放图片,使图片完全适应'

    }, {

      mode: 'aspectFit',

      text: 'aspectFit:保持纵横比缩放图片,使图片的长边能完全显示出来'

    }, {

      mode: 'aspectFill',

      text: 'aspectFill:保持纵横比缩放图片,只保证图片的短边能完全显示出来'

    }, {

      mode: 'top',

      text: 'top:不缩放图片,只显示图片的顶部区域'

    }, {

      mode: 'bottom',

      text: 'bottom:不缩放图片,只显示图片的底部区域'

    }, {

      mode: 'center',

      text: 'center:不缩放图片,只显示图片的中间区域'

    }, {

      mode: 'left',

      text: 'left:不缩放图片,只显示图片的左边区域'

    }, {

      mode: 'right',

      text: 'right:不缩放图片,只显示图片的右边边区域'

    }, {

      mode: 'top left',

      text: 'top left:不缩放图片,只显示图片的左上边区域'

    }, {

      mode: 'top right',

      text: 'top right:不缩放图片,只显示图片的右上边区域'

    }, {

      mode: 'bottom left',

      text: 'bottom left:不缩放图片,只显示图片的左下边区域'

    }, {

      mode: 'bottom right',

      text: 'bottom right:不缩放图片,只显示图片的右下边区域'

    }],

    src: '../resources/cat.jpg'

  },

  imageError: function(e) {

    console.log('image3发生error事件,携带值为', e.detail.errMsg)

  }

})

4-8 商城内容展示

示例代码

// index.js

const app = getApp()



Page({

  data: {

    imgUrls:[

      "https://img.youpin.mi-img.com/youpinoper/1002265792a851c83054da07959d976c.jpg?id=&w=1080&h=450",

      "https://img.youpin.mi-img.com/youpinoper/f62e9e59b21f97f81ed3ab567f0e4b08.jpg?id=&w=1080&h=450",

      "https://img.youpin.mi-img.com/youpinoper/0bb6ad5d5c85b03534b460c297c6b85a.jpeg?id=&w=1080&h=450"

    ],



    category:[

      { url:"https://img.youpin.mi-img.com/shopmain/b68323e900ffbb4835dd9e6a15d10b77.png?w=800&h=800",

      tag:'小米众筹'},

            {

              url: "https://img.youpin.mi-img.com/800_pic/27a789a428038214a8dda98f97d5fe4c.png",

        tag: '限时抢购'

      },

            {

              url: "https://img.youpin.mi-img.com/800_pic/c13ae49b289b269d9a39496e3e31708d.png",

        tag: '热销榜单'

      },

            {

              url: "https://img.youpin.mi-img.com/800_pic/da003d715c9e832b2c8e62402e48bfa0.png",

        tag: '随便逛逛'

      }

    ]

  },



})


<!-- index.wxml -->

<!-- 商城首页 -->

<swiper indicator-dots="true" autoplay="true" interval="2000">

  <block wx:for="{{imgUrls}}" wx:key='imgs'>

  <swiper-item>

    <image src='{{item}}' mode="widthFix"></image>

  </swiper-item>

  </block>

</swiper>

<!-- 分类推荐 -->

<view class="category">

  <block wx:for="{{category}}" wx:key="types">

  <view class="category-item">

    <image src='{{item.url}}' class='category-img'></image>

    <text>{{item.tag}}</text>

    </view>

  </block>

</view>

<!-- 今日特价 -->

<view class="separate"></view>

<view class="discount">

<view class="tag">

  <view class="tag-insider"></view>

  <view>今日特价</view></view>

  <view>更多>></view>

</view>

/* index.wxss */

.category{

  display: flex;

  justify-content: space-around

}

.category-item{

  display: flex;

  flex-direction: column;

  align-items: center;

  padding: 20rpx;



}

.category-img{

  background-color: #C5C1C0;

  width: 100rpx;

  height: 100rpx;

  border-radius: 30%;

}

.category-item text{

  padding-top: 10rpx;

  font-size: 25rpx;

}

.separate{

  height: 10rpx;

  background-color: #F3F3F3;



}

.discount{

  display: flex;

  justify-content: space-between;

  padding: 0 10px;

  font-size: 30rpx;

}

.tag{

  display: flex;

  color: red;

}

.tag-insider{

  margin-right: 10rpx;

  margin-top: 5rpx;

  width: 10rpx;

  height: 30rpx;

  background-color:#fea96a;

}


效果

5-1、5-2小程序API介绍

API(Application Programming Interface)
  • 由小程序开发框架提供的预先定义的函数
  • 可直接按需调用功能,无需获取源码或理解内部细节
小程序API分类
  • 网络 媒体 文件 数据缓存 画布
  • 位置 设备 开放接口等
小程序API的类型
事件监听

约定以 

on

开头的 API 用来监听某个事件是否触发,如:

wx.onSocketOpen

wx.onCompassChange

 等。

这类 API 接受一个回调函数作为参数,当事件触发时会调用这个回调函数,并将相关数据以参数形式传入。

代码示例:

wx.onCompassChange(function (res) {

  console.log(res.direction)

})

数据操作

数据的读取及写入 

读取:

wx.get***

写入:

wx.set***

同步-可由函数返回值获取结果

约定以 

Sync

 结尾的 API 都是同步 API, 如

wx.setStorageSync

wx.getSystemInfoSync

 等。此外,也有一些其他的同步 API,如 

wx.createWorker

wx.getBackgroundAudioManager

 等

同步 API 的执行结果可以通过函数返回值直接获取,如果执行出错会抛出异常。

代码示例:

try {

  wx.setStorageSync('key', 'value')

} catch (e) {

  console.error(e)

}

异步

大多数 API 都是异步 API,如 

wx.request

wx.login

 等。这类 API 接口通常都接受一个 Object 类型的参数,这个参数都支持按需指定以下字段来接收接口调用结果:

三个回调函数 

success

fail

complete

原理:回调函数的核心是委托制 

  适用场景:在特定条件满足时执行一个任务 

  使用:

  • 定义一个函数将要执行的任务与一特定事件建立关联
  • 当该函数关联的事件触发的时候,函数将被执行(回调机制)

优点:程序的任务执行变为异步,无需一直等待任务执行完成

success

fail

complete

 函数调用时会传入一个 Object 类型参数,包含以下字段:

异步 API 的执行结果需要通过 Object 类型的参数中传入的对应回调函数获取。部分异步 API 也会有返回值,可以用来实现更丰富的功能,如 

wx.request

wx.connectSocket

 等。

代码示例:

wx.login({

  success(res) {

    console.log(res.code)

  }

})

5-3结合回调函数操作API

例一:

我们先来看媒体中的获取图片信息的函数:

wx.getImageInfo

(Object object)

该函数的作用是获取图片信息。网络图片需先配置download域名才能生效。

函数参数:

object.success 回调函数参数:

res.orientation 的合法值:

代码示例:

//index.js

const app=getApp()

Page({

    data:{

        src:'../../imgs/pic.jpg'

        },

    getImgInfo:function(){

        wx.getImageInfo({

            src:this.data.src,

            success(res)  {  //回调函数

                console.log(res)

            },

            fail(res){

                console.log(res)

            },

            complete(res){

                console.log('一定会执行')

            }

        })

        }   

    })

//index.wxml

<view>下面是一个获取图片信息的按钮</view>

<button bindtap='getImgInfo'>点一下</button>

运行

输出图片信息:

例二:

再来看一下界面交互中的

wx.showToast

函数 

该函数的作用是显示消息提示框

函数参数:

代码示例:

//index.js

const app = getApp()



Page({

  data: {

    src:'../../imgs/pic.jpg'

  },

  //事件触发函数

  showImgInfo:function(text){

    wx.showToast({

      title: text,

      icon:'none'

    })

  },



  getImgInfo:function(){

    var that=this

    wx.getImageInfo({

      src: this.data.src,

      success(res) {  // 回调函数 委托 执行者变化

        that.showImgInfo('路径->'+res.path+'\n尺寸'+res.width+'X'+res.height)

      },

      fail(res){

        console.log(res)

      },

      complete(res){

        console.log('一定会执行',res)

      }

    })

  }



})


运行,获得图片信息,并在交互界面看到所输出的图片信息

5-4音频API

InnerAudioContext

函数

InnerAudioContext 实例,可通过 wx.createInnerAudioContext 接口获取实例。

属性:

tring src

音频资源的地址,用于直接播放。2.2.3 开始支持云文件ID

number startTime

开始播放的位置(单位:s),默认为 0

boolean autoplay

是否自动开始播放,默认为 false

boolean loop

是否循环播放,默认为 false

boolean obeyMuteSwitch

是否遵循系统静音开关,默认为 true。当此参数为 false 时,即使用户打开了静音开关,也能继续发出声音。从 2.3.0 版本开始此参数不生效,使用 

wx.setInnerAudioOption

 接口统一设置。

number volume

基础库 1.9.90 开始支持,低版本需做兼容处理。

音量

范围 0~1。默认为 1

number duration

当前音频的长度(单位 s)。只有在当前有合法的 src 时返回(只读)

number currentTime

当前音频的播放位置(单位 s)。只有在当前有合法的 src 时返回,时间保留小数点后 6 位(只读)

boolean paused

当前是是否暂停或停止状态(只读)

number buffered

音频缓冲的时间点,仅保证当前播放时间点到此时间点内容已缓冲(只读

代码示例:

//index.js

Page({



  /**

   * 页面的初始数据

   */

  data: {

    src: 'http://ws.stream.qqmusic.qq.com/M500001VfvsJ21xFqb.mp3?guid=ffffffff82def4af4b12b3cd9337d5e7&uin=346897220&vkey=6292F51E1E384E061FF02C31F716658E5C81F5594D561F2E88B854E81CAAB7806D5E4F103E55D33C16F3FAC506D1AB172DE8600B37E43FAD&fromtag=46',

    poster: '../../imgs/pic.jpg',

    name: '普通disco',

    author: '佚名',

    src2:'../../music/light_emotion.mp3',

    src3: 'http://sc1.111ttt.cn:8282/2018/1/03m/13/396131229550.m4a?tflag=1546606800&pin=97bb2268ae26c20fe093fd5b0f04be80#.mp3',

  },

  audioPlay() {

    this.audioData.play()

    console.log('播放')

  },

  audioPause() {

    this.audioData.pause()

    console.log('暂停')

  },

  audioPlayBack() {

    this.audioData.seek(this.audioData.currentTime-3)

  },

  audioStop() {

    // this.audioData.stop()

  },

  audioStart() {

  },



  /**

   * 生命周期函数--监听页面加载

   */

  onLoad: function(options) {

    this.audioData =wx.createInnerAudioContext()

    this.audioData.src = this.data.src

    this.audioData.autoplay = false

    console.log(this.audioData)

  },



  /**

   * 生命周期函数--监听页面初次渲染完成

   */

  onReady: function() {

    // this.audioData = wx.createAudioContext('myAudio')



  },



  /**

   * 生命周期函数--监听页面显示

   */

  onShow: function() {



  },



  /**

   * 生命周期函数--监听页面隐藏

   */

  onHide: function() {



  },



  /**

   * 生命周期函数--监听页面卸载

   */

  onUnload: function() {



  },



  /**

   * 页面相关事件处理函数--监听用户下拉动作

   */

  onPullDownRefresh: function() {



  },



  /**

   * 页面上拉触底事件的处理函数

   */

  onReachBottom: function() {



  },



  /**

   * 用户点击右上角分享

   */

  onShareAppMessage: function() {



  }

})


//index.wxml

<!--pages/index2/index2.wxml-->

<audio poster="{{poster}}" name="{{name}}" author="{{author}}" src="{{src3}}" id="myAudio" controls loop></audio>



<button bindtap='audioPlay'>播放</button>

<button bindtap='audioPause'>暂停</button>

<button bindtap='audioPlayBack'>回放测试</button>

<button bindtap='audioStop'>结束</button>

<button bindtap='audioStart'>回到开头</button>

值得注意的是,

<audio/>

组件不再维护,可以使用能力更强的 

wx.createInnerAudioContext

接口.

5-5位置API

首先我们看一下

wx.getLocation

函数

作用:获取当前的地理位置、速度。当用户离开小程序后,此接口无法调用。

参数:

object.success 回调函数

代码示例:

//index.js

Page({



  /**

   * 页面的初始数据

   */

  data: {

    demo_la: 48.1545,

    demo_lo: 82.3555907,

    latitude: 0,

    longitude: 0,

    begin_la: 0,

    begin_lo: 0,

    interval: 0,

    speed: 0,

    accuracy: 0,

    markers: [{

      iconPath: "../../imgs/pic.jpg",

      id: 0,

      latitude: 39.9205,

      longitude: 116.4605,

      width: 30,

      height: 30

    }]



  },

  openLoc() {

    this.mapCtx.includePoints({

      padding: [10],

      points: [{

        latitude: 23.10229,

        longitude: 113.3345211,

      }, {

        latitude: 23.00229,

        longitude: 113.3345211,

      }]

    })

  },



  geoInfo() {

    var that = this

    wx.getLocation({

      type: 'gcj02',

      scale:18,

      success(res) {



        that.setData({

          latitude: res.latitude,

          longitude: res.longitude,

          speed: res.speed,

          accuracy: res.accuracy,

        })

      }

    })

    console.log(this.data.latitude, this.data.longitude)

  },



  startRun() {

    var that = this

    clearInterval(this.data.interval)



    wx.getLocation({

      type: 'gcj02',

      success(res) {

        var la = res.latitude

        var lo = res.longitude

        that.setData({

          begin_la: la,

          begin_lo: lo,

          'markers[1]': {

            iconPath: "../../imgs/red.png",

            id: 1,

            latitude: la,

            longitude: lo,

            width: 20,

            height: 20

          }

        })

      }

    })



    var interval = setInterval(() => {

      this.running()

    }, 1000)





  },

  stopRun() {

    var that = this

    clearInterval(this.data.interval)

    wx.getLocation({

      type: 'gcj02',

      success(res) {

        var la = res.latitude

        var lo = res.longitude

        that.setData({

          latitude: la,

          longitude: lo,

          'markers[2]': {

            iconPath: "../../imgs/yellow.png",

            id: 2,

            latitude: la,

            longitude: lo,

            width: 20,

            height: 20

          }

        })

      }

    })

    console.log('开始位置', this.data.begin_la, this.data.begin_lo)

    console.log('结束位置', this.data.latitude, this.data.longitude)

    console.log(this.data)

  },

  running() {

    this.data.latitude += 0.0002

    this.data.longitude += 0.0002

  },

  center() {

    this.mapCtx.getCenterLocation({

      success: function (res) {

        console.log(res.latitude,res.longitude)

      }

    })



  },

  trans() {

    this.mapCtx.translateMarker({

      markerId: 0,

      autoRotate: false,

      duration: 3000,

      destination: {

        latitude: 40.30229,

        longitude: 116.7545211,

      },

      animationEnd() {

        console.log('animation end')

      }

    })

  },

  onReady: function () {

    this.mapCtx = wx.createMapContext('myMap')

  },

})

//index.wxml

<view class='top'>

  <button bindtap='geoInfo'>位置</button>

  <button bindtap='openLoc'>打开</button>

  <button bindtap='center'>定心</button>

  <button bindtap='trans'>动</button>

</view>



<map id='myMap' style="width:100%; height:350px" longitude='{{longitude}}' latitude='{{latitude}}' markers='{{markers}}' polyline="{{polyline}}" show-location subkey='IWLBZ-CK6R4-YXDUO-DQCXK-J5H4Z-DSBCI'>

  <cover-view calss='cover'>

    经度:{{longitude}} 纬度:{{latitude}} 速度:{{speed}}

  </cover-view>

</map>



<button bindtap='startRun'>开始</button>

<button bindtap='stopRun'>结束</button>

5-6网络请求原理

网络请求过程

小程序访问服务器,经过TCP握手后建立起TCP连接,然后小程序向服务器发送HTTP请求,即发送一个request请求去获取客户器上的数据,服务器把response文件对象发送回给小程序。

数据结构

请求 

HTTP请求格式如下所示四部分组成,分别是请求行、请求头、空行、消息体,每部分内容占一行。

<request-line>

<general-headers>

 
      
  1. <request-headers>

<entity-headers>

<empty-line>

[message-body]

请求行:由三部分组成:分别是请求方法(GET/POST/DELETE/PUT/HEAD)、URI路径、HTTP版本号。

主体:客户端发给服务端的请求数据,这部分数据并不是每个请求必须的

返回数据 

服务器接收处理完请求后返回一个HTTP响应消息给客户端。HTTP响应消息的格式包括:状态行、响应头、空行、消息体。每部分内容占一行。 

<status-line>

<general-headers>

<response-headers>

<entity-headers>

<empty-line>

<[message-body]>

状态行:有HTTP协议版本号,状态码和状态说明三部分构成。

响应头:用于说明数据的一些信息,比如数据类型、内容长度等键值对。 

空行。

消息体:服务端返回给客户端的HTML文本内容。或者其他格式的数据,比如:视频流、图片或者音频数据。

豆瓣示例

<general-headers>

<response-headers>

<request-headers>

小程序网络请求API

wx.request

函数

作用:发起HTTPS 网络请求

参数:

代码示例:

//index.js

Page({



  /**

   * 页面的初始数据

   */

  data: {

    url:'https://easy-mock.com/mock/5cd4d8aa4993c3317af392a5/example/test',

    url_2:'https://easy-mock.com/mock/5cd4d8aa4993c3317af392a5/example/douban'

  },



  getData:function(){

    wx.request({

      url: this.data.url_2, 

      header: {

        'content-type': 'application/json' // 默认值

      },

      success(res) {

        console.log(res.data)

      }

    })

  },



  /**

   * 生命周期函数--监听页面加载

   */

  onLoad: function (options) {



  },



  /**

   * 生命周期函数--监听页面初次渲染完成

   */

  onReady: function () {



  },



  /**

   * 生命周期函数--监听页面显示

   */

  onShow: function () {



  },



  /**

   * 生命周期函数--监听页面隐藏

   */

  onHide: function () {



  },



  /**

   * 生命周期函数--监听页面卸载

   */

  onUnload: function () {



  },



  /**

   * 页面相关事件处理函数--监听用户下拉动作

   */

  onPullDownRefresh: function () {



  },



  /**

   * 页面上拉触底事件的处理函数

   */

  onReachBottom: function () {



  },



  /**

   * 用户点击右上角分享

   */

  onShareAppMessage: function () {



  }

})

//index.wxml

<button bindtap='getData'>请求模拟数据</button>

6-1 云开发介绍

介绍

开发者可以使用云开发开发微信小程序、小游戏,无需搭建服务器,即可使用云端能力。

云开发为开发者提供完整的原生云端支持和微信服务支持,弱化后端和运维概念,无需搭建服务器,使用平台提供的 API 进行核心业务开发,即可实现快速上线和迭代,同时这一能力,同开发者已经使用的云服务相互兼容,并不互斥。

云开发提供了几大基础能力支持:

步骤

可以按如下步骤快速开始使用云开发。

  1. 新建云开发模板
  2. 开通云开发
  3. 体验小程序
  4. 查看控制台
1. 新建云开发模板

新建项目选择一个空目录,填入 AppID(使用云开发能力必须填写 AppID),勾选创建 “云开发 QuickStart 项目”,点击创建即可得到一个展示云开发基础能力的示例小程序。该小程序与普通 QuickStart 小程序有以下不同需注意:

  1. 无游客模式、也不可以使用 测试号
  2. project.config.json 中增加了字段 cloudfunctionRoot 用于指定存放云函数的目录
  3. cloudfunctionRoot 指定的目录有特殊的图标
  4. 云开发能力从基础库 2.2.3 开始支持(覆盖率 97.3%,查看兼容性问题)

从基础库 2.4.1 开始,在小程序插件中可以使用云开发,插件中使用云开发时,使用的是插件方的云资源而非宿主的云资源,在使用方式上与在小程序中使用无异。

2. 开通云开发、创建环境

创建了第一个云开发小程序后,在使用云开发能力之前需要先开通云开发。在开发者工具工具栏左侧,点击 “云开发” 按钮即可打开云控制台、根据提示开通云开发、创建云环境。默认配额下可以创建两个环境,各个环境相互隔离,每个环境都包含独立的数据库实例、存储空间、云函数配置等资源。每个环境都有唯一的环境 ID 标识,初始创建的环境自动成为默认环境。

注:AppID 首次开通云环境后,需等待大约 10 分钟方可正常使用云 API,在此期间官方后台服务正在做准备服务,如尝试在小程序中调用云 API 则会报 cloud init error:{ errMsg: “invalid scope” } 的错误

3. 体验小程序

开通创建环境后,即可以开始在模拟器上操作小程序体验云开发提供的部分基础能力演示。

4. 查看控制台

云开发控制台是管理云开发资源的地方,控制台提供以下能力:

  1. 运营分析:查看云开发监控、配额使用量、用户访问情况
  2. 数据库:管理数据库,可查看、增加、更新、查找、删除数据、管理索引、管理数据库访问权限等
  3. 存储管理:查看和管理存储空间
  4. 云函数:查看云函数列表、配置、日志

数据库

云开发提供了一个 JSON 数据库,顾名思义,数据库中的每条记录都是一个 JSON 格式的对象。一个数据库可以有多个集合(相当于关系型数据中的表),集合可看做一个 JSON 数组,数组中的每个对象就是一条记录,记录的格式是 JSON 对象。

关系型数据库和 JSON 数据库的概念对应关系如下表:

以下是一个示例的集合数据,假设我们有一个 books 集合存放了图书记录,其中有两本书:

[

  {

    "_id": "Wzh76lk5_O_dt0vO",

    "title": "The Catcher in the Rye",

    "author": "J. D. Salinger",

    "characters": [

      "Holden Caulfield",

      "Stradlater",

      "Mr. Antolini"

    ],

    "publishInfo": {

      "year": 1951,

      "country": "United States"

    }

  },

  {

    "_id": "Wzia0lk5_O_dt0vR",

    "_openid": "ohl4L0Rnhq7vmmbT_DaNQa4ePaz0",

    "title": "The Lady of the Camellias",

    "author": "Alexandre Dumas fils",

    "characters": [

      "Marguerite Gautier",

      "Armand Duval",

      "Prudence",

      "Count de Varville"

    ],

    "publishInfo": {

      "year": 1848,

      "country": "France"

    }

  }

]

在图书信息中,我们用 title, author 来记录图书标题和作者,用 characters 数组来记录书中的主要人物,用 publishInfo 来记录图书的出版信息。在其中我们可以看到,字段既可以是字符串或数字,还可以是对象或数组,就是一个 JSON 对象。

每条记录都有一个 _id 字段用以唯一标志一条记录、一个 _openid 字段用以标志记录的创建者,即小程序的用户。需要特别注意的是,在管理端(控制台和云函数)中创建的不会有 _openid 字段,因为这是属于管理员创建的记录。开发者可以自定义 _id,但不可自定义和修改 _openid 。_openid 是在文档创建时由系统根据小程序用户默认创建的,开发者可使用其来标识和定位文档。

数据库 API 分为小程序端和服务端两部分,小程序端 API 拥有严格的调用权限控制,开发者可在小程序内直接调用 API 进行非敏感数据的操作。对于有更高安全要求的数据,可在云函数内通过服务端 API 进行操作。云函数的环境是与客户端完全隔离的,在云函数上可以私密且安全的操作数据库。

数据库 API 包含增删改查的能力,使用 API 操作数据库只需三步:获取数据库引用、构造查询/更新条件、发出请求。以下是一个在小程序中查询数据库的发表于美国的图书记录的例子:

// 1. 获取数据库引用

const db = wx.cloud.database()

// 2. 构造查询语句

// collection 方法获取一个集合的引用

// where 方法传入一个对象,数据库返回集合中字段等于指定值的 JSON 文档。API 也支持高级的查询条件(比如大于、小于、in 等),具体见文档查看支持列表

// get 方法会触发网络请求,往数据库取数据

db.collection('books').where({

  publishInfo: {

    country: 'United States'

  }

}).get({

  success: function(res) {

  // 输出 [{ "title": "The Catcher in the Rye", ... }]

  console.log(res)

 }

})

存储

云开发提供了一块存储空间,提供了上传文件到云端、带权限管理的云端下载能力,开发者可以在小程序端和云函数端通过 API 使用云存储功能。

在小程序端可以分别调用 wx.cloud.uploadFile 和 wx.cloud.downloadFile 完成上传和下载云文件操作。下面简单的几行代码,即可实现在小程序内让用户选择一张图片,然后上传到云端管理的功能:

// 让用户选择一张图片

wx.chooseImage({

  success: chooseResult => {

    // 将图片上传至云存储空间

    wx.cloud.uploadFile({

      // 指定上传到的云路径

      cloudPath: 'my-photo.png',

      // 指定要上传的文件的小程序临时文件路径

      filePath: chooseResult.tempFilePaths[0],

      // 成功回调

      success: res => {

        console.log('上传成功', res)

      },

    })

  },

})

上传完成后可在控制台中看到刚上传的图片。

云函数

云函数是一段运行在云端的代码,无需管理服务器,在开发工具内编写、一键上传部署即可运行后端代码。

小程序内提供了专门用于云函数调用的 API。开发者可以在云函数内使用 wx-server-sdk 提供的 getWXContext 方法获取到每次调用的上下文(appid、openid 等),无需维护复杂的鉴权机制,即可获取天然可信任的用户登录态(openid)。

比如我们如下定义一个云函数,命名为 add ,功能是将传入的两个参数 a 和 b 相加:

// index.js 是入口文件,云函数被调用时会执行该文件导出的 main 方法

// event 包含了调用端(小程序端)调用该函数时传过来的参数,同时还包含了可以通过 getWXContext 方法获取的用户登录态 `openId` 和小程序 `appId` 信息

const cloud = require('wx-server-sdk')

exports.main = (event, context) => {

  let { userInfo, a, b} = event

  let { OPENID, APPID } = cloud.getWXContext() // 这里获取到的 openId 和 appId 是可信的

  let sum = a + b



  return {

    OPENID,

    APPID,

    sum

  }

}

在开发者工具中上传部署云函数后,我们在小程序中可以这么调用:

wx.cloud.callFunction({

  // 需调用的云函数名

  name: 'add',

  // 传给云函数的参数

  data: {

    a: 12,

    b: 19,

  },

  // 成功回调

  complete: console.log

})

// 当然 promise 方式也是支持的

wx.cloud.callFunction({

  name: 'add',

  data: {

    a: 12,

    b: 19

  }

}).then(console.log)

如需在云函数中操作数据库、管理云文件、调用其他云函数等操作,可使用官方提供的 npm 包 wx-server-sdk 进行操作。

6-2 云开发的同步与异步

同步与异步

同步 synchronous

- 程序正常按照顺序依次执行

- 亲力亲为 需要等待当前任务完成才执行下一个

异步 asynchronous

- 将事情交给其他函数去做

- 甩手掌柜 可能马上就完成也可能需要一段时间

- Promise

pending(进行中)

resolved(已完成)

rejected (已拒绝)

举个例子:

”同步“就好比:你去外地上学(人生地不熟),突然生活费不够了;此时你决定打电话回家,通知家里转生活费过来,可是当你拨出电话时,对方一直处于待接听状态(即:打不通,联系不上),为了拿到生活费,你就不停的oncall、等待,最终可能不能及时要到生活费,导致你今天要做的事都没有完成,而白白花掉了时间。

“异步”就是:在你打完电话发现没人接听时,猜想:对方可能在忙,暂时无法接听电话,所以你发了一条短信(或者语音留言,亦或是其他的方式)通知对方后便忙其他要紧的事了;这时你就不需要持续不断的拨打电话,还可以做其他事情;待一定时间后,对方看到你的留言便回复响应你,当然对方可能转钱也可能不转钱。但是整个一天下来,你还做了很多事情。 或者说你找室友临时借了一笔钱,又开始happy的上学时光了。

Promise()

var promise = new Promise(function(resolve, reject) {

 if (/* 异步操作成功 */){

 resolve(value);

 } else {

 reject(error);

 }

});



promise.then(function(value) {

 // success

}, function(value) {

 // failure

});

Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 方法和 reject 方法。

  • 如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从「未完成」变为「成功」(即从 pending 变为 resolved);
  • 如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从「未完成」变为「失败」(即从 pending 变为 rejected)。

async函数

正常执行

  • 包含函数语句、函数表达式、lambda表达式
  • 返回Promise对象 不阻塞

await

  • 暂停当前async执行,等待Promise处理完成
  • 正常(处理结果作为await表达式值)
  • 异常 抛出异常原因

特点

  • 优化处理then(解决多层回调问题)逻辑链 异步变同步
  • 异常用catch直接捕捉

1.async函数返回的是一个Promise对象。Async函数(包含函数语句、函数表达式、Lambda表达式)会返回一个Promise对象,如果在函数中return一个直接量,async会把这个直接量通过Promise.resolve()封装成Promise对象。

2.async函数返回的是一个Promise对象,所以在最外层不能用await获取其返回值的情况下,我们当然应该用原来的方式:then()链来处理这个Promise对象。

3.await表达式会暂停当前async function的执行,等待Promise处理完成若Promise正常处理,其处理结果作为await表达式的值,继续执行async function。若Promise处理异常(rejected),await表达式会把Promise的异常原因抛出。另外,如果await操作符号的表达式的值不是一个Promise,那么该值将被转换为一个正常处理的Promise。

4.async/await的优势在于处理then链

单一的Promise链并不能发现async/await的优势,但是,如果需要处理由多个Promise组成的then链的时候,优势就能体现出来了(Promise通过then链来解决多层回调的问题,现在又用async/await来进一步优化它)

en()链来处理这个Promise对象。

3.await表达式会暂停当前async function的执行,等待Promise处理完成若Promise正常处理,其处理结果作为await表达式的值,继续执行async function。若Promise处理异常(rejected),await表达式会把Promise的异常原因抛出。另外,如果await操作符号的表达式的值不是一个Promise,那么该值将被转换为一个正常处理的Promise。

4.async/await的优势在于处理then链

单一的Promise链并不能发现async/await的优势,但是,如果需要处理由多个Promise组成的then链的时候,优势就能体现出来了(Promise通过then链来解决多层回调的问题,现在又用async/await来进一步优化它)

ise对象,如果在函数中return一个直接量,async会把这个直接量通过Promise.resolve()封装成Promise对象。

2.async函数返回的是一个Promise对象,所以在最外层不能用await获取其返回值的情况下,我们当然应该用原来的方式:then()链来处理这个Promise对象。

3.await表达式会暂停当前async function的执行,等待Promise处理完成若Promise正常处理,其处理结果作为await表达式的值,继续执行async function。若Promise处理异常(rejected),await表达式会把Promise的异常原因抛出。另外,如果await操作符号的表达式的值不是一个Promise,那么该值将被转换为一个正常处理的Promise。

4.async/await的优势在于处理then链

单一的Promise链并不能发现async/await的优势,但是,如果需要处理由多个Promise组成的then链的时候,优势就能体现出来了(Promise通过then链来解决多层回调的问题,现在又用async/await来进一步优化它)

  • 17
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值