微信小程序 从入门到实践开发

微信小程序介绍

小程序是一种新的开放能力,开发者可以快速地开发一个小程序。
小程序可以在微信内被便捷地获取和传播,同时具有出色的使用体验。

微信小程序的特点:免安装、操作更接近原生 APP、必须在微信中使用
开放注册范围:个人、企业、政府、媒体、其他组织。

微信小程序和微信的原生功能应用在本质上是一样的 —— 都是 Web App。 Web App 就是一种通过 H5 页面技术实现的,和 Native App 的功能和界面几乎一样的手机 App 形态。

微信小程序的优势

1、微信有海量用户,而且粘性很高,在微信里开发产品更容易触达用户;
2、推广 App 或 公众号的成本太高。
3、开发适配成本低。
4、容易小规模试错,然后快速迭代。
5、跨平台。

微信小程序对于创业者的优势

1、App 流量成本的急剧攀上
2、移动互联网格局基本已定,用户主要需求场景已被巨头把持
3、面向所有产品对用户时间的竞争

开发支持:

小程序宣传方式

  • 小程序搜索入口 & 附近的小程序
  • 扫一扫、长按识别小程序码
  • 好友分享、群分享
  • 关联公众号
  • 第三方的小程序应用商店
  • 小程序之间相互跳转

官方文档介绍

API
指南
组件
工具

小程序开发流程

  1. 注册
    在微信公众平台注册小程序,完成注册后可以同步进行信息完善和开发。

  2. 小程序信息完善
    填写小程序基本信息,包括名称、头像、介绍及服务范围等。

  3. 开发小程序
    完成小程序开发者绑定、开发信息配置后,开发者可下载开发者工具、参考开发文档进行小程序的开发和调试。

  4. 提交审核和发布
    完成小程序开发后,提交代码至微信团队审核,审核通过后即可发布(公测期间不能发布)。

微信小程序注册

  • 通过公众号注册(避免了重复认证)(暂不支持个人类型公众号复用资质创建小程序)。
  • 进入微信小程序官网注册。(访问注册页面,耐心完成注册即可)

注册注意事项

  • 个人主体无法完成支付,卡包,搜索附近小程序功能。
  • 使用邮箱进行注册时一个邮箱仅能申请一个小程序。
  • 邮箱不能使用注册过公众号、开发平台、企业号及绑定过个人号的邮箱。
  • 进行资料完善时保证信息准确性,主体信息一旦填写无法进行修改。
  • 上传企业基本资料时需要签名加盖公章,保证图片清晰度,否则导致审核不通过。
  • 已经申请微信公众号的企业可直接在首页中点击小程序进入下一步。

微信开发者工具使用

新建项目

当符合以下条件时,可以在本地创建一个小程序项目

  1. 需要一个小程序的 AppID;如没有 AppID,可以选择申请使用测试号。
  2. 登录的微信号需要是该 AppID 的开发者;
  3. 需要选择一个空目录,或者选择的非空目录下存在 app.json 或者 project.config.json。当选择空目录时,可以选择是否在该目录下生成一个简单的项目。

多开项目

工具支持同时打开多个项目,每次打开项目时会从新窗口打开,入口有以下几种:

  1. 从项目选择页打开项目,处于项目窗口时可以从菜单栏的项目 -> 查看所有项目打开项目选择页
  2. 从菜单栏的最近打开项目列表中打开的项目会从新窗口打开
  3. 新建项目
  4. 命令行或 HTTP 调用工具打开项目

管理项目

对本地项目进行删除和批量删除。

主界面

开发者工具主界面,从上到下,从左到右,分别为:菜单栏、工具栏、模拟器、目录树、编辑区、调试器 六大部分。
主界面

菜单栏

微信 web 开发者工具

  • 切换帐号
  • 关于
  • 检查更新
  • 开发者论坛
  • 开发者文档
  • 调试
  • 更换开发模式:快速切换公众号网页调试和小程序调试
  • 推出

项目

  • 新建项目
  • 打开最近
  • 查看所有项目
  • 关闭当前项目

文件

  • 新建文件
  • 保存
  • 保存所有
  • 关闭文件

编辑:可以查看编辑相关的操作和快捷键

工具

  • 编译:编译当前小程序项目
  • 刷新:与编译的功能一致,由于历史原因保留对应的快捷键 ctrl(⌘) + R
  • 编译配置:可以选择普通编译或自定义编译条件
  • 前后台切换:模拟客户端小程序进入后台运行和返回前台的操作
  • 清除缓存:清除文件缓存、数据缓存、以及授权数据

界面:控制主界面窗口模块的显示与隐藏

设置:

  • 外观设置:控制编辑器的配色主题、字体、字号、行距
  • 编辑设置:控制文件保存的行为,编辑器的表现
  • 代理设置:选择直连网络、系统代理和手动设置代理
  • 通知设置:设置是否接受某种类型的通知

工具栏

点击用户头像可以打开个人中心,在这里可以便捷的切换用户和查看开发者工具收到的消息。
工具栏中间,可以选择普通编译,也可以新建并选择自定义条件进行编译和预览。
通过切后台按钮,可以模拟小程序进入后台的情况
工具栏上提供了清缓存的快速入口。可以便捷的清除工具上的文件缓存、数据缓存、还有后台的授权数据,方便开发者调试。
工具栏右侧是开发辅助功能的区域,在这里可以上传代码、版本管理、查看项目详情

模拟器

在模拟器底部的状态栏,可以直观地看到当前运行小程序的场景值,页面路径及页面参数

独立窗口

点击 模拟器/调试器 右上角的按钮可以使用独立窗口显示 模拟器/调试器

项目页卡

详情

  • 基本信息
    包括图标、AppID、第三方平台名(只有第三方平台的开发小程序才会显示)、目录信息、上次提交代码的时间以及代码包大小。
  • 本地设置
    开发者可以在此选择任意基础库版本,用于开发和调试旧版本兼容问题。
  • 项目配置
项目设置

代码保护

主要是对文件进行扁平化处理并替换 require 引用的文件名,以下情况不适合使用此功能

  1. 对于小程序只有简单页面的情况下,开启此功能效果不佳
  2. 有文件超过 500kb,且其中有使用 require 引用项目中的文件的情况,在运行时可能会报文件没有找到
  3. 动态引用的情况,如 var a = ‘somefile.js’; require(a);
  4. 将 require 函数赋值给其他变量的情况,如 var a = require; a(‘somefile.js’);
  5. 将 require 作为二元运算符的参数的情况,如 require + 1; 6.使用 … 运算符且未开启 ES6 转 ES5 的情况

