uni-app onShareAppMessage hook 原理分析

前言

大家好,这里是《前端毕业班》,前端开发者的自救互助小组。在 AI 与不确定性并存的时代,我们一起看清焦虑,聊技术、聊趋势,也聊前端还能走多远,走去哪。

背景

onShareAppMessage 是微信小程序中用于定义用户点击右上角分享时的行为的一个生命周期函数,它允许开发者自定义分享内容、路径和图片等信息。但是 vue 并没有提供一个专门的 onShareAppMessage 钩子函数来处理这个生命周期事件,开发者依然能够在 vue 组件中使用 onShareAppMessage 来定义分享行为,本篇文章会分析 uni-app 的编译时和运行时到底做了什么工作。

编译

你的页面写了如下代码

html 复制代码
<template>
   <view>测试</view>
</template>
<script setup>
  import { onShareAppMessage } from '@dcloudio/uni-app'
  onShareAppMessage(() => {
      return {
          
      }
  })
</script>

会被编译为

js 复制代码
"use strict";
const common_vendor = require("../../common/vendor.js");
const _sfc_main = {
  __name: "index",
  setup(__props) {
    common_vendor.onShareAppMessage(() => {
      return {};
    });
    return (_ctx, _cache) => {
      return {};
    };
  }
};
_sfc_main.__runtimeHooks = 2;
wx.createPage(_sfc_main);

页面产物调用 wx.createPage(_sfc_main) 来创建小程序页面实例。_sfc_main 上的 __runtimeHooks 是编译器生成的一个标记,表示这个组件使用了运行时钩子(如 onShareAppMessage)。在运行时,uni-app 会检查这个标记,并根据需要注册相应的生命周期函数,以确保 onShareAppMessage 能够正确地被调用。

注册

js 复制代码
wx.createPage = createPage
createPage(vuePageOptions) -> Component(parsePage(vuePageOptions, parseOptions))

parsePage 里拿到小程序页面的 methods 后,会注册页面钩子:

js 复制代码
initHooks(methods, PAGE_INIT_HOOKS)
initUnknownHooks(methods, vueOptions)
initRuntimeHooks(methods, vueOptions.__runtimeHooks)

其中 onShareAppMessage 不在默认注册的 hook 列表中,需要开发者手动注册。所以它主要靠下面几种方式补注册:

  • initUnknownHooks:Options API 顶层直接写了 onShareAppMessage。
  • initRuntimeHooks:编译器生成了 vueOptions.__runtimeHooks。

initRuntimeHooks

运行时钩子用 bitmask 标识:

js 复制代码
const MINI_PROGRAM_PAGE_RUNTIME_HOOKS = {
  onPageScroll: 1,
  onShareAppMessage: 1 << 1,
  onShareTimeline: 1 << 2,
  // ...
}

所以 onShareAppMessage 的值是 2。

initRuntimeHooks 会检查页面的 __runtimeHooks 是否包含这个 bit:

js 复制代码
function initRuntimeHooks(mpOptions, runtimeHooks) {
  if (!runtimeHooks) return

  Object.keys(MINI_PROGRAM_PAGE_RUNTIME_HOOKS).forEach((hook) => {
    if (runtimeHooks & MINI_PROGRAM_PAGE_RUNTIME_HOOKS[hook]) {
      initHook(mpOptions, hook, [])
    }
  })
}

如果 runtimeHooks & 2 成立,就会执行:

js 复制代码
initHook(methods, 'onShareAppMessage', [])

initHook

initHook 只做一件事:给小程序 options/methods 补一个代理函数。

js 复制代码
function initHook(mpOptions, hook, excludes) {
  if (excludes.indexOf(hook) === -1 && !hasOwn(mpOptions, hook)) {
    mpOptions[hook] = function(args) {
      return this.$vm && this.$vm.$callHook(hook, args)
    }
  }
}

对于 onShareAppMessage,微信实际调用的是这个代理函数。

callHook

callHook 的逻辑其实很简单,主要是调用用户注册的 hook 数组:

js 复制代码
function callHook(name, args) {
  const hooks = this.$[name]
  return hooks && invokeArrayFns(hooks, args)
}

这里的 this.$ 是 Vue internal instance,this.$['onShareAppMessage'] 是用户注册的 hook 数组。

用户写的 hook 会通过 injectHook 收集进去:

js 复制代码
instance['onShareAppMessage'] = [wrappedHook1, wrappedHook2]

invokeArrayFns

uni-app 这里使用的 invokeArrayFns 会返回最后一个 hook 的返回值:

js 复制代码
const invokeArrayFns = (fns, arg) => {
  let ret
  for (let i = 0; i < fns.length; i++) {
    ret = fns[i](arg)
  }
  return ret
}

这对分享钩子很关键,因为微信要求 onShareAppMessage 返回分享配置:

js 复制代码
return {
  title: '标题',
  path: '/pages/index/index'
}

uni-app 通过:

js 复制代码
return this.$vm.$callHook('onShareAppMessage', args)

把用户 hook 的返回值原样传回微信

总结

下面是整个运行时的最短调用链

text 复制代码
wx.createPage(_sfc_main)
-> createPage
-> parsePage
-> initRuntimeHooks(methods, vueOptions.__runtimeHooks)
-> initHook(methods, 'onShareAppMessage', [])
-> 微信调用 methods.onShareAppMessage(args)
-> this.$vm.$callHook('onShareAppMessage', args)
-> callHook
-> invokeArrayFns(this.$['onShareAppMessage'], args)
-> 返回分享配置给微信
相关推荐
Eiceblue8 分钟前
使用 Python 实现 Excel 行列转置(保留单元格样式)
前端·python·excel
WeiXin_DZbishe10 分钟前
基于Node.js的马拉松赛事管理系统的设计与实现-计算机毕设 附源码【课程设计】90211
java·javascript·vue.js·python·php·课程设计
西西学代码23 分钟前
像素和颜色之间的转换(Flutter)
前端·javascript·flutter
茶底世界之下36 分钟前
羽毛颜色已经很鲜艳,还要继续加色吗?用 Awaking 试一次减饱和
前端·ios·运营
风早爽太44 分钟前
Mac 搭建 Vue 开发环境
前端·vue.js
Lank_M1 小时前
Word图片裁剪转HTML失效,转换器各漏读了哪一层
前端
青烟小生1 小时前
前端架构设计实战:从组件思维到系统思维
前端
时间海8711 小时前
对象键清洗之前,先拦住重名
javascript·后端
Amos_Web1 小时前
Rspack 源码解析(二十):Typed Hook 系统如何靠宏生成
前端·rust·前端框架
程序员大阳1 小时前
副队长CSS教程(29)–CSS Grid 实战:实现通用图文混排布局
前端·css·grid·图文混排