微信小程序转发朋友圈详解
作者:郝加升
部门:增长中心-前端
在2020年7月7日微信小程序低调的开放了一个功能,微信小程序“分享到朋友圈”。最近被产品提了相关需求,过程中遇到了一些坑。作者带着踩坑经验,给大家介绍下这个功能,以及其如何实现。
概述
点击右上角分享朋友圈
分享到朋友圈样式
朋友圈打开样式
这个功能目前只支持Android(在IOS高版本微信支持朋友圈打开小程序能力,但不能分享)。
用户打开朋友圈分享的小程序,看到不是真正的小程序,而是原本页面的“单页模式”。
什么是“单页模式”?
以下是微信官方对于“单页模式”的描述:
“单页模式”下,页面顶部固定有导航栏,标题显示为当前页面 JSON 配置的标题。底部固定有操作栏,点击操作栏的“前往小程序”可打开小程序的当前页面。顶部导航栏与底部操作栏均不支持自定义样式。 “单页模式”默认运行的是小程序页面内容,但由于页面固定有顶部导航栏与底部操作栏,很可能会影响小程序页面的布局。因此,请开发者特别注意适配“单页模式”的页面交互,以实现流畅完整的交互体验。
限制
另外,“单页模式”存在着很多限制。以下是官方给出的禁用能力列表:
限制主要包括以下几点:
- 页面无登录态,与登录相关的接口,如
wx.login
均不可用 - 不允许跳转到其它页面,包括任何跳小程序页面、跳其它小程序、跳微信原生页面
- 若页面包含 tabBar,tabBar 不会渲染,包括自定义 tabBar
- 本地存储与小程序普通模式不共用
这些限制,让“单页模式”只适用于内容展示,不适用于有较多交互。
配置
针对“单页模式”,新增了单页模式相关配置。目前这个配置里只有一个navigationBarFit属性:
navigationBarFit属性主要是针对原页面设置了自定义导航栏的情况。也就是原页面的json文件中配置了这个属性:
{
// ...
"navigationStyle":"custom"
// ...
}
给大家看一下普通导航栏和自定义导航栏的区别,下图是普通导航栏页面:
下图是自定义导航栏页面,我们在原本的导航栏位置使用了banner:
"navigationStyle":"custom"
这个设置在“单页模式”下也会生效。前文微信官方对“单页模式”的描述有说到“顶部导航栏与底部操作栏均不支持自定义样式”。如果我们在原页面设置了自定义导航栏。那么“单页模式”样式就会变成这样:
通过设置navigationBarFit为 squeezed
就可以解决这个问题:
{
// ...
"singlePage": {
"navigationBarFit": "squeezed"
}
// ...
}
设置后的样式:
开发
接下来介绍如何在小程序中实现这个功能。
第一步在需要转发朋友圈的页面中注册用户点击右上角转发功能,这是实现转发朋友圈功能的必要满足条件。
onShareAppMessage: function () {
return {
title: '转发标题',
path: '/pages/home/index',
imageUrl: '自定义图片路径'
}
}
第二步注册分享朋友圈功能(从基础库 2.11.3
开始支持):
onShareTimeline: function () {
return {
title: '转发标题',
query: 'from=pyq',
imageUrl: '自定义图片路径'
}
}
注意,这里有个问题,分享朋友圈功能不支持自定义页面路径,意味着只能转发当前页面。如果当前页面存在较多“单页模式”限制功能,就可能让我们的页面不能按预期展示。
当页面存在限制功能时,我们存在两个方案,第一个方案,针对“单页模式”做改动,不调用那些限制的功能。第二个方案,另外写一个针对“单页模式”的页面。
这两种方案都需要能判断当前是否正处在小程序“单页模式”。
我们通过判断场景值(场景值用来描述用户进入小程序的路径)是否等于 1154 来判断当前是否正处在小程序“单页模式”。场景值可以在 App
的 onLaunch
获取。
// app.js
App({
// ...
onLaunch(options) {
const { scene } = options;
this.isSinglePage = scene === 1154;
}
// ...
})
我们将是否正处在“单页模式”的Boolean值放入App实例,方便全局拿到值。
接下来说说两种方案。
第一种方案,在“单页模式”不调用那些限制功能(这是一种不推荐的方案,代码耦合性太强)。举个例子:
const app = getApp();
Page({
// ...
onLoad() {
if (!app.isSinglePage) {
wx.login({
// ...
})
}
}
// ...
})
第二种方案,针对“单页模式”另写一个页面。因为分享朋友圈功能并不支持自定义页面路径,我们只能另外写一个组件来作为“单页模式”的内容承载。
将isSinglePage放入页面的初始数据,方便在wxml中拿到:
// pages/home/index.js
const app = getApp();
Page({
data: {
isSinglePage: app.isSinglePage,
}
// ...
})
home-single-page就是分享到朋友圈的内容承载组件:
// pages/home/index.json
{
// ...
"usingComponents": {
"home-single-page": "components/home-single-page/index"
},
}
当“单页模式”时,我们展示 home-single-page
组件,否则就展示普通页面内容:
// pages/home/index.wxml
<home-single-page wx:if="{{ isSinglePage }}" />
<view wx:else>
<!-- 普通页面内容 -->
</view>
样式上虽然搞定了,但是在原本的生命周期中可能会调用一些限制功能,或者跑一些其它“单页模式”用不上的内容。我们得停止原本生命周期函数调用。
建议对传入Page的对象进行统一处理,当“单页模式”时,不调用原本的生命周期:
// pages/home/index.js
import ExtendPage from 'common/extend-page/index'
const app = getApp();
ExtendPage({
data: {
isSinglePage: app.isSinglePage,
}
// ...
})
ExtendPage函数针对“单页模式”进行统一处理:
// common/extend-page/index.js
const app = getApp();
const PAGE_LIFE = [
'onLoad',
'onReady',
'onShow',
'onHide',
'onError',
'onUnload',
'onResize',
'onPullDownRefresh',
'onReachBottom',
'onPageScroll'
];
export default function(option) {
let newOption = {};
if(app.isSinglePage) {
newOption = PAGE_LIFE.reduce((res, lifeKey) => {
if (option[lifeKey]) {
res[lifeKey] = undefined;
}
return res;
}, {})
}
return Page({
...option,
...newOption,
});
}
在“单页模式”下,我们将原本的生命周期都停止了调用。这样就能很好的将“单页模式”下的页面和普通页面进行解耦。
如果”单页模式“页面比较复杂,需要使用生命周期。我们也可以添加 singlePageLife
属性,当处在“单页模式”下,就调用 singlePageLife
内的生命周期:
// pages/home/index.js
import ExtendPage from 'common/extend-page/index'
const app = getApp();
ExtendPage({
data: {
isSinglePage: app.isSinglePage,
},
singlePageLife: {
onLoad() {
// ...
},
}
// ...
})
// common/extend-page/index.js
const app = getApp();
const PAGE_LIFE = [
'onLoad',
'onReady',
'onShow',
'onHide',
'onError',
'onUnload',
'onResize',
'onPullDownRefresh',
'onReachBottom',
'onPageScroll'
];
export default function(option) {
let newOption = {};
if(app.isSinglePage) {
const { singlePageLife } = option;
newOption = PAGE_LIFE.reduce((res, lifeKey) => {
if (singlePageLife[lifeKey]) {
res[lifeKey] = singlePageLife[lifeKey];
} else if(option[lifeKey]) {
res[lifeKey] = undefined;
}
return res;
}, {})
}
return Page({
...option,
...newOption,
});
}
文章如有疏漏、错误欢迎批评指正。
- 一文看懂ovirt的supervdsmd服务
- openstack如何扩展API之二:扩展原有核心API
- selenium+python自动化77-autoit文件上传
- selenium+python自动化78-autoit参数化与批量上传
- libvirt-内存分配和内存热插拔
- selenium+python自动化79-文件下载(SendKeys)
- selenium+python自动化80-文件下载(不弹询问框)
- libvirt-cpu分配和cpu热插拔
- 如何使用curl调试openstack的api
- selenium+python自动化81-报告优化
- Selenium+python自动化82-只截某个元素的图
- libvirt-TLS加密
- 在openstck中配置使用cloud-init
- libvirt-使用cgroup做资源分割控制
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 第014课 Jz2400_ARM异常与中断体系详解
- 作为一个程序员居然还没有属于自己的一个博客?
- 第015课 NOR Flash操作原理及裸机程序分析
- Redash 二开 - 前端环境搭建
- Verilog Task Concurrent Activation
- then, catch, finally如何影响返回的Promise实例状态
- 第016课 Nand Flash操作原理及裸机程序分析
- 第20课 SPI协议详解及裸机程序开发分析
- 第017课 LCD原理详解及裸机程序分析
- 微信小程序:一文彻底搞懂openid和unionid
- MySQL 最佳实践:程序端连接池配置
- Java获取CPU序列号
- 第018课 ADC和触摸屏硬件原理详解及裸机编程
- Mysql优化概述及其压力测试工具
- 第019课 I2C协议详解及裸机程序分析