不校验请求域名及 TLS 版本

域名信息
详情/项目配置
将显示小程序的安全域名信息,合法域名可在 mp 管理后台进行设置。

设置

外观设置
  • 主题
  • 调试器主题
  • 字体
  • 字号
  • 行距
  • 模拟器位置
编辑设置
  • 文件保存
  • 编辑器
  • Tab 大小
代理设置

可以配置不使用代理,或使用系统代理,或使用自定义代理。

安全设置

可以开启和关闭 CLI/HTTP 调用功能

代码编辑

文件格式

因 iOS 下仅支持 UTF8 编码格式,最新版本的开发者工具会在上传代码时候对代码文件做一次编码格式校验。

文件支持

工具目前提供了 5 种文件的编辑:wxml、wxss、js、json、wxs 以及图片文件的预览。

文件操作

新建页面有两种方式

  1. 在目录树上右键,选择新建 Page,将自动生成页面所需要的 wxml、wxss、js、json
  2. 在 app.json 的 pages 字段,添加需要新建的页面的路径,将会自动生成该页面所需要的文件

编译模式

小程序项目

代码结构

  1. .json 后缀的 JSON 配置文件

    project.config:项目配置

    根目录下的 app.json
    是当前小程序的全局配置,包括了小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等。
    pages 字段 —— 用于描述当前小程序所有页面路径,这是为了让微信客户端知道当前你的小程序页面定义在哪个目录。
    window 字段 —— 定义小程序所有页面的顶部背景颜色,文字颜色定义等。

    工具配置 project.config.json

    页面配置 page.json

    JSON 注意事项
    JSON 文件都是被包裹在一个大括号中 {},通过 key-value 的方式来表达数据。JSON 的 Key 必须包裹在一个双引号中。
    JSON 文件中无法使用注释,试图添加注释将会引发报错

    JSON 的值只能是以下几种数据格式,其他任何格式都会触发报错,例如 JavaScript 中的 undefined。
    数字,包含浮点数和整数
    字符串,需要包裹在双引号中
    Bool 值,true 或者 false
    数组,需要包裹在方括号中 []
    对象,需要包裹在大括号中 {}
    Null

  2. .wxml 后缀的 WXML 模板文件
    wxml

  3. .wxss 后缀的 WXSS 样式文件
    新增了尺寸单位。
    提供了全局的样式和局部样式。

    app.wxss 作为全局样式,会作用于当前小程序的所有页面,
    局部页面样式 page.wxss 仅对当前页面生效。
    wxss

    引入外部 wxss:@import ‘./test_0.wxss’

  4. .js 后缀的 JS 脚本逻辑文件
    WXML-事件
    小程序的 API
    bind 和 catch ,catch 会阻止事件向上冒泡。

页面

小程序文件结构和传统 Web 对比

结构 传统 web 微信小程序
结构 HTML WXML
样式 CSS WXSS
逻辑 JavaScript JavaScript
配置 JSON

小程序宿主环境

微信客户端给小程序所提供的环境为宿主环境。

渲染层和逻辑层

小程序的运行环境分成渲染层和逻辑层,其中 WXML 模板和 WXSS 样式工作在渲染层,JS 脚本工作在逻辑层。

小程序的渲染层和逻辑层分别由 2 个线程管理:
渲染层的界面使用了 WebView 进行渲染;逻辑层采用 JsCore 线程运行 JS 脚本。
一个小程序存在多个界面,所以渲染层存在多个 WebView 线程,
这两个线程的通信会经由微信客户端(下文中也会采用 Native 来代指微信客户端)做中转,逻辑层发送网络请求也经由 Native 转发。

小程序的通信模型

有关渲染层和逻辑层的详细文档参考:小程序框架

程序与页面

微信客户端在打开小程序之前,会把整个小程序的代码包下载到本地。
紧接着通过 app.json 的 pages 字段就可以知道你当前小程序的所有页面路径:
于是微信客户端就把首页的代码装载进来,通过小程序底层的一些机制,就可以渲染出这个首页。
小程序启动之后,在 app.js 定义的 App 实例的 onLaunch 回调会被执行:
整个小程序只有一个 App 实例,是全部页面共享的,更多的事件回调参考文档 注册程序 App
页面的事件回调参考文档

组件

API

要获取用户的地理位置时,只需要:

1
2
3
4
5
6
7
wx.getLocation({
type: "wgs84",
success: res => {
var latitude = res.latitude; // 纬度
var longitude = res.longitude; // 经度
}
});

调用微信扫一扫能力,只需要:

1
2
3
4
5
wx.scanCode({
success: res => {
console.log(res);
}
});

小程序配置

全局配置
常用配置项
pages 页面路径列表
window 全局的默认窗口表现
tabBar 底部 tab 栏的表现
networkTimeout 网络超时时间
debug 是否开启 debug 模式,默认关闭

页面配置
页面中配置项会覆盖 app.json 的 window 中相同的配置项。

页面常用配置项
navigationBarBackgroundColor 导航栏背景颜色
navigationBarTextStyle 导航栏标题颜色,仅支持 black / white
navigationBarTitleText 导航栏标题文字内容
backgroundColor 窗口的背景色
backgroundTextStyle 下拉 loading 的样式,仅支持 dark / light

注意:配置文件中不能出现注释。

视图层

基础组件

数据绑定

1
<view>{{message}}</view>
1
2
3
4
5
Page({
data: {
message: 'Hello MINA!'
}
})

组件属性

1
<view id="item-{{id}}"> </view>
1
2
3
4
5
Page({
data: {
id: 0
}
})

三元运算

1
<view hidden="{{flag ? true : false}}"> Hidden </view>

算数运算

1
<view> {{a + b}} + {{c}} + d </view>
1
2
3
4
5
6
7
Page({
data: {
a: 1,
b: 2,
c: 3
}
})

字符串运算

1
<view>{{"hello" + name}}</view>
1
2
3
4
5
Page({
data:{
name: 'MINA'
}
})

逻辑判断

1
2
3
4
5
6
7
8
9
10
11
<view wx:if="{{length > 5}}"> </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view

<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>

<view wx:hidden = "{{length > 5}}">
</view>

列表渲染

1
2
3
<view wx:for="{{array}}" wx:key="index">
{{index}}: {{item.message}}
</view>
1
2
3
4
5
6
7
8
9
Page({
data: {
array: [{
message: 'foo',
}, {
message: 'bar'
}]
}
})
1
2
3
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
{{idx}}: {{itemName.message}}
</view>

