小程序的主要开发语言是 JavaScript ,虽然有与网页开发有类似性可是还有必定的区别javascript
渲染非原生组件以及脚本执行环境的区别以下css
运行环境 | 逻辑层 | 渲染层 |
---|---|---|
Android | V8 | Chromium定制内核 |
iOS | JSCore | WKWebView |
小程序开发者工具 | NWJS | Chrome WebView |
在前言中提到小程序的宿主环境为微信客户端,因此借助宿主环境提供的能力,能够完成许多普通网页没法完成的功能html
首先,咱们来简单了解下小程序的运行环境。小程序的运行环境分红渲染层和逻辑层,其中 WXML 模板和 WXSS 样式工做在渲染层,JS 脚本工做在逻辑层。java
小程序的渲染层和逻辑层分别由2个线程管理:渲染层的界面使用了 WebView 进行渲染;逻辑层采用 JsCore 线程运行 JS 脚本。一个小程序存在多个界面,因此渲染层存在多个 WebView 线程,这两个线程的通讯会经由微信客户端作中转,逻辑层发送网络请求也经由 Native 转发,小程序的通讯模型下图所示。node
多 WebView 模式下,每个 WebView 都有一个独立的 JSContext,那视图和逻辑是如何进行通信?以下图双线程生命周期所示。git
相对于浏览器双线程模型github
当咱们对 View 层进行事件操做后,会经过 WeixinJSBridge 将数据传递到 Native 系统层。Native 系统层决定是否要用 native 处理,而后丢给逻辑层进行用户的逻辑代码处理。逻辑层处理后将数据经过 WeixinJSBridge 返给 View 层。View 渲染更新视图,以下图所示。web
微信开者工具模拟器运行的代码是通过本地预处理、本地编译,才能看见的页面。而微信客户端运行的代码是额外通过服务器编译的。只有通过编译才能识别并运行小程序的代码。咱们先来看一下小程序文件的基本结构npm
.wxml
页面结构.wxss
页面样式.js
页面逻辑.json
页面相关配置按照【约定优于配置】的原则接下来咱们打开小程序开发者工具,点击左上角微信开发者工具 >> 调试 >> 调试微信开发者工具,看到以下界面(官网组件demo)json
能够看到渲染层和逻辑层是两个 webview,第一个对应的 webview 是渲染层,每一个页面都有一个webview,而逻辑层的 appservice 是只有一个。
而后咱们接着往下看 webview 中究竟是什么?打开 webview 发现 iframe 标签是空的,但咱们想要查看怎么办,打开调试面板(console)下面输入
// 第一步先找到全部的webview
document.getElementsByTagName('webview')
// 第二步找到第一个渲染层页面用开发工具命令打开
document.getElementsByTagName('webview')[0].showDevTools(true,null)
复制代码
这个时候咱们会看到以下窗口,这个页面就是咱们渲染层的页面结构了。
既然能看到每个渲染层,确定也能看到逻辑层代码,在开发者工具控制台中输入
document
,出现以下页面
接下来咱们看一下微信小程序使用的基础库文件。在开发者工具控制台中输入 openVendor()
就会打开本地小程序的 WeappVendor
目录,有几个重要的文件
wcc
编译器负责将 wxml
编译成 js
文件wcsc
编译器负责将 wxss
文件编译成 js
文件xxx.wxvpkg
是不一样版本的小程序基础库,主要包含小程序基础库 WAService
和 WAWebview
,这块后续分析。一、新建一个名为 compiler.js
的文件,并写入如下代码
const fs = require("fs");
const miniprogramCompiler = require("miniprogram-compiler");
const path = require("path");
let JsCompiler = miniprogramCompiler.wxmlToJs(path.join(__dirname));
let cssCompiler = miniprogramCompiler.wxssToJs(path.join(__dirname));
fs.writeFileSync("wxml.js", JsCompiler);
fs.writeFileSync("wxcss.js", cssCompiler);
复制代码
二、执行 npm install miniprogram-compiler
三、执行 node compiler.js
,会在同目录生成 wxml 和 wxcss 两个 JS 文件 四、新建 index.wxml 并写入以下代码
<view class="box">
<text class="box-text">{{ text }}</text>
</view>
复制代码
五、新建 index.html 引入文件 wxml.js,以下代码
<script src="./wxml.js"></script>
<script> const res = $gwx("index.wxml"); const virtualTree = res({ text: 'data数据' }); console.log(virtualTree); </script>
复制代码
用浏览器打开 index.html 控制台会输出相似 Virtual DOM 的对象
{
"tag":"wx-page",
"children":[{
"tag":"wx-view",
"attr":{ "class":"box"},
"children":[{
"tag":"wx-text",
"attr":{ "class":"box-text" },
"children":[ "data数据" ],
"raw":{},
"generics":{}
}],
"raw":{},
"generics":{}
}]
}
复制代码
wcc的做用就是:
path
(页面 wxml 路径)和 global
(顶层对象)上面提到过开发者工具中输入openVendor
以后会看到不少 .wxvpkg
文件,这是小程序的基础库,主要有两部分组成 WAService
和 WAWebview
微信小程序基础库版本是不断更新的,当前我本地的最高版本为 2.14.1.wxvpkg ,咱们能够利用unwxapkg 解压 2.14.1.wxvpkg。解压后的目录为
├── WAAutoService.js
├── WAAutoWebview.js
├── WAGame.js
├── WAGameSubContext.js
├── WAGameVConsole.html
├── WAGfxEmsc.js
├── WAGfxEmsc.wasm
├── WAPageFrame.html
├── WAPerf.js
├── WARemoteDebug.js
├── WAService.js
├── WAServiceMainContext.js
├── WASourceMap.js
├── WASubContext.js
├── WAVConsole.js
├── WAVersion.json
├── WAWKWorker.html
├── WAWebview.js
├── WAWidget.js
└── WAWorker.js
复制代码
其余文件先忽略,咱们先看一下 webview 引用的两个基础文件源码概览 WAWebview 和 WAService
其中,WAWebview 最主要的几个部分:
Foundation
:基础模块(发布订阅、通讯桥梁 ready 事件)WeixinJSBridge
:消息通讯模块(js 和 native 通信) Webview 和 Service都有相同的一套exparser
:组件系统模块,实现了一套自定义的组件模型,好比实现了 wx-view__virtualDOM__
:虚拟 Dom 模块__webViewSDK__
:WebView SDK 模块Reporter
:日志上报模块(异常和性能统计数据)其中,WAService 最主要的几个部分:
Foundation
:基础模块WeixinJSBridge
:消息通讯模块(js 和 native 通信) Webview 和 Service都有相同的一套WeixinNativeBuffer
:原生缓冲区WeixinWorker
:Worker 线程JSContext
:JS Engine ContextProtect
:JS 保护的对象__subContextEngine__
:提供 App、Page、Component、Behavior、getApp、getCurrentPages 等方法基础模块提供环境变量 env、发布订阅 EventEmitter、配置/基础库/通讯桥 Ready 事件。
微信小程序的组件组织框架,内置在小程序基础库中,为小程序的各类组件提供基础的支持。小程序内的全部组件,包括内置组件和自定义组件,都由 Exparser 组织管理。Exparser 的组件模型与 WebComponents 标准中的 ShadowDOM 高度类似。Exparser 会维护整个页面的节点树相关信息,包括节点的属性、事件绑定等,至关于一个简化版的 Shadow DOM 实现。
生成 wx-element
对象,和 virtual-dom 相似
提供了视图层 JS 与 Native、视图层与逻辑层之间消息通讯的机制,以下图几个方法:
经过上面这些主体结构和函数,对小程序应该有大体的了解,那么咱们把上面串起来看一下首次渲染的流程
document.getElementsByTagName('webview')[0].showDevTools(true)
上图中最后标注的代码
var decodeName = decodeURI("./pages/index/index.wxml");
var generateFunc = $gwx(decodeName);
if (generateFunc) {
var CE = (typeof __global === 'object') ? (window.CustomEvent || __global.CustomEvent) : window.CustomEvent;
document.dispatchEvent(new CE("generateFuncReady", {
detail: {
generateFunc: generateFunc
}
})) __global.timing.addPoint('PAGEFRAME_GENERATE_FUNC_READY', Date.now())
} else {
document.body.innerText = decodeName + " not found"console.error(decodeName + " not found")
};
复制代码
因此初次渲染流程以下
c = function() {
setTimeout(function() {
! function() {
var e = arguments;
r(function() {
WeixinJSBridge.publish.apply(WeixinJSBridge, o(e))
})
}("GenerateFuncReady", {})
}, 20)
}
document.addEventListener("generateFuncReady", c)
复制代码
小程序逻辑层和渲染层的通讯会由 Native (微信客户端)作中转,逻辑层发送网络请求也经由 Native 转发。
内置组件中有部分组件(map、video等)是利用到客户端原生提供的能力,那是怎么通讯的呢?iOS 是利用了 WKWebView 的提供 messageHandlers 特性,而在安卓则是往 WebView 的 window 对象注入一个原生方法,最终会封装成 WeiXinJSBridge 这样一个兼容层,主要提供了调用(invoke)和监听(on)这两种方法。
iOS 平台往 JavaScripCore 框架注入一个全局的原生方法,而安卓方面则是跟渲染层一致。 不论视图层(部分组件)仍是逻辑层,开发者都是间接地调用客户端原生底层的能力。
最后,推荐一套TS全系列的教程吧。近期在提高TS,收藏了一套很不错的教程,无偿分享给xdm www.yidengxuetang.com/pub-page/in…