小程序云开发基础实战

前言

随着 serverless 越来越火,基本大厂都使用云开发的技术,使自己的业务托管上云,开发者则大多把重心关注在业务的开发上,无需关注扩容等操作,一切交给云服务商即可,大大减少了开发部署等操作,而对于小程序云开发,开发者完全可以作为全栈工程师去开发项目(包括前端、后端、数据库-mongodb 等),无需搭建服务器,即可使用云端能力;云开发为开发者提供完整的原生云端支持和微信服务支持,弱化后端和运维概念,无需搭建服务器,使用平台提供的 API 进行核心业务开发,即可实现快速上线和迭代,同时这一能力,同开发者已经使用的云服务相互兼容,并不互斥;

能力 作用 说明
云函数 无需自建服务器 在云端运行的代码,微信私有协议天然鉴权,开发者只需编写自身业务逻辑代码
数据库 无需自建数据库 一个既可在小程序前端操作,也能在云函数中读写的 JSON 数据库
存储 无需自建存储和 CDN 在小程序前端直接上传/下载云端文件,在云开发控制台可视化管理
云调用 原生微信服务集成 基于云函数免鉴权使用小程序开放接口的能力,包括服务端调用、获取开放数据等能力

1. 新建云开发模板

也可以省略此步骤,直接在已有项目上开通和使用云开发。

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

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

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

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

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

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

完成后我们就能看到控制台信息了
image-20200911172630511

接着在 project.config.json 文件中配置云函数存放的位置

1
{ "cloudfunctionRoot": "cloudfunctions/" }

配置完后,在开发者工具中会出现当前环境的标示,这样就配置成功了;
image-20200911172630511

然后在 app.js 中配置云开发的初始化方法:其中 env 为当前云开发的环境,不指定的话则默认为第一个创建的环境

  • 注: 我们可以根据小程序开发环境的不同,去动态映射云开发的环境;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//app.js
App({
onLaunch: function() {
if (!wx.cloud) {
console.error("请使用 2.2.3 或以上的基础库以使用云能力");
} else {
wx.cloud.init({
// env 参数说明:
// env 参数决定接下来小程序发起的云开发调用(wx.cloud.xxx)会默认请求到哪个云环境的资源
// 此处请填入环境 ID, 环境 ID 可打开云控制台查看
// 如不填则使用默认环境(第一个创建的环境)
// env: 'my-env-id',
traceUser: true,
});
}
},
});

3. 体验云开发

3.1 常量 DYNAMIC_CURRENT_ENV

支持端:云函数 1.1.0

标志当前所在环境,注意该值不是当前所在环境 ID 的字符串,其值等价于 Symbol.for('DYNAMIC_CURRENT_ENV'),是用于标志当前所在环境的。如在 init 中如果给 env 参数传该常量值,则后续的 API 请求会自动请求当前所在环境的云资源,如云函数 A 当前所在环境是 test-123,则其接下来请求数据库、文件存储、云函数时都默认请求环境 test-123 的数据库、文件存储、云函数。

常量可用于:

  • cloud.initenv 参数
  • cloud.updateConfigenv 参数
  • 各 API 的 config 参数中的 env 参数

注意事项:

  • 1.7.1 起,该变量支持在定时触发器中使用,之前的版本不支持。
    cloud.database: 设置新数据库对象的调用环境等于当前所在环境

    1
    2
    3
    4
    5
    6
    7
    8
    cloud.init({
    env: "xxx",
    });

    // 不同于 init 时设置的环境,db 对象的请求将会去到和当前云函数所在环境相同的环境
    const db = cloud.database({
    env: cloud.DYNAMIC_CURRENT_ENV,
    });

3.2 云数据库

云数据库底层使用的是 mongodb,对于前端同学也不是很陌生;小程序调用数据库比较方便,可以直接在小程序 js 文件中调用(不像 uni-app 中,一定要在云函数中才能使用,比较恶心!);
首先按照下图先创建一个集合,作为测试我已经创建了一个名为 test 的集合
image-20200911172630511

下面代码中,我们 onload 回调中我们向刚刚创建的 test 集合中添加了一个数据

1
2
3
4
5
6
7
8
9
10
11
onLoad: async function () {
const db = wx.cloud.database()
await db.collection('test').add({
data: {
a: 1
},
config: {
env: 'test-123' // 环境 ID,指定使用哪个环境下的对应的云函数,填写后将忽略 init 时指定的环境 ID,这里将访问test-123云空间的test集合,然后添加一条数据
}
})
}

随后我们刷新控制台就能看到刚刚添加的数据啦
image-20200911172630511

其他常用的方法,见官方文档

3.2 云函数

在云函数中使用 wx-server-sdk,需先调用初始化方法 init 一次,init 用于设置接下来在该云函数实例中调用云函数、数据库、文件存储时要访问的环境。

1
2
3
4
5
6
7
8
9
10
11
// test
const cloud = require("wx-server-sdk");
cloud.init({
env: "test-x1dzi",
});

// 当我们调用云函数test的时候,访问的就是test-x1dzi这个云空间里的资源了
let msg = await wx.cloud.callFunction({
name: "test",
data: {},
});

4 云开发实战-模糊图像高清处理

4.1 目的

解决模糊图片带来的不好体验,利用 AI 图片处理使其变高清;

4.2 技术栈

腾讯云 AI 图片处理 API、云调用、云函数、云储存

4.3 开发

1.申请开通腾讯云图像处理工作台;
image-20200911172630511

2.开通完,等几分钟就可以看到控制台了;

image-20200911172630511

3.然后点击后上角的接口文档