block wx:for

1
2
3
4
<block wx:for="{{[1, 2, 3]}}">
<view> {{index}}: </view>
<view> {{item}} </view>
</block>

尺寸单位 rpx

样式导入

1
2
3
4
@import "common.wxss";
.middle-p {
padding:15px;
}

样式其他补充

1
2
<view style="color:{{color}};" />
<view class="normal_view" />

基本组件

view hover-class=””
text selecttable decode
image 默认宽 320px 高 240px
swiper 轮播图

样式设置 给 swiper 一个样式,给 swiper-item 内容器一个样式

1
2
3
4
5
6
7
8
9
10
<view class="page-section page-section-spacing swiper">
<swiper indicator-dots="{{indicatorDots}}"
autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}">
<block wx:for="{{background}}" wx:key="*this"> // *this 表示是 循环项 例如:[1,2,3]、['1','2','3']
<swiper-item>
<view class="swiper-item {{item}}"></view>
</swiper-item>
</block>
</swiper>
</view>

navigator 导航

1
2
3
4
5
6
<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>

video 视频

1
2
<video id="myVideo" src="http://wxsnsdy.tc.qq.com/105/20210/snsdyvideodownload?filekey=30280201010421301f0201690402534804102ca905ce620b1241b726bc41dcff44e00204012882540400&bizid=1023&hy=SH&fileparam=302c020101042530230204136ffd93020457e3c4ff02024ef202031e8d7f02030f42400204045a320a0201000400" danmu-list="{{danmuList}}" enable-danmu danmu-btn controls>
</video>

scroll-view 可滚动视图区域

bind:tap=”hanleTap”
catch:tap=”hanleTap”
mut-bind:tap=”hanleTap”
data-index=
data-post-id-current
e.target.dataset.index
e.target.dataset.postIdCurrent

自定义组件 components

