美文网首页
2019-02-22 微信小程序·开发指南

2019-02-22 微信小程序·开发指南

作者: 咖啡浮点 | 来源:发表于2019-03-01 21:52 被阅读0次

    第一章 小程序介绍与开发环境

    一. Hello World
    // app.json 
    "pages": [
        "pages/index/index"
      ]
    
    // index/index.wxml
    <text>Hello World</text>
    
    二. 小程序介绍
    1.小程序技术发展史

    (1).移动web在微信的WebView中显示,微信有相关JS API ,但只对团队内部使用
    (2). 网页开发工具包 JS-SDK,对外开放,缺点:体验不好,资源加载白屏/缺少操纵反馈。
    (3). 小程序应运而生。

    2.小程序与普通网页开发的区别

    (1).网页开发中渲染线程与脚本线程是互斥的。小程序逻辑层与渲染层是分开的,可以并行的。
    (2).小程序逻辑层没有一个完整浏览器对象,因而缺少相关的DOM API和BOM API。同时JSCore 的环境同 NodeJS 环境不尽相同。
    (3).网页开发者需要面对的环境是各式各样的浏览器,小程序要面对的是两大操作系统 iOS 和 Android 的微信客户端,以及用于辅助开发的小程序开发者工具。


    小程序运行环境
    三. 小程序开发准备

    在微信公众平台注册一个小程序,设置”—“开发设置” 就可以看到小程序的 AppID。
    安装开发者工具
    在开发者工具中创建项目 填入AppID 开始项目。

    第二章 小程序代码组成

    一. JSON 配置

    1.在小程序中,JSON扮演的静态配置的角色。
    2.小程序是无法在运行过程中去动态更新JSON 配置文件从而发生对应的变化的。
    JSON 语法:
    1.JSON的Key必须包裹在一个双引号中
    2.JSON的值数据格式:
    1.数字 包括浮点数和整数
    2.字符串
    3.Bool: true or false
    4.数组:包裹在[]中
    5.对象:包裹在{}中
    6.null
    其他任何格式都会触发报错
    3.JSON 文件中无法使用注释,试图添加注释将会引发报错。

    二.WXML模板
    1.基本介绍

    1.全称:WeiXin Markup Language
    2.基本语法:

    <!-- 在此处注释 —>
    <text 属性名=“属性值”></text>
    
    2.数据绑定

    1.WXML中的属性是大小写敏感的
    2.数据绑定中 变量也是大小写敏感的
    3.属性值可以绑定,有所不同的是,属性值必须被包裹在引号中

    <!-- 数据绑定 -->
    // index.wxml
    <view>当前时间:{{time}}</view>
    <!-- 绑定属性值 -->
    <view data-test="{{test}}">hello world</view>
    <view>{{var1}}</view>
    <view>{{var2}}</view>
    
    // index.js
     data: {
        time: (new Date()).toString(),
        var2: undefined,
        var3: null
    }
    
    3.逻辑语法
    <!-- 逻辑运算 -->
    // index.js
    data: {
        a: 10,
        b: 2,
        c: 4,
        d: 6,
        name: 'world',
        arr: ['hei',2,3]
    }
    // index.wxml
    <view>{{a === 10?"变量a等于10":"变量a不等于10"}}</view>   //变量a等于10
    <view>{{a + b}} + {{c}} + {{d}}</view>  // 12 + 4 + 6
    <view>{{'hello ' + name }}</view>   // hello world
    <view>hello {{name}}</view>   // hello world
    <view>{{arr}}</view>  // hei,2,3
    <view>{{1}}</view>   // 1
    <view>{{'hhahahha'}}</view> // hhahahha
    
    4. 条件逻辑
    <!-- 条件逻辑 -->
    // index.js
     arr: ['hei',2,3]
    // index.wxml
    <view wx:if="{{arr.length > 5}}">1</view>
    <view wx:elif="{{length > 3}}">2</view>
    <view wx:else>3</view>
    
    5. 列表渲染
    // index.js
    data: {
    list: [
          {
            message: 'foo',
          },
          {
            message: 'bar',
          }
        ],
        objArr: [
          { id: 5, unique: 'unique5' },
          { id: 4, unique: 'unique4' },
          { id: 3, unique: 'unique3' },
          { id: 2, unique: 'unique2' },
          { id: 1, unique: 'unique1' },
          { id: 0, unique: 'unique0' },
        ],
        numArr: [1, 2, 3, 4]
    }switch: function(e){
        const length = this.data.objArr.length
        for(var i = 0;i < length; i++) {
          const x = Math.floor(Math.random() * length);
          const y = Math.floor(Math.random() * length);
          const temp = this.data.objArr[x];
          this.data.objArr[x] = this.data.objArr[y];
          this.data.objArr[y] = temp;
        }
        this.setData({
          objArr: this.data.objArr
        })
      },
      addToFront: function(e){
        const length = this.data.objArr.length
        this.data.objArr = [{id: length, unique: 'unique_' + length}].concat(this.data.objArr);
        this.setData({
          objArr: this.data.objArr
        })
      },
      addNumToFront: function(e){
        this.data.numArr = [this.data.numArr.length + 1].concat(this.data.numArr)
        this.setData({
          numArr: this.data.numArr
        })
      }
    
    // index.wxml
    <view wx:for='{{list}}'>
      {{index + 1}}: {{item.message}}
    </view>
    
    <!-- 列表渲染 指定当前元素的变量名 及其对应的下标 -->
    <view wx:for="{{list}}" wx:for-index='idx' wx:for-item='itemName'>
      {{idx + 1}}: {{itemName.message}}
    </view>
    <!-- 列表渲染 指定唯一标识符 -->
    <switch wx:for="{{objArr}}" wx:key="unique">{{item.id}}</switch>
    <button bindtap="switch">switch</button>
    <button bindtap="addToFront">addToFront</button>
    
    <switch wx:for="{{numArr}}" wx:key="*this">{{item}}</switch>
    <button bindtap="addNumToFront">addNumToFront</button>
    

    使用wx:key指定列表渲染中的唯一标识符,key值两个条件:
    (1)字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
    (2) 保留关键字 this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字
    好处: 当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件 保持自身的状态,并且提高列表渲染时的效率。

    4. 模版

    模板中渲染的所有数据是传入对象的属性的值

    <!-- 模板 -->
    <template name="msg">
      <view>
        <text>{{id}}: {{unique}}</text>
      </view>
    </template>
    <view wx:for="{{objArr}}" wx:for-item="item" wx:for-index="index">
      <template is="msg" data="{{...item}}"></template>
    </view>
    <template name="even">
      <view>even</view>
    </template>  
    <block wx:for="{{[1, 2, 3, 4, 5]}}" wx:for-item="item">
      <template is="{{item%2 == 0?'even': ''}}" data="{{...item}}"></template>  
    </block> 
    
    5. 引用
    // b.wxml
    <template name='B'>
      <text>B template</text>
    </template>
    // 同目录下index.wxml
    <import src='b.wxml' />
    <template is="B"/>  
    
    // header.wxml
    <view>header</view>
    // footer.wxml
    <view>footer</view>
    <!-- 引用 include -->
    <include src='header.wxml'/>
    <view>body</view>
    <include src='footer.wxml'/> 
    
    6. 共同属性
    共同属性
    三. javascript 脚本组成
    1.小程序javascript组成
    屏幕快照 2019-02-22 下午8.56.42.png
    注意:

    (1)没有BOM与DOM,无法使用Jquery、zepto等浏览器类库
    (2) Native 模块和NPM包管理的机制,无法加载原生库与大部分NPM包

    2.小程序的执行环境

    1.小程序目前可以运行在三大平台:
    (1).iOS平台,包括iOS9、iOS10、iOS11
    (2).Android平台
    (3).小程序IDE
    原因:三大平台实现的 ECMAScript 的标准有所不同。

    2.iOS9和iOS10 所使用的运行环境并没有完全的兼容到 ECMAScript 6 标准。
    小程序解决方案:小程序IDE在‘右上角详情’中提供转码工具,勾选 ES6 转 ES5 后,可实现转码。

    3.模块化

    1.小程序中将所有javascript文件作为一个模块处理,通过module.exports 或者 exports 对外暴露接口。
    2.引用模块的方法时,可采用require的方式

    var aBy2 = require('./a.js');
    var result = aBy2(5);
    console.log(result);
    

    3.小程序脚本执行顺序:
    (1)入口文件app.js
    (2)app.js中引入的模块
    (3)app.js中pages属性定义的js文件顺序:

    "pages": [
        "pages/wxml/index",
        "pages/index/index"
      ],
    

    4.作用域
    (1)不同文件声明的变量以及方法只在本文件有效
    (2)可以通过getApp()全局方法设置全局性的属性以及方法

    第三章 理解小程序宿主环境

    不同版本的微信客户端提供的宿主环境版本不同,所以小程序需要做兼容!!

    一.渲染层和逻辑层

    小程序的运行环境分成渲染层和逻辑层,这是其运行环境的基本工作模式。

    1.渲染“Hello World”页面

    hello world案例:

    wxml:
    <view>{{ msg }}</view>
    js:
    Page({
      onLoad: function () {
        this.setData({ msg: 'Hello World' })
      }
    })
    说明:1.渲染层和数据相关。
    2.逻辑层负责产生、处理数据。
    3.逻辑层通过 Page 实例的 setData 方法传递数据到渲染层。
    

    所以接下类讲述第三点描述的通讯模型。

    2.通信模型

    小程序的逻辑层与渲染层由2个线程管理,渲染层使用webview线程渲染,逻辑层采用JsCore线程运行脚本。一个小程序有多个界面,所以存在多个webview线程。下图为通讯模型:


    小程序通讯模型

    图中Native指‘微信客户端’,渲染层与逻辑通通过微信客户端做中转进行通讯。

    3.数据驱动

    数据驱动:将数据与状态相关联,当数据更新时,自动更新视图。
    (1)Hello World 数据渲染示意图:


    数据渲染

    (2)更改数据后,视图更新流程示意图:


    数据更新
    4.双线程下的界面渲染

    (1)wxml转化为对应的js对象
    (2)逻辑层数据更新后,通过setData方法把数据传递到渲染层,
    (3)对比前后js对象差异,更新修改的部分。


    界面渲染过程
    二.程序与页面
    1.程序

    注意: ‘小程序’指的是产品层面的程序,‘程序’指的是代码层面的程序实例。
    程序构造器App():

    // app.js
    App({
      onLaunch: function (options) { },
      onShow: function (options) { },
      onHide: function () { },
      onError: function (msg) { },
      globalData: 'I am global data'
    })
    
    App构造器的参数

    注意: App的生命周期是由微信客户端根据用户操作主动触发的。为了避免程序上的混乱,我们不应该从其他代码里主动调用App实例的生命周期函数。

    上述方法中onLaunch、onShow带参数:


    参数内容

    以下场景支持返回referrerInfo.appId:


    支持返回referrerInfo.appId

    构造器中的其他数据为全局性的数据,其他页面可通过getApp()方法获取到。

    2. 页面:

    1.文件构成与路径:
    页面分三部分: wxml、wxss负责界面;json负责配置;js负责逻辑。
    页面路径在小程序根目录app.json中声明,第一个声明的是首页。

    {
     "pages":[
       "pages/index/page", // 第一项默认为首页
       "pages/other/other"
     ]
    }
    

    2.页面构造器Page():

      data: { text: "This is page data." },
      onLoad: function(options) { },
      onReady: function() { },
      onShow: function() { },
      onHide: function() { },
      onUnload: function() { },
      onPullDownRefresh: function() { },
      onReachBottom: function() { },
      onShareAppMessage: function () { },
      onPageScroll: function() { }
    })
    

    其中,data为数据、前四个方法为声明周期函数、后面四个为页面的用户行为。


    Page构造器的参数

    当使用wx.navigateTo切换到其它页面、底部Tab切换时触发onHide方法;
    当使用wx.redirectTo或者wx.navigateBack返回其他页面时,页面被销毁调用onUnload方法。

    Page构造器传参实例:

    // pages/list/list.js
    // 列表页使用navigateTo跳转到详情页
    wx.navigateTo({ url: 'pages/detail/detail?id=1&other=abc' })
    
    // pages/detail/detail.js
    Page({
      onLoad: function(option) {
            console.log(option.id)
            console.log(option.other)
      }
    })
    

    注意:页面URL上的value如果涉及特殊字符(例如:&字符、?字符、中文字符等,详情参考URI的RFC3986说明 ),需要采用UrlEncode后再拼接到页面URL上。

    4.页面的数据:
    (1)data参数是页面第一次渲染时从逻辑层传递到渲染层的数据。

    <!-- page.wxml -->
    <view>{{text}}</view>
    <view>{{array[0].msg}}</view>
    
    // page.js
    Page({
      data: {
        text: 'init data',
        array: [{msg: '1'}, {msg: '2'}]
      }
    })
    

    (2)使用setData函数传递数据,传递过程中渲染层、逻辑层在两个线程中运行,故该过程是异步的。setData方法中有callback回调,在渲染完成后触发。

    // page.js
    Page({
      onLoad: function(){
        this.setData({
          text: 'change data'
        }, function(){
          // 在这次setData对界面渲染完毕后触发
        })
      }
    })
    

    (3)在通过setData方法重设字段时,不必把所有字段重设,只需把改变的值重设,原则是每次只设置需要改变的最小单位数据。
    注意:
    必须通过this.setData设置数据
    每次设置的数据不应超过1024kb
    不要把任意一项value设置为undefined,

    1. 页面的用户行为:
      (1) 监听用户下拉刷新事件 onPullDownRefresh
      需在app.json的window选项中或者页面配置page.json中设置enablePullDownRefresh为true,
      处理完数据刷新后,可使用wx.stopPullDownRefresh停止页面下拉刷新。
      (2)监听用户上拉触底 onReachBottom
      可在app.json的window选项中或者页面配置page.json中设置触发距离onReachBottomDistance,在触发距离内滑动,该事件只触发一次。
      (3)监听用户滑动页面事件 onPageScroll
      参数为object,包含scrollTop字段,表示页面在垂直方向已经滚动的距离(单位:px)
      (4) 用户转发 onShareAppMessage:
      定义此事件后,右上角显示转发按钮,该事件需要返回一个object,包含 title、path
    // page.js
    Page({
    onShareAppMessage: function () {
     return {
       title: '自定义转发标题',
       path: '/page/user?id=123'
     }
    }
    })
    
    1. 页面跳转和路由
      (1) 页面栈:例子:在多次使用(少于等于10)wx.navigateTo后,页面层级会有多层,行程的页面层级为页面栈。如下图:


      页面栈

    为方便,使用[ pageA, pageB, pageC ]表述页面栈,其中pageA在最下面,pageC在最上面。
    下面使用以下方法表述页面栈的变化:
    wx.navigateTo({ url: 'pageD' }) 页面栈变为 [ pageA, pageB, pageC, pageD ];
    wx.navigateBack() 页面栈变为 [ pageA, pageB, pageC ];
    wx.redirectTo({ url: 'pageE' }) 页面栈变为 [ pageA, pageB, pageE ];
    当页面栈到达10层没法再新增的时候,往往就是使用redirectTo这个API进行页面跳转。

    小程序Tabbar 声明:

    // app.json
    {
      "tabBar": {
        "list": [
          { "text": "Tab1", "pagePath": "pageA" },
          { "text": "Tab1", "pagePath": "pageF" },
          { "text": "Tab1", "pagePath": "pageG" }
        ]
      }
    }
    

    接着上面的页面栈变化:
    wx.switchTab({ url: 'pageF' }) 页面栈变为 [pageF ];(其中除pageA外,其它页面被销毁)。
    wx. reLaunch({ url: 'pageH' })重启小程序,页面栈为 [ pageH ]
    注意:wx.navigateTo和wx.redirectTo只能打开非TabBar页面,wx.switchTab只能打开Tabbar页面。

    (2) 页面路由触发方式及页面生命周期函数的对应关系。


    页面触发方式与声明周期函数

    (3) Tab 切换对应的生命周期(以 A、B 页面为 Tabbar 页面,C 是从 A 页面打开的页面,D 页面是从 C 页面打开的页面为例) 注意Tabbar页面初始化之后不会被销毁。


    页面路由触发方式及页面生命周期函数的对应关系
    三.组件

    1.小程序页面WXML的基本组成单位是:组件。

    1. 小程序的宿主环境提供了一系列基础组件。
      3.组件样式:
      (1) 通常包含开始标签和结束标签,该标签的属性用来描述该组件。
      (2) 所有组件名和属性都是小写,多个单词会以英文横杠 "-" 进行连接。
      (3) 所有组件的公共样式和事件绑定:


      组件公共样式、事件绑定

      (4) 组件都拥有各自自定义的私有属性,可以对该组件的功能或者样式进行修饰。以image为例:

     <image mode="scaleToFill" src="img.png"></image>
    

    (5) 基础组件地址:
    https://mp.weixin.qq.com/debug/wxadoc/dev/component/

    四.API

    1.定义:
    以wx.navigate为例,wx是小程序宿主环境的全局对象。
    所有小程序的API(构造器Page/App除外)都挂载在wx对象下。
    API通指wx对象下的方法。
    2.API按照功能分为网络、媒体、文件、数据缓存、位置、设备、界面、界面节点信息以及开放接口。
    3.API一般调用的约定:
    (1) wx.on开头的API是监听某个事件发生的API接口,接受一个callback回调,该事件触发时,回调。
    (2) 多数API为异步接口、接受一个object参数
    (3) API的Object参数一般由success、fail、complete三个回调来接收接口调用结果.

    API接口参数回调
    (4) wx.get
    开头的API:获取宿主环境数据
    (5). wx.set*开头的API:写入宿主环境数据
    wx.request({
    url: 'test.php',
    data: {},
    header: { 'content-type': 'application/json' },
    success: function(res) {
     // 收到https服务成功后返回
     console.log(res.data)
    },
    fail: function() {
     // 发生网络错误等情况触发
    },
    complete: function() {
     // 成功或者失败后触发
    }
    })
    

    详细了解API文档地址: https://mp.weixin.qq.com/debug/wxadoc/dev/api/

    五.事件:
    1.定义:

    用户在渲染层的行为反馈或者组件的部分状态反馈(播放的进度条)。
    渲染层传递给逻辑层的事件。


    事件触发过程
    2.事件类型与事件对象:

    1.组件的部分状态反馈的事件可参考https://mp.weixin.qq.com/debug/wxadoc/dev/component/

    2.常见的事件类型:


    常见事件类型

    以上事件都是冒泡事件,除此之外都不是。

    3.事件触发后的事件对象属性:


    事件对象属性

    4.事件对象属性中target和currentTarget详情:


    target和currentTarget

    两者区别:currentTarget为当前事件所绑定的组件,而target则是触发该事件的源头组件

    <!-- page.wxml -->
    <view id="outer" catchtap="handleTap">
      <view id="inner">点击我</view>
    </view>
    
    // page.js
    Page({
      handleTap: function(evt) {
           // 当点击inner节点时
        // evt.target 是inner view组件
           // evt.currentTarget 是绑定了handleTap的outer view组件
           // evt.type == “tap”
           // evt.timeStamp == 1542
           // evt.detail == {x: 270, y: 63}
           // evt.touches == [{identifier: 0, pageX: 270, pageY: 63, clientX: 270, clientY: 63}]
           // evt.changedTouches == [{identifier: 0, pageX: 270, pageY: 63, clientX: 270, clientY: 63}]
      }
    })
    

    5.事件对象属性中touch和changedTouches详情:


    touch和changedTouches
    3.事件绑定与冒泡捕获:

    1.写法:
    key="value"
    自基础库版本1.5.0起,key 以bind或catch开头,接:,如: bind:tap、catch:tap、
    capture-bind:tap、capture-catch-tap
    value为字符串
    2.功用:
    bind和capture-bind的含义分别代表事件的冒泡阶段和捕获阶段;
    触发的顺序:


    捕获与冒泡触发顺序

    描述: 捕获为从外到内,冒泡为从内到外,先捕获后冒泡

    <view id="outer" bind:tap="handleTap4" capture-bind:tap="handleTap1">
      outer view
      <view id="inner" bind:tap="handleTap3" capture-bind:tap="handleTap2">
        inner view
      </view>
    </view>
    // 点击 inner  view 事件触发顺序为: handleTap1、handleTap2、handleTap3、handleTap4
    

    3.bind事件绑定不会阻止冒泡事件向上冒泡,catch事件绑定可以阻止冒泡事件向上冒泡

    <view id="outer" bind:tap="handleTap4" capture-catch:tap="handleTap1">
      outer view
      <view id="inner" bind:tap="handleTap3" capture-bind:tap="handleTap2">
        inner view
      </view>
    </view>
    只会触发handleTap1,catch事件阻止了tap事件冒泡
    
    六. 兼容:

    1.影响微信小程序的实现的几个因素:
    手机品牌/操作系统/微信版本/小程序基础库版本号
    2.可通过 wx.getSystemInfo 或者 wx.getSystemInfoSync 获取小程序使用环境信息

    wx.getSystemInfoSync()
    /*
      {
        brand: "iPhone",      // 手机品牌
        model: "iPhone 6",    // 手机型号
        platform: "ios",      // 客户端平台
        system: "iOS 9.3.4",  // 操作系统版本
        version: "6.5.23",    // 微信版本号
        SDKVersion: "1.7.0",  // 小程序基础库版本
        language: "zh_CN",    // 微信设置的语言
        pixelRatio: 2,        // 设备像素比
        screenWidth: 667,    // 屏幕宽度
        screenHeight: 375,     // 屏幕高度
        windowWidth: 667,    // 可使用窗口宽度
        windowHeight: 375,     // 可使用窗口高度
        fontSizeSetting: 16   // 用户字体大小设置
      }
     */
    

    3.判断API是否兼容:

    if (wx.openBluetoothAdapter) {
      wx.openBluetoothAdapter()
    } else {
      // 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
      wx.showModal({
        title: '提示',
        content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
      })
    }
    

    4.wx.canIUse
    作用:用于判断接口或者组件在当前宿主环境是否可用
    参数格式为: API.method.param.options或者component.attribute.option
    (1).API 代表 API 名字
    (2).method 代表调用方式,有效值为return, success, object, callback
    (3).param 代表参数或者返回值
    (4).options 代表参数的可选值

    // 判断接口及其参数在宿主环境是否可用
    wx.canIUse('openBluetoothAdapter')
    wx.canIUse('getSystemInfoSync.return.screenWidth')
    wx.canIUse('getSystemInfo.success.screenWidth')
    wx.canIUse('showToast.object.image')
    wx.canIUse('onCompassChange.callback.direction')
    wx.canIUse('request.object.method.GET')
    

    component 代表组件名字
    attribute 代表组件属性
    option 代表组件属性的可选值

    // 判断组件及其属性在宿主环境是否可用
    wx.canIUse('contact-button')
    wx.canIUse('text.selectable')
    wx.canIUse('button.open-type.contact')
    

    5.兼容办法
    做向前兼容,保证小程序在旧版微信中的运行
    在小程序管理后台设置“基础库最低版本设置”。例子:
    当小程序只支持1.5.0版本以上的宿主环境时,运行低版本时,微信会提示版本过低之类的。

    相关文章

      网友评论

          本文标题:2019-02-22 微信小程序·开发指南

          本文链接:https://www.haomeiwen.com/subject/vfsyyqtx.html