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)
-> 返回分享配置给微信
相关推荐
gogoing1 小时前
React 分包加载优化
前端·react.js
gogoing1 小时前
Babel 配置与工具
前端·javascript
亲亲小宝宝鸭1 小时前
重新install,项目就跑不起来了?!
前端·npm
Mike117.1 小时前
GBase 8a 物化视图依赖和 DDL 风险排查记录
java·服务器·前端
蜡台1 小时前
Vue3 Hook 与 Store 状态管理:深度解析与选型指南
前端·javascript·vue.js
存在的五月雨1 小时前
项目中 Vitest 配置详解:vitest.config.ts
开发语言·javascript·vue.js
淡笑沐白2 小时前
JavaScript零基础到精通
开发语言·javascript·ecmascript
無名路人2 小时前
小程序点餐页吸顶滚动
前端·微信小程序·ai编程
小小小前端啊2 小时前
前端手写代码大全
前端