1
2
3
4
5
6
7
<view class="my_header">
{{cData}}
<view>
<slot name="before"></slot>
<slot name="after"></slot>
</view>
</view>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Component({
options: {
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
properties: {
// 这里定义了innerText属性,属性值可以在组件使用时指定
innerText: {
type: String,
value: 'default value',
},
text: Number
},
externalClasses: ['f-class'] // 外部样式类
data: {
// 这里是一些组件内部数据
someData: {}
},
methods: {
// 这里是一个自定义方法
customMethod(event){
this.triggerEvent('posttap',{pid:1}) // 只会触发posttap
// 详细看 组件之间通信与事件
}
}
})

组件注册

1
2
3
4
5
{
"usingComponents":{
"PageHeader": "page/to/"
}
}
1
2
3
4
5
<PageHeader innerText="正在上映" f-class="movie-list" bind:customMethod="posttap">
<view slot="before">这里是插入到组件slot name="before"中的内容</view>
<!-- 这部分内容将被放置在组件 <slot name="after"> 的位置上 -->
<view slot="after">这里是插入到组件slot name="after"中的内容</view>
</PageHeader>
1
2
3
4
posttap(event){
// 获取自定义事件传参
console.log(event.detail.pid)
}
1
2
3
4
5
// 对于对象或数组字段,可以直接修改一个其下的子字段,这样做通常比修改整个对象或数组更好
this.setData({
'array[0].text':'changed data',
'object.text': 'changed data'
})

路由

1
2
3
4
5
6
wx.navigateTo({
url: ''
})
wx.redirectTo({
url: ''
})

js 模块

1
2
3
4
5
6
7
8
9
var a = {
name: xiaoming
}

export {
a
}

import { a } from '../../data/data.js'

操作菜单

wx.showActionSheet({})

背景音乐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Page({
data: {
_mgr: wx.getBackgroundAudioManager()
},
onLoad(options){
this.data._mgr.onPlay(this.onMusicStart)
this.data._mgr.onStop(this.onMusicStop)
this.data._mgr.onPause(this.onMusicStop)
}
onMusicStart(event){
mgr.src = music.url
mgr.title = music.title
mgr.coverImgUrl = music.coverImg
}
onMusicStop(event){

}
})

1
2
3
4
// app.json
{
"requiredBackgroundModes":["audio","location"]
}

动态设置当前页面的标题

1
2
3
wx.setNavigationBarTitle({
title: '当前页面'
})

引用

WXML 提供两种文件引用方式importinclude

1
2
3
4
<!-- item.wxml -->
<template name="item">
<text>{{text}}</text>
</template>
1
2
<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>
1
2
3
4
5
6
7
8
9
10
<!-- index.wxml -->
<include src="header.wxml"/>
<view> body </view>
<include src="footer.wxml"/>

<!-- header.wxml -->
<view> header </view>

<!-- footer.wxml -->
<view> footer </view>

组件库

weui-wxss:微信官方设计团队

iview-weapp

vant-weapp

MinUI

wux-weapp

ColorUI

Linui

组件库使用

Linui

在小程序跟目录初始化

npm init -y

npm i lin-ui@0.8.7

找到工具点击构建 npm

注册组件

1
2
3
4
5
6
// 全局 局部注册一致
{
"usingComponents":{
"l-avatar":"/miniprogram_npm/lin-ui/avatar/index"
}
}

小程序开发

字体图标引入

将字体 url 转成 base64 的格式后使用

在线 url 使用

小技巧

不做数据绑定推荐用 _ 开头例如: _postsCollected

小程序之间跳转

全局配置
navigateToMiniProgramAppIdList

API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
wx.navigateToMiniProgram({
// 打开小程序的 appid
appId: '',
// 打开的页面路径以及参数 小程序的 App.onLaunch、App.onShow 和 Page.onLoad 的回调函数中可以获取到query 数据
path: 'page/index/index?id=123',
// 需要传递给目标小程序的数据,目标小程序可在 App.onLaunch,App.onShow 中获取到这份数据。
extraData: {
foo: 'bar'
},
// 体验版、开发版、正式版
envVersion: 'develop',
success(res) {
// 打开成功
}
})

自定义 tabbar

将小程序官方的 自定义 tabBar 示例中的 custom-tab-bar 复制到项目的根目录下(与 pages 同级)

配置信息
在 app.json 中的 tabBar 项指定 custom 字段,同时其余 tabBar 相关配置也补充完整。
所有 tab 页的 json 里需声明 usingComponents 项,也可以在 app.json 全局开启。

在 tabbar 页面 onShow() 页面中添加如下代码

1
2
3
4
5
6
7
onShow() {
if (typeof this.getTabBar === "function" && this.getTabBar()) {
this.getTabBar().setData({
selected: 2, // 修改选中的 tabbar 0是第一个 tab
});
}
},

自定义 navigation

我通过 bilibili 的视频找到一个比较好的 github 组件。地址我放在相关资料中了。

生成二维码

  1. weapp-qrcode 使用方法
1
<canvas style="width: 200px; height: 200px;" canvas-id="myQrcode"></canvas>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//  引入 weapp.qrcode.js 文件
import drawQrcode from '../../utils/weapp.qrcode.min.js'

data: {
},
draw () {
drawQrcode({
width: 160,
height: 160,
x: 20,
y: 20,
canvasId: 'myQrcode',
// ctx: wx.createCanvasContext('myQrcode'),
typeNumber: 10,
text: '123',
callback(e) {
console.log('e: ', e)
}
})
}
onLoad: function(options) {
this.draw()
},
  1. wxapp-qrcode 使用方法
1
<canvas style="width: 686rpx;height: 686rpx;background:#f1f1f1;" canvas-id="mycanvas"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import QR from '../../utils/qrcode.js'

data: {
imagePath: ''
},

onReady: function() {
this.createQrCode('wxapp-qrcode', 'mycanvas', 300, 300)
},

createQrCode: function (content, canvasId, cavW, cavH) {
//调用插件中的draw方法,绘制二维码图片
//this.canvasToTempImage 为绘制完成的回调函数,可根据自己的业务添加
QR.api.draw(content, canvasId, cavW, cavH, this, this.canvasToTempImage);
},

//获取临时缓存图片路径,存入data中
canvasToTempImage: function (canvasId) {
let that = this;
wx.canvasToTempFilePath({
canvasId, // 这里canvasId即之前创建的canvas-id
success: function (res) {
let tempFilePath = res.tempFilePath;
console.log(tempFilePath);
that.setData({ // 如果采用mpvue,即 this.imagePath = tempFilePath
imagePath:tempFilePath,
});
},
fail: function (res) {
console.log(res);
}
});
}

小程序内嵌 H5页面

web-view

小程序操作 dom

1
2
3
4
5
6
<view id="the-id">
达达
达达
达达
</view>
<canvas canvas-id="qrcode"></canvas>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
onLoad:function(){},
onReady:function(){
const query = wx.createSelectorQuery()
query.select('#the-id').boundingClientRect()
query.selectViewport().scrollOffset()
query.exec(function (res) {
res[0].top // #the-id 节点的上边界坐标
res[0].bottom // #the-id 节点的下边界坐标
res[1].scrollTop // 显示区域的竖直滚动位置
console.log(res)
})
console.log(query);

var context = wx.createCanvasContext('qrcode')
context.setStrokeStyle("#00ff00")
context.setLineWidth(5)
context.rect(0, 0, 200, 200)
context.stroke()
context.setStrokeStyle("#ff0000")
context.setLineWidth(2)
context.moveTo(160, 100)
context.arc(100, 100, 60, 0, 2 * Math.PI, true)
context.moveTo(140, 100)
context.arc(100, 100, 40, 0, Math.PI, false)
context.moveTo(85, 80)
context.arc(80, 80, 5, 0, 2 * Math.PI, true)
context.moveTo(125, 80)
context.arc(120, 80, 5, 0, 2 * Math.PI, true)
context.stroke()
context.draw()
}

小程序过滤器

1
2
3
4
5
6
7
8
9
10
11
// /filter/filter.wxs

var foo = "'hello world' from tools.wxs";
var bar = function (d) {
return d;
}
module.exports = {
FOO: foo,
bar: bar,
};
module.exports.msg = "some msg";
1
2
3
4
<!-- page/index/index.wxml -->
<wxs src="./../filter.wxs" module="filter" />
<view> {{tools.msg}} </view>
<view> {{tools.bar(tools.FOO)}} </view>

千位符过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function Thousands(num) {
//num = parseInt(num);
var num = num + "";
var d = "";
if (num.slice(0, 1) == "-") {
d = num.slice(0, 1);
num = num.slice(1);
}
var len = num.length;
var index = num.indexOf(".");
if (index == -1) {
num = num + ".00";
} else if (index + 2 == len) {
num = num + "0";
}
var index = num.indexOf("."); // 字符出现的位置
var num2 = num.slice(-3);
num = num.slice(0, index);
var result = "";
while (num.length > 3) {
result = "," + num.slice(-3) + result;
num = num.slice(0, num.length - 3);
}
if (num) {
result = num + result;
}
return d + (result + num2);
}
//
{{filter.Thousands(1000)}}

时间过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function formatTime(str) {
var formatNumber = function (n) {
n = n.toString();
return n[1] ? n : "0" + n;
};
var date = getDate(str);
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();

var hour = date.getHours();
var minute = date.getMinutes();
var second = date.getSeconds();

return (
[year, month, day].map(formatNumber).join(".") +
" " +
[hour, minute, second].map(formatNumber).join(":")
);
}

{{filter.formatTime('时间戳')}}

小程序页面之间传值

1、onShow 直接请求接口
2、globalData 存储数据 onShow 做刷新获取数据
3、获取页面实例,调用页面方法

1
2
3
4
5
6
7
8
9
10
11
12
Page({
onCartAdd(num) {
// 获取页面栈
let pages = getCurrentPages()
// 获取当前页的实例
let currentPage = pages[pages.length - 1]
// 获取当前页的路由
const url = currentPage.route;
// 使用当前页的方法
currentPage.onCartAdd(num);
}
})

4、eventBus(或者叫PubSub)方式
5、gloabelData watcher方式
5、通过hack方法直接调用通信页面的方法

小程序组件之间传值

子传父

  • 获取组件实例
1
2
3
4
5
6
7
8
9
// 父组件中
onReady:function(){
// 获取子组件的示例
this.child = this.selectComponent('.the-id');
// 子组件内的方法
this.child.showToast()
}

<my-component id="the-id" />

小程序异步请求

小程序中支持 promise ,但是在实际开发中还是会有问题。
例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
this.getArticle.then(res=>{
console.log(res);
// 我们想等这个异步执行完成后再执行一个异步
}).then(()=>{
// 需要写成这种链式结构
this.getArticle.then(res=>{
console.log(res=>{
console.log(res);
})
})
})
.catch(error=>{
console.log(error);
})

这种情况下还是避免不了嵌套,也许是我没有把 promise 理解透彻。
这时候我想到了async/await 但是 async/await 是 ES7 的语法。

  • 不勾选es6转es5,不勾选增强编译;该模式是纯es7的async/await,需要基础库高版本。
  • 勾选es6转es5,勾选增强编译;一般是因为调用了第三方的es5插件,通过增强编译支持async/await。
  • 勾选es6转es5,不勾选增强编译;手工引入runtime.js支持async/await。

我们需要引入 [regenerator-runtime]中 runtime.js 即可。在这之前你需要找个文件夹将它存到本地。
例如:lib\runtime\runtime.js

regenerator-runtime 编译的生成器和 async函数的独立运行时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// my 页面
import regeneratorRuntime from '../../lib/runtime/runtime';
async getGoodsList(){
const res=await request({url:"/goods/search",data:this.QueryParams});
// 获取 总条数
const total=res.total;
// 计算总页数
this.totalPages=Math.ceil(total/this.QueryParams.pagesize);
// console.log(this.totalPages);
this.setData({
// 拼接了数组
goodsList:[...this.data.goodsList,...res.goods]
})

// 关闭下拉刷新的窗口 如果没有调用下拉刷新的窗口 直接关闭也不会报错
wx.stopPullDownRefresh();
},

小程序转发

1
2
3
4
5
6
7
8
9
10
11
12
Page({
onShareAppMessage: function (res) {
if (res.from === 'button') {
// 来自页面内转发按钮
console.log(res.target)
}
return {
title: '自定义转发标题',
path: '/page/user?id=123'
}
}
})
1
<button open-type="share">分享</button>

扫码进入小程序或获取参数

小程序开发/开发设置中/扫普通链接二维码打开小程序,微信扫码的时候直接打开小程序,小程序内扫码的时候,获取参数。

小程序中页面栈最多十层。

  • 减少不必要使用 wx.navigateTo。
  • 要想触发销毁页面onUnload生命周期执行方法,必须要使用不存在页面栈的路由。比如:wx.reLaunch、wx.redirectTo、wx.navigateBack。
1
2
3
4
5
6
7
8
9
10
11
wx.redirectTo({
url:'/pages/address/address'
})
// 页面卸载时触发。如wx.redirectTo或wx.navigateBack到其他页面时。
onUnload:function(){
let pages = getCurrentPages().length - 1;
console.log('需要销毁的页面:'+pages);
wx.navigateBack({
delta: pages //关闭当前页面,跳转到上一个页面。但是不允许跳转到 tabbar 页面。参数
})
}

setData 数组和对象

setData 的替代品
wx-updata

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Page({
data: {
list:[]
obj:{}
},
changelist(){
// 数组
let listData = this.data.list;
listData = listData.splice(0,1);
listData = listData.push(1);
listData = listData.concat([1,2,3])
// 对象
this.setData({
// 对象更新
["obj.price"]: value,
// 数组更新
list:listData,
"novel[3].name": "《巴黎圣母院》",
"novel[3].comment": "是浪漫主义作品中一座里程碑",
"novel[3].imagePath": "/pages/img/小说5.jpg"
["serviceList[" + index + "].services[" + pos + "].count"]: num + 1
})
}
})

小程序键盘带搜索

1
<input confirm-type='search' bindconfirm='shop_search_function'/>

小程序 api Promise风格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/**
* promise 形式 getSetting
*/
export const getSetting=()=>{
return new Promise((resolve,reject)=>{
wx.getSetting({
success: (result) => {
resolve(result);
},
fail: (err) => {
reject(err);
}
});
})
}
/**
* promise 形式 chooseAddress
*/
export const chooseAddress=()=>{
return new Promise((resolve,reject)=>{
wx.chooseAddress({
success: (result) => {
resolve(result);
},
fail: (err) => {
reject(err);
}
});
})
}

/**
* promise 形式 openSetting
*/
export const openSetting=()=>{
return new Promise((resolve,reject)=>{
wx.openSetting({
success: (result) => {
resolve(result);
},
fail: (err) => {
reject(err);
}
});
})
}

/**
* promise 形式 showModal
* @param {object} param0 参数
*/
export const showModal=({content})=>{
return new Promise((resolve,reject)=>{
wx.showModal({
title: '提示',
content: content,
success :(res) =>{
resolve(res);
},
fail:(err)=>{
reject(err);
}
})
})
}


/**
* promise 形式 showToast
* @param {object} param0 参数
*/
export const showToast=({title})=>{
return new Promise((resolve,reject)=>{
wx.showToast({
title: title,
icon: 'none',
success :(res) =>{
resolve(res);
},
fail:(err)=>{
reject(err);
}
})
})
}

/**
* promise 形式 login
*/
export const login=()=>{
return new Promise((resolve,reject)=>{
wx.login({
timeout:10000,
success: (result) => {
resolve(result);
},
fail: (err) => {
reject(err);
}
});
})
}

/**
* promise 形式的 小程序的微信支付
* @param {object} pay 支付所必要的参数
*/
export const requestPayment=(pay)=>{
return new Promise((resolve,reject)=>{
wx.requestPayment({
...pay,
success: (result) => {
resolve(result)
},
fail: (err) => {
reject(err);
}
});
})
}
// 使用
import { getSetting, chooseAddress, openSetting, showModal, showToast, requestPayment } from "../../utils/asyncWx.js";
requestPayment(pay);
// 代码来源黑马

小程序请求

  • 极简 promise 封装接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 同时发送异步代码的次数
let ajaxTimes=0;
export const request=(params)=>{
// 判断 url中是否带有 /my/ 请求的是私有的路径 带上header token
let header={...params.header};
if(params.url.includes("/my/")){
// 拼接header 带上token
header["Authorization"]=wx.getStorageSync("token");
}
ajaxTimes++;
// 显示加载中 效果
wx.showLoading({
title: "加载中",
mask: true
});
// 定义公共的url
const baseUrl="https://api.zbztb.cn/api/public/v1";
return new Promise((resolve,reject)=>{
wx.request({
...params,
header:header,
url:baseUrl+params.url,
success:(result)=>{
resolve(result.data.message);
},
fail:(err)=>{
reject(err);
},
complete:()=>{
ajaxTimes--;
if(ajaxTimes===0){
// 关闭正在等待的图标
wx.hideLoading();
}
}
});
})
}
// 使用
import { request } from "../../request/index.js";
request({url:"/goods/search",data:this.QueryParams}).then(res=>{
console.log(res);
})
// 代码来源黑马

扫普通二维码打开小程序

中文乱码

扫码、页面传值过程中,有遇到中文传输乱码的情况,需要 encodeURIComponent()编码、或者decodeURIComponent()解码。

骨架屏

返回键

1
2
3
4
5
6
//生命周期函数--监听页面卸载
onUnload: function ({
    wx.redirectTo({
      url: "/pages/index/index",
    });
  },

获取小程序码

auth.getAccessToken
wxacode.getUnlimited
微信小程序带参数小程序码
微信小程序扫描二维码、小程序码进入并获得携带参数

小程序跳转

wx.navigateToMiniProgram
wx.navigateBackMiniProgram
小程序跳转

下拉刷新

什么情况下使用 scroll-view 最好呢!我目前觉得自定义导航栏的时候,自定义导航会导致下拉整个页面跟着动。
但是用 scroll-view 也会有几个问题:

  • 上拉刷新 加载动画,用原生的整个页面都会动,需要设置、注意兼容。
  • 下拉加载更多 bindscrolltolower 事件触发 需要获取高度、然后动态设置高度。

scroll-view 自定义下拉刷新示例

小程序的AR能力

canvas 画图

Painter

小程序图表

wx-charts

时间 new Date()

在 Android 和 Ios 下执行得到的结果不一致。(微信小程序、webApp上均遇到此类问题。)

订阅消息

模板消息2020年1月10日下线,开发者可使用订阅消息功能。

长期订阅消息只针对特定行业开放(目前长期性订阅消息向政务、医疗、交通、金融、教育等线下公共服务开放),
所以普通开发者并无法使用,而且,现在长期消息模板是没有的,添加的入口估计目前没全量放开。

1
2
3
4
wx.requestSubscribeMessage({
tmplIds: ['模板消息id1'],
success (res) { }
})

一次获取多张图片信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const promixify = (api) => {
return (options, ...params) => {
return new Promise((resolve, reject) => {
api(Object.assign({}, options, { success: resolve, fail: reject }), ...params)
})
}
}
const getImageInfo = promixify(wx.getImageInfo)
const images = ['img1.jpg', 'img2.jpg', 'img3.jpg']
Promise.all(
images.map(img => getImageInfo({ src: img }))
).then((imageInfos) => {
console.log(imageInfos)
})

小程序显示隐藏 tabbar

1
2
wx.showTabBar()
wx.hideTabBar()

微信支付

对比栏目 JSAPI JSSDK 小程序
统一下单 都需要先获取到Openid,调用相同的API
调起数据签名 五个字段参与签名(区分大小写):appId,nonceStr,package,signType,timeStamp
调起支付页面协议 HTTP或HTTPS HTTP或HTTPS HTTPS
支付目录
授权域名
回调函数 success回调 complete、fail、success回调函数
程序访问商户服务都是通过HTTPS,开发部署的时候需要安装HTTPS服务器
1
2
3
4
5
6
7
8
9
wx.requestPayment({
timeStamp: '',
nonceStr: '',
package: '',
signType: 'MD5',
paySign: '',
success (res) { },
fail (res) { }
})

实时音视频截图

截图:LivePlayerContext.snapshot(string quality)
保存图片:wx.saveImageToPhotosAlbum(Object object)

微信小程序截屏保存图片

小程序上传图片后旋转问题

scroll-view 问题

小程序 扩展能力

扩展组件

视频滑动切换组件:video-swiper
小程序长列表组件:recycle-view
吸顶布局:sticky
选项卡:tabs
纵向选项卡组件:vtabs
索引列表组件通讯录效果:index-list
可选文本组件:select-text

工具类库

API Promise化
wx-js-utils

  • 小程序用户
  • 小程序模板消息
  • 小程序统一消息
  • 小程序动态消息
  • 小程序码
  • 微信支付

小程序实践

腾讯在线教育小程序开发实践之路

CSS预处理

  • css 预处理:使用 postcss 来编写样式并编译成 wxss
  • postcss-url 解决 background-image不支持本地图片问题
  • 通过 postcss-font-base64插件将字体变成base64格式

数据管理
westore

构建
gulp 来实现图片压缩以及前面提到的Post CSS编译

微信小程序项目脚手架

分包优化
tab 页面 以 util 常用组件放到主包、其他每个页面分成300-500k 左右的子包

分包预加载
通过配置preloadRule即可实现分包预加载。

独立分包:活动页类

setDate 优化
减少调用 setDate,合并 setDate。
少于64K,避免不必要的数据。
与界面无关的数据不要放在 date 中。
去掉不必要的事件绑定,减少更改次数。
不要在节点 data 前缀放置大量数据。
公共组件的复用

小程序日志监控工具 Fundebug

Fundebug

云开发

云函数
获取 appid
获取 openid
生成分享图
调用腾讯云 SDK

云函数


云数据库
数据增加
数据删除
数据修改
数据查询

JSON 数据库,提供 2GB 免费存储空间 文档型数据库
关系型数据库和文档型数据库对比

文档型数据库:频繁读写操作更合适
关系型数据库:查询复杂的表关联占优势

数据类型
String 字符串
Number 数字
Object 对象
Array 数组
Bool 布尔值
GeoPoint 地理位置点 地理位置查找时建立地理位置索引
Date 时间 创建的时间指的是客户端的时间
Null

操作云数据库
小程序控制 (读写数据库受权限控制限制)
云函数控制 (拥有所有读写数据库的权限)
控制台控制 (拥有所有读写数据库的权限)

云数据库权限管理

  • 仅创建者可写,所有人可读(适合文章)
  • 仅创建者可读写(适合私密相册)
  • 仅管理端可写(适合商品信息)
  • 仅管理端可读写(适合后台敏感数据)

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

数据库 API 分为

  • 小程序端
  • 服务端

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

代码编写

使用 API 操作数据库只需三步:获取数据库引用、构造查询/更新条件、发出请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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)
}
})
const testDB = wx.cloud.database({
env: "test"
});

