小程序的由来
最初微信WebView逐渐成为移动web重要入口,微信发布了一整套网页开发工具包,称之为 JS-SDK,给所有的 Web 开发者打开了一扇全新的窗户,让所有开发者都可以使用到微信的原生能力,去完成一些之前做不到或者难以做到的事情。
但JS-SDK 的模式并没有解决使用移动网页遇到的体验不良的问题,比如受限于设备性能和网络速度,会出现白屏的可能。因此又设计了一个增强版JS-SDK,也就是“微信 Web 资源离线存储”,但在复杂的页面上依然会出现白屏的问题,原因表现在页面切换的生硬和点击的迟滞感。这个时候需要一个 JS-SDK 所处理不了的,使用户体验更好的一个系统,小程序应运而生
快速的加载
更强大的能力
原生的体验
易用且安全的微信数据开放
高效和简单的开发
与h5页面的区别
运行环境:小程序基于浏览器内核重构的内置解析器,而 h5 的宿主环境是浏览器。所以小程序中没有 DOM 和 BOM 的相关 API,jQuery和一些 NPM 包都不能在小程序中使用,以及禁止页面跳转的一些相关api,因为这与小程序的初衷相违背,为了避免浏览器api更新一次就打一次补丁,小程序建立了自己的语法;
系统权限:小程序能获得更多的系统权限,如网络通信状态、数据缓存能力等;
渲染机制:小程序的逻辑层和渲染层是分开的,而 h5 页面 UI 渲染跟 JavaScript 的脚本执行都在一个单线程中,互斥。所以 h5 页面中长时间的脚本运行可能会导致页面失去响应。
其实,小程序开发过程中我们面对的是 iOS 和 Android 微信客户端和辅助开发的小程序开发者工具。根据官方文档,这三大运行环境也是有所区别的:
运行环境 | 逻辑层 | 逻辑层 |
---|---|---|
iOS | JavaScriptCore | WKWebView |
Android X5 | JSCore | X5浏览器 |
小程序开发者工具 | NWJS | Chrome WebView |
所以微信小程序介于 web 端和原生 App 之间,能够丰富调用功能接口,同时又跨平台。
小程序架构
双线程模型
小程序的渲染层和逻辑层分别由2个线程管理:
渲染层:界面渲染相关的任务全都在 WebView 线程里执行。一个小程序存在多个界面,所以渲染层存在多个 WebView线程。
逻辑层:采用 JsCore 线程运行JS脚本。
WeixinJsBridage 起到架起上层开发与Native(系统层)的桥梁,使得小程序可通过API使用原生的功能,且部分组件为原生组件实现,从而有良好体验。
视图层和逻辑层通过系统层的 WeixinJsBridage 进行通信:逻辑层把数据变化通知到视图层,触发视图层页面更新,视图层把触发的事件通知到逻辑层进行业务处理。
(页面渲染的具体流程是:在渲染层,宿主环境会把 WXML 转化成对应的 JS 对象,在逻辑层发生数据变更的时候,我们需要通过宿主环境提供的 setData 方法把数据从逻辑层传递到渲染层,再经过对比前后差异,把差异应用在原来的Dom树上,渲染出正确的UI界面)
双线程模型是小程序框架与业界大多数前端 Web 框架不同之处。基于这个模型,可以更好地管控以及提供更安全的环境。缺点是带来了无处不在的异步问题(任何数据传递都是线程间的通信,也就是都会有一定的延时),不过小程序在框架层面已经封装好了异步带来的时序问题。
这样做还有一个目的是管控和安全:需要阻止开发者使用一些,例如浏览器的window对象,跳转页面、操作DOM、动态执行脚本的开放性接口。小程序的webview标签以跳转网页,但个人类型的小程序暂不支持使用。可打开关联的公众号的文章,其它网页需登录小程序管理后台配置业务域名。
Service和View通信
使用消息publish和subscribe机制实现两个Webview之间的通信,实现方式就是统一封装一个WeixinJSBridge对象,而不同的环境封装的接口不一样,具体实现的技术如下:
windows(开发环境)
通过window.postMessage实现(使用chrome扩展的接口注入一个contentScript.js,它封装了postMessage方法,实现webview之间的通信,并且也它通过chrome.runtime.connect方式,也提供了直接操作chrome native原生方法的接口)
发送消息:window.postMessage(data, ‘*’);,// data里指定 webviewID
接收消息:window.addEventListener(‘message’, messageHandler); // 消息处理并分发,同样支持调用nwjs的原生能力
拓展:[window.postMessage]:可以实现跨文本文档,多窗口,跨域消息传递,跨域通信解决方案.
语法:otherWindow.postMessage(message, targetOrigin]);
其中,otherWindow:窗口的一个引用,比如iframe的contentWindow属性,执行window.open返回的窗口对象,或者是命名过的或数值索引的window.frames.
targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,指定后只有对应origin下的窗口才可以接收到消息,设置为通配符"*"表示可以发送到任何窗口。
IOS
通过 WKWebview的window.webkit.messageHandlers.NAME.postMessage实现
微信navite代码里实现了两个handler消息处理器
invokeHandler: 调用原生能力
publishHandler: 消息分发
组件系统-Exparser框架
如view 会编译成wx-view 组件渲染,不会编译成h5的标签。避免开发者通过a 标签等实现跳转; 这些基本组件就是基于 Exparser 框架。
Exparser 基于 WebComponents 的 ShadowDOM 模型,但是不依赖浏览器的原生支持,而且可在 纯 JS 环境中运行。优点:
1.基于Shadow DOM模型:模型上与WebComponents的ShadowDOM高度相似,但不依赖浏览器的原生支持,也没有其他依赖库;实现时,还针对性地增加了其他API以支持小程序组件编程。
2.可在纯JS环境中运行:这意味着逻辑层也具有一定的组件树组织能力。
3.高效轻量:性能表现好,在组件实例极多的环境下表现尤其优异,同时代码尺寸也较小。
小程序中,所有节点树相关的操作都依赖于 Exparser,包括 WXML 到页面最终节点树的构建、CreateSelectorQuery 调用和自定义组件特性等。
原生组件
在内置组件中,有一些组件并不完全在 Exparser 的渲染体系下,而是由客户端原生参与组件的渲染。比如说 Map 组件。它渲染的层级比在 WebView 层渲染的普通组件要高。
引入原生组件的优点是:
扩展 Web 的能力
体验更好,减轻 WebView 的渲染工作
绕过 setData、数据通信和重渲染流程,性能更好
运行机制
启动
热启动::假如用户已经打开过某小程序,然后在一定时间内再次打开该小程序,此时无需重新启动,只需将后台态的小程序切换到前台,这个过程就是热启动;(相应周期onHide,onShow)
冷启动:用户首次打开或小程序被微信主动销毁后再次打开的情况,此时小程序需要重新加载启动,即冷启动。
销毁
只有当小程序进入后台一定时间,或者系统资源占用过高,才会被真正的销毁。
更新机制
开发者在后台发布新版本之后,无法立刻影响到所有现网用户,但最差情况下,也在发布之后 24 小时之内下发新版本信息到用户。
小程序每次冷启动时,都会检查是否有更新版本,如果发现有新版本,将会异步下载新版本的代码包,并同时用客户端本地的包进行启动,即新版本的小程序需要等下一次冷启动才会应用上。
所以如果想让用户使用最新版本的小程序,可以利用 wx.getUpdateManager 做个检查更新的功能。
开发时遇到的一些问题:
Dataset 取值时都转成小写,如data-ID=‘{{header}}’,e.currentTarget.dataset.id;
调用其如保存图片的api 时安卓和苹果返回的错误码是不同的。 解决:用机型进行真机调试或者在vconsole 上打印。
覆盖原生组件 map、video、canvas、camera、live-player、live-pusher、textarea。(placeHolder穿透蒙层等问题,自定义视频播放按钮等),用cover-view写; 注意,在cover-view中只能嵌套cover-view、cover-image和button 。
开发工具请求正常真机异常,通常是域名没配置
this.setData({['messages['+ targetIndex +'].isLoad']:false})
不涉及页面渲染的变量可以不放在this里,在改值的时候不需要触发渲染
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。