# 1. 浏览器环境VS小程序环境
微信小程序是介于Native和web app之间的产物。它依托浏览器(webview)展示,同时可以调用原生能力(如获取通信录,拍照等等),同一份代码可运行在Android,iOS和微信调试开发工具内(跨平台能力)。
与RN的跨平台不同,小程序大部分UI组件并不是原生渲染,还是类似web app用浏览器渲染。只有少量组件是Native实现(Native组件层在WebView层之上)
# 浏览器运行环境
首先,浏览器的主要组件有:
- 用户界面(User Interface) - 地址栏、前进/后退按钮、书签菜单等(除了浏览器主窗口外,其他显示的各个部分都属于用户界面)。
- 浏览器引擎(Browser engine) - 在用户界面和呈现引擎之间传送指令。
- 呈现引擎(Rendering engine) important- 负责显示请求的内容(如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上)。
- JavaScript 解释器(JavaScript Interpreter) important - 用于解析和执行 JavaScript 代码。
- 网络(Networking) - 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。
- 数据存储(Data Persistence)。这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。
- 用户界面后端(UI Backend) - 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。

一般来说,浏览器运行在一个进程中(但是chrome比较特殊,每个标签页都是一个独立进程)。同时,浏览器是多线程的,比较重要的线程有:
- 呈现引擎(又称为渲染引擎):运行在UI线程中。
- JavaScript 解释器(又称为JS解析引擎):运行在JS引擎线程中。
注意:UI 渲染线程与 JavaScript 引擎线程为互斥的关系,当 JavaScript 引擎线程执行时 UI 渲染线程会被挂起,UI 更新会被保存在一个队列中等到 JavaScript 引擎线程空闲时立即被执行。
# 小程序运行环境
小程序运行时会有两个线程:『View Thread』 和 『AppService Thread』,相互隔离,通过桥接协议WeixinJsBridage进行通信(包括 setData 调用、canvas指令和各种DOM事件)
线程名称 | 所属模块 | 运行代码 | 原理 | 备注 |
---|---|---|---|---|
View | 视图层(可能有多个) | WXML/WXSS | webview渲染 | wxml编译器把wxml文件转为js(构建virtual dom);wxss编译器把wxss文件转化为js |
AppService | 逻辑层(一个) | JS | JavascriptCore运行 | 无法访问 window/document对象 |
TIP
两个线程直接如何进行数据传递?可参考微信官方说明:
通过两边提供的 evaluateJavascript 所实现。即用户传输的数据,需要将其转换为字符串形式传递,同时把转换后的数据内容拼接成一份 JS 脚本,再通过执行 JS 脚本的形式传递到两边独立环境
也就是说,两个『模块/线程』是通过系统层的JSBridage来通信的,逻辑层把数据变化通知到视图层,触发视图层页面更新,视图层把触发的事件通知到逻辑层进行业务处理。可参考下图:

所以可以得出如下结论:
小程序js代码无法操作DOM对象,也无法直接操作wxml上的容器或组件(js代码和webview没有运行在同一个线程中)
如果需要在View Thread中运行自定义js代码,可以使用wxs(微信开发的脚本语言),它和View同一个线程。
平台 | 渲染 | js运行环境 |
---|---|---|
iOS | WKWebView渲染(环境有 iOS8、iOS9、iOS10) | JavaScriptCore |
Android | X5 基于 Mobile Chrome 37 内核来渲染 | X5 JSCore来解析 |
开发工具 | Chrome Webview 渲染 | nwjs 中 |
虽然目前小程序使用 webview 渲染,但是不意味着它以后也一直使用webview渲染。作为开发者,只能依赖小程序提供的环境。而这个环境再下层如何处理,并不受开发者控制,这意味小程序未来很可能全面采用原生渲染,类似RN或Weex,毕竟,原生的UI体验更好。
# 2. 小程序底层原理与技术解析
小程序的运行机制之所以能够在宿主应用(如微信、支付宝等)内部高效运行,并且拥有独立的进程,主要依赖于其独特的架构设计和操作系统级的资源隔离。
# 一. 为什么能在宿主APP内运行?
小程序的运行并非直接嵌入宿主APP的主进程,而是通过宿主APP构建的轻量级容器环境实现,其核心依赖以下技术:
# 1.容器化架构
宿主APP作为容器:微信等宿主APP内置了一个混合渲染引擎,集成了WebView(渲染视图层)和独立的JavaScript引擎(如JSCore、V8)运行逻辑层,形成一个隔离的沙箱环境。
资源隔离:小程序的代码(WXML/WXSS/JS)和资源文件(图片、配置文件)被封装为独立包,由宿主APP下载后存储于私有目录,与宿主APP自身资源隔离。
# 2. 双线程模型与通信机制
逻辑层(App Service):运行在独立的JavaScript线程中,负责数据处理、API调用和生命周期管理,无权直接操作UI。
视图层(View Layer):由WebView渲染页面,但通过Native组件桥接实现高性能渲染(如地图、视频等原生组件)。
Native层作为中介:逻辑层和视图层的通信通过宿主APP的**Native层(JSBridge)**中转,数据经序列化后传递,避免直接跨线程访问。
那为什么要设计成双线程架构呢?首先我们来回顾一下浏览器的线程模型,浏览器是一个单线程架构,主要原因是js允许访问操作DOM,因此js线程和渲染线程只能互斥运行。
那小程序又是如何做到双线程的呢,根本原因就是微信小程序禁止js操作DOM。
# 使用双线程架构的优势一目了然:
- 提高用户体验(ui和逻辑分离,避免页面长时间阻塞和卡顿)
- 优化应用性能(运行在不同的线程中,可以同时渲染或者计算)
- 开发效率更高(解耦和松散耦合)
# 3. API代理与权限控制
Native能力代理:小程序调用摄像头、地理位置等系统功能时,实际通过宿主APP的Native代码实现,而非直接访问系统API。
权限沙箱:宿主APP对小程序的所有API调用进行权限校验,例如未授权的小程序无法访问用户通讯录。
# 二. 为什么有独立进程?
小程序在手机中显示为“独立进程”,本质是宿主APP通过操作系统机制(如Android的WebView进程隔离、iOS的WKWebView多进程模型)实现的进程级隔离。具体原因如下:
# 1. 稳定性保障
- 崩溃隔离:若小程序的WebView或逻辑线程崩溃,宿主APP的主进程不受影响(例如微信聊天功能仍可正常使用)。
- 内存限制:独立进程的内存由系统单独分配,避免小程序内存泄漏拖慢宿主APP。
# 2. 安全增强
- 沙箱隔离:小程序的JavaScript代码运行在受限环境中,无法访问宿主APP的私有数据(如微信的聊天记录)。
- 系统资源管控:独立进程的权限由宿主APP动态授予,例如网络请求需通过宿主代理,防止恶意行为。
# 3. 性能优化
- 并行渲染:视图层(WebView)和逻辑层(JS引擎)可运行在不同CPU核心,减少主线程阻塞。
- 资源优先级:系统可为小程序进程分配独立优先级,避免宿主APP的动画、交互被抢占资源。
# 三、操作系统级实现细节
# 1. Android平台
多进程WebView:Android 8+ 默认将WebView运行在独立进程(com.google.android.webview),宿主APP通过Binder IPC与小程序进程通信。
渲染进程隔离:每个小程序的WebView实例可能分配至不同进程,进一步隔离风险。
# 2. iOS平台
WKWebView多进程:iOS的WKWebView默认运行在独立的com.apple.WebKit进程,与宿主APP(如微信)隔离。
JavaScriptCore引擎:逻辑层的JavaScript代码运行在宿主APP的独立线程中,而非独立进程,但通过GCD队列实现线程级隔离。
# 四、总结:架构与系统的协同设计
小程序的运行依赖宿主APP与操作系统的深度协作:
1.宿主APP提供容器:集成双线程引擎、API桥接和资源管理。
2.操作系统提供隔离机制:通过进程/线程隔离保障安全性和稳定性。
3.性能与体验平衡:牺牲部分灵活性(如无DOM操作),换取接近原生APP的流畅体验。
# 3. 小程序分包
# 一、分包基础概念
- 主包:包含启动页面、TabBar页面和公共资源
- 分包:仅包含与当前分包相关的页面和私有资源
- 分包优势
- 优化启动速度:首次只需加载主包
- 突破体积限制:解决2MB单包限制
- 提升协作效率:不同团队可负责不同分包
# 二、分包架构设计原理
# 1. 模块化隔离机制
- 代码隔离:每个分包拥有独立的JS执行环境(WebView上下文)
- 作用域控制:
// 主包可以访问
require('/main-package/utils.js')j
// 分包A无法直接访问分包B的资源
require('../packageB/utils.js') // 报错
2
3
4
- 资源隔离:分包内资源路径自动重写,确保正确引用
# 2. 双线程通信优化
# 三、分包加载核心流程
# 1. 冷启动加载过程
- 主包下载:优先下载app.json中定义的pages和公共资源
- 分包映射表加载:解析subpackages配置生成路由映射
- 按需加载:
wx.navigateTo({
url: '/packageA/pages/shop/index' // 触发分包下载
})
2
3
# 四、底层实现关键技术
# 1. JS沙箱隔离
// 分包JS执行环境创建
function createSubpackageContext(packageName) {
const context = new VMContext()
context.global = Object.create(mainContext.global)
return context
}
2
3
4
5
6
# 2. 原生通信协议
// 微信客户端原生代码
class WXBridge {
public:
void evaluateScript(const string& package, const string& script);
void postMessage(const string& package, const string& message);
};
2
3
4
5
6