上传文件\下载文件\删除文件\换取临时链接


云存储
管理文件
上传文件
下载文件
分享文件

代码编写

存储
在小程序端可以分别调用 wx.cloud.uploadFile 和 wx.cloud.downloadFile 完成上传和下载云文件操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 让用户选择一张图片
wx.chooseImage({
success: chooseResult => {
// 将图片上传至云存储空间
wx.cloud.uploadFile({
// 指定上传到的云路径
cloudPath: 'my-photo.png',
// 指定要上传的文件的小程序临时文件路径
filePath: chooseResult.tempFilePaths[0],
// 成功回调
success: res => {
console.log('上传成功', res)
},
})
},
})

小程序传统开发模式

小程序云开发模式

传统开发对比云开发

基础环境

调试基础库版本:2.2.3 以上版本才能使用云开发

了解云开发基础

云开发是什么

1、云开发是什么

云开发是微信团队联合腾讯云提供的原生 Serverless 云服务,致力于帮助更多的开发者快速实现小程序业务的开发,快速迭代。

2、云开发与传统开发模式的对比

传统模式

传统模式

云开发模式

云开发模式

云开发与传统开发模式的对比

云开发与传统开发模式的对比

3、云开发能力介绍

存储:在小程序端直接上传/下载云端文件,可视化管理。
云函数:在云端运行代码,微信私有天然鉴权,开发者只需编写自身业务逻辑代码。
云数据库:一个即可在小程序前端操作,也能在云函数中读写的 JSON 数据库。
音视频服务:提供互通高品质实时音视频通话服务,支持互动白板,美颜滤镜,高清视频通话等,基于云开发快速接入。
智能图像服务:集成智能鉴黄、人脸识别、人脸核身等 AI 视觉能力,基于云开发快速接入。

