| 2017.1 | 2018.7 | 2018.9 | 2019.6 |
基于小程序几乎相同的技术原理,以及小程序的方便快捷的特性,还衍生出了多款小程序,比如抖音小程序、快手小程序、京东小程序、美团小程序等,帮助各大厂商更好的为用户提供便捷的服务。
2018 年微信小程序 “跳一跳” 爆火,记得当年食堂排队打饭的时候很多同学都在玩,助力了微信小程序在用户中的扩张,也激发了其他厂商开发小程序的热潮。
2. 原理分析
============
2.1 双线程模型
无论是微信小程序还是支付宝小程序还是百度智能小程序等等,他们的总体架构都是基于双线程的。
其中用于处理业务逻辑的 JS 代码运行在单独的线程里,渲染层(template、css)则运行在另外一个单独的线程里。
以微信小程序为例:
双线程模型不同于单线程模型,逻辑层与渲染层的数据交互需要通过 JSBridge,二者是通过发布订阅,基于当前比较比较著名的 MVVM,来实现数据的双向绑定的,从而实现数据通信。
这样我们在微信小程序中通过在逻辑层中 setData 来改变 Model 层的数据就能够实现视图数据的异步更新。
以下是微信小程序的生命周期:
2.2 整体架构
注:以下所有内容均围绕微信开发者工具展开。
打开微信开发者工具的源代码,他是基于 NW.js 运行的,所以下图中的 package.nw 就是我们要重点钻研的对象:
这里面有很多代码,都是经过混淆与压缩的,将代码在 VSCode 中打开,并安装 Prettier - Code formatter 插件可以实现对源码的格式化。但此时代码中已经不具有语义化的变量了,只能通过 API 大致推断代码是干什么的。
源码中有一个 vendor 文件夹是值得注意的,通过它可以快速新建一个示例项目,同时里面有一个十分重要的 2.17.0.wxvpkg 包,它是微信小程序的基础库,包含了下文所提及的 WebService 与 WebView 等逻辑层与渲染层的处理。
2.2.1 WAWebview
小程序视图层基础库,提供视图层的基础能力:
var __wxLibrary = {
fileName: ‘WAWebview.js’,
envType: ‘WebView’,
contextType: ‘others’,
execStart: Date.now()
};
var WAWebviewStartTime = Date.now();
var libVersionInfo = {
“updateTime”: “2020.4.4 10:25:02”,
“version”: “2.10.4”
};
/**
- core-js 模块
*/
!function(n, o, Ye) {
…
}, function(e, t, i) {
var n = i(3),
o = “core-js_shared”,
r = n[o] || (n[o] = {});
e.exports = function(e) {
return r[e] || (r[e] = {})
}
…
}(1, 1);
var __wxConfig;
var wxTest = false;
var wxRunOnDebug = function(e) {
e()
};
/**
- 基础模块
*/
var Foundation = function(i) {
…
}]).default;
var nativeTrans = function(e) {
…
}(this);
/**
- 消息通信模块
*/
var WeixinJSBridge = function(e) {
…
}(this);
/**
- 监听 nativeTrans 相关事件
*/
!function() {
…
}();
/**
- 解析配置
*/
!function® {
…
__wxConfig = _(__wxConfig), __wxConfig = v(__wxConfig), Foundation.onConfigReady(function() {
m()
}), n ? __wxConfig.__readyHandler = A : d ? Foundation.onBridgeReady(function() {
WeixinJSBridge.on(“onWxConfigReady”, A)
}) : Foundation.onLibraryReady(A)
}(this);
/**
- 异常捕获(error、onunhandledrejection)
*/
!function(e) {
function t(e) {
Foundation.emit(“unhandledRejection”, e) || console.error(“Uncaught (in promise)”, e.reason)
}
“object” == typeof e && “function” == typeof e.addEventListener ? (e.addEventListener(“unhandledrejection”, function(e) {
t({
reason: e.reason,
promise: e.promise
}), e.preventDefault()
}), e.addEventListener(“error”, function(e) {
var t;
t = e.error, Foundation.emit(“error”, t) || console.error(“Uncaught”, t), e.preventDefault()
})) : void0 === e.onunhandledrejection && Object.defineProperty(e, “onunhandledrejection”, {
value: function(e) {
t({
reason: (e = e || {}).reason,
promise: e.promise
})
}
})
}(this);
/**
- 原生缓冲区
*/
var NativeBuffer = function(e) {
…
}(this);
var WeixinNativeBuffer = NativeBuffer;
var NativeBuffer = null;
/**
- 日志模块:wxConsole、wxPerfConsole、wxNativeConsole、webviewConsole
*/
var wxConsole = [“log”, “info”, “warn”, “error”, “debug”, “time”, “timeEnd”, “group”, “groupEnd”].reduce(function(e, t) {
return e[t] = function() {}, e
}, {});
var wxPerfConsole = [“log”, “info”, “warn”, “error”, “time”, “timeEnd”, “trace”, “profile”, “profileSync”].reduce(function(e, t) {
return e[t] = function() {}, e
}, {});
var wxNativeConsole = function(i) {
…
}([function(e, t, i) {
…
}]).default;
var webviewConsole = function(i) {
…
}([function(e, t, i) {
…
}]);
/**
- 上报模块
*/
var Reporter = function(i) {
…
}([function(e, L, O) {
…
}]).default;
var Perf = function(i) {
…
}([function(e, t, i) {
…
}]).default;
/**
- 视图层 API
*/
var webViewSDK = function(i) {
…
}([function(e, L, O) {
…
}]).default;
var wx = webViewSDK.wx;
/**
- 组件系统
*/
var exparser = function(i) {
…
}([function(e, t, i) {
…
}]);
/**
-
框架粘合层
-
使用 exparser.registerBehavior 和 exparser.registerElement 方法注册内置组件
-
转发 window、wx 对象上到事件转发到 exparser
*/
!function(i) {
…
}([function(e, t) {
…
}, function(e, t) {}, , function(e, t) {}]);
/**
- Virtual DOM
*/
var virtualDOMDataThread = false;
var virtualDOM = function(i) {
…
}([function(e, t, i) {
…
}]);
/**
- webviewEngine
*/
var webviewEngine = function(i) {
…
}([function(e, t, i) {
…
}]);
/**
- 注入默认样式到页面
*/
!function() {
…
function e() {
var e = i(‘…’);
__wxConfig.isReady ? void0 !== __wxConfig.theme && i(t, e.nextElementSibling) : __wxConfig.onReady(function() {
void0 !== __wxConfig.theme && i(t, e.nextElementSibling)
})
}
window.document && “complete” === window.document.readyState ? e() : window.onload = e
}();
var WAWebviewEndTime = Date.now();
typeof __wxLibrary.onEnd === ‘function’ && __wxLibrary.onEnd();
__wxLibrary = undefined;
WAWebview 主要由以下几个部分组件:
-
Foundation
:基础模块 -
WeixinJSBridge
:消息通信模块 -
exparser
:组件系统模块 -
__virtualDOM__
:Virtual DOM 模块 -
__webViewSDK__
:WebView SDK 模块 -
Reporter
:日志上报模块 (异常和性能统计数据)
2.2.2 WAService
小程序逻辑层基础库,提供逻辑层基础能力:
var __wxLibrary = {
fileName: ‘WAService.js’,
envType: ‘Service’,
contextType: ‘App:Uncertain’,
execStart: Date.now()
};
var WAServiceStartTime = Date.now();
(function(global) {
var exportGlobal = {};
var libVersionInfo = {
“updateTime”: “2020.4.4 10:25:02”,
“version”: “2.10.4”
};
var Function = global.Function;
var Function = Function;
/**
- core-js 模块
*/
!function(r, o, Ke) {
}(1, 1);
var wxTest = false;
var wxRunOnDebug = function(e) {
e()
};
var __wxConfig;
/**
- 基础模块
*/
var Foundation = function(n) {
…
}([function(e, t, n) {
…
}]).default;
var nativeTrans = function(e) {
…
}(this);
/**
- 消息通信模块
*/
var WeixinJSBridge = function(e) {
…
}(this);
/**
- 监听 nativeTrans 相关事件
*/
!function() {
…
}();
/**
- 解析配置
*/
!function(i) {
…
}(this);
/**
- 异常捕获(error、onunhandledrejection)
*/
!function(e) {
…
}(this);
/**
- 原生缓冲区
*/
var NativeBuffer = function(e) {
…
}(this);
WeixinNativeBuffer = NativeBuffer;
NativeBuffer = null;
var wxConsole = [“log”, “info”, “warn”, “error”, “debug”, “time”, “timeEnd”, “group”, “groupEnd”].reduce(function(e, t) {
return e[t] = function() {}, e
}, {});
var wxPerfConsole = [“log”, “info”, “warn”, “error”, “time”, “timeEnd”, “trace”, “profile”, “profileSync”].reduce(function(e, t) {
return e[t] = function() {}, e
}, {});
var wxNativeConsole = function(n) {
…
}([function(e, t, n) {
…
}]).default;
/**
- Worker 模块
*/
var WeixinWorker = function(e) {
…
}(this);
/**
- JSContext
*/
var JSContext = function(n) {
…
}([
…
}]).default;
var appServiceConsole = function(n) {
…
}([function(e, N, R) {
…
}]).default;
var Protect = function(n) {
…
}([function(e, t, n) {
…
}]);
var Reporter = function(n) {
…
}([function(e, N, R) {
…
}]).default;
var subContextEngine = function(n) {
…
}([function(e, t, n) {
…
}]);
var waServiceInit = function() {
…
}
function doWAServiceInit() {
var e;
“undefined” != typeof wx && wx.version && (e = wx.version), waServiceInit(), e && “undefined” != typeof exportGlobal && exportGlobal.wx && (exportGlobal.wx.version = e)
}
subContextEngine.isIsolateContext();
subContextEngine.isIsolateContext() || doWAServiceInit();
subContextEngine.initAppRelatedContexts(exportGlobal);
})(this);
var WAServiceEndTime = Date.now();
typeof __wxLibrary.onEnd === ‘function’ && __wxLibrary.onEnd();
__wxLibrary = undefined;
WAService 基本组成:
-
Foundation
:基础模块 -
WeixinJSBridge
:消息通信模块 -
WeixinNativeBuffer
:原生 Buffer -
WeixinWorker
:Worker 线程 -
JSContext
:JS Engine Context -
Protect
:JS 保护的对象 -
__subContextEngine__
:提供 App、Page、Component、Behavior、getApp、getCurrentPages 等方法
2.2.3 虚拟 DOM
微信小程序在 WAService 里面实现了小程序的 __virtualDOM__
,通过 __virtualDOM__
模块,可以实现 JS 对象到 DOM 对象的映射。
但是这个虚拟 DOM 通过 diff 和 patch 后并不是转换成原生的 DOM 元素,而是微信小程序里面自定义的 DOM 元素,这些 DOM 元素的操作通过 Exparser 模块来统一管理:
在 WAWebview 中包含了所有的 wx 自定义标签:
同时,__virtualDOM__
模块提供了很多的基础 API,比如:
-
getAll:获取所有 Node
-
getNodeById:根据 Id 获取 Node
-
getNodeId:获取 NodeId
-
addNode:添加节点
-
removeNode:删除节点
-
getExparser:获取 Exparser 对象(基于 WebComponent 的 shadow DOM 模型,可以在 JS 环境中运行,所有与节点树相关的操作都依赖于他)
-
…
(更多的 API 定义可以在 WAService.js 里面去查询)
2.2.4 WeiXinJSBridge
WeixinJSBridge 提供了视图层 JS 与 Native、视图层与逻辑层之间消息通信的机制,提供了如下几个方法:
里面最重要的便是 on 和 invoke,通过 on 来注册事件,通过 invoke 来触发相应的事件。
2.3 微信开发者工具
微信开发者工具中的小程序是跑在 NW.js 中的,这里是他的官方 API 文档:https://nwjs.readthedocs.io/en/latest/
他是基于 Chromium
和 Node.js
的,因此我们编译后的虚拟 DOM 转换成真实 DOM 后,通过他来运行。
2.3.1 一些反编译技巧
我们可以通过开发者工具,在 Devtools 里输入 help 可以得到很多指令:
其中比较有用的是 openVendor。这个函数可以打开当前项目的源码,其实也就是包含了 wcc 和 wcsc 编译工具的一个文件夹:
有了这些文件之后,对我们之后的分析会很有帮助。
我们可以将这些文件拷贝到一个单独的目录,在 VSCode 中打开该项目,并安装以下插件:
这个插件可以将微信开发者工具中的所有以 .wxvpkg 结尾的文件进行解压缩。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://img-blog.csdnimg.cn/img_convert/c793b6bf0eac93d1b5871f1a40123878.jpeg)
知其然不知其所以然,大厂常问面试技术如何复习?
1、热门面试题及答案大全
面试前做足功夫,让你面试成功率提升一截,这里一份热门350道一线互联网常问面试题及答案助你拿offer
2、多线程、高并发、缓存入门到实战项目pdf书籍
3、文中提到面试题答案整理
4、Java核心知识面试宝典
覆盖了JVM 、JAVA集合、JAVA多线程并发、JAVA基础、Spring原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm的大量技术点且讲解的非常深入
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://img-blog.csdnimg.cn/img_convert/c793b6bf0eac93d1b5871f1a40123878.jpeg)
知其然不知其所以然,大厂常问面试技术如何复习?
1、热门面试题及答案大全
面试前做足功夫,让你面试成功率提升一截,这里一份热门350道一线互联网常问面试题及答案助你拿offer
[外链图片转存中…(img-JLajLlg2-1713531529885)]
2、多线程、高并发、缓存入门到实战项目pdf书籍
[外链图片转存中…(img-BKckdeTj-1713531529885)]
[外链图片转存中…(img-7pUvPKN2-1713531529885)]
[外链图片转存中…(img-B01dm78e-1713531529885)]
3、文中提到面试题答案整理
[外链图片转存中…(img-owZ4WWQD-1713531529885)]
4、Java核心知识面试宝典
覆盖了JVM 、JAVA集合、JAVA多线程并发、JAVA基础、Spring原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm的大量技术点且讲解的非常深入
[外链图片转存中…(img-6G1KK1Pv-1713531529886)]
[外链图片转存中…(img-JL6rL6wy-1713531529886)]
[外链图片转存中…(img-BLEx9j7H-1713531529886)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!