近来微信小程序十分火热,终于解放了我手机的存储空间,那么抛开这些不说,小程序到底是怎么实现的呢?是真正的Native吗?本文以iOS端为例,一探究竟!
小程序简介
先看下官方的简单描述:
小程序开发框架的目标是通过尽可能简单、高效的方式让开发者可以在微信中开发具有原生 APP 体验的服务。
框架提供了自己的视图层描述语言 WXML 和 WXSS,以及基于 JavaScript 的逻辑层框架,并在视图层与逻辑层间提供了数据传输和事件系统,可以让开发者可以方便的聚焦于数据与逻辑上。
可以看出微信提供了自己的视图层描述语言和逻辑框架,也就是微信给了一个框架帮你处理数据和视图的绑定,生命周期等等,开发者只需要关注自己的界面和逻辑编写。那么所谓的这套框架是不是类似于RN呢?还是只是一套前端框架+开放出来的JSApi。
其实我们可以在 细节点 看到相应的说明:
- 在 iOS 上,小程序的 javascript 代码是运行在 JavaScriptCore 中,是由 WKWebView 来渲染的,环境有 iOS8、iOS9、iOS10
- 在 Android 上,小程序的 javascript 代码是通过 X5 JSCore来解析,是由 X5 基于 Mobile Chrome 37 内核来渲染的
- 在 开发工具上, 小程序的 javascript 代码是运行在 nwjs 中,是由 Chrome Webview 来渲染的
根据猜测就是在移动端还是由web来渲染的,并不是原生的。可能除了部分组件是Native,里面的控件都是web渲染的,后面我们来验证一下。
框架
根据官方的描述它的框架是这样的:
视图层(WXML和WXSS)负责页面的布局和样式,逻辑层(JavaScript)负责业务代码逻辑的编写,对于微信提供的这个框架,我们并不需要关心逻辑层到视图层的数据传输,视图层到逻辑层的事件系统。可以直接在逻辑业务层操作数据就行了。
开发工具
首先来看一下小程序的开发工具微信web开发者工具
,显示包内容可以看到如下内容:
从里面的结构可以看出,它是NodeWebkit
封装的Web应用。然后看编辑器的界面:
它是基于monaco,基于浏览器的代码编辑器。
我们知道微信小程序是基于微信提供的视图层(WXML+WXSS),当然这个只是一个描述,最终需要转换成js、html或css的代码,可以在开发工具里面找到:
这些工具负责WXML和WXSS
的转换。
上面的Pf
指的是pageFrame
模板文件:
我们所写的代码最终都会转成这样的一样文件,里面包括转换后的WXML
WXSS
以及写的js
,除了这些还会有微信提供的处理工具,如:WAWebview
、webviewSDK
等等。后面会讲到pageFrame
,WAWebview
和WAService
。
里面还提供了两个工具:
- wcc: 转换wxml中的自定义标签为json描述树。
- wcsc: 转换wxss到样式。
所以从这里看来还都是和web
相关的东西,接着往下看。
Button标签
现在开始从Button
标签入手,来看下Button
标签是怎么转换成最终手机上显示的Button
的。
首先编写wxml
文件hello.wxml
:
1 | <button>I'm a Button!</button> |
然后使用上面提到的wcc
这个工具,做一个转换:
1 | ./wcc -d hello.wxml -o hello.js |
得到的这个js
文件其实是一个生成工具,来生成我们Json的描述树。编写一个简单的index.html
, 去调用这个js
文件。
1 | <!DOCTYPE html> |
最终的输出为:
这是一个描述界面节点关系的JSON树结构,后面我们会看到这里面的wx-button
就是手机里面加载的自定义标签。
WAWebview.js
刚刚看到了从一个WXML
写的button到一个wx-button
的转换过程,现在来看一下wx-button
到底是什么,可以在WAWebview.js
里面找到这些自定义标签的定义:
这里会定义标签的行为是web
还是native
,以及标签的属性和事件处理。还可以看看其它标签:
这里可以猜测,button
和text
都是web的,而textarea
是Native的,而在wx-textarea
里面也可以找到这样的一个方法:
这里通过WeixinJSBridge
调用了Native的insertTextArea
方法去插入一个原生的textarea
控件。后面可以再进一步验证一下。
WAService.js
除了刚刚提到的WAWebview.js
外,还有一个非常主要的js文件就是WAService.js
。这个文件提供了官网的api接口,比如:wx.uploadFile
里面通过JSBridge
调用原生的uploadFile
。
验证
有了一个初步的了解之后,我们来通过几个方面来验证一下。首先是界面,然后看看内部的加载。
界面分析
在微信打开官方的小程序示例
,点击表单组件里面的button
, 然后来查看它的界面结构:
可以看到右边除了导航栏是原生的以外,里面的按钮demo部分都是在一个YYWKWebView
的控件里面。
而从dump出来的头文件里面可以看到:
1 | @interface YYWKWebView : WKWebView <WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler, YYWebViewInterface> |
YYWKWebView
继承自WKWebView
, 所以里面就是一个WebView的容器。
而看到的Button
也是webview里面的控件,不是原生的。通过同样的方法看了一下官方提供的其它控件,以下控件是原生Native的空间。
- canvas
- contact-button
- map
- textarea
- video
- pickview
- textfield
- scrollview
webview分析
既然里面是webview
,那么使用Safari
提供的调试工具来看下里面webview
加载的代码吧。
手机端:
进入设置
-Safari
-高级
打开Web检查器
。
Safari:
在偏好设置
-高级
启用开发菜单。
选择微信里面的webview
加载的url。
查看页面上的元素:
可以看到里面的控件对应就是web里面的自定义标签。比如右边的button对应的就是html里面的自定义标签<wx-button>
。
那么来看下在开发的时候这个页面是怎么写的?就拿那个Loading的按钮吧。
源代码:
1 | <button type="primary" loading="true">页面主操作 Loading</button> |
加载后的代码:
1 | <wx-button loading="" type="primary">页面主操作 Loading</wx-button> |
小程序会把wxml里面的标签转成对应的自定义标签。
直接在Safari
里面修改对应的html代码也会马上反映到界面上面:
小程序生成过程
我们开发编写的文件分三种wxml
、wxss
、js
,wxml
的转换刚刚也看到了首先转成json的结构树,然后根据WAWebview.js
里面的定义转成自定义标签或者原生控件。wxss
转成最终的css
样式,这里就是拿的weui那一套。js
转成真正的js代码。最后都会转成一个文件page-frame.html
。
所以在Safari
调试工具里面看到的页面都是page-frame.html
。
小程序加载
接下来来简单看看从点击小程序到小程序加载的大致过程:
首先来到发现里面的小程序,点击进去后就能看到打开过的小程序列表,还是从点击官方的小程序示例
, 开始!
1 | cy# [[[UIWindow keyWindow] rootViewController] _printHierarchy].toString() |
当前的ViewController
是WAMainListViewController
, 里面是个tabview,所以点击事件的相应函数是:
1 | -[WAMainListViewController tableView:didSelectRowAtIndexPath:] |
从IDA里面看流程就是从历史列表中取出当前点击的对象WAAppItemData
,然后调用:
1 | WAAppContactPreLoader openAppWithUserName:navigationController:fromScene:debugMode:onSuccess:onFailed: |
这里的username是一个小程序id的标示:gh_d43f693ca31f@app
,通过手机扫码等到的url,解密后也会有一个这样的标示。
如果已经加载过了的会直接打开,否则会有一个loading加载的页面去加载资源。
如果是新的小程序会根据这个标示去查询小程序的信息, 包括小程序的名字,描述以及webappinfo, 然后去拉取url。
1 | { |
然后会获取到小程序主界面的url, 生成一个page-frame.html的页面, 再通过webview打开。
1 | https://res.servicewechat.com/weapp/release/wxe5f52902cf4de896/13/page/component/index.html |
小程序的生命周期是通过WAWebViewController
调用JSApi告诉小程序。小程序里面的接口也是通过JSApi接口来和Native交互的。
上面的流程分析的不是很完整,很多细节没有去分析了~~~
开发流程
目前小程序只面向企业
,政府
,媒体
,认证通过之后可以创建一个小程序,然后会给一个appid
。里面分为三个身份:
- 管理员
- 开发者
- 体验者
流程是这样的:
题外
微信小程序只能通过摄像头扫码识别打开,不能通过本地图片识别二维码打开。在前面看到加载小程序的类WAAppContactPreLoader
,里面有个-[WAAppContactPreLoader openAppWithQRFullUrl:fromScene:]
,scene表示二维码识别的来源,1012表示图片识别的,1011表示摄像头扫码的,改一下就能从本地长按打开小程序了。
最后
所以这样来看微信小程序主要是通过它定义的描述层,把我们写的标签根据WAWebview.js
转成对应的自定义标签或Native控件,小程序提供的API由WAService.js
提供。这次只是一个很简单的分析,很多东西都还没有深入研究,大家请轻拍。