小程序基础
小程序与普通网页开发的区别
小程序框架本身所具有的快速加载和快速渲染能力。
网页开发渲染线程和脚本线程是互斥的,这也是为什么长时间的脚本运行可能会导致页面失去响应。
而在小程序中,二者是分开的,分别运行在不同的线程中。
网页开发者可以使用到各种浏览器暴露出来的 DOM API,进行 DOM 选中和操作。
小程序的逻辑层和渲染层是分开的 ,逻辑层运行在 JSCore 中,并没有一个完整浏览器对象,因而缺少相关的DOM API和BOM API。
小程序开发过程中需要面对的是两大操作系统 iOS 和 Android 的微信客户端,以及用于辅助开发的小程序开发者工具,小程序中三大运行环境也是有所区别的,如表1所示。
表1 小程序的运行环境
运行环境 | 逻辑层 | 渲染层 |
---|---|---|
iOS | JavaScriptCore | WKWebView |
安卓 | X5 JSCore | X5浏览器 |
小程序开发者工具 | NWJS | Chrome WebView |
小程序代码组成
小程序由配置代码JSON文件、模板代码 WXML 文件、样式代码 WXSS文件以及逻辑代码 JavaScript文件组成。
用户界面呈现会因为当前时刻数据不同而有所不同,或者是因为用户的操作发生动态改变,这就要求程序的运行过程中,要有动态的去改变渲染界面的能力。在 Web 开发中,开发者使用 JavaScript 通过Dom 接口来完成界面的实时更新。在小程序中,使用 WXML 语言所提供的数据绑定功能,来完成此项功能。
WXML 通过 {{变量名}} 来绑定 WXML 文件和对应的 JavaScript 文件中的 data 对象属性。
通过 {{ 变量名 }} 语法可以使得 WXML 拥有动态渲染的能力,除此外还可以在 {{ }} 内进行简单的逻辑运算。
rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。
wx.getSystemInfoSync().windowWidth / 750 ,如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
小程序中的 JavaScript 是由ECMAScript 以及小程序框架和小程序 API 来实现的。 同浏览器中的JavaScript 相比没有 BOM 以及 DOM 对象,所以类似 JQuery、Zepto这种浏览器类库是无法在小程序中运行起来的,同样的缺少 Native 模块和NPM包管理的机制,小程序中无法加载原生库,也无法直接使用大部分的 NPM 包。
理解小程序宿主环境
小程序的运行环境分成渲染层和逻辑层, WXML 模板和 WXSS 样式工作在渲染层,JS 脚本工作在逻辑层。
小程序的渲染层和逻辑层分别由2个线程管理:渲染层的界面使用了WebView 进行渲染;逻辑层采用JsCore线程运行JS脚本。 一个小程序存在多个界面,所以渲染层存在多个WebView线程,这两个线程的通信会经由微信客户端(下文中也会采用Native来代指微信客户端)做中转,逻辑层发送网络请求也经由Native转发,小程序的通信模型如图所示。
通常界面视图和变量状态是相关联的,如果有某种“方法”可以让状态和视图绑定在一起(状态变更时,视图也能自动变更),那我们就可以省去手动修改视图的工作。 这个方法就是“数据驱动”。
在渲染层,宿主环境会把WXML转化成对应的JS对象,在逻辑层发生数据变更的时候,我们需要通过宿主环境提供的setData方法把数据从逻辑层传递到渲染层,再经过对比前后差异,把差异应用在原来的Dom树上,渲染出正确的UI界面。
所以小程序切换页面时,小程序逻辑层的JS脚本运行上下文依旧在同一个JsCore线程中。
要特别留意一点,所有页面的脚本逻辑都跑在同一个JsCore线程,页面使用setTimeout或者setInterval的定时器,然后跳转到其他页面时,这些定时器并没有被清除,需要开发者自己在页面离开的时候进行清理。
页面Page构造器的data字段,data参数是页面第一次渲染时从逻辑层传递到渲染层的数据。
在Page实例下的方法调用this.setData把数据传递给渲染层,从而达到更新界面的目的。由于小程序的渲染层和逻辑层分别在两个线程中运行, 所以setData传递数据实际是一个异步的过程 ,所以setData的第二个参数是一个callback回调,在这次setData对界面渲染完毕后触发。
this.setData({
text: 'change data'
}, function(){
// 在这次setData对界面渲染完毕后触发
})
小程序基础库的组成
基础库成分
基础库除了处理 VD 的渲染问题,它还包括内置组件和逻辑层API,总的来说负责处理数据绑定、组件系统、事件系统、通信系统等一系列框架逻辑。
小程序的基础库是 JavaScript 编写的,它可以被注入到渲染层和逻辑层运行。在渲染层可以用各类组件组建界面的元素,在逻辑层可以用各类 API 来处理各种逻辑。
同时,小程序的一些补充能力:自定义组件和插件,也有相应的基础代码,当然也需要添加到基础库里。
小程序的基础库主要包括:
- 提供 VD 渲染机制相关基础代码。(Exparser 框架)
- 提供封装后的内置组件。
- 提供逻辑层的 API。
- 提供其他补充能力(自定义组件和插件等)的基础代码。
小程序基础库机制
由于小程序的渲染层和逻辑层是两个线程管理,而我们 一般说起基础库,也通常包括 WebView 基础库(渲染层),和 AppService 基础库(逻辑层)。
显然,所有小程序在微信客户端打开的时候,都需要注入相同的基础库。所以,小程序的基础库不会被打包在某个小程序的代码包里边,它会被提前内置在微信客户端。
将基础库内置在微信客户端,有两个好处:
- 降低业务小程序的代码包大小。
- 可以单独修复基础库中的Bug,无需修改到业务小程序的代码包。
在小程序启动前,微信会提前准备好一个页面层级用于展示小程序的首页。
这里就包括了逻辑层和渲染层分别的初始化以及公共库的注入。
小程序的很多能力需要微信客户端来支撑,例如蓝牙、直播能力、微信运动等,可以说,小程序基础库的迭代离不开微信客户端的发布。
为了避免新版本的基础库给线上小程序带来未知的影响,微信客户端都是携带上一个稳定版的基础库发布的。等到微信客户端正式发布后,小程序会开始灰度推送新版本的基础库到微信客户端里,在这个过程需要仔细监控各类异常现象以及开发者和用户的反馈,一般灰度时长为12小时,灰度结束后,用户设备上就会有新版本的基础库。如果存在重大Bug,那此次推送会被回退。
原生组件
用户的一次交互,如点击某个按钮,开发者的逻辑层要处理一些事情,然后再通过 setData 引起界面变化。这样的一个过程需要四次通信:
- 渲染层 -> Native(点击事件)
- Native -> 逻辑层(点击事件)
- 逻辑层 -> Native(setData)
- Native -> 渲染层(setData)
在一些强交互的场景(表单、canvas、拖动视频进度条等),这样的操作流程会导致用户体验卡顿。
原生组件本身就是属于Native层,所以和逻辑层通信会少2次。引入原生组件主要有 3 个好处:
- 绕过 setData、数据通信和重渲染流程,使渲染性能更好。
- 扩展 Web 的能力。比如像输入框组件(input, textarea)有更好地控制键盘的能力。
- 体验更好,同时也减轻 WebView 的渲染工作。比如像地图组件(map)这类较复杂的组件,其渲染工作不占用 WebView 线程,而交给更高效的客户端原生处理。
而原生组件的渲染过程:
- 组件被创建,包括组件属性会依次赋值。
- 组件被插入到 DOM 树里,浏览器内核会立即计算布局,此时我们可以读取出组件相对页面的位置(x, y坐标)、宽高。
- 组件通知客户端,客户端在相同的位置上,根据宽高插入一块原生区域,之后客户端就在这块区域渲染界面。
- 当位置或宽高发生变化时,组件会通知客户端做相应的调整。
简单来说,就是 原生组件在 WebView 这一层只需要渲染一个占位元素,之后客户端在这块占位元素之上叠了一层原生界面。
有利必有弊,原生组件也是有限制的:
- 最主要的限制是一些 CSS 样式无法应用于原生组件
- 由于客户端渲染,原生组件的层级会比所有在 WebView 层渲染的普通组件要高(原生组件和Webview不是在同一层级进行渲染,原生组件其实是叠在Webview之上)
一些记录
openid和unionid区别
来自: https://www.jianshu.com/p/cd3f178dee81
- 微信openid和unionid长度是不一样的
- openid同一用户同一应用唯一,unionid同一用户不同应用唯一。
- 对于在pc端和客户端
- 获取用户的openid是无需用户同意的,获取用户的基本信息则需要用户同意
参考文章
小程序架构设计(一)