代码编译
微信开发者工具和微信客户端都无法直接运行小程序的源码,因此我们需要对小程序的源码进行编译
代码编译过程包括本地预处理、本地编译和服务器编译
微信开发者工具模拟器运行的代码只经过本地预处理、本地编译,没有服务器编译过程,而微信客户端运行的代码是额外经过服务器编译的
编译WXML
小程序的渲染层的运行环境是一个WebView,而WebView无法直接理解WXML标签,所以需要经过编译
微信开发者工具内置了一个二进制的WXML编译器
这个编译器接受WXML代码文件列表
,处理完成之后输出JavaScript代码
,这段代码是各个页面的结构生成函数
- 编译过程将所有的WXML代码最终变成一个JavaScript 函数,预先注入在WebView中
- 在运行时确定了页面路径之后,将路径作为参数传递给这个函数得到该页面的结构生成函数
- 页面结构生成函数接受页面数据,输出一段描述页面结构的JSON
- 最终通过小程序组件系统生成对应的HTML
//$gwx 是WXML编译后得到的函数
//根据页面路径获取页面结构生成函数
var generateFun = $gwx('name.wxml')
//页面结构生成函数接受页面数据,得到描述页面结构的JSON
var virtualTree = generateFun({
name: 'miniprogram'
})
/** virtualTree == {
tag: 'view',
children: [{
tag: 'view',
children: ['miniprogram']
}]
}**/
//小程序组件系统在虚拟树对比后将结果渲染到页面上
virtualDom.render(virtualTree)
上传代码时,微信开发者工具直接将本地的WXML代码文件提交到后台,由后台进行WXML编译,后台的WXML编译器和开发者工具本地内置的WXML编译器是同一套代码生成的
编译WXSS
微信开发者工具内置了一个二进制的WXSS编译器
这个编译器接受WXSS文件列表
,分析文件之间的引用关系
,同时预处理rpx
,输出一个样式信息数组
,每个WXSS文件对应于这个数组中的一项
运行时,根据当前的屏幕宽度,计算出1rpx对应多少像素单位,然后将样式信息数组转换成最终的样式添加到页面中
为了方便开发者,微信开发者工具提供了上传代码时样式自动补全的功能,利用PostCSS 对WXSS文件进行预处理,自动添加样式前缀
编译JavaScript
微信客户端在运行小程序的逻辑层的时候只需要加载一个JS文件
(我们称为app-service.js)
在代码上传之前,微信开发者工具会对开发者的JS 文件做一些预处理,包括ES6转ES5和代码压缩
在服务器编译过程将每个JS文件的内容分别包裹在define域中,再按一定的顺序合并成 app-service.js
模拟器
小程序模拟器模拟小程序在微信客户端的逻辑和界面表现,方便开发者实时查看代码效果
少部分的API无法在模拟器上进行模拟,绝大部分的 API 均能够在模拟器上呈现出正确的状态
同时微信开发者工具提供多种机型尺寸以及自定义机型尺寸功能,方便开发者进行界面样式的机型适配
逻辑层模拟
在微信开发者工具上我们采用了一个隐藏着的Webivew来模拟小程序的逻辑运行环境
用于模拟小程序逻辑层的<webview/>
加载的链接是
http://127.0.0.1:9973/appservice/appservice
我们在开发者工具底层搭建了一个本地HTTP服务器
来处理小程序模拟器的网络请求
-
./__asdebug/asdebug.js
: 是开发者工具注入的脚本 -
./__dev__/WAService.js
:是小程序逻辑层基础库 -
./util.js、./app.js、./index.js
:开发者JS代码
WebView在请求开发者JS代码时,开发者工具读取JS代码进行必要的预处理后,将处理结果返回,然后由WebView解析执
WebView是一个浏览器环境,开发者工具做了一个很巧妙的工作,将开发者的代码包裹在define域的时候,将浏览器的BOM对象局部变量化,从而使得在开发阶段就能发现问题
渲染层模拟
微信开发者工具使用chrome的<webview />
标签来加载渲染层页面,每个渲染层WebView加载
http://127.0.0.1:9973/pageframe/pageframe.html
开发者工具底层搭建的HTTP本地服务器
在收到这个请求的时候,就会编译WXML文件和WXSS文件,然后将编译结果作为HTTP请求的返回包
当确定加载页面的路径之后,开发工具会动态注入如下一段脚本
// 改变当前webview 的路径,确保之后的图片网络请求能得到正确的相对路径
history.pushState('', '', 'pageframe/index')
// 创建自定义事件,将页面结构生成函数派发出去,由小程序渲染层基础库处理
document.dispatchEvent(new CustomEvent("generateFuncReady", {
detail: {
generateFunc: $gwx('./index.wxml')
}
}))
// 注入对应页面的样式,这段函数由WXSS编译器生成
setCssToHead()
客户端模拟
微信客户端为丰富小程序的功能提供了大量的API
在微信开发者工具上,通过借助BOM以及node.js访问系统资源的能力,同时模拟客户端的UI和交互流程,使得大部分的API能够正常执行
通讯模拟
需要一个有效的通讯方案使得小程序的逻辑层、渲染层和客户端之间进行数据交流,才能将这三个部分串联成为一个有机的整体
微信开发者工具的有一个消息中心底层模块维持着一个WebSocket服务器
小程序的逻辑层的WebView和渲染层页面的WebView通过WebSocket与开发者工具底层建立长连,使用WebSocket的protocol字段来区分Socket的来源
// <webview/>的userAgent是可定制的
// 通过userAgent中获取开发者工具WebSocket服务器监听的端口
var port = window.navigator.userAgent.match(/port\/(\d*)/)[1]
// 通过指定 protocol == 'APPSERVICE' 告知开发者工具这个链接是来自逻辑层
var ws = new WebSocket(`ws://127.0.0.1:${port}`, 'APPSERVICE')
ws.onmessage = (evt) => {
let msg = JSON.parse(evt.data)
// …处理来自开发者工具的信息
}
// 调用API接口 wx.navigateBack
ws.send(JSON.stringify({
command: 'APPSERVICE_INVOKE',
data: {
api: 'navigateBack',
args: {}
}
}))
调试器
代码调试是开发者工具的最主要的功能之一,包括界面调试和逻辑调试
提供打开Chrome Devtools
调试界面的接口,开发者工具在Chrome Devtools的基础上进行扩展和定制
开发者工具上显示的调试器是调试逻辑层
WebView,主要使用Chrome Devtools的Sources面板调试逻辑层JS代码
微信小程序IDE开发了Chrome Devtools插件WXML 面板,用于展示渲染层
界面调试的用户交互界面
开发者写的WXML源码与真实的DOM树之间存在较大的差异,需要经过一个最小树算法,将非小程序组件节点剔除后才能呈现出与WXML源码一致的效果