# 微信小程序云迁移uniCloud

## 概念澄清

uniCloud，可以自选阿里云和腾讯云的serverless。类似于uni-app屏蔽了各家小程序的端侧差异，uniCloud也屏蔽了微信小程序云开发和支付宝小程序云开发的差异。

也就是说，uniCloud选择腾讯云时，和微信小程序云开发，连的是一套云服务，都是腾讯云官方提供的。

虽然硬件相同，包括API也很相似，但uniCloud和微信云开发仍然有一些软件层面的差异。

uniCloud去掉了微信云开发的一些功能，添加了更多功能。以及在生态配套上差别也比较大。

## 优劣对比

|				|微信云开发																	|uniCloud																																																|
|--				|--																			|--																																																		|
|跨端			|只有微信小程序(虽可用其他工具变相开发H5、QQ小程序，但端不够全也难以保持一致性)					|全端。App、web、各家小程序快应用全支持																																									|
|跨云			|只支持腾讯云。为其编写的云函数不能运行在其他云平台							|阿里云、腾讯云均支持，方便互相切换。对其他云也持开放态度																																				|
|定价			|													|选用uniCloud腾讯云版与微信云开发价格相同。DCloud只是获取腾讯云的返点，而不是加价售卖。如选用uniCloud阿里云版，则比微信云开发更便宜[详见](price.md)																																	|
|前端操作数据库	|使用微信的账户权限而不是应用的账户权限，无法编程，不能有效控制数据权限安全。客户端js sdk体积大，影响性能	|完善的[clientDB](https://uniapp.dcloud.io/uniCloud/clientdb)模块，使用应用自己的用户权限体系，可灵活编程，安全可靠控制权限																				|
|数据库schema	|不支持																		|完善的数据库[schema](https://uniapp.dcloud.io/uniCloud/schema)设计，自带权限验证和数据合法性验证																										|
|数据库查询语法	|MongoDB语法。学习门槛高、写法复杂，尤其联表查询很难用							|除了MongoDB语法外，支持JQL语法，大幅降低学习门槛，减少数据库操作的代码量，快速完成复杂查询																												|
|开发工具		|微信小程序工具，编码体验欠佳												|通用编程工具HBuilderX，高效操作完善，插件体系开放																																						|
|前端数据库watch	|支持。权限按微信账户体系执行 |有更强大的免费的websocket服务[详情查看](https://uniapp.dcloud.io/unipush-v2.html)|
|opendb			|无																			|开放的数据库规范，众多价值，[详情](opendb.md)																																	|
|账户服务		|仅微信登录，但鉴权更简单														|[uni-id](uni-id-summary.md)支持应用自己的账户体系，手机号或email，内置短信验证码和app端一键登录，支持微信、支付宝等三方登录，支持权限、角色、社交裂变等众多功能					|
|admin系统		|不自带																		|自带开源[uniCloud admin](https://uniapp.dcloud.io/uniCloud/admin)系统，大小屏自适配，自带用户、角色、权限功能，还有更多[插件](https://ext.dcloud.net.cn/?cat1=7&cat2=74&orderBy=UpdatedDate)拿来即用	|
|支付			|仅微信支付，但鉴权更简单														|[uniPay](https://uniapp.dcloud.io/uniCloud/unipay)，跨端统一支付																																		|
|cms			|支持。但前端部分跨端不足													|支持。前端跨端、[管理端](https://ext.dcloud.net.cn/plugin?id=3543)开源																																	|
|插件生态		|腾讯云开发了部分插件															|丰富的插件生态，包含腾讯云为uniCloud开发的插件，众多现成项目模块，[详情](https://ext.dcloud.net.cn/?cat1=7&orderBy=UpdatedDate)																		|

上述差异，总结来看，uniCloud更开放、生态更丰富、开发效率更高。

开发效率之所以高的原因，包括clientDB、JQL、HBuilderX这些对效率起很大影响的重要功能和工具，也包括大量现成的轮子拿来就用。详见：[2021了，让我们把开发效率提升10倍吧！](https://mp.weixin.qq.com/s/d3y3pQqC_SMm3938_i2qNw)

## 技术迁移指南

如果已经开发了微信小程序，想迁移到uniCloud，下面提供云函数和数据库部分的技术迁移指南。

### 云函数迁移指南

#### 对接微信登录

uniCloud可以使用uni-id更简单的接入微信小程序登录。参考[uni-id](uni-id-summary.md)

不同于微信云开发中的直接获取openid，uni-id提供的登录接口会在数据库的uni-id-users表内添加用户记录。

如果要控制云存储的权限可以使用自定义登录，[uniCloud腾讯云自定义登录](https://uniapp.dcloud.net.cn/uniCloud/authentication)

#### 对接微信支付

uniCloud提供了uniPay来实现支付功能，同时支持微信和支付宝[unipay文档](https://uniapp.dcloud.net.cn/uniCloud/unipay)

老的微信云开发+原生小程序的代码是这样：
```js
// 云函数部分的代码
const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})

exports.main = async (event, context) => {
  const res = await cloud.cloudPay.unifiedOrder({
    "body" : "商品描述",
    "outTradeNo" : "商户订单号",
    "spbillCreateIp" : "127.0.0.1",
    "totalFee" : 1,
	// 用于接收支付异步通知的云函数所在的服务空间ID和云函数名
    "envId": "test-f0b102",
    "functionName": "pay_cb"
  })
  return res
}

// 小程序部分的代码
wx.cloud.callFunction({
  name: '函数名',
  data: {
    // ...
  },
  success: res => {
    const payment = res.result.payment
    wx.requestPayment({
      ...payment,
      success (res) {
        console.log('pay success', res)
      },
      fail (res) {
        console.error('pay fail', err)
      }
    })
  },
  fail: console.error,
})
```

迁移到uniCloud + uni-app体系，代码改成这样：

```js
// 云函数部分的代码
const unipayIns = unipay.initWeixin({
  appId: 'your appId',
  mchId: 'your mchId',
  key: 'you parterner key',
  // pfx: fs.readFileSync('/path/to/your/pfxfile'), // p12文件路径，使用微信退款时需要，需要注意的是务必使用绝对路径
})


exports.main = async (event, context) => {
	const res = await unipayIns.getOrderInfo({
		openid: 'user openid',
		body: '商品描述',
		outTradeNo: '商户订单号',
		totalFee: 1, // 金额，单位分
		notifyUrl: 'https://xxx.xx' // 支付结果通知地址
	})
  return res
}

// 客户端部分的代码
uniCloud.callFunction({
	name: '云函数名',
	data: {
		// ...
	},
	success(res) {
		uni.requestPayment({
			provider: 'wxpay',
			...res.result.orderInfo
			success (res) {
			  console.log('pay success', res)
			},
			fail (res) {
			  console.error('pay fail', err)
			}
		})
	}
})
```

**注意**

- uniPay和微信小程序的CloudPay接口很相似，需要注意的是uniPay接收的参数都是驼峰形式的，输出的参数也都是驼峰形式的。微信小程序的CloudPay有些接口是驼峰形式参数有些是蛇形参数

#### 临时CDN

临时CDN主要用于解决需要传输大文件到云函数的场景，使用uniCloud时可以通过先上传到云存储再传递fileID给云函数的方式实现

微信云开发写法：
```js
wx.cloud.callFunction({
  name: 'test',
  data: {
    filePathDemo: wx.cloud.CDN({
      type: 'filePath',
      filePath: 'xxxxxxxx',
    })
  },
})
```

uniCloud写法：
```js
uniCloud.uploadFile({
	filePath: filePath,
	cloudPath: 'a.jpg'
}).then(res => {
	const fileID = res.fileID
	uniCloud.callFunction({
		name: 'test',
		data: {
			filePathDemo: fileID
		}
	})
})
```

#### 微信开放接口

使用微信开放接口的场景可以替换为[mp-cloud-openapi](https://ext.dcloud.net.cn/plugin?id=1810)来实现。用法基本和微信云开发一致

微信云开发写法：
```js
const cloud = require('wx-server-sdk')
cloud.init()
exports.main = async (event, context) => {
  try {
    const result = await cloud.openapi.wxacode.createQRCode({
        path: 'page/index/index',
        width: 430
      })
    return result
  } catch (err) {
    return err
  }
}
```

uniCloud写法：
```js
// mp-cloud-openapi，地址：https://ext.dcloud.net.cn/plugin?id=1810
const openapi = require('mp-cloud-openapi')
const openapiWeixin = openapi.initWeixin({
  appId: 'appId',
  secret: 'secret'
})
exports.main = async (event, context) => {
  try {
    const result = await openapiWeixin.wxacode.createQRCode({
        path: 'page/index/index',
        width: 430
      })
    return result
  } catch (err) {
    return err
  }
}
```

### 数据库迁移指南

#### 数据导出导入

微信小程序云开发的控制台可以导出json数据，这个格式与uniCloud相同，可以将导出文件直接在uniCloud的web控制台操作导入。

#### 新增与更新数据的写法

微信小程序云开发add、update、set操作时参数比uniCloud多了一层data **从微信小程序云开发迁移时最需要注意的事项**

微信云开发写法：
```js
const res = await db.collection('todos').doc('todo-id').add({
  data: {
    description: "learn cloud database",
    done: false
  }
})
```

uniCloud写法：
```js
const res = await db.collection('todos').doc('todo-id').add({
  description: "learn cloud database",
  done: false
})
```

#### 客户端操作数据库

uniCloud内也支持客户端操作数据库（下面成为clientDB），但是与微信云开发稍有不同。主要有以下两点

- 权限依赖于uni-id
- 需要在表结构的permission里面配置权限，可以配置字段级的权限

另外clientDB支持以下扩展能力

- 数据校验能力，参考：[DB schema](https://uniapp.dcloud.net.cn/uniCloud/schema)
- 在数据库操作前/后执行额外操作，参考：[action](https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=action)
- 更加简单的查询方式，参考：[JQL](jql.md)
