本节概述

经过前面两节的开发,我们已经完成了小程序逻辑线程和 UI 线程的启动引擎准备,这节开始,我们将完善 native bridge 层的搭建,构建起逻辑线程和UI线程之间的桥梁。

开始之前我们先来回顾一下逻辑引擎小节相关的流程图:

在这里插入图片描述

一次小程序的启动过程,我们在创建好小程序的 逻辑引擎worker 和 绘制引擎webview 之后,从启动到渲染依次会经过:

  1. 通知 webview 加载小程序资源,如果是首次启动,还需要通知逻辑线程加载资源(非首次启动则不用,一个小程序的逻辑 worker 层是公用的)
  2. 资源加载完毕后,开始通知逻辑线程创建应用实例
  3. 实例初始化完毕,请求 worker 线程获取小程序初始化渲染数据
  4. bridge 将worker层获取到的初始化数据发送给ui线程,ui线程启动渲染
  5. ui渲染完毕通过bridge 通知给逻辑worker,触发小程序的生命周期函数

在前面的双线程结构的小节中,我们已经完成了前置的: 创建worker创建webview 的准备。现在我们继续在其基础上连接起逻辑线程引擎ui线程引擎,打通经脉,启动小程序渲染

环境准备

在开始之前,我们先在之前小节的基础上调整下代码环境: 当时我们创建 webview 的时候,是模拟的小程序的配置信息。现在我们来模拟一个小程序的配置文件,然后通过网络请求读取配置信息后再注入

先创建一个小程序的编译后的配置文件,放在public目录下方便直接通过服务加载:

// config.json,这个配置文件的内容也是和我们上两节模拟的小程序逻辑代码和页面代码一致对应的
{"app": {"entryPagePath": "pages/home/index","pages": ["pages/home/index"],"window": {"navigationBarBackgroundColor": "#ffffff","navigationBarTextStyle": "black","navigationBarTitleText": "微信接口功能演示","backgroundColor": "#eeeeee"},"tabBar": [],"networkTimeout": {},"debug": true},"modules": {"pages/home/index": {"navigationBarBackgroundColor": "#ffd200","navigationBarTextStyle": "black","navigationBarTitleText": "美团","backgroundColor": "#fff","usingComponents": {}}}
}

src/native/miniApp.ts 文件夹下的 init 方法中,我们进行下调整:

