对比微信和支付宝小程序的项目结构,看看各自的实现原理
微信小程序项目结构
上图为微信小程序的项目结构。页面包含小部件中的每个页面。每个页面由页面结构、页面样式、页面配置和逻辑代码四部分组成。
页面结构文件是通过自定义微信标签编写的。
页面逻辑是用 JavaScript 编写的。
与 CSS 文件类似,它定义页面上元素的样式。
页面上的权限及其他配置信息。
微信小程序技术选型
小程序的定位特点是轻松、快捷。基于这两个特点,微信在技术选型上做出了一些判断。
渲染接口技术
缺点:无法动态打包、动态分发。
缺点:如果我们使用纯Web技术来渲染小程序,在一些交互复杂的页面上可能会面临一些性能问题。这是因为在Web技术中,UI渲染和JavaScript脚本执行是在一个线程中单一执行的,这很容易导致一些逻辑任务占用UI渲染资源。
- 嵌入式客户端技术和Web技术之间,结合各自特点进行渲染的技术
从底层渲染的角度来看,PhoneGap和微信JS-SDK是相似的。他们最终都使用浏览器。核心来渲染界面。 RN 则不同。虽然使用了Web相关技术编写,并且也利用了JavaScript解析和执行的特性,但是RN在渲染的底层使用了原生的客户端渲染。我们选择类似于微信JSSDK的Hybrid技术,即界面以成熟的Web技术渲染为主,辅以大量接口,提供丰富的客户端原生能力。同时,每个widget页面都用不同的WebView渲染,可以提供更好的交互体验,更接近原始体验,避免单个WebView任务过于繁重。
微信没有选择RN的原因
- RN支持的样式是CSS的子集,无法满足Web开发者日益增长的需求,并且RN的改造成本和风险很大。
- RN 当前功能下还存在一些不稳定问题,比如性能、Bug 等。RN 将所有渲染工作交给客户端原生渲染。事实上,一些简单的界面元素完全可以使用Web技术进行渲染,并且非常稳定。
- RN 有一些不可预测的因素,比如之前出现的许可协议问题
内置组件的渲染方法
在Android中,一个内置方法被注入到WebView的window对象中,最终会封装在WeiXinJSBridge中。兼容层主要提供了两个方法:invoke(调用)和monitor(监听)。开发人员插入嵌入式组件。一般来说,当组件在运行时插入到 DOM 树中时,会调用客户端接口来通知客户端应该在哪里渲染原生接口。后续开发者更新组件属性时,也会调用客户端提供的更新接口来更新原接口的某些部分。
网页渲染带来的问题及解决方案
由于JavaScript的灵活性和浏览器的丰富功能,会导致很多不可控的隐私。因此,微信提供了一个纯JS的执行环境,其中的控件也是可定制的。因此,这个沙箱环境不能有任何浏览器相关的接口,仅提供纯JavaScript解释和执行环境。那么HTML5中的ServiceWorker和WebWorker函数就满足了这样的条件,这两个函数都允许另一个线程运行。 JavaScript。但考虑到小程序是多WebView架构,每个小程序页面都是经过另一个WebView渲染后才显示的。在这种架构下,我们很难在特定的WebView中使用一个ServiceWorker来管理所有的小程序页面。得益于客户端系统中的JavaScript解释引擎(iOS中使用内置的JavaScriptCore框架,Android中使用腾讯x5核心提供的JsCore环境),我们可以创建一个单独的线程来运行JavaScript。在这个环境下执行的是小程序业务逻辑相关的代码,也就是我们前面提到的逻辑层。所有与界面渲染相关的任务都在WebView线程中执行,逻辑层代码用来控制渲染哪些界面,所以这一层当然就是所谓的渲染层。这就是小程序双线程模型的由来。
为了防止标签定义带来的一些问题,微信适配了一套标签语言,WXML。这套标签语言编译完成后,最终会生成HTML。
渲染与逻辑分离
以上是小程序渲染技术的选择。通过选择,由于渲染和逻辑不再在同一个浏览器中进行,一个是在纯JS环境中渲染,一个是通过WebView渲染,所以小程序的运行环境分为渲染层和逻辑层。 WXML模板和WXSS样式工作在渲染层,JS脚本工作在逻辑层。
小程序的渲染层和逻辑层分别由两个线程管理:渲染层的界面使用WebView进行渲染;逻辑层使用JsCore线程来运行JS脚本。一个小程序有多个接口,因此渲染层有多个WebView线程。这两个线程之间的通信将通过微信客户端进行中介。逻辑层发送的网络请求也是通过Native进行转发。小程序的通信模型如图所示。 。
数据驱动的显示变化
在开发UI界面的过程中,程序要维护很多变量的状态,同时操作相应的UI元素。随着界面变得越来越复杂,我们需要维护很多变量状态,处理界面上很多交互事件,整个程序变得越来越复杂。通常界面视图和变量状态是相关的。如果有某种“方法”将状态和视图绑定在一起(当状态改变时,视图也可以自动改变),那么我们就可以省去手动改变视图的工作。 。
小程序的逻辑层和渲染层是两个独立的线程。在渲染层,宿主环境会将WXML转换为相应的JS对象。当逻辑层的数据发生变化时,我们需要通过宿主环境提供的setData方法将逻辑层的数据发送到渲染层,然后比较前后的差异。利用原始Dom树上的差异来渲染正确的界面。
使用setData将消息数据从“Hello World”改为“Goodbye”,生成的JS对象对应的节点就会发生变化。此时,你可以比较前后两个JS对象,得到变化的部分,然后将这个差异应用到原来的Dom树上,从而达到更新UI的目的。这就是“数据驱动”的原则。
事件处理
UI 程序必须与用户交互。例如,用户可以单击界面上的按钮,或长按特定区域。这种类型的反馈应该通知到开发人员的逻辑层。相应的处理状态必须呈现给用户。由于WebView目前的功能只是渲染,微信对事件分发进行了特殊处理。捕获所有事件后,将其扔到逻辑层并交给 JavaScript 进行处理。
事件派发处理有两种机制:事件捕获和冒泡。通过native发送到JSCore,通过JS响应相应的事件后,Dom发生改变。这些变化将会反映在虚拟Dom上,然后进行真正的渲染。
数据通信
小程序基于双线程模型,这意味着所有数据传输都是线程之间的通信,这意味着会存在一定的延迟。这与传统Web相反,当界面需要更新时,通过调用更新接口来同步渲染用户界面。在小程序架构中,一切都将是异步的。
异步会使各部分的运行时更加复杂。例如,当渲染首屏时,逻辑层和渲染层会同时开始初始化工作,但渲染层需要逻辑层的数据来渲染界面。如果渲染层初始化工作完成得很快,就必须等待逻辑层的指令。只有这样我们才能进行下一步。因此,逻辑层和渲染层必须有某种机制来保证正确的时序。
在每个小程序页面的生命周期中,都会有多次页面数据通信。逻辑层将页面数据(data和setData的内容)发送给视图层,视图层将用户事件回送给逻辑层。
通过Json传输数据。提高性能的方法是减少交互数据量。
缓存机制
小程序托管环境会管理不同小程序的数据缓存。不同widget的本地缓存是分开的。每个小程序的缓存空间上限为10MB。如果当前缓存已达到10MB,写入缓存将触发失败回调。
小部件的本地缓存不仅通过小部件的维度隔离了空间,考虑到同一设备可以登录不同的微信用户,宿主环境也隔离了不同用户的缓存,避免不同用户之间的数据保护泄露用户。
由于本地缓存存储在当前设备中,用户切换设备后无法从其他设备读取当前设备数据。因此,不建议用户的关键信息只驻留在本地缓存中。数据应该放在服务器上进行持久化。贮存。
支付宝小程序
支付宝小程序简介
支付宝小程序和微信小程序的实现大致相同,所以这里主要关注两者的区别。
支付宝小程序目录结构
支付宝小程序业务架构图
在渲染引擎上,支付宝小程序不仅提供了JavaScript+Webview方式,还提供了JavaScript+Native方式,对性能要求更高对于场景可以选择原生渲染模式,给用户更好的体验。
运行时架构
小程序编程模型分为几个页面。每个页面都有自己的模板、CSS 和 JS。实际执行时,业务逻辑在独立的JavaScript引擎中运行JS代码。 In在其自己独立的Web视图中运行每个页面的模板和CSS,并通过navigateTo函数在页面之间切换。
每个 Web 视图中的页面与公共 JavaScript 引擎中的逻辑之间的交互是通过消息服务进行的。页面上的一些事件将通过此消息通道传递到 JavaScript 引擎运行时。运行环境会对这个事件做出响应。 Do Some API调用可以调整为客户端支付宝小程序提供的一些功能。处理完成后,数据将被发送回相应的页面渲染容器进行处理,数据与模板结合生成最终的用户界面。 。
支付宝小程序虚拟机隔离
通常的做法是在WebView中运行渲染代码,然后启动一个新线程来运行Service Worker。当 Service Worker 需要更新域时,事件和数据通过消息通道发送到渲染线程。执行时,当业务需要向渲染层发送大量数据且对象复杂时,交互性能会比较差,所以我们针对这种情况提出了优化方案。
该方案将原有的JS虚拟机实例(即Isolate)重新设计为两部分:Global Runtime和Local Runtime。
- 全局运行时部分存储共享实体和数据,具有一个全局实例。
- Local Runtime 存储与实例本身相关的模块和私有数据,不会被共享。
在新的隔离模型下,Web视图中的v8实例是Local Runtime,工作线程中的v8实例也是Local Runtime。当工作层与渲染层交互时,直接在共享堆中创建setData对象。 ,使得渲染层的本地运行时可以直接读取该对象并使用它来渲染渲染层,减少了对象的序列化和网络传输,大大提高了启动性能和渲染性能。
初屏速度的优化
由于小程序的启动是受生命周期控制的,任何一个过程从onLaunch -> onLoad -> onShow -> onReady -> 用户操作 -> 离开首页,随时在此过程中,每个链接都可能因客观或主观原因而中断,这可能导致保存的离线页面不准确以及启动时向用户呈现的页面不正确。
所以对于首页离线缓存渲染的效果来说,保存页面的时间非常重要。我们提供了开发人员可以配置的时间。配置分两次:渲染完成后和离开首页前。为了完成渲染,在用户执行任何操作之前,首页渲染完成,并将页面保存为离线缓存页面。离开主页之前是指用户在主页上进行了一系列操作后,在跳转到其他页面之前看到的页面被存储为离线缓存页面。
出现欢迎界面问题的场景是因为缓存的页面和实际渲染的页面是分开的,是两个独立的页面。缓存页面是静态页面,真实页面是通过js动态创建的页面,所以这是正常的。方法是在真实页面创建后替换缓存页面。在这种情况下,将会出现启动画面。
为了解决这个问题,我们用虚拟判断来解决。当我们加载缓存页面时,我们将缓存页面放入原始虚拟域中。创建真实页面后生成的虚拟域与缓存页面的虚拟域进行dom-diff。通过补丁的方式将变化的内容发送到浏览器核心并渲染相应的页面,这样就可以只更新部分变化的页面内容,避免了刷新整个页面的需要,保证了内容的准确性和实时性。
支付宝采用了UC浏览器的核心优势
1。图像内存:针对低端机,实施了更严格的图像缓冲限制,以维持进一步有限的图像缓冲区。使用;多个Web视图共享图像缓冲池;完全支持 webp、apng 这些更节省内存和尺寸的图像格式。
2.性别记忆:网页视图不可见时,原始记忆处理没有特殊处理。 UC核心会释放不可见的webview的渲染内存;合理设置和调整渲染内存,避免降低滚动性能和消耗过多内存。
3.JS内存:更合理的处理v8内存gc,延迟启动时full gc的执行,避免影响启动耗时。?渲染黑色块;在某些 OOM 情况下,它避免了原始内核主动崩溃的逻辑。当内存极低时,某些功能不但不会崩溃,反而无法使用。
对我们的启发
增加widget的存储空间,包括内存和磁盘,可以缓存一些数据,加快输出页面的速度。同时,按照小程序账号的双维度划分磁盘管理。
支持第三方接入,目前的方式会导致安全和第三方行为完全不可控。可以参考微信和支付宝使用自定义标记语言来限制标记语言,并提供纯JS环境运行JS环境,WebView只负责渲染。
查看支付宝解决方案。加载时,旧页面现在呈现给用户,新页面完成后,将相应地计算并显示差异。
Native绘图采用了通过JS与Native通信的方式,在布局制定区域添加Native价格控制。
所有网络请求都由Native托管,它可以更好地控制和监控网络请求。
作者:Jensen95
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。