七. 微信小程序 [简单主要流程, 详细参考官方文档]

523 阅读12分钟

1.开发前的准备工作

1.1 申请AppID

  • 登录微信公众平台: mp.weixin.qq.com
  • 进入之后,点击右下角'小程序', 之后下滑进行注册
  • 注册完毕后进行登录. 点击右侧'开发', 之后点击上边的'开发设置'
  • 下边就可以看到AppID 和 AppSecret

1.2 开发工具

  • 官方微信Web开发者工具, VSCode,Hbuilder
  • 右上角'文档'--->点击'工具'--->'微信开发者工具'--->下载稳定版
  • 新建项目 选择一个空的文件夹--->完成GitHub版本管理

1.3小程序码

  • 右上角的'工具'
  • 输入已经上线小程序的AppID, 方可获取小程序码

2. 基础

2.1应程序结构

  • 最上层app-> 多个Page页面 -> 多个组件component
  • app层有 app.js, app.wxss, app.json
  • page层有 .js, .json, .wxss, .wxml
  • component有 .js, .json, .wxss, .wxml

2.2 数据绑定

  • wxml中用插值语法{{ name }}
  • js中 data:{ name: '张三' }

2.3 列表渲染

  • <view wx:for="{{ datalist }}"> {{item}} </view>

2.4 事件监听

  • <button bindtap=' handleAdd'> 按钮 </button>
  • 在.js中
    handleAdd() {  
        this.setData({name: '李四'})
    } 
    

2.5 配置小程序

2.6 小程序的双线程模型

  • webView执行wxml和wxss
  • jsCore执行js代码

3. 小程序的App()和Page()

3.1 小程序的启动流程

  • 下载小程序包-->启动小程序-->
  • 解析app.json-->执行app.js来注册App()-->会执行App()内部的周期函数-->
  • 加载自定义组件-->注册自定义组件-->
  • 加载各个页面-->解析个页面的.json-->
  • 渲染层webView加载 .wxml-->逻辑层.js注册Page()函数-->执行Page()内部的生命周期函数

3.2 在App()中可能做的事情

  • 监听生命周期函数
  • 判断小程序的进入场景
  • 定义全局变量( App()是单例对象 )

3.3 在Page()中可能做的事情

  • 监听生命周期函数
  • 初始化数据
  • 监听wxml页面中的事件
  • 监听一些其他事件:
    • 页面滚动onPageScroll()
    • 监听页面滚动到底部onReachBottom() 下载加载
    • 上拉刷新 onPullDownRefresh()
  • 生命周期图 生命周期

4. 小程序的组件

4.1 Text组件

  • 类似于span标签,是行内元素
  • selectable属性:是否可被选中
  • space属性: 显示连续空格, nbsp默认的空格 emsp中文空格 ensp半个中文的大小
  • decode属性: 会把文本中的 &gt; 解码成 大于号 >

4.2 Button组件

  • size属性: 大小
  • type属性: 按钮颜色
  • plain属性: 镂空效果
  • disabled属性: 是否禁用
  • loading属性:
  • hover-class属性: 按钮按下的样式
  • open-type属性:

4.3 view 组件

  • hover-class属性: 按下时的样式
  • hover-stop-propagation: 阻止冒泡
  • hover-start-time: 按下后多久出现点击状态
  • hover-stay-time: 手指所开后状态保留时间长短

4.4 image组件

  • 可以写成单标签,也可以写成单标签
  • 默认有自己的大小,320*240
  • 它是行内块元素
  • src属性: 本地地址/远程地址
  • lazy-load属性: 图片懒加载
  • bindload事件,图片加载完成自动触发的事件
  • show-menu-by-longpress
  • mode缩放模式

4.5 input组件

  • value属性
  • type属性: 决定键盘类型
  • password属性
  • placeholder属性
  • disabled属性
  • maxlength属性: 最大长度
  • confirm-type属性: 设置键盘右下角的文字
  • bindinput事件: 正在输入
  • bindfocus事件: 获得焦点
  • bindblur事件: 失去焦点

4.6 scroll-view组件

  • scroll-x属性: 水平滚动
  • scroll-y属性: 垂直滚动
  • bindscroll事件: 发生滚动时自动触发的事件,可以打印事件详情

4.7 共同属性

  • id
  • class
  • style 设置内联样式
  • hidden 隐藏
  • data-* 自定义属性
  • bind* 和catch 事件

5. 小程序的样式

5.1 页面的三种样式

  • 内联样式
  • 页面样式
  • 全局样式

