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 instancethis.$['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)
-> 返回分享配置给微信
相关推荐
meilindehuzi_a2 小时前
从零理解并实现 RAG:用 LangChain.js 构建语义检索问答系统
开发语言·javascript·langchain
世界哪有真情3 小时前
拿人类意识卡 AI?等于用 bug 验收正式产品
前端·人工智能·后端
鱼樱前端3 小时前
别再自己拼凑了!这款 Vue 3.5+ 严肃产品级组件库,把 AI 对话、工作流和复杂数据全包了!
javascript·vue.js·github
Listen·Rain4 小时前
Vue3:setup详解
前端·javascript·vue.js
Patrick_Wilson5 小时前
修好 bug 只是开始:一次由监控需求反向重构日志结构的复盘
前端·监控·数据可视化
kyriewen5 小时前
面试官让我优化一个卡死的表格,我打开 Claude 五秒重写,他放下咖啡问我要不要来当组长
前端·javascript·面试
小粉粉hhh6 小时前
React(二)——dom、组件间通信、useEffect
前端·javascript·react.js
Csvn6 小时前
🤖 AI 生成前端代码的 5 个典型翻车现场及修复指南
前端
IT_陈寒7 小时前
SpringBoot自动装配坑了我一天,原来问题出在这
前端·人工智能·后端
索西引擎8 小时前
【React】key 属性:协调算法中的元素标识机制与最佳实践
前端·javascript·react.js