async init() {// 模拟读取小程序配置文件信息
+ const configPath = `/${this.app.appId}/config.json`;
+ const res = await fetch(configPath).then(res => res.text());
+ this.appConfig = JSON.parse(res);// 获取小程序入口文件配置: 传入的path 或者 配置文件中的 entryPagePath
+ const entryPagePath = this.app.path || this.appConfig!.app.entryPagePath;// 入口页面对应的页面配置信息
+ const pageConfig = this.appConfig!.modules?.[entryPagePath];const entryPageBridge = await this.createBridge({jscore: this.jscore,isRoot: true,appId: this.app.appId,pagePath: this.app.path,pages: this.appConfig!.app?.pages,query: this.app.query,scene: this.app.scene,configInfo: mergePageConfig(this.appConfig!.app, pageConfig), // 合并配置信息,主要是页面配置和全局window配置信息的合并});
}
export function mergePageConfig(appConfig: Record<string, any>, pageConfig: Record<string, any>) {const result: Record<string, any> = {};const appWindowConfig = appConfig.window || {}; // 全局window配置信息const pagePrivateConfig = pageConfig || {};     // 页面对应的配置信息result.navigationBarTitleText = pagePrivateConfig.navigationBarTitleText || appWindowConfig.navigationBarTitleText || '';result.navigationBarBackgroundColor = pagePrivateConfig.navigationBarBackgroundColor || appWindowConfig.navigationBarBackgroundColor || '#000';result.navigationBarTextStyle = pagePrivateConfig.navigationBarTextStyle || appWindowConfig.navigationBarTextStyle || 'white';result.backgroundColor = pagePrivateConfig.backgroundColor || appWindowConfig.backgroundColor || '#fff';result.navigationStyle = pagePrivateConfig.navigationStyle || appWindowConfig.navigationStyle || 'default';return result;
}

完善webview消息通信

在前面实现webview管理模块的时候,我们预留了消息通信相关的实现,经过上一小节 UI 引擎的实现我们可以知道,bridge 侧和ui线程的通信我们直接通过挂载ui全局window上的 JSBridge 对象来完成。bridge 侧需要添加 onReceiveUIMessage API给ui线程侧调用,来发送消息到bridge 侧

src/native/webview/index.ts 文件中我们来完善通信的逻辑;

async init(callback: () => void) {// 等待frame 加载完成await this.frameLoaded();const iframeWindow = window.frames[this.iframe.name];// 给webview内部的JSBridge对象添加 onReceiveUIMessage 方法iframeWindow.JSBridge.onReceiveUIMessage = (message: IMessage) => {this.event.emit('message', message);}callback && callback();
}postMessage(message: IMessage) {const iframeWindow = (window.frames as any)[this.iframe.name];if (iframeWindow) {// 触发webview内部 JSBridge对象上的 onReceiveNativeMessage 方法完成通信iframeWindow.JSBridge.onReceiveNativeMessage(message);}
}

启动页面渲染

从上面分析的流程中我们可以发现,启动过程的触发点只需要通知两个线程加载资源即可,后续的过程将有两个线程的消息来持续推进。

现在我们来实现一个启动渲染的方法,开始让两个线程工作:

// src/native/bridge/index.ts
/*** bridge 通知逻辑线程和UI线程加载小程序资源*/
start(loadLogicSource = true) {// 通知UI线程加载资源this.webview?.postMessage({type: 'loadResource',body: {appId: this.opts.appId,pagePath: this.opts.pagePath,}});// 初始化触发一次小程序逻辑资源加载if (loadLogicSource) {this.jscore.postMessage({type: 'loadResource',body: {appId: this.opts.appId,bridgeId: this.id,pages: this.opts.pages,}});} else {this.status++;}
}

这里有个参数是是否需要逻辑线程加载资源,经过前面小节的介绍其实我们可以快速的知道,因为一个小程序的逻辑线程worker是公用的,在初次启动后,后面就可以不用再继续加载了。

同时逻辑中还有一个 status 字段,这个状态字段是用于判断小程序进行到哪一步了,是否可以进行某一个等;

比如小程序要启动创建App实例,就需要两侧线程的资源都加载准备完毕,此时 status 的状态就需要变到 2 才能继续往下进行(ui线程资源加载完毕+1 和 逻辑线程资源加载完毕+1)

现在启动的契机开始之后,后续就是完成bridge监听两侧线程的消息,来推进逻辑的渲染:

逻辑线程消息监听

逻辑线程的启动事件通知包括:

  • logicResourceLoaded 逻辑线程资源加载完毕,如果此时 status 为 2,及ui侧也完毕时,启动App实例创建
  • appIsCreated 逻辑线程App创建完毕,后面要开始通知逻辑线程初始化渲染数据
  • initialDataReady 初始化渲染数据创建完毕返回,bridge 要通知 ui 线程挂载页面了
  • updateModule 逻辑线程侧调用了 setData api更新了数据,需要把新的数据发送个ui线程重新渲染
jscoreMessageHandler(message: IMessage) {console.log('接收到来自于逻辑线程的消息: ', message);const { type, body } = message;// 判断 bridgeId 是否对应if (body.bridgeId !== this.id) return;switch (type) {case 'logicResourceLoaded':this.status++;this.createApp(); // 逻辑线程和UI准备好之后就可以开始创建App了break;case 'appIsCreated':this.status++;this.notifyMakeInitialData(); // 通知逻辑线程初始化小程序渲染数据break;case 'initialDataReady':this.status++;this.setInitialData(body); // 把逻辑线程的初始化数据设置给UI线程,UI线程开始渲染页面break;case 'updateModule':this.updateModule(body); // 逻辑线程调用setData 更新数据,通知UI渲染}
}// 通知逻辑线程创建小程序App实例
createApp() {// 只有logic和ui线程的loadResource 都完毕后,才能开始创建,此时status会变成2if (this.status !== 2) return;this.jscore.postMessage({type: 'createApp',body: {bridgeId: this.id,scene: this.opts.scene,pagePath: this.opts.pagePath,query: this.opts.query,}});
}
// 通知逻辑线程初始化渲染数据
notifyMakeInitialData() {this.jscore.postMessage({type: 'makePageInitialData',body: {bridgeId: this.id,pagePath: this.opts.pagePath,}});
}
// 将逻辑线程初始化好的渲染数据发送给ui线程渲染页面
setInitialData(data) {const { initialData } = data;this.webview?.postMessage({type: 'setInitialData',body: {initialData,bridgeId: this.id,pagePath: this.opts.pagePath,}});
}
// 逻辑线程数据更新,通知ui线程重新渲染
updateModule(payload) {const { id, data } = payload;this.webview?.postMessage({type: 'updateModule',body: {id,data,}})
}
UI 线程启动消息处理

ui 线程启动过程主要包括的事件节点有:

  • uiResourceLoaded ui线程资源加载完毕,如果 status 为2,及逻辑线程也加载完毕,可以启动创建 App 实例
  • moduleCreated ui线程模块创建完毕(在绘制过程了),此时需要通知逻辑线程创建页面实例 PageModule
  • moduleMounted ui线程页面已经挂载好了,此时通知逻辑线程触发 ready 事件
  • triggerEvent ui线程事件交互,通知逻辑线程触发相应的处理函数
uiMessageHandler(message: IMessage) {console.log('接收到来自UI线程的消息: ', message);const { type, body } = message;switch (type) {case 'uiResourceLoaded':this.status++;this.createApp();break;case 'moduleCreated':this.uiInstanceCreated(body);break;case 'moduleMounted':this.uiInstanceMounted(body);break;case 'triggerEvent':this.triggerEvent(body);break;}
}
// ui线程模块创建好,通知逻辑线程可以创建页面实例了
// 这里后面真实触发的时机回调整为 vue created 状态时执行
uiInstanceCreated(payload) {const { path, id } = payload;this.jscore.postMessage({type: 'createInstance',body: {id,path,bridgeId: this.id,query: this.opts.query,}});
}
// ui挂载完毕,通知逻辑线程触发 ready
uiInstanceMounted(payload) {const { id } = payload;this.jscore.postMessage({type: 'moduleMounted',body: { id }});
}
// 用户事件,通知逻辑线程触发处理函数
triggerEvent(payload) {const { id, methodName, paramsList } = payload;this.jscore.postMessage({type: 'triggerEvent',body: {id,methodName,paramsList}})
}

经过上面的步骤之后,我们的启动过程就连接好了,此时运行项目点击美团小程序可以看到如下效果:

录屏2025-06-29 18.05.39

本小节的代码已发布至github仓库,可前往查看完整代码: mini-wx-app

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87034.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87034.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/87034.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【攻防篇】解决:阿里云docker 容器中自动启动xmrig挖矿

解决&#xff1a;阿里云服务器docker容器被植入挖矿程序 **1. 紧急处理&#xff1a;停止挖矿进程****&#xff08;1&#xff09;查找并终止 xmrig 进程****&#xff08;2&#xff09;删除恶意文件** **2. 清理被感染的容器****&#xff08;1&#xff09;停止并删除容器****&…

对称非对称加密,https和http,https通讯原理,Charles抓包原理

文章目录 对称加密的非对称加密http和https原理TCP三次握手四次挥手https通讯流程&#xff1a;Charles抓包原理 对称加密的非对称加密 对称加密&#xff1a;发送方的接收方式使用同一个秘钥进行加密和解密&#xff0c;发送方将需要发送的数据&#xff0c;选择某种加密算法&…

Kubernetes(K8s)_15_调度原理

文章目录 Pod调度实现原理调度队列优先队列底层数据 调度缓存调度框架 Pod调度 Pod调度: 通过污点、容忍度和亲和性影响Pod的调度 调度器实现, 其基于配置器构造(其配置来源于配置API)调度过程中任何插件返回拒绝, 都会导致Pod可能再次返回调度队列 如: Pod调度简略流程 调度…

moduo之tcp客户端TcpClient

结构 #mermaid-svg-muvN6eOMXA4rCyXP {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-muvN6eOMXA4rCyXP .error-icon{fill:#552222;}#mermaid-svg-muvN6eOMXA4rCyXP .error-text{fill:#552222;stroke:#552222;}#merm…

中国科技术语杂志中国科技术语杂志社中国科技术语编辑部2025年第3期目录

理论研究 认知术语学与社会认知术语学比较研究 吴小芳; 3-11 大语言模型背景下的术语翻译研究&#xff1a;现状、问题与展望 朱玉彬;王梓; 12-20 航空事件谣言叙事中的术语初探 刘成盼;刘东亮; 21-28 定名研讨 浅谈训诂、训诂学和训诂学术语 林童; 29-35 …

自然语言处理NLP期末复习

目录 第一章1. NLP的基本过程包括哪些-自然语言处理面临的困难是什么2. 自然语言处理算法定义&#xff0c;过程和应用3. 结合自己的研究-描述研究中涉及的自然语言处理模型或算法&#xff0c;模型或算法原理&#xff0c;具体的处理过程4. 自然语言处理的的两大核心任务是5. 程序…

单片机 - STM32F103“复用功能重映射”完整解析:从JTAG释放到TIM重映射实战详解

本文将详细讲解 STM32F103 系列中常见的“复用功能重映射”&#xff08;Remap&#xff09;机制&#xff0c;包括 JTAG 占用、引脚默认功能与复用功能的关系&#xff0c;以及如何通过寄存器或标准库代码实现重映射。以 TIM3 在 PB4/PB5 上输出 PWM 为例&#xff0c;进行实战讲解…

【C语言】知识总结·内存函数

目录 前言&#xff1a; 一、内存复制函数 1. memcpy - 内存块复制 2. memmove - 内存块移动 二、内存设置函数 1. memset - 内存块填充 三、内存比较函数 1. memcmp 2.memchr 三内存分配函数 1 .malloc 2.free 总结&#xff1a; 注意事项&#xff1a; 前言&…

python+uniapp基于微信小程序面向品牌会员的在线商城系统

文章目录 具体实现截图本项目支持的技术路线源码获取详细视频演示&#xff1a;文章底部获取博主联系方式&#xff01;&#xff01;&#xff01;&#xff01;本系统开发思路进度安排及各阶段主要任务java类核心代码部分展示主要参考文献&#xff1a;源码获取/详细视频演示 ##项目…

小鱼fish系统 sudo apt update报错(密钥失效)

在使用小鱼fish提供的系统镜像文件&#xff0c;sudo apt update系统更新时遇到了以下报错&#xff0c;即ROS 2 仓库的 GPG 密钥已过期&#xff0c;以及 Docker 仓库使用了过时的密钥存储方式 fishrosfishros-linux:~$ sudo apt update 获取:1 http://mirrors.tuna.tsinghua.ed…

深度优先搜索 (DFS) 详解

1. 什么是深度优先搜索&#xff1f; 深度优先搜索&#xff08;Depth-First Search, DFS&#xff09;是一种用于遍历或搜索树或图的算法。这个算法会尽可能深地搜索树的分支。当节点v的所在边都已被探寻过&#xff0c;搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进…

文心4.5开源大模型的使用和部署

前言 就在今天&#xff0c;文心4.5模型开源了&#xff0c;不是一个&#xff0c;而是整个系列模型正式开源。很突然&#xff0c;我都震惊了。文心4.5系列开源模型共10款&#xff0c;涵盖了激活参数规模分别为47B 和3B 的混合专家&#xff08;MoE&#xff09;模型&#xff08;最…

HarmonyOs开发之——TypeScript介绍、入门,及 TypeScript、JavaScript、ArkTs的具体区别解读。

HarmonyOs开发之——TypeScript介绍、入门&#xff0c;及 TypeScript、JavaScript、ArkTs的具体区别解读。 一、 开发语言介绍&#xff1a; TypeScript是JavaScript的超集&#xff0c;ArkTS则是TypeScript的超集。ArkTs是 HarmonyOs的主力开发语言&#xff0c;它在TypeScript…

《JMS事务性会话彻底解析:消息监听中的 commit、rollback 和幂等设计》

大家好&#xff0c;我是G探险者&#xff01; &#x1f4cc; 场景引入 在实际项目中&#xff0c;我们常常面临以下挑战&#xff1a; 监听 MQ 消息失败了&#xff0c;希望自动重试&#xff1f;消费 MQ 消息后&#xff0c;要写数据库&#xff0c;但中间报错了&#xff1f;消息处…

vue3 el-table 列增加 自定义排序逻辑

在 Vue 3 中使用 Element Plus 的 <el-table> 组件时&#xff0c;如果你想增加自定义排序逻辑&#xff0c;可以通过以下几个步骤实现&#xff1a; 1. 使用 default-sort 属性 首先&#xff0c;你可以在 <el-table> 组件上使用 default-sort 属性来指定默认的排序…

ISP Pipeline(7): Gamma Correction 伽马校正

AI_Plays/ISP/Fast_ISP_Progress.ipynb at main ameengee/AI_Plays GitHub Gamma Correction&#xff08;伽马校正&#xff09;是图像处理中的一个重要步骤&#xff0c;目的是调整图像的亮度&#xff0c;使其更符合人眼的感知或显示设备的特性。 为什么需要 Gamma Correcti…

AI提取伴奏,实现卡拉OK效果 —— 「suno api/luno api/kuka api」

导读 喜欢唱歌&#xff0c;却总苦于找不到纯净的伴奏&#xff1f;或者你想把喜欢的歌曲翻唱一遍&#xff0c;却被人声干扰搞得头大&#xff1f;现在&#xff0c;AI技术已经悄悄解决了这个问题。借助AI智能工具&#xff0c;你可以轻松提取任何一首歌的伴奏&#xff0c;享受宛如…

pip介绍

pip是什么&#xff1f; pip&#xff08;Pip Installs Packages&#xff09;是Python的官方管理工具&#xff0c;用于安装、升级、卸载和管理Python第三方库及其依赖关系。它是Python生态系统的核心组件&#xff0c;通过连接PyPI&#xff08;Python Package Index&#xff09;这…

机器学习20-线性网络思考

机器学习20-线性网络思考 针对线性网络的基础问题&#xff0c;使用基础示例进行解释 1-核心知识点 1-线性模型家族的线性回归和逻辑回归分别是什么&#xff0c;线性模型家族还有没有其他的模型 线性模型家族是一系列基于线性假设的统计模型&#xff0c;它们假设因变量和自变量…

【科研绘图系列】R语言绘制世界地图分布(world map)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据数据预处理准备画图画图总结系统信息介绍 本教程旨在通过R语言及其相关地理空间分析包,展示如何对环境数据进行空间聚类分析,并将结果可视化。教程从读…