4、云开发对小程序开发的变革

一天一交付,一天多交付成为可能:云开发的模式可以帮助开发者快速迭代产品,一天多次产品交付成为可能。
小团队也可以做大事情:云开发的模式简单易懂,小的团队也可以借助云计算的能力,做一些更大的事情。
弹性成本几乎为 0:所有资源都由服务方来管理,团队只需要关注业务逻辑。

云开发基本功能演示

1、云开发的数据存储能力

云开发为小程序开发者提供了数据存储能力,帮助开发者快速完成应用的开发。

传统的数据存储模式
传统的数据存储模式

云开发的数据存储模式
云开发的数据存储模式

创建数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1. 获取数据库引用
const db = wx.cloud.database()
// 2. 构造查询语句
// collection 方法获取一个集合的引用
// todos 集合
// add 新增
db.collection('todos').add({
data: {
description: 'learn cloud database'
done: false
},
success: function(res) {
console.log(res)
}
})

查询数据

1
2
3
4
5
6
7
8
9
10
11
// 1. 获取数据库引用
const db = wx.cloud.database()
// 2. 构造查询语句
// collection 方法获取一个集合的引用
// 获取一个记录的数据,已有一个 ID 为 todo-identifiant-aleatoire 的在集合 todos 上的记录
// get 方法会触发网络请求,往数据库取数据
db.collection('todos').doc('todo-identifiant-aleatoire').get({
success: function(res) {
console.log(res.data)
}
})

