主题
云函数调用与打包上线(三)
本页对应 云函数调用与打包上线,共 7 段视频,约 1 小时 3 分。
学习目标
- 能够说明 IP 与域名的基本关系,并解释本地开发与域名访问的差异。
- 能够在
login目录下新增 QQ 登录页面,并完成主包pages路径与导航标题配置。 - 能够创建
QQ-connect云函数,完成代码上传部署,并区分本地云函数与云端云函数的调用方式。 - 能够通过云函数调用 QQ 接口获取
openid,并从返回结果中正确解构所需字段。 - 能够调用 QQ 用户信息接口获取头像和昵称,并将数据返回页面完成渲染。
- 能够按“先 QQ 登录、再检测绑定手机号”的顺序调用接口,并根据绑定状态控制页面内容展示。
知识点
先把 localhost、IP 和域名分清
为什么需要:QQ 登录回调必须回到一个“可识别的地址”,本地开发常用 localhost 或 127.0.0.1,但第三方平台通常要求配置域名,否则授权后跳不回来。
怎么想:本地调试只是临时手段;真正联调 QQ 互联时,要用已配置的域名访问项目,让回调地址、跨域配置和云函数调用保持一致。
怎么做:先用本地地址把页面和流程跑通,再换成域名访问,重新点登录验证是否能跳转成功。
易错点:把 localhost 写成“logo host”;以为 IP 和域名完全等价;域名配置里误写 http://,转写稿特别强调“域名当中不包含 HTTP 协议”。
搭好 QQ 登录页和路由配置
为什么需要:QQ 授权成功后会跳到我们自己的页面,所以必须先有页面和路由,否则回调无处落地。
怎么想:在 login 目录下新增 qq.vue,再在主包路由里补一条配置,标题写成“QQ登录”。
怎么做:页面路径按转写稿放在 login/qq,配置项示意如下:
json
{
"path": "login/qq",
"style": { "navigationBarTitleText": "QQ登录" }
}易错点:路径不要写成 qq.vue 或点号形式;新增页面后忘记加路由配置,页面就访问不到。
从回调地址里截取 access_token
为什么需要:QQ 授权跳回页面后,access_token 会拼在地址的 hash 部分,后续换 openid、拉用户信息都要用它。
怎么想:先用 location.hash 拿到 # 后面的整段,再按固定位置截取 token。转写稿中做法是从第 14 位开始截 32 位,即 slice(14, 46)。
怎么做:
js
const hash = location.hash
const accessToken = hash.slice(14, 46)易错点:把 hash 说成“范式”;截取位置数错;token 失效后不重新扫码,导致接口返回异常。调试时可在控制台打印截取结果,确认中间段是否正确。
用云函数跨域请求 QQ 接口
为什么需要:直接在页面请求 QQ 接口会跨域,云函数相当于自己的服务器,可以替页面发请求。
怎么想:新建云函数 QQ-connect,在云函数里用 uniCloud.request 调 QQ 官方接口,把 access_token 作为参数传进去。
怎么做:云函数关键结构:
js
const res = await uniCloud.request({
url: '按QQ官方文档复制的接口地址',
data: { access_token: accessToken },
dataType: 'json'
})
return res页面调用:
js
const { result } = await uniCloud.callFunction({
name: 'QQ-connect',
data: { accessToken }
})易错点:把 callFunction 写成“靠方式”;忘记在 uniCloud 后台配置跨域域名;改完云函数忘记“上传部署”,页面仍调用云端旧代码。转写稿中多次出现“本地能打印、云端没结果”,原因就是没上传部署。
获取 openid 并正确解构
为什么需要:openid 是 QQ 用户的唯一标识,后面查绑定状态、拉用户信息都要用。
怎么想:先打印 QQ 接口返回的完整结构,确认 data 里包含 openid,再解构,不要一上来就猜字段。
怎么做:
js
const { data } = res
const { openid } = data
return { openid }易错点:参数大小写写错,如 openid 写成 openId,接口会提示“openid 是必须的”;返回有两层 result 时忘记解构;改完代码未重新上传部署。
拉取 QQ 昵称头像并渲染
为什么需要:登录页要展示用户头像和昵称,让用户确认授权对象。
怎么想:用 access_token、oauth_consumer_key(即 APPID/client_id)和 openid 再调用户信息接口。
怎么做:云函数中继续请求用户信息,返回时整理成页面需要的字段:
js
return {
openid,
nickname: data.nickname,
avatar: data.头像字段
}页面用 ref 保存 userInfo,模板中绑定 userInfo.avatar、userInfo.nickname。
易错点:oauth_consumer_key 拼错;头像字段随便猜;把云函数返回结果和页面接收结果混在一起,导致渲染为空。
检测绑定状态再决定是否展示绑定页
为什么需要:QQ 登录后不一定绑定手机号,要先调“QQ登录/检测绑定状态”接口,再决定是否展示绑定手机号页面。转写稿明确:顺序应先调 QQ 登录检测,再发送验证码、绑定手机号。
怎么想:接口返回 code === 1001 表示未绑定,把绑定状态设为 false;页面用 v-if="!isBind" 展示绑定区域。
怎么做:
js
if (res.code === 1001) {
isBind.value = false
}html
<view v-if="!isBind">绑定手机号区域</view>易错点:先绑定手机号再调 QQ 登录,顺序反了;初始值设为 true 后忘记在未绑定时改 false;参数 openid 大小写与接口文档不一致。调试时先看网络请求参数,再看响应里的 code 和 flag。
页面模板与路由配置:先把 QQ 登录页搭出来
为什么需要:QQ 授权成功后会跳回我们自己的页面,如果页面和路由没配好,回调地址就没有落点。
怎么想:先在 pages/login 下新增 qq.vue,再到主包 pages 配置里注册路径和导航标题,最后把准备好的模板贴进去。
怎么做:转写中强调路径不要写错地方,path 直接写到 qq,不要写成 .vue;标题配置为“QQ登录”。
json
{
"path": "pages/login/qq",
"style": {
"navigationBarTitleText": "QQ登录"
}
}页面文件放在 pages/login/qq.vue,模板替换后保存。
易错点:路径大小写、目录层级要一致;path 不写 .vue;标题别忘配。转写里“不要写点不啊,就直接Q”就是提醒不要写扩展名。
从地址 hash 取 access_token:回调参数已经拼在地址上
为什么需要:QQ 授权跳转后,access_token 会拼在地址的 hash 部分,后续换 openid 要用它。
怎么想:location 是 BOM 对象,location.hash 表示从 # 开始到地址结束的部分。转写中先用 location.hash 观察结构,再截取 access_token= 后面的值。
怎么做:转写采用固定截取:从第 14 位开始,截 32 位,即 slice(14, 46)。
js
const access_token = location.hash.slice(14, 46)易错点:hash 前缀长度和 token 长度变化时要重新核对;转写里“俄克赛 token”应写 access_token;不要从 localhost 或 127.0.0.1 的回调里想当然取,正式联调要用配置好的域名访问。
创建云函数获取 openid:不能前端直接跨域请求
为什么需要:QQ 的 openid 接口不能直接在前端请求,否则会跨域;云函数相当于自己的服务器。
怎么想:先创建云函数 QQ-connect,在云函数里用 uniCloud.request 调 QQ 接口,把 access_token 传过去,返回 JSON 数据。
怎么做:右键新建云函数,默认模板创建;云函数内发请求,参数按官方文档传 access_token,返回格式选 JSON。
js
const res = await uniCloud.request({
url: 'QQ官方openid接口地址',
data: { access_token }
})
return res先打印结果看结构,再解构 data.openid、data.client_id,最终返回 openid。
易错点:转写中“jason”应写 json;接口地址以官方文档/讲义为准;参数名、大小写不能错;改完云函数必须“上传部署”,否则云端仍跑旧代码。
配置跨域域名与本地/云端云函数:联调最容易漏的一步
为什么需要:调用云函数和第三方接口时,后台需要配置当前域名,否则可能跨域或调用失败。
怎么想:到 uniCloud 后台的跨域设置里新增域名;域名只写域名本身,不带 http 协议。开发时还要区分“连接本地云函数”和“连接云端云函数”。
怎么做:后台新增当前访问域名;云函数改完后右键“上传部署”;需要看云端结果时勾选连接云端函数。
易错点:转写中多次出现“改了代码没上传”导致结果不对;云端执行时本地控制台看不到 console.log;域名不要写成 http://...。转写里“logo host”应纠正为 localhost,“127”指 127.0.0.1,它们只适合本地开发调试。
获取 QQ 昵称头像并渲染:再调一次用户信息接口
为什么需要:拿到 openid 后,才能继续获取 QQ 用户昵称和头像,用于页面展示。
怎么想:在云函数里再发一次请求,参数包括 access_token、oauth_consumer_key(即 APPID/client_id)和 openid;解构返回的 data,取昵称和头像字段。
怎么做:云函数返回 openid、nickname、avatar;页面用 callFunction 调用后存到 userInfo,模板绑定头像和昵称。
js
const res = await uniCloud.callFunction({
name: 'QQ-connect',
data: { access_token: location.hash.slice(14, 46) }
})
userInfo.value = res.result // 转写强调注意是否还有一层 resulthtml
<img :src="userInfo.avatar" />
<text>{{ userInfo.nickname }}</text>易错点:oauth_consumer_key 不要拼错;头像字段以实际返回为准,转写中选了较大尺寸头像;callFunction 返回层级要注意,转写里反复提醒“有两个 result”。
检测绑定状态:先 QQ 登录,再绑定手机号
为什么需要:展示头像昵称后,需要判断当前 QQ 用户是否绑定手机号;未绑定才展示绑定页。
怎么想:先调“QQ登录/检测绑定状态”接口,传 openid 等参数和 s: 'QQ';根据返回 code === 1001 或 b_flag === false 判断未绑定,用 v-if 控制绑定页面展示。
怎么做:定义 isBind 初始为 true,未绑定时置 false,模板用 v-if="!isBind" 展示绑定区域。
js
const res = await uniCloud.callFunction({
name: 'QQ-connect',
data: {
...userInfo.value,
s: 'QQ'
}
})
if (res.result.code === 1001) {
isBind.value = false
}易错点:转写中“检测绑定状态”和“QQ登录”接口是一个意思;顺序必须是先 QQ 登录、再发送验证码和绑定手机号,反过来会出错;openid 大小写要对应;改云函数后仍要上传部署。
页面准备:QQ 登录页与 token 入口
为什么需要:QQ 授权成功后会跳回我们自己的页面,地址里带着 access_token。后续获取 openid、用户信息都要用它,所以先把页面和参数入口准备好。
怎么想:先在 login 目录下新增 QQ.vue,再到 pages.json 的主包登录配置里补上页面路径和标题“QQ登录”。授权回跳后,从 location.hash 中截取 token。转写稿里说的“logo host”应纠正为 localhost,“俄克赛 token”应理解为 access_token。
怎么做:页面路径按转写稿写在 login 下,配置里不要带 .vue。token 从 location.hash 截取:slice(14, 46),即从第 14 位开始取 32 位。
js
// 页面中:从地址 hash 中取 access_token
const accessToken = location.hash.slice(14, 46)
const res = await uniCloud.callFunction({
name: 'QQ-connect',
data: { access_token: accessToken }
})易错点:hash 从 # 开始,截取位置和长度要数准;路径配置不要写成 QQ.vue;不要把 localhost 写成“logo host”。
为什么不能直接请求 QQ 接口
为什么需要:转写稿明确提到,直接请求 QQ 的接口会跨域。浏览器页面不能随便跨域拿第三方数据,所以要让云函数替我们发请求。
怎么想:云函数相当于“自己的服务器”。页面调用云函数,云函数再请求 QQ 接口,最后把结果 return 给页面。转写稿里的“靠方式”是 callFunction,“瑞特”是 return。
怎么做:在云函数里用 uniCloud.request 发请求,地址按官方文档填,参数里带 access_token,返回格式按文档要求传 json。
js
// 云函数中:按转写稿整理的关键代码
exports.main = async (event, context) => {
const result = await uniCloud.request({
url: 'QQ 获取 openid 的接口地址,按官方文档填写',
data: {
access_token: event.access_token,
fmt: 'json' // 返回 json 格式
}
})
console.log(result) // 云函数日志在云端看,页面看不到
return result
}易错点:不要在页面里直接请求 QQ 接口;云函数里的 console.log 页面看不到,调试时要把结果 return 出来;access_token 不要拼错。
创建 QQ-connect 云函数
为什么需要:获取 openid 的逻辑要放在服务端,云函数就是承载这段逻辑的地方。
怎么想:右键新建云函数,命名 QQ-connect,表示 QQ 互联。用默认模板创建即可。转写稿里“欧盆 ID”应纠正为 openid。
怎么做:创建后得到空白云函数,在 exports.main 里写请求逻辑。参数从 event 里取,event.access_token 就是页面传进来的 token。
易错点:注意创建位置和同名文件夹;不要忘了传返回格式参数;openid 是小写,不是 openId。
调用云函数并解构 openid
为什么需要:页面后续登录、绑定手机号都要用 openid,所以云函数要把 openid 返回给页面。
怎么想:页面用 uniCloud.callFunction 调 QQ-connect,传 access_token;云函数请求成功后,从返回数据里解构 openid 和 client_id。转写稿里的“可烂的 ID”是 client_id,“date”是 data。
怎么做:按转写稿返回结构,先取 data,再解构 openid、client_id,最后 return 出去。
js
// 云函数中:按转写稿返回结构解构
const { data } = result
const { openid, client_id } = data
return { openid, client_id }页面接收时注意:res.result 才是云函数返回结果。转写稿里提到“解构过两次”,是因为外层还有 result,写代码时别把层级套错。
易错点:openid 大小写要和接口文档一致;client_id 就是申请 QQ 互联时的 APPID;return 结构别多套一层。
上传部署、跨域配置与调试
为什么需要:云端运行的是“已上传部署”的代码,不是本地刚改的代码。转写稿里多次出现“改完没上传,结果不对”,就是这个问题。
怎么想:先在 uniCloud 后台的跨域设置里新增当前域名,域名不带 http 协议;再右键云函数“上传部署”。页面可切换“连接本地云函数”或“连接云端云函数”。转写稿里的“clog”是 console.log。
怎么做:改完云函数后右键上传部署,刷新页面看返回。若报 openid 是必须的,先检查是否忘上传、字段是否大小写不一致、token 是否失效。
js
// 页面中:查看云函数返回
const res = await uniCloud.callFunction({
name: 'QQ-connect',
data: { access_token: accessToken }
})
console.log(res.result)易错点:本地云函数和云端云函数混淆;改代码后忘上传部署;跨域域名带协议;token 失效要重新授权。
后续:检测绑定手机号
为什么需要:拿到 openid 后,要先调 QQ 登录/检测绑定状态接口,再发短信验证码、绑定手机号。转写稿明确说顺序不能反。
怎么想:检测接口返回 code=1001 或 flag=false,表示未绑定手机号;此时展示绑定页面,否则不展示。
怎么做:在 callFunction 成功后调检测接口,传 openid、source: 'QQ' 等参数,用变量控制绑定页展示。
易错点:先绑定再登录会出错;openid 小写;云端代码没部署会导致接口参数对不上。
代码示例
1. pages.json —— 注册 QQ 登录页面
json
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "优医咨询"
}
},
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/login/qq",
"style": {
"navigationBarTitleText": "QQ登录"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "优医咨询",
"navigationBarBackgroundColor": "#FFFFFF"
}
}要点 / 易错点
- 新增页面后必须在主包的
pages数组里加一项,否则页面打不开。 path只写目录和文件名,不要写.vue后缀:pages/login/qq,写成qq.vue会报找不到页面。- 在数组最后一项前面加逗号,
style里的navigationBarTitleText就是页面顶部标题,这里写QQ登录。 - 页面文件要放在
pages/login/目录下、命名qq.vue,路径和配置必须一一对应。
2. pages/login/qq.vue —— 取 token、调云函数、渲染头像昵称、检测绑定状态
vue
<template>
<view class="qq-login">
<!-- 用户信息:头像 + 昵称 -->
<view class="user-info">
<image class="avatar" :src="userInfo.avatar" mode="aspectFill" />
<text class="nickname">{{ userInfo.nickname }}</text>
</view>
<!-- 没绑定手机号时才展示绑定区域 -->
<view v-if="!isBind" class="bind-box">
<input class="phone" v-model="phone" type="number" placeholder="请输入手机号" />
<view class="code-row">
<input class="code" v-model="code" type="number" placeholder="请输入验证码" />
<button size="mini" @click="sendCode">获取验证码</button>
</view>
<button type="primary" @click="bindPhone">绑定手机号</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
// QQ 用户信息:openid / 昵称 / 头像
const userInfo = ref({ openid: '', nickname: '', avatar: '' })
// 绑定状态:初始认为已绑定,只有接口明确告诉没绑定才展示绑定区域
const isBind = ref(true)
const phone = ref('')
const code = ref('')
// 讲义中封装在 api/user.js,这里内联方便直接跑
function checkBindStatus(params) {
return new Promise((resolve, reject) => {
uni.request({
url: 'https://换成接口文档里的地址/api/user/qq/login',
method: 'POST',
data: params,
success: resolve,
fail: reject
})
})
}
onLoad(async () => {
// 1. 地址栏 # 后面就是 access_token,从第 14 位开始截 32 位
const accessToken = location.hash.slice(14, 46)
// 2. 调云函数:用 access_token 换 openid + 昵称 + 头像
const res = await uniCloud.callFunction({
name: 'QQ-connect',
data: { access_token: accessToken }
})
userInfo.value = res.result // 云函数 return 的对象
// 3. 先调 QQ 登录接口,检测是否绑定过手机号
const { code: statusCode, data } = await checkBindStatus({
openid: userInfo.value.openid,
nickname: userInfo.value.nickname,
avatar: userInfo.value.avatar,
source: 'QQ'
})
// code === 1001 表示未绑定,b_flag 为 false
if (statusCode === 1001) {
isBind.value = data.b_flag
}
})
function sendCode() {
// 发送短信验证码(接口见讲义)
}
function bindPhone() {
// 绑定手机号(接口见讲义)
}
</script>
<style>
.qq-login {
padding: 60rpx 40rpx;
}
.user-info {
display: flex;
flex-direction: column;
align-items: center;
}
.avatar {
width: 160rpx;
height: 160rpx;
border-radius: 50%;
}
.nickname {
margin-top: 20rpx;
font-size: 32rpx;
}
.bind-box {
margin-top: 60rpx;
}
.code-row {
display: flex;
align-items: center;
margin: 20rpx 0;
}
</style>要点 / 易错点
location.hash拿到的是#access_token=xxxx,#access_token=正好 14 个字符,所以slice(14, 46)就是从第 14 位开始取 32 位——下标数错一位,token 就废了。uniCloud.callFunction返回的是{ result: ... },所以取值用res.result,不要直接res.openid。- 云函数里
return什么,页面就能拿到什么,所以 openid、nickname、avatar 要在云函数里一次性返回。 isBind初始值是true(当作已绑定),接口返回1001时才把b_flag(false)赋进去,模板用v-if="!isBind"取反,写反了就变成“已绑定反而让你绑手机号”。- 顺序不能错:先调 QQ 登录(检测绑定状态),再发验证码、绑手机号,反了后端接口会报错。
onLoad要从@dcloudio/uni-app引入,不是从vue引入。
3. uniCloud-aliyun/cloudfunctions/QQ-connect/index.js —— 云函数换取 openid 与用户信息
js
'use strict'
exports.main = async (event, context) => {
// 页面传过来的 access_token
const { access_token } = event
// 1. 用 access_token 换 openid(同时返回 client_id)
const res1 = await uniCloud.httpclient.request(
'https://graph.qq.com/oauth2.0/me',
{
method: 'GET',
data: { access_token },
dataType: 'json' // 固定写死 json,否则返回的不是 JSON
}
)
const { client_id, openid } = res1.data
// 2. 用 openid 拿 QQ 用户的昵称和头像
const res2 = await uniCloud.httpclient.request(
'https://graph.qq.com/user/get_user_info',
{
method: 'GET',
data: {
access_token: access_token,
oauth_consumer_key: client_id, // 就是申请 QQ 互联时的 APPID
openid: openid
},
dataType: 'json'
}
)
const { nickname, figureurl_qq_2 } = res2.data
// 3. 只把页面要用的三个数据返回
return {
openid,
nickname,
avatar: figureurl_qq_2
}
}要点 / 易错点
- 为什么不在页面里直接请求
graph.qq.com?会跨域。云函数相当于自己的服务器,由它去请求 QQ 的接口。 dataType: 'json'虽然文档写“可选”,但这里必须传,否则 QQ 返回的是callback(...)文本,res.data解不出来。oauth_consumer_key传的就是申请 QQ 互联时拿到的 APPID(即 client_id),参数名一个字母都不能错,写错会提示“openid 是必须的”。- 解构返回结构时注意层级:
res1.data、res2.data里才是openid/nickname/figureurl_qq_2,头像有多个尺寸,随便选一个(这里用大图)。 - 最关键的坑:云函数代码改完必须右键「上传部署」,页面上调的是云端那份代码,只保存不上传,结果永远是旧逻辑。
常见错误
- 现象:页面调用云函数报跨域/请求失败 → 原因:uniCloud 后台未配置当前前端域名 → 修法:在 uniCloud 跨域设置中新增当前域名,域名不要带
http/https。 - 现象:本地改完云函数后页面结果仍旧/参数报错 → 原因:运行的是云端函数,但代码未上传部署 → 修法:右键“上传部署”,确认函数列表时间已更新,再刷新验证。
- 现象:
openid参数提示必填或返回异常 → 原因:参数名大小写/拼写不一致,或result层级未解构正确 → 修法:对照接口文档逐字核对参数名,按返回结构解构data,必要时先console.log完整结果。 - 现象:
access_token截取错误 → 原因:直接从location.hash取整段,未按等号后固定位置/长度截取 → 修法:先打印location.hash,确认起始索引和长度,再用slice(start, end)截取并校验。 - 现象:QQ 登录后绑定手机号流程失败 → 原因:先调绑定手机号,后调 QQ 登录检测绑定状态,顺序与后端约定不一致 → 修法:先调 QQ 登录/检测绑定状态,再发送验证码、绑定手机号。
- 现象:新页面扫码授权后跳转 404/找不到页面 → 原因:页面文件已建但
pages.json未配置路径或路径写错 → 修法:在主包pages中补充页面路径与导航标题,确认路径与文件实际位置一致。
练习
- 按“现象→排查→修复”写出一次云函数调用失败的排查过程,必须覆盖跨域配置与上传部署两项检查。
- 给定一段
location.hash,写出获取access_token的截取代码,并说明如何验证截取结果正确。 - 根据云函数返回结构,写出解构
openid、昵称、头像的代码,并说明为什么要先打印完整结果。 - 画出 QQ 登录到绑定手机号的调用顺序,并标注哪一步必须先执行、为什么。
- 新建 QQ 登录页面后,列出页面文件、
pages.json配置、云函数调用三处需要检查的清单。
课堂画面






原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 介绍ip和域名的关系 | 00:08 |
| 2 | 优医咨询QQ登录页面模板 | 03:43 |
| 3 | 优医咨询-创建云函数 | 11:51 |
| 4 | 优医咨询-云函数获取openid | 09:21 |
| 5 | 优医咨询-云函数获取openid | 05:05 |
| 6 | 优医咨询-获取QQ用户信息 | 18:00 |
| 7 | 优医咨询-检测是否绑定手机号 | 15:19 |
小结
- 本地开发地址与正式域名行为不同,涉及第三方接口时要优先检查域名与跨域配置。
- 云函数本地调试和云端运行是两条链路,改代码后必须确认是否上传部署。
- 第三方联调要先打印完整返回,再按真实结构解构,不能凭猜测写参数。
access_token、openid等关键参数要逐字核对大小写、拼写和截取范围。- QQ 登录、检测绑定状态、发送验证码、绑定手机号存在明确先后顺序,不能颠倒。
- 新页面能访问的前提是文件路径与
pages.json配置一致。 - 排查此类问题应按“域名配置→云函数部署→参数传递→返回解构→业务流程顺序”逐层定位。