4.可以看到系统为你生成了一套代码,不过我们需要添加SecretId、SecretKey,ImageUrl 为图片地址可以接受线上地址或者 Base64 地址,这个我们一会动态传递即可,返回的值是一个 base64 的地址;

image-20200911172630511

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
const tencentcloud = require("tencentcloud-sdk-nodejs");

const TiiaClient = tencentcloud.tiia.v20190529.Client;
const models = tencentcloud.tiia.v20190529.Models;

const Credential = tencentcloud.common.Credential;
const ClientProfile = tencentcloud.common.ClientProfile;
const HttpProfile = tencentcloud.common.HttpProfile;

let cred = new Credential("AKIDrjxxxxxx", "iBRUIq5ssssss");
let httpProfile = new HttpProfile();
httpProfile.endpoint = "tiia.tencentcloudapi.com";
let clientProfile = new ClientProfile();
clientProfile.httpProfile = httpProfile;
let client = new TiiaClient(cred, "ap-beijing", clientProfile);

let req = new models.EnhanceImageRequest();

let params = "{}";
req.from_json_string(params);

client.EnhanceImage(req, function(errMsg, response) {
if (errMsg) {
console.log(errMsg);
return;
}
console.log(response.to_json_string());
});

5.然后我们需要把上面的代码放到云函数中,托管代码,然后前端去调用,首先创建一个新的云函数;

image-20200911172630511

6.把代码粘贴到 index.js 中,稍加修改,用云函数包一下即可;

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
// 云函数入口文件
const tencentcloud = require("tencentcloud-sdk-nodejs");
const cloud = require("wx-server-sdk");
cloud.init({
// API 调用都保持和云函数当前所在环境一致
env: cloud.DYNAMIC_CURRENT_ENV,
});
// 云函数入口函数
exports.main = async (event, context) => {
const TiiaClient = tencentcloud.tiia.v20190529.Client;
const models = tencentcloud.tiia.v20190529.Models;

const Credential = tencentcloud.common.Credential;
const ClientProfile = tencentcloud.common.ClientProfile;
const HttpProfile = tencentcloud.common.HttpProfile;

let cred = new Credential("AKIDrjxxxxxx", "iBRUIq5ssssss");
let httpProfile = new HttpProfile();
httpProfile.endpoint = "tiia.tencentcloudapi.com";
let clientProfile = new ClientProfile();
clientProfile.httpProfile = httpProfile;
let client = new TiiaClient(cred, "ap-beijing", clientProfile);

let req = new models.EnhanceImageRequest();
const fileList = event.fileID; // event=是我们传递的参数的对象集合,fileID是我们传递的参数名

let params = '{"ImageUrl":"https://www.google.com/imgres?imgurl=https%3A%2F%2Fimg.iplaysoft.com%2Fwp-content%2Fuploads%2F2019%2Ffree-images%2Ffree_stock_photo.jpg&imgrefurl=https%3A%2F%2Fwww.iplaysoft.com%2Ffree-images.html&tbnid=vQjlM9KtkGsb_M&vet=12ahUKEwiY9Zfn3-frAhVG15QKHeudABMQMygAegUIARCeAQ..i&docid=JeaDEV9l4RQZhM&w=680&h=453&q=%E5%9B%BE%E7%89%87&ved=2ahUKEwiY9Zfn3-frAhVG15QKHeudABMQMygAegUIARCeAQ"}';
req.from_json_string(params);

function run() {
return new Promise((resolve) => {
client.EnhanceImage(req, function(errMsg, response) {
if (errMsg) {
console.log(errMsg);
resolve(errMsg);
}
console.log(response.to_json_string());
resolve(response.EnhancedImage);
});
});
}

return {
result: await run(),
};
};

7.我们把代码粘贴到 index.js 中,然后开启本地调试,可以看到生成了 base64 地址,说明转换成功了
image-20200911172630511

8.接下来我们需要把写死的图片通过前端传递过去,而且图片需要从本地选择,又图片转换功能不支持本地图片转换,需要线上地址,故用到了小程序端-云储存上传、云函数端-云储存下载;

流程: 前端选择图片=>把本地图片上传到云储存=>调用云函数传递云储存传递 fileID=>云函数调用云储存拿到真实线上地址=>调用 AI 接口=>返回转换后的地址给前端=>前端展示

  1. 前端选择图片
1
2
3
4
5
6
7
8
9
10
11
12
13
wx.chooseImage({
count: 1,
sizeType: ["compressed"],
sourceType: ["album"],
success: (res) => {
// tempFilePath可以作为img标签的src属性显示图片
const tempFilePaths = res.tempFilePaths;

this.setData({
ImageUrl: tempFilePaths[0],
});
},
});
  1. 把本地图片上传到云储存&调用云函数传递云储存传递 fileID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
wx.cloud.uploadFile({
cloudPath: rdm + ".png", //云储存的文件名
filePath: this.data.ImageUrl, // 文件路径
success: async (res) => {
let msg = await wx.cloud.callFunction({
name: "Upload",
data: {
fileID: [res.fileID],
},
});
this.setData({
img: "data:image/png;base64," + msg.result.result,
});
},
});
  1. 云函数调用云储存拿到真实线上地址=>调用 AI 接口=>返回转换后的地址给前端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const fileList = event.fileID;
const result = await cloud.getTempFileURL({
fileList: fileList,
});
let params = '{"ImageUrl":"' + result.fileList[0].tempFileURL + '"}';
req.from_json_string(params);

function run() {
return new Promise((resolve) => {
client.EnhanceImage(req, function(errMsg, response) {
if (errMsg) {
resolve(errMsg);
}
console.log(response.to_json_string());
resolve(response.EnhancedImage);
});
});
}

return {
result: await run(),
};
  • 最终展示视频如下