2、云开发的计算能力

云开发为小程序开发者提供了开箱即用的计算平台,开发者只需关注自己的核心逻辑,就可以完成复杂逻辑的编写。

传统模式下的计算能力的实现
传统模式下的计算能力的实现

云开发模式下的计算能力的实现
云开发模式下的计算能力的实现
不再需要了解 Apache Linux、MySQL,只需要在小程序中创建用函数编写代码并将其部署到云端就可以进行调用就可以获得计算能力的使用了。
云开发模式下的计算能力的实现

3、云开发的文件存储能力

云开发为小程序开发者提供了配置好常用环境的海量非结构化数据存储,帮助开发者解决数据存储问题。
云开发的文件存储能力

云开发控制面板介绍

如何进入云开发控制台

进入云开发控制台

云开发控制台的几个功能

查看数据统计
精简版数据统计
详细版数据统计
管理用户信息
用户信息查看
管理数据库
数据库管理
管理云函数
云函数管理
管理云存储

云开发控制台的讲解

云开发 API 简介

云开发 API 分类

按端分类
按类型分类

云开发 API 初始化方法

小程序 API 初始化
服务端 API 初始化

初始化选项 env
云开发的初始化选项支持传入一个 Object,指定各个服务使用的默认环境
传入一个 Object,这样可以分别指定数据库、文件存储、云函数所使用的的默认环境,从而实现在一个应用程序当中同时调用两个环境的内容。
指定 Object

云开发 API 使用注意事项

云开发 API 同时支持 callback 风格和 promise 风格
云开发 API 初始化时如果没有设置 ID,默认使用先创建的那个。
在服务端可以借助云开发 SDK 内置的 getWXContext 来获取用户的身份信息

云开发数据库能力介绍

云开发·数组查询

使用云开发,可以实现诸如 A 在数组 B 中或 A 不在数组 B 中的条件判断。

1
2
3
4
5
6
7
8
9
10
11
const db = wx.cloud.database();
const _ = db.command;
db.collection("todos")
.where({
// progress: _.nin([0,100])
progress: _.in([0,100])
})
.get({
success: console.log,
fail: console.error
})

字段类型查询

如何使用云开发 API 进行字段类型查询
使用云开发,可以实现只查询所需字段内容,而不是查询所有字段内容。

1
2
3
4
5
6
7
8
9
10
const db = wx.cloud.database();
db.collection("todos")
.field({
description: true,
done: true,
progress: true
})
.get()
.then(console.log)
.catch(console.error)

正则表达式

什么是正则表达式

正则表达式描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

云开发中的正则表达式对象

从基础库 2.3.2 开始,云开发支持使用正则表达式查询,开发者可以使用原生的正则对象,或者构建 db.RegExp 对象。

原生正则对象
/miniprogram/i

db.RegExp 对象

db.RegExp({
regexp: “miniprogram”,
options: “i”,
})

在云开发中使用正则查询

1
2
3
4
5
6
db.collection('todos').where({
description: db.RegExp({
regexp: "miniprogram",
options: "i",
})
})
1
2
3
4
5
6
7
8
db.collection('todos').where({
name: new db.RegExp({
regexp: "name-0[1-9]",
options: "i",
})
})
.get()
.then(console.log)

地理位置查询

在云开发中创建一个地理位置索引

1
2
3
4
5
const db = wx.cloud.database();
db.collection("location").add({
description: 'eat an apple',
location: db.Geo.Point(113,23)
})

在云开发中使用地理位置索引查询

1
2
3
4
db.collection('location').get().then(res =>{
// 读取经纬度
console.log(res.data[0].location.latitude)
})

云开发数据库权限介绍

云开发数据库中的几种权限

