小程序架构设计与 web 技术的差别及底层实现原理源码查看方法

2024-07-10
来源:网络整理

从小程序开发者工具源码分析原理实现

更新时间:2021-06-09 15:09:14 作者:

小程序的架构设计与 Web 技术有些不同,它吸收了 Web 技术的一些优点,同时也摒弃了 Web 技术的体验等缺点。下面以提问的形式,对小程序架构中的一些设计点进行探讨。

目录

如何查看小程序开发者工具源码

接下来我们通过微信小程序开发者工具源码讲解小程序的底层实现原理,通过开发者工具 v1.02 版本源码一窥小程序的实现思路。如何查看微信源码?Mac 用户查看微信小程序开发者工具包内容,然后输入 //app.nw/js/core/.js,注释掉以下代码即可查看开发者工具渲染出来的代码。

// 打开 inspect 窗口 if (nw.App.argv.indexOf('inspect') !== -1) { tools.openInspectWin() }

然后重启小程序开发者工具,会出现如下图左侧的页面,点击其中一个页面可以看到视图层的DOM结构,如下图右侧所示。

小程序架构设计 1.小程序渲染是在同一个线程吗?双线程机制

开发过小程序的人都知道,小程序是双线程设计,即视图渲染和业务逻辑运行在不同的线程中。这样的设计主要是为了解决 Web 技术上的一个痛点:

网页开发的渲染线程与脚本线程是互斥的,长时间的脚本运行可能会导致页面无响应或者白屏,体验不佳。

为了提供更好的体验,小程序将页面渲染线程与脚本线程分离,放在不同的线程中执行,具体实现为:

这样就解决了脚本长时间阻塞页面渲染的问题,但也带来了一些新的问题:

开发者工具通过代码加载业务逻辑层,虽然依赖环境有 DOM、BOM API,但为了保持一致性,小程序已将所有模块本地化,使其无法访问这些 API。这样两个线程就打通了,开发者工具通过后台服务充当两者之间的消息传输媒介,并提供一些基础功能。具体可参考官网图:

2.小​​程序是网页渲染吗?界面渲染机制

呈现页面主要有三种方式:

因为小程序的宿主环境是微信,所以不太可能使用纯渲染,否则所有小程序都需要跟微信一起写代码发布。使用纯 Web 渲染看似可行,支持快速在线更新,本地安装最新资源即可渲染;但是纯 Web 渲染在一些交互复杂的页面上可能会面临一些性能问题。这是因为在 Web 技术中,UI 渲染和脚本执行都是在单线程中执行的,容易导致一些逻辑任务占用 UI 渲染资源。因此小程序采用了渲染,官网描述如下:

界面以成熟的Web技术渲染为主,并辅以大量接口提供富客户端原生能力。

同时每个小程序页面的渲染都有所不同,这样可以提供更好的交互体验,更加贴近原生的体验,避免单个任务过于繁重。

既然使用了渲染方式,那么页面渲染就有可能使用原生渲染,什么情况下会使用原生渲染呢?

答案是使用小程序提供的地图、、等组件。页面中原生渲染的渲染原理可以参考官网的原生组件。不过在小程序开发者工具中,原生组件是使用html标签模拟的,具体见下一节地图组件的渲染结果。

3. 小程序是使用网页HTML标签渲染的吗? 组件框架

上面提到小程序主要采用成熟的Web技术来呈现,那么我们是否可以直接使用HTML提供的标签(如div)来组织页面呢?答案是不行。主要考虑:

因此小程序不能直接使用HTML标签来渲染页面,它提供了10多个内置组件来聚合Web标签,并且提供了沙盒环境,防止JS访问任何浏览器API。

既然小程序不能直接使用 HTML 标签来渲染页面,那么它提供的 view、-view 等内置组件,最终是不是都会转化为 HTML 提供的内置标签来渲染呢?答案是否定的。我们来看如下代码:

test

上述代码最终在开发者工具中渲染出如下图所示的元素:

可以看到,小程序提供的组件最终并没有转化为对应的HTML标签进行渲染,而是使用自定义元素进行渲染。这些内置的组件由框架进行管理,框架内置于小程序基础库中,为小程序各类组件提供基础支持。

框架基于DOM模型实现,与DOM模型高度相似,具体可以参考官网组件系统。

内置组件的命名规范都是以wx-开头的,外部引用view等内置组件最终都会调用底层的wx-view组件;view组件的创建方式如下:

4. 小程序可以操作DOM吗?数据驱动

为了控制和安全,小程序提供了沙盒环境来运行代码。JS 代码不能访问任何浏览器相关的接口,也就是说 JS 不能操作 DOM 和 BOM,否则可能会报错。小程序如何实现沙盒环境呢?就是通过将业务逻辑封装到本地环境中,本地环境修改 DOM 和 BOM 的相关 API 指针。具体封装形式如下:

那么问题来了,小程序如何将上述封装添加到业务代码中呢?其实很简单,小程序开发者工具中有一个后台服务,在访问小程序各个模块的路径时,后台服务会分别调用ne方法将请求的JS文件内容包装到中,该方法的代码如下图所示:

这是小程序在底层实现模块化的方法之一,还有一种方法就是定义一个模块,然后引用一个已定义的模块。从上面小程序中业务模块代码的封装我们可以看出:

定义的模块对传递与浏览器相关接口同名的API,比如,,等等。

有人可能会说,可以通过('this')()来访问全局作用域对象,但是小程序会阻塞这条路径,并进行改写,eval 会被重置。例如下图:

引用模块时,只传递三个参数:、、和,其他参数的值都是,业务代码中无法访问这些接口。

你可以看一下源代码的定义:

在实际的微信环境中,业务逻辑层运行在 ,没有浏览器相关的信息,无法访问 DOM;而小程序开发者工具用于运行业务逻辑代码,其有 DOM 相关的接口;所以使用上面的沙盒环境,统一禁止 JS 操作 DOM。

如果业务代码无法访问DOM,如何动态更新页面?

答案就是采用Vue这样的MVVM框架的数据驱动思想,也就是把视图状态和视图绑定在一起,当状态改变的时候,视图也能自动改变,不需要直接操作DOM。

视图的动态更新是利用DOM技术实现的,相信大家已经对DOM有了一定的了解,其流程大致如下图所示:

实际处理过程可简要描述如下:

使用 JS 对象模拟 DOM 树 -> 比较两棵虚拟 DOM 树的差异 -> 将差异应用到真实的 DOM 树中。

其中dom可以通过内置的wcc将wxml转换成js对象形式来表示DOM树结构。

下面是一张来自官网的图片来说明动态视图更新的过程:

// wxml {{msg}} // js data: { msg: 'Hello World' }

上面讲解了视图如何更新,其实在数据响应的过程中,还有一个最重要的环节,就是业务逻辑层如何将变化的数据同步到视图层,这就涉及到双线程通信了。

5.小程序基础库有什么作用?

我们在开发者工具中开发小程序时,一般会选择一个基础库,例如小程序开发者工具选择界面:

小程序基础库是用 .NET 编写的,但我们在小程序中不会直接引用它。那么我们该如何使用基础库提供的功能呢?答案是:

微信宿主环境会预先内置基础库,打开小程序时会自动将基础库注入到小程序的视图层和业务逻辑层,小程序开发者工具则由底层HTTP服务注入。

分享