5.2 支持的选择器

  • id class 元素

5.3 尺寸单位

  • 750rpx = 屏幕宽度(iPhone6宽度为375px), 则1rpx等于0.5px
  • iPhone5是1rpx= 0.42px
  • iPhone6 Plus是 1rpx= 0.552px
  • width: calc(750rpx * 100 / 375); 在宽度为375个px的屏幕中,设置某个元素的width值为100px

5.4 样式导入

  • 在页面文件夹下新建style文件夹,于其中写入.wxss文件. 在页面中 @import ' ./style/box1.wxss ';
  • 也可全局新建style文件夹, 然后在app.wxss文件夹中导入

5.5 官方样式库

6. 语法

6.1 wxml基本格式

  • 类似于Html代码
  • 必须严格闭合标签
  • 大小写敏感: (如class和Class是不同的属性)

6.2 Mustache语法{{}}

  • 绑定变量
  • 支持字符串拼接
  • 三目运算
  • 绑定属性也使用mustache

6.3 条件和hidden

  • wx:if="{{ score>=90 }}"
  • wx:elif="{{ score>= 60 }}"
  • wx:else
  • hidden属性:显隐

6.4 循环

遍历数组 <view wx:for="{{ [a,v,f,o,e,d] }}"> {{item}} {{index}} </view>
遍历字符串 <view wx:for="aweyurtpasdjhf"> {{item}} {{index}} </view>
遍历数字 <view wx:for="{{ 13 }}"> {{item}} {{index}} </view>
  • block临时标签
    • 最终不会渲染出来的, 类似于vue的template标签
    • 一般接受控制属性, wx:for, wx:if等等.
    • class, id 等属性不要在block标签中写
  • 给item和index 起名字
<view wx:for="{{movies}}" wx:for-item="m" wx:for-index="i"> {{m}}  {{i}}</view> 
  • 循环中加: wx:key="{{index}}",key主要作用是高效更新DOM, 最好用id作为key的

6.5 模板语法 template标签

<template name="content">
    <button> 按钮 </button>
    <button> {{ btn_text }}</button>
    <view>hello world </view>
