编写背景接触小程序有一段时间了。一般来说,小程序开发的门槛比较低,但是基本的运行机制和原理还是需要了解的。“比如我面试的时候问了一个关于applet的问题,applet有没有window对象?他说有”,其实是没有的。感觉小程序底部有些东西他没看懂。归根结底,他应该只能使用这个工具,只是他不明白其中的道理。小程序与普通的web开发有很大的区别,需要从其技术架构的底层来分析。比如习惯了Vue和React开发的开发者会抱怨小程序新建页面的繁琐。页面必须由多个文件组成,组件支持不完善,每次更改data中的数据,都需要setData。没有像Vue.js这样的便利。观看监视器,不能操作Dom。对于复杂的场景不是很好。之前不支持npm、sass、less等预编译处理语言。“有人说小程序就像被阉割的Vue”,哈哈当然他们从设计出发点不同,我们也要了解小程序设计的初衷,通过它的使用场景,为什么会采用这个技术架构,这个技术架构有什么好处,相信大家了解了这些之后就明白了。下面我将从以下几个角度分析小程序的运行机制及其整体技术架构。了解小程序的由来在小程序出来之前,微信WebView逐渐成为移动端的重要入口。微信发布了一套完整的网页开发工具包JS-SDK,为所有网页开发者打开了一个全新的世界。Windows,让所有开发者都可以利用微信的原生能力,完成以前不可能或很难做到的事情。但是JS-SDK模式并没有解决手机网页使用体验差的问题,比如受限于设备性能和网速可能出现白屏等问题。因此设计了一个增强版的JS-SDK,即“微信Web资源离线存储”,但是在复杂的页面上还是会出现白屏的问题。原因是页面切换僵硬,点击滞后。这时候就需要一个JS-SDK无法处理的系统来让用户体验更好,小程序应运而生。快速加载,更强大的能力,原生体验,易用安全微信数据开放,高效简单开发,小程序和普通web开发的区别,小程序的开发非常相似普通的web开发,小程序的主要开发语言也是JavaScript,但是两者还是有一些区别的。普通的web开发可以使用各种浏览器提供的DOMAPI来进行DOM操作。小程序的逻辑层和渲染层是分离的。逻辑层运行在JSCore中,没有完整的浏览器对象,所以缺乏相关的DOM。API和BOMAPI。普通的网页开发渲染线程和脚本线程是相互排斥的,这就是为什么长时间的脚本执行可能会导致页面失去响应,而在小程序中,两者是分开的,运行在不同的线程中。Web开发人员在开发网页时,只需要使用带有一些辅助工具或编辑器的浏览器即可。小程序的发展是不同的。需要经过申请小程序账号、安装小程序开发者工具、配置项目等流程。小程序执行环境小程序架构1.技术选择渲染界面一般有三种技术:纯客户端原生技术渲染、纯Web技术渲染、客户端原生技术与Web相结合的混合技术技术。(Hybridtechnology)通过以下几个方面来渲染,小程序采用哪种技术方案的开发门槛:Web门槛低,Native也有类似RN的框架来支持体验:Native体验比Web好很多,并且Hybrid在一定程度上与Web相比更接近于原体验版和更新:Web支持在线更新,Native需要封装到微信审核发布控制和安全:Web可以跳转或者换页内容,且由于小程序的宿主环境为微信,存在一些不可控因素和安全风险。如果使用纯客户端原生技术编写小程序,那么小程序代码每次都需要和微信代码一起发布。这种方法是绝对不能接受的。所以,像web技术一样,必须要有一个资源包,可以在云端随时更新,下载到本地,动态执行后可以呈现界面。如果使用纯Web技术来渲染小程序,在一些复杂的交互中可能会面临一些性能问题。这是因为在web技术中,UI渲染和JavaScript脚本执行是在单线程中执行的,这很容易导致一些逻辑任务抢占UI渲染资源。所以最后采用了两者结合的Hybrid技术来渲染小程序,可以类似web的方式开发,可以在线更新代码。同时,组件的引入还有以下好处:扩展Web的能力。例如输入框组件(input、textarea)对键盘的控制能力更好。体验更好,同时也减少了WebView的渲染工作。绕过setData、数据通信和重新渲染过程,渲染性能更好。使用客户端原生渲染内置复杂组件可以提供更好的性能2.双线程模型小程序的渲染层和逻辑层分别由两个线程管理:视图层的界面使用WebView进行渲染,而逻辑层使用JsCore线程运行JS脚本。那我们为什么要这样设计呢?我们之前也提到了控制和安全。为了解决这些问题,我们需要防止开发者使用浏览器的window对象、跳转页面、操作DOM、动态执行脚本等开放接口。我们可以使用客户端系统的JavaScript引擎,iOS下的JavaScriptCore框架,Android下腾讯x5内核提供的JsCore环境。该沙箱环境仅提供纯JavaScript解释执行环境,不带任何浏览器相关接口。这就是小程序双线程模型的由来:逻辑层:创建一个单独的线程来执行JavaScript,这里执行所有与小程序业务逻辑相关的代码,负责逻辑处理、数据请求、接口调用等View层:interface所有渲染相关的任务都在WebView线程中执行,渲染哪些接口是通过逻辑层代码来控制的。一个小程序中有多个接口,所以视图层有多个WebView线程。JSBridge作为上层开发和Native(系统层)之间的桥梁,让小程序可以通过API使用原生功能,部分组件由原生组件实现,从而有很好的体验3、双线程通信将开发者的JS逻辑代码放到单独的线程中运行,但是在Webview线程中,开发者不能直接操作DOM。那么如何实现动态变化的界面呢?如上图所示,逻辑层和视图层的通信会通过Native(微信客户端)进行中继,逻辑层发送的网络请求也会通过Native进行转发。也就是说,我们可以通过简单的数据通信来更新DOM。VirtualDOM相信大家都不陌生。大概是这样一个过程:用JS对象模拟一棵DOM树->比较两个虚拟DOM树的差异->将差异应用到真实的DOM树上。如图:1.在渲染层将WXML转换成相应的JS对象。2、当数据在逻辑层发生变化时,通过宿主环境提供的setData方法将数据从逻辑层传输到Native,再转发到渲染层。3.对比前后差异后,将差异应用到原来的DOM树上,更新界面。我们将WXML转化为数据,通过Native进行转发,实现逻辑层和渲染层的交互和通信。而如此完整的框架,离不开小程序的基础库。4.小程序基础库小程序基础库可以注入视图层和逻辑层运行,主要用于以下几个方面:在视图层中,提供各种组件来构建界面元素。在逻辑层中,提供了各种组件。处理数据绑定、组件系统、事件系统、通信系统等一系列框架逻辑的类API。由于小程序的渲染层和逻辑层是由两个线程管理的,所以将两个线程注入到基础库中分别。小程序的基础库不会打包在某个小程序的代码包中,会提前在微信客户端中构建。这样可以:减少业务小程序代码包的大小,单独修复基础库中的bug,无需修改业务小程序代码包,为小程序的各个组件提供基础支持。小程序中的所有组件,包括内置组件和自定义组件,都由Exparser组织管理。Exparser的主要特点包括以下几点:基于ShadowDOM模型:该模型与WebComponents的ShadowDOM高度相似,但不依赖浏览器的原生支持,也不依赖其他库;在实现的时候,还增加了其他的API来支持小程序组件编程。可以在纯JS环境下运行:这意味着逻辑层也有一定的组织组件树的能力。高效轻量:性能好,尤其是在组件实例数量多的环境下,代码量也小。在小程序中,所有与节点树相关的操作都依赖于Exparser,包括WXML到页面最终节点树的构建、createSelectorQuery调用和自定义组件属性等,内置组件均基于Exparser框架。小程序内置了一套组件,提供了视图容器类、表单类、导航类、媒体类、开放类等几十个组件。有了如此丰富的组件集,再结合WXSS,你可以打造出任何效果的界面。在功能层面上,也满足了大部分需求。6、运行机制小程序启动有两种情况,一种是“冷启动”,一种是“热启动”。如果用户已经打开过某个小程序,然后在一定时间内再次打开小程序,此时不需要重启,只需将后台状态的小程序切换到前台即可,这个过程就是热启动;冷启动是指用户第一次打开小程序或被微信主动销毁后再次打开时,需要重新加载小程序并启动。没有重启小程序的概念。当小程序进入后台后,客户端会保持运行状态一段时间。一定时间后(目前为5分钟),会被微信主动销毁。当在短时间内(5s)接收到两次以上,收到系统内存告警后,小程序会被销毁。7、更新机制如果小程序冷启动时发现有新版本,会异步下载新版本代码包,并与客户端本地包同时启动,即新版本直到下一次冷启动才会应用小程序。如果需要立即应用最新版本,可以使用wx.getUpdateManagerAPI进行处理。8.性能优化主要的优化策略可以概括为三点:简化代码,降低WXML结构和JS代码的复杂度;合理使用setData调用,减少setData的次数和数据量;必要时使用分包优化。一、setData的工作原理小程序的视图层目前使用WebView作为渲染载体,而逻辑层则使用独立的JavascriptCore作为运行环境。在架构上,WebView和JavascriptCore是独立的模块,没有直接数据共享的通道。目前,视图层和逻辑层之间的数据传输实际上是通过双方提供的evaluateJavascript来实现的。即用户传输的数据需要先转换成字符串再拼接成JS脚本,再通过执行JS脚本传递给双方独立的环境。evaluateJavascript的执行会受到很多方面的影响,数据到达view层并不是实时的。2、常见的setData操作错误频繁去setData我们分析过一些情况,一些小程序会去setData非常频繁(毫秒级),这导致两个后果:Android用户在滑动时会感觉卡顿,操作反馈延迟严重,因为JS线程一直在编译执行渲染,用户操作事件不能及时传递给逻辑层,逻辑层不能及时传递操作处理结果给视图层;渲染有延迟,由于WebView的JS线程一直很忙,增加了逻辑层到页面层的通信时间,视图层收到的数据消息从发送到发送时间已经过了几百毫秒,并且渲染结果不是实时的;传输大量的新数据,从setData的底层实现可以看出。我们的数据传输其实就是一个evaluateJavascript脚本过程。当数据量过大时,会增加脚本的编译和执行时间,占用WebViewJS线程,页面进入后台状态时在后台页面setData(用户不可见),应该不继续setData,用户感受不到后台状态页的渲染,后台状态页的settingData也会抢占前台页面的执行。总结从以上几个角度大致分析了小程序的底层架构,从小程序的起源,到双线程的出现,设计,通信,到基础库,Exparser框架,到运行机制,性能优化,等等,都是相互关联、相互影响的选择。关于小程序底层框架的设计,其实涉及的东西很多,比如自定义组件、原生组件、性能优化等等,不是一蹴而就的,需要多读源码思考更多的。每一个框架的诞生都有它的意义。作为开发者,我们能做的不仅仅是使用这个工具,还要了解它的设计模式。只有这样,我们才能不被工具所左右,才能走得更远!
