Categories
程式開發

微信小程序轉發朋友圈詳解


在2020年7月7日微信小程序低調的開放了一個功能,微信小程序“分享到朋友圈”。最近被產品提了相關需求,過程中遇到了一些坑。作者帶著踩坑經驗,給大家介紹下這個功能,以及其如何實現。

概述

點擊右上角分享朋友圈

微信小程序轉發朋友圈詳解 1

分享到朋友圈樣式

微信小程序轉發朋友圈詳解 2

朋友圈打開樣式

微信小程序轉發朋友圈詳解 3

這個功能目前只支持Android(在IOS高版本微信支持朋友圈打開小程序能力,但不能分享)。

用戶打開朋友圈分享的小程序,看到不是真正的小程序,而是原本頁面的“單頁模式”。

什麼是“單頁模式”?

以下是微信官方對於“單頁模式”的描述:

“單頁模式”下,頁面頂部固定有導航欄,標題顯示為當前頁面JSON 配置的標題。底部固定有操作欄,點擊操作欄的“前往小程序”可打開小程序的當前頁面。頂部導航欄與底部操作欄均不支持自定義樣式。
“單頁模式”默認運行的是小程序頁面內容,但由於頁面固定有頂部導航欄與底部操作欄,很可能會影響小程序頁面的佈局。因此,請開發者特別注意適配“單頁模式”的頁面交互,以實現流暢完整的交互體驗。

限制

另外,“單頁模式”存在著很多限制。以下是官方給出的禁用能力列表:

微信小程序轉發朋友圈詳解 4

限制主要包括以下幾點:

  1. 頁面無登錄態,與登錄相關的接口,如 wx.login 均不可用
  2. 不允許跳轉到其它頁面,包括任何跳小程序頁面、跳其它小程序、跳微信原生頁面
  3. 若頁麵包含tabBar,tabBar 不會渲染,包括自定義tabBar
  4. 本地存儲與小程序普通模式不共用

這些限制,讓 “單頁模式”只適用於內容展示,不適用於有較多交互

配置

針對“單頁模式”,新增了單頁模式相關配置。目前這個配置裡只有一個navigationBarFit屬性:

微信小程序轉發朋友圈詳解 5

navigationBarFit屬性主要是針對原頁面設置了 自定義導航欄 的情況。也就是原頁面的json文件中配置了這個屬性:

{
  // ...
  "navigationStyle":"custom"
  // ...
}

給大家看一下普通導航欄和自定義導航欄的區別,下圖是普通導航欄頁面:

微信小程序轉發朋友圈詳解 6

下圖是自定義導航欄頁面,我們在原本的導航欄位置使用了banner:

微信小程序轉發朋友圈詳解 7

"navigationStyle":"custom"這個設置在“單頁模式”下也會生效。前文微信官方對“單頁模式”的描述有說到“頂部導航欄與底部操作欄均不支持自定義樣式”。如果我們在原頁面設置了自定義導航欄。那麼“單頁模式”樣式就會變成這樣:

微信小程序轉發朋友圈詳解 8

通過設置navigationBarFit為 squeezed就可以解決這個問題:

{
  // ...
  "singlePage": {
    "navigationBarFit": "squeezed"
  }
  // ...
}

設置後的樣式:

微信小程序轉發朋友圈詳解 9

開發

接下來介紹如何在小程序中實現這個功能。

第一步在需要轉發朋友圈的頁面中註冊用戶點擊右上角轉發功能,這是實現轉發朋友圈功能的必要滿足條件。

onShareAppMessage: function () {
  return {
    title: '转发标题',
    path: '/pages/home/index',
    imageUrl: '自定义图片路径'
  }
}

第二步註冊分享朋友圈功能(從基礎庫 2.11.3 開始支持):

onShareTimeline: function () {
  return {
    title: '转发标题',
    query: 'from=pyq',
    imageUrl: '自定义图片路径'
  }
}

注意,這裡有個問題,分享朋友圈功能不支持自定義頁面路徑,意味著 只能轉發當前頁面 。如果當前頁面存在較多“單頁模式”限制功能,就可能讓我們的頁面不能按預期展示。

當頁面存在限制功能時,我們存在兩個方案,第一個方案,針對“單頁模式”做改動,不調用那些限制的功能。第二個方案,另外寫一個針對“單頁模式”的頁面。

這兩種方案都需要能判斷當前是否正處在小程序“單頁模式”。

我們通過判斷場景值(場景值用來描述用戶進入小程序的路徑)是否等於1154 來判斷當前是否正處在小程序“單頁模式”。場景值可以在 ApponLaunch 獲取。

// 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



  

樣式上雖然搞定了,但是在原本的生命週期中可能會調用一些限制功能,或者跑一些其它“單頁模式”用不上的內容。我們得停止原本生命週期函數調用。

建議對傳入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,
  });
}

文章如有疏漏、錯誤歡迎批評指正。

本文轉載自公眾號有贊coder(ID:youzan_coder)。

原文鏈接

微信小程序轉發朋友圈詳解