在智能机刚兴起的时代,网络还不是很发达,网页浏览速度也很慢,以文字为主。市面上的应用以 Native App 为主。
Native App 是基于 iOS 或者安卓的原生应用,特点是开发成本高,迭代慢,但是性能和体验很好,消息推送及时,比如 qq,微信等。
2014 年 HTML5 完成标准定制,他的设计目的是为了在移动设备上支持多媒体,引入了 Video、Audio 等技术。
在网页上浏览视频变得很方便,特点是开发和发布成本低,打开方便,无需下载到本地,但是性能受浏览器的处理能力的限制,相较于原生 App 来说差了一些,消息推送不及时。
Hybrid App 就是混合式的 App,也就是在移动端原生应用的基础上,通过 JSBrdige 等方法,访问原生应用的 API 进行 JS 的交互,并通过 WebView 等技术实现 HTML 与 CSS 的渲染。
WebView 可以理解为嵌套了一个浏览器内核(比如 webkit)的移动端组件。
这种技术实现的应用一般都是跨平台的,并且维护起来比较容易,性能介于 H5 和原生应用之间。
小程序是一种不需要下载安装即可使用的应用,它实现了应用 “触手可及” 的梦想,用户扫一扫或者搜一下即可打开应用。也体现了 “用完即走” 的理念,用户不用关心是否安装太多应用的问题。应用将无处不在,随时可用,但又无需安装卸载。—— 百度百科
自 2017 年 1 月微信小程序正式发布以来,目前互联网上已经有多种小程序:
微信小程序 | 百度智能小程序 | 支付宝小程序 | QQ 小程序 |
---|---|---|---|
2017.1 | 2018.7 | 2018.9 | 2019.6 |
基于小程序几乎相同的技术原理,以及小程序的方便快捷的特性,还衍生出了多款小程序,比如抖音小程序、快手小程序、京东小程序、美团小程序等,帮助各大厂商更好的为用户提供便捷的服务。
2018 年微信小程序 “跳一跳” 爆火,记得当年食堂排队打饭的时候很多同学都在玩,助力了微信小程序在用户中的扩张,也激发了其他厂商开发小程序的热潮。
无论是微信小程序还是支付宝小程序还是百度智能小程序等等,他们的总体架构都是基于双线程的。
其中用于处理业务逻辑的 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 等逻辑层与渲染层的处理。
小程序视图层基础库,提供视图层的基础能力:
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(r) {
...
__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
:日志上报模块 (异常和性能统计数据)小程序逻辑层基础库,提供逻辑层基础能力:
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
:原生 BufferWeixinWorker
:Worker 线程JSContext
:JS Engine ContextProtect
:JS 保护的对象__subContextEngine__
:提供 App、Page、Component、Behavior、getApp、getCurrentPages 等方法微信小程序在 WAService 里面实现了小程序的 __virtualDOM__
,通过 __virtualDOM__
模块,可以实现 JS 对象到 DOM 对象的映射。
但是这个虚拟 DOM 通过 diff 和 patch 后并不是转换成原生的 DOM 元素,而是微信小程序里面自定义的 DOM 元素,这些 DOM 元素的操作通过 Exparser 模块来统一管理:
在 WAWebview 中包含了所有的 wx 自定义标签:
同时,__virtualDOM__
模块提供了很多的基础 API,比如:
(更多的 API 定义可以在 WAService.js 里面去查询)
WeixinJSBridge 提供了视图层 JS 与 Native、视图层与逻辑层之间消息通信的机制,提供了如下几个方法:
里面最重要的便是 on 和 invoke,通过 on 来注册事件,通过 invoke 来触发相应的事件。
微信开发者工具中的小程序是跑在 NW.js 中的,这里是他的官方 API 文档: https://nwjs.readthedocs.io/en/latest/
他是基于 Chromium
和 Node.js
的,因此我们编译后的虚拟 DOM 转换成真实 DOM 后,通过他来运行。
我们可以通过开发者工具,在 Devtools 里输入 help 可以得到很多指令:
其中比较有用的是 openVendor。这个函数可以打开当前项目的源码,其实也就是包含了 wcc 和 wcsc 编译工具的一个文件夹:
有了这些文件之后,对我们之后的分析会很有帮助。
我们可以将这些文件拷贝到一个单独的目录,在 VSCode 中打开该项目,并安装以下插件:
这个插件可以将微信开发者工具中的所有以 .wxvpkg 结尾的文件进行解压缩。
同时通过他来将 quickstart 中的 miniProgramJs.wxvpkg 进行解压,得到我们在开发者工具中的源码文件。
微信小程序提供了 wcc 工具来编译 wxml 代码。通过上面得到的代码,我们可以实现对 wxml 的编译,以开发者工具创建的 Demo 项目中的首页为例:
<view class="container">
<view class="userinfo">
<block wx:if="{{canIUseOpenData}}">
<view class="userinfo-avatar" bindtap="bindViewTap">
<open-data type="userAvatarUrl"></open-data>
</view>
<open-data type="userNickName"></open-data>
</block>
<block wx:elif="{{!hasUserInfo}}">
<button wx:if="{{canIUseGetUserProfile}}" bindtap="getUserProfile"> 获取头像昵称 </button>
<button wx:elif="{{canIUse}}" open-type="getUserInfo" bindgetuserinfo="getUserInfo"> 获取头像昵称 </button>
<view wx:else> 请使用1.4.4及以上版本基础库 </view>
</block>
<block wx:else>
<image bindtap="bindViewTap" class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover"></image>
<text class="userinfo-nickname">{{userInfo.nickName}}</text>
</block>
</view>
<view class="usermotto">
<text class="user-motto">{{motto}}</text>
</view>
</view>
经过以下指令编译:
./wcc ./quickstart/miniProgramJs.unpack/pages/index/index.wxml > index.js
会得到 JS 描述文件:
它会声明一个 $gwx 函数,通过它可以得到 Virtual DOM。接着我们在这个文件里添加几行代码去调用它,并通过 Node.js 或者 NW.js 执行这个文件:
var data = $gwx('./quickstart/miniProgramJs.unpack/pages/index/index.wxml')();
console.log(JSON.stringify(data, null, 2));
可以得到我们想要的最终的 Virtual DOM 结构:
{
"tag": "wx-page",
"children": [
{
"tag": "wx-view",
"attr": {
"class": "container"
},
"children": [
{
"tag": "wx-view",
"attr": {
"class": "userinfo"
},
"children": [
{
"tag": "wx-view",
"attr": {},
"children": [
" 请使用1.4.4及以上版本基础库 "
],
"raw": {},
"generics": {}
}
],
"raw": {},
"generics": {}
},
{
"tag": "wx-view",
"attr": {
"class": "usermotto"
},
"children": [
{
"tag": "wx-text",
"attr": {
"class": "user-motto"
},
"children": [
""
],
"raw": {},
"generics": {}
}
],
"raw": {},
"generics": {}
}
],
"raw": {},
"generics": {}
}
]
}
然后通过 window.exparser.registerElemtent 方法将这些 tag 转换成真实 DOM:
比如说,以上的 wx-text 就会被转换成类似于以下 DOM:
<span>
<span style="display: none"></span>
<span>{{这里是具体的文字内容}}</span>
</span>
同样的,以 Demo 项目里的 index.wxss 为例,运行一下指令:
./wcsc ./quickstart/miniProgramJs.unpack/pages/index/index.wxss -js -o ./css.js
可以将该文件从 wxss 格式的内容,转换成 JS 的内容:
这里面会生成 setCssToHead 方法,用于将相应的 css 转换后(如 rpx 转 px 等等),通过 style 标签插入到文档的 head 里面。
小程序逻辑层和渲染层的通信会由 Native (微信客户端)做中转,逻辑层发送网络请求也经由 Native 转发。
视图层组件:
内置组件中有部分组件是利用到客户端原生提供的能力,既然需要客户端原生提供的能力,那就会涉及到视图层与客户端的交互通信。这层通信机制在 iOS 和安卓系统的实现方式并不一样,iOS 是利用了 WKWebView 的提供 messageHandlers 特性,而在安卓则是往 WebView 的 window 对象注入一个原生方法,最终会封装成 WeiXinJSBridge 这样一个兼容层,主要提供了调用(invoke)和监听(on)这两种方法。
逻辑层接口:
逻辑层与客户端原生通信机制与渲染层类似,不同在于,iOS 平台可以往 JavaScriptCore 框架注入一个全局的原生方法,而安卓方面则是跟渲染层一致的。
无论是视图层还是逻辑层,开发者都是间接地调用到与客户端原生通信的底层接口。一般微信小程序会对逻辑层接口做层封装后,才暴露给开发者,封装的细节可能是统一入参、做些参数校验、兼容各平台或版本问题等等。
小程序有冷启动与热启动两种方式:
小程序没有重启的概念:
启动流程:
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/5nQqBFFWwxtcf8S2Ba9PRA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。