</template>
模板包裹的内容在没有使用前,是不会进行任何的渲染的. 
模板定义完,就是让我们用的
如下是使用的过程:
<template is="contentItem" data="{{btn_text: value2}}"/>
于.js文件中data内 设置value2的值
  • wxml引入模板
    <import src="模板文件的路径" /> `
    不支持循环导入,乙里导入甲,丙里导入乙. 丙中无法使用甲.
    include可以将目标文件中除了 <template/>和<wxs/>外的整个代码引入, 相当于拷贝到include位置. include支持循环导入.
    include模式非常简单,就是简单的代码替换.不存在作用域,也不像template需要data传递变量. 它只是简单的将代码拿出去,用的时候再拿回来. 作用是单纯的简化页面.

7. wxs模块

7.1 基础

  • wxs(WeiXin Script) 是小程序的一套脚本语言,结合wxml,可以构建出页面结构
  • WXS与JavaScript是不同的语言,有自己的语法,并不和JavaScript完全一致.
  • wxml中是不能直接调用Page/Component中定义的函数的

7.2 wxs的限制和特点

  • wxs的运行环境和其他JavaScript代码是隔离的,wxs中不能调用其他JavaScript文件中定义的函数,也不能调用小程序提供的API
  • WXS函数不能作为组件的事件回调
  • 用处: 当我们在页面中有些数据需要处理,而我们.js文件中的函数是不能对他进行处理的. 这时我们就可以用wxs来进行处理了

7.3 wxs如何定义使用

<wxs module="info">
    //js代码
    var name = 'zhangsan'
    var sum = function(a1,a2){
        return a1 + a2
    }
    
    // commonjs的模块化导出的写法
    module.exports = {
        name: name,
        sum: sum,
    }
</wxs>


<view> {{info.message}} </view>

定义在单独的.wxs文件中,再使用<wxs src="">标签进行导入,不能使用绝对路径

8. 事件

8.1 常见公共事件

  • bindtap=" " 或者 bind:tap="" 点击事件
  • catchtap=" " 或者 catch:tap="" 点击事件
  • touchstart 开始触摸
  • touchmove 开始移动
  • touchcancel 触摸突然被打断 (如突然出现弹窗,突然来电)
  • touchend 触摸结束
  • longpress 长按事件 350ms以上

8.2 某些组件会有自己的事件

  • 如<input>有 bindinput/bindblur/bindfocus
  • 如<scroll-view>有 bindscrolltowpper/bindscrolltolower

8.3 事件对象的分析

属性类型说明备注
typeString对事件类型的描述我的备注
timeStamp属性Integer页面打开到触发事件 所经历的时间
target属性Object产生事件的组件, 其一些属性的集合
currentTargetObject响应事件的组件, 其一些属性集合
detailObject额外的信息
touchesArray触摸事件, 停留在屏幕中的触摸点数组
changedTouchesArray触摸事件,记录增加(或减少)的触摸点的数组

8.4 事件的传递参数

  • data-index2="{{index}}"
  • 从data.currentTarget.setdata.index2来取这个值

8.5 事件冒泡和捕获

  • capture-bind:tap 监听事件捕获
  • bind:tap 监听事件冒泡
  • capture-catch:tap 在事件捕获阶段,阻止向内传递
  • catch:tap 在事件冒泡阶段,阻止向外传递
  • (以上冒号可以省略)

9. 组件化开发

9.1 基本使用

  • 定义组件 新建components文件夹, 内部建子文件夹,下设四个文件
  • 在自定义组件中.json文件下,component: true. 只有该字段为true才能是一个组件
  • 引用处: "usingComponents": {"起标签名": "/组件路径"}
  • 标签名只能是小写字母,数字,中划线和下划线
  • 一个自定义组件可以引入另一个自定义组件
  • 自定义组件起名时不要以wx-作为前缀
  • 很多页面要引入的话, 可以在app.json文件中,将其注册成全局组件
  • template模板只有wxml和wxss两个部分,而且两个部分要分别引入
  • 自定义组件由.json .js .wxml .wxss 组成,不需要分别引入

9.2 组件里的样式

  • 组件里边定义的样式,不会对引用它的页面 造成影响, 同样页面的样式也不会影响组件
  • 在组件里边,不能使用id选择器,属性选择器,标签选择器
  • 如果想相互影响,在组件.js文件下,options:{ styleIsolation: "isolated" }
    • "isolated"是默认值,是隔离的.
    • "apply-share"这个是不隔离,页面样式可以影响组件
    • "share" 这个值是不隔离,组件也可以影响页面,页面也可以影像组件

9.3页面给组件传值

(1)页面给组件传数据
于组件的.js文件中properties: {name3: String}
或者properties: { 
    name3: {
        type:String, 
        value: "默认值", 
        observer: function(new, old) {} 
    }
}
于页面引用处的标签加name3属性,并赋值

(2)页面给组件传样式
在组件的.wxml文件中使用box1这个样式
在组件的.js文件中externalClass: ["box1","box2","box3"]
在父页面引用处: box1="box1class"
在父页面引用的.wxss文件中编写.boxclass{ color: #f00 }

(3)页面给组件传标签, slot插槽
单个插槽
在组件中用<slot/>
在引用的页面<自定义组件> 此处写入,想传给组件的HTML结构 </自定义组件>

多个插槽: 
在组件.js文件中 options: { multipleSlots: true }
在组件中用<slot name='slot1'/>
在引用的页面<自定义组件 slot="slot1"> 此处写入,想传给组件的HTML结构 </自定义组件>

9.4 组件给页面传值

组件给页面传事件:
在组件中
methods: {
    handleEvent() { this.triggerEvent('myEvent',{数据},{options})}
}
注: handleEvent是自己随便起的名字. 这个名绑定到组件的.wxml中
注: myEvent是自己随便起,这是传递到外边页面的事件名.

页面中 <自定义组件 bind:myEvent="handleEvent3" />
在页面.js文件中实现handleEvent3事件
如果子组件在事件中传递了数据,在页面.js文件handleEvent3事件中打印event,打印后的detail中就可以拿到此数据

9.5 页面通过组件的引用,修改组件内部的值

获取组件对象
在父页面的.js文件中用 this.selectComponent('.class/#id')来获取组件对象. 然后通过组件对象来更改这个组件内部的值.
最好通过调用组件内部的方法来更改组件内部的值.

9.6 Component构造器

(1)内部基本内容
Component{
    properties:
    data:
    methods:
    options:
    externalClasses: ['box1','box2']
    observers: {} 监听data和properties改变,只能拿到newValue
    生命周期
}

(2)组件监听页面的生命周期
pageLifetimes: {
    show(){},组件所在页面显示出来
    hide(){},组件所在页面隐藏起来
    resize(){},组件所在页面大小改变
}

(3)组件监听本身的生命周期
lefttimes: {
    created() {},组件创建出来
    attached() {},添加到页面
    ready() {}, 组件被渲染出来
    moved() {},组件移动到另一个节点内
    detached() {},组件被页面移除
}

10. 小程序的API

10.1 网络请求

  • 先配置合法域名列表: 小程序后台-->设置--->开发设置-->服务器域名
测试阶段: "详情"---->"不校验合法域名....."
可以用这个网址http://httpbin.org/ 来模仿各种请求的方式
wx.request({
    url: 'http://......:8080/index',
    success: (res) => console.log(res),
    data: {发网络请求时要传的参数},
    method: post / get,   // post请求参数不允许拼接到url上边
    header: 设置请求头..比如访问某些网页,需要往header中加上token才可以访问,
    dataType: 返回数据的格式,
    responseType: 相应的数据类型,
    fail: 失败回调,
    complete: 完成时就会回调
})

10.2 封装网络请求

(1)跟目录新建service文件夹,内部新建一个network.js文件:
retutn new Promise( (resolve,reject) => {
    wx.request({
        url: options.url,
        method: options.method || 'get'
        data: optionss.data || {},
        success: res => resolve(res)
        fail: err => reject(err)
    })
} )

(2)使用处导入这个文件即可 import request from '../../service/network.js'
使用自定义的函数: request({ url:"", data: "",  ...}).then(res=>console.log(res,"获取
成功回调").catch(err=>console.log(err,"获取失败回调")
这种链式方式不会出现回调地狱.

10.3 展示弹窗

(1)
wx.showToast({
    title: '你好',
    duration: 时间,
    icon: 图标,
    mask: true, 蒙版
})
(2)
wx.showModal({})
(3)
wx.showLoading
(4)
wx.showactionSheet({})

10.4 页面分享

  • 有这个函数就可以分享: onShareAppMessage: options => { title: 转发标题, path: 转发路径, imageUrl: 转发时的封面,默认是本页截图 }
  • <button size='mini' open-type="share">分享1</button> 此时这个按钮也有分享的功能

10.5 小程序的登录流程

  • 49 ---> 图
  • wx.login() 从微信的服务器请求一个code
  • 开发者服务器通过一个接口把 code + AppID + AppSecret 传给微信服务器
  • 这时微信服务器会给我们传回来两个东西, session_key和openid
  • 这时就可以把openid当成用户的唯一标识
  • 可以设置自己的登录方式: 账号+密码
  • 把openID + 账号 + 密码 存到表中, 生成token令牌传递给用户. 小程序里边有个storage, 把令牌保存到storage中.
App({ })同级: const TOKEN = 'token', 定义字符串常量
onLaunch() {
    // 先从缓存中取出token
    const token = wx.getStorageSync(TOKEN)
    if (token && token.length !== 0 ){
        // 验证token是否过期
        this.check_token(token)
    }
    else{
        this.login3()
    }
},

// 自定义登录函数
login3() {
    wx.login({
        success: (res) => {
            // 1. 获取code
            const code = res.code;
            // 2.向自己的服务器发送请求
            wx.request({
              url: '...',
                method: 'post',
                data: { code },
                success: (res) => {
                    // 取出token
                    const token = res.data.token
                    // 将token保存在globalData中
                    this.globalData.token = token
                    // 同时进行本地存储
                    wx.setStorageSync(TOKEN,token)
                }
            })
        }
    })
}
// 自定义验证token是否过期的函数
check_token(token) {
    wx.request({
        url: '',
        method: 'post',
        header: {
            token
        },
        success: (res) => {
            if (!res.data.errCode) {
                this.globalData.token = token
            }
            else {
                this.login
            }
        },
        fail: err => {
            
        }
    })
}

10.6 页面跳转

  • navigator组件实现页面跳转
(1)
<navigator url='./...'>跳到某页</navigator>
open-type属性: 
    navigate默认值,保留当前页跳转
    redirect关闭当前页面跳到非tabbar页面,
    switchTab跳到tabbar页面
    reLaunch 关闭所有页面之后跳转
    navigateBack 自定义返回
delta属性: 2返回两个层级
(2) 页面跳转时传数据
    在url后边 ?name=lisi&age=18
    在目标页面onLoad(options) { console.log(options) }
(3) 目标页面往回传数据
    onUnload() {
        const pages = getCurrentPages()
        const home = pages[pages.length - 2]
        this.setData({age: 30})
    }
  • 通过事件接口实现页面跳转
    • wx.navigate
    • wx.redirectTo
    • wx.switchTab
    • wx.reLaunch
    • wx.navigateBack