云开发数据库一共有四种权限,从宽到紧排列如下:

仅创建者可写,所有人可读:数据只有创建者可写、所有人可读;
仅创建者可读写:数据只有创建者可读写,其他用户不可读写;
仅管理端可写,所有人可读:该数据只有管理端可写,所有人可读;
仅管理端可读写:该数据只有管理端可读写;

云开发数据库中的几种权限

云开发数据库中的权限对应场景

仅创建者可写,所有人可读
仅创建者可读写
仅管理端可写,所有人可读
仅管理端可读写

如何修改云开发数据库权限

如何修改云开发数据库权限

云开发文件存储能力介绍

文件存储

云开发提供的文件存储能力

生成临时链接

为什么要生成文件的临时链接

云开发的 fileID 无法在小程序以外的平台上使用
一些服务需要文件的源地址

1
2
3
4
5
6
7
8
9
10
11
wx.cloud.getTempFileURL({
// 需要获取链接的文件
fileList: ['cloud://xxx','cloud://yyy'],
success: res =>{
// 获取临时文件链接
console.log(res.fileList)
},
fail: err =>{
// 处理错误
}
})

云函数基础用法之定时器

云函数定时器的使用场景

函数需要定时、定期执行时,可以使用云函数定时器来进行触发。

如何使用云函数定时触发器

在云函数目录下创建 config.json 文件,并设置触发器
上传触发器

1
2
3
{
'triggers'
}

WXML 基础
tabBar-底部导航
设置小程序的状态栏、导航条、标题、窗口背景色
客服消息
swiper-轮播图
拨打电话
提示框
判断小程序 API 是否在当前版本可用
获取系统信息

获取场景值
可以在 App 的 onLaunch 和 onShow,或 wx.getLaunchOptionsSync 中获取上述场景值。

1
2
3
onLaunch: function(e) {
console.log(e.scene);
}

小程序常见问题

API 请求
网络请求
小程序端和云函数发送请求方式

基础库兼容

不同页面传值方式

微信登陆

传统微信登陆

云开发微信登陆


相关资料
[视频]《入门微信小程序开发》

[视频]2小时轻松实现人脸识别的小程序
[视频]1-1、微信小程序云开发–自定义tabBar中间凸起效果

[视频]28 节课快速玩转微信小程序

[视频]微信小程序入门与实战

[视频]微信小程序教程

[视频]零基础玩转微信小程序

[视频]纯正商业级应用-微信小程序开发实战

[视频]轻松入门微信小程序与云开发

腾讯云大学重磅打造 —— 小程序学习路径课

小程序自定义tabBar
weapp-qrcode
SelectorQuery wx.createSelectorQuery()
微信小程序操作dom元素节点 wx.createSelectorQuery()
微信小程序动态生成二维码的实现代码
wxapp-qrcode
微信小程序 使用filter过滤器几种方式
regenerator
微信小程序中使用Async-await方法异步请求变为同步请求
微信小程序-实现分享(带参数)
让微信小程序页面之间的通信不在变得困难
微信小程序设置引导页后navigateTo失效
微信小程序setData复杂数组的更新、删除、添加、拼接
微信小程序使用键盘搜索功能
微信小程序正则校验
小程序改造成async/await模式
小程序踩过的一个小坑—解析二维码decodeURIComponent() url解码
微信小程序wx.request二次封装
小程序日志监控工具
腾讯在线教育小程序开发实践之路
[微信小程序] API Promise化
微信小程序之使用函数防抖与函数节流
小程序销毁页面的方法
微信小程序scroll-view bindscrolltolower事件不触发
小程序scroll-view自定义下拉刷新
Painter
wx-charts
github上5个实用的微信小程序开发工具
微信小程序中的日期格式在Android和iOS真机下兼容性问题的坑
getImageInfo一次获取多张图片信息,怎么处理?
微信小程序上传照片后旋转问题解决
微信小程序三种授权登录的方式
一招搞定微信小程序ScrollView等组件高度自适应
在微信小程序中使用 ECharts
echarts-for-weixin
小程序promise工具的使用
强烈推介的几个微信小程序开发小技巧,简单又实用
微信小程序父组件调用子组件方法
微信小程序,格式化千分位并保留两位小数
微信小程序订阅消息踩坑初体验
模板消息
小程序广告投放到达率提升的优化实践
微信小程序代码最大限制2M的解决方案
2小时轻松实现人脸识别的小程序

如何在微信小程序中使用字体图标

微信小程序之页面路由(九)

vant-weapp组件得外部样式类引用说明

u-miniprogram统计分析埋点

微信小程序搜索内容显示关键词高亮

小程序实现复制粘贴功能

小程序页面通信、数据刷新、事件总线 、event bus 终极解决方案之 iny-bus | 微信开放社区 (qq.com)

mitt:小型的发布订阅模式的实现,组件事件管理,页面传参。

微信小程序添加埋点_codezha的博客-CSDN博客_微信小程序埋点

scroll-view内部的第一个盒子添加margin-top属性会出现滚动条
IOS scroll-view中的自定义组件fixed问题

小程序海报生成工具,可视化编辑直接生成代码使用,你的海报你自己做主微信小程序拖拽生成海报

微信H5网页跳转 App、跳转小程序

一起来做小程序直播吧!
小程序海报生成工具,可视化编辑直接生成代码使用,你的海报你自己做主
微信小程序截屏保存图片
小程序如何生成海报分享朋友圈
微信小程序canvas生成并保存图片
微信小程序 canvas圆角矩形的绘制
小程序利用canvas实现波浪动态图,原生canvas的部分限制
小程序动态生成canvas海报
微信小程序之生成图片分享
微信小程序Canvas隐藏 不影响canvas绘图处理办法
微信小程序中把页面生成图片
openid的获取顺序并解决openid的异步问题
如何使用小程序的AR能力
微信小程序弹窗组件
微信小程序组件封装
小程序通过摄像头拍摄个人身份证
微信小程序 自定义头部导航栏和导航栏背景图片 navigationStyle
微信小程序自定义navigation-bar导航栏(自适应安卓苹果)
小程序授权流程化


开源
小程序组件 navigation-bar

awesome-github-wechat-weapp

微信小程序 从入门到实践开发

http://example.com/2020/01/22/Blog-about-learning-10/

作者

Fallen-down

发布于

2020-01-22

更新于

2021-08-11

许可协议

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

评论

You forgot to set the shortname for Disqus. Please set it in _config.yml.
You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.