打开小程序开发者工具,在调试控制台输入openVendor
就会打开小程序的WeappVendor目录,该目录包括如下几个主要内容:css
wcc xxx.wxml
wcsc xxx.wxss
正如上面分析的,经过调用小程序内置的可执行程序执行wcc xxx.wxml
,将指定的wxml转换为js脚本内容。其具体用法能够--help查看,以下图:
html
小程序开发者工具底层会将小程序项目中全部wxml转为js内容,能够理解为为每一个页面wxml进行了注册。例如咱们小程序demo有两个页面index.wxml和logs.wxml,其中index.wxml内容以下图:
经过wcc可执行程序生成的相关页面注册的代码以下图所示:
web
从页面转换的js内容来看,主要记录标签的属性及其值等。另外,转化的js脚本提供最核心的方法是$gwx
方法,能够在开发者工具开发控制台访问到,其方法签名以下:json
$gwx = function(path, global) { ... return function(env,dd,global){ ... } }
该方法根据传入具体的页面wxml路径,找到对应的页面,而后返回一个函数,向该函数传入页面渲染须要的数据(即Page中data对象)就能获得该页面wxml对应的js对象形式表示的dom树。其实每一个小程序页面在页面准备初始化渲染时会调用这个$gwx
方法,调用以下图所示:
小程序
另外,咱们直接在开发者工具的控制台直接调用,输入以下语句,能够获得的js对象表示以下图:浏览器
$gwx('./pages/index/index.wxml')({show: true});
wcsc
可执行程序用于处理wxss,小程序底层使用该可执行程序转换为js内容来处理页面css的引用。首先咱们来看下wxss
提供功能,以下图:
缓存
小程序底层使用wcsc -db -pc
来转换对应wxss文件的,其生成的js内容以下图eval函数中的字符串所示:
微信
生成是js主要做用:app
setCssToHead
方法将转换后的css内容添加到header小程序开发者工具的主入口也是小程序的启动入口,是整个小程序开发者工具的控制层,例如建立或者销毁webview等。它主要包括小程序的视图层的webview,业务逻辑层webview,调试器的webview和编辑区的webview几大块;咱们只需关心视图层和业务逻辑层的webview。启动入口对应这一个index.html页面,里面引入主入口js,以下:框架
<div id=container class=container></div> <script src=../js/core/index.js> </script>
最终初次进入小程序主页后,主入口index.html的渲染html中有关视图层和业务逻辑层结果以下图所示:
由此能够证实,小程序开发者工具业务逻辑层是在webview中执行的,该webview虽然提供浏览器相关接口,可是小程序只是在其中单纯的执行js代码。
在咱们小程序demo中有index首页navigateTo到logs日志页时,能够看主入口dom的变化,见下图:
从dom变化能够看出,调用navigateTo至关于新打开一个webview加载另外一个页面视图,随着打开的页面愈来愈多,内存就比较吃紧。这也是为何小程序对打开页面数量有限制的缘由。从图中可能也看出了,为啥多加载了一个pageframe.html
的webview,这个是干什么用的?后面会说到它的做用。
咱们在写小程序页面视图时,貌似并不关心webview中的html结构,这些都是小程序底层帮咱们实现, 咱们只须要写页面ui和业务逻辑便可。下面咱们来看看view视图层小程序帮咱们作了什么。先来看一下视图层pageframe.html的模板:
其中,模板中的注释占位符通过后台服务处理会注入不一样js脚本,主要js内容:
<!-- deviceinfo -->
: 暂时无用的占位符,会被空字符串""替换<!-- jsdebug -->
: 提供视图层的WeixinJSBridge模拟实现以及一些事件的处理如enablePullDownRefresh,其对应的js内容为extensions/pageframe/index.js.<!-- plugincode -->
: 小程序插件相关的代码,若小程序使用插件则会注入<!-- wxmlcode -->
: 调用wcc
可执行命令生成的小程序注册全部页面wxml对应的js脚本内容<!-- wxsscode -->
: 调用wcss
可执行命令生成的js脚本内容,提供注入css到页面的js方法;该内容会提早注入全局的css。<!-- wxappcode -->
: 小程序当前视图页面相关的配置json内容以及wxml和wxss转换为js的内容,可在控制台输入__wxAppCode__看相关信息<!-- vendorlist -->
: 小程序为视图层注入的基础库功能,包括WAWebview.js、WARemoteDebug.js和hls.js本节来详细介绍下小程序视图层实现的一些技术细节
首先看一下小程序官网页面层级准备小节描述的一段内容:
wx.navigateTo会建立一个新的页面层级,对于每个新的页面层级,视图层都须要进行一些额外的准备工做。在小程序启动前,微信会提早准备好一个页面层级用于展现小程序的首页。除此之外,每当一个页面层级被用于渲染页面,微信都会提早开始准备一个新的页面层级,使得每次调用wx.navigateTo都可以尽快展现一个新的页面。
正如上文提到的,咱们在打开pages/logs/logs视图页面时,发现dom中多加载了一个__pageframe__/pageframe.html的视图层,其模板内容正如上一节描述的。这个视图层的做用正是为了小程序提早为一个新的页面层准备的。
小程序每一个视图层页面内容都是经过pageframe.html模板来生成的,包括小程序启动的首页;下面来看看小程序为快速打开小程序页面作的技术优化:
这样在后续新打开页面时,都会走缓存的pageframe的内容,避免重复生成,快速打开一个新页面。
其实在小程序开发者工具实现中,在建立每一个视图层页的webview时,都会为其绑定了onLoadCommit
事件(它会在页面加载完成后触发,包含当前文档的导航和副框架的文档加载)。初始时webview的src会被指定为空页面地址http://127.0.0.1:${global.proxyPort}/aboutblank?${c}
,其中c为对应webview的id。webview从空页面到具体页面视图的过程以下:
http://127.0.0.1:${global.proxyPort}/__pageframe__/pageframe.html
。加载完成后,设置其src为pageframe.html:
新的src内容加载完成后再次触发onLoadCommit事件但根据条件不会执行reload方法。
pageframe.html页面在dom ready以后触发注入并执行具体页面相关的代码,此时经过history.pushState方法修改webview的src可是webview并不会发送页面请求。
pageframe.html模板生成的内容除小程序基础库视图层的底层功能以外,还包括小程序全部页面的模板信息、配置信息以及样式内容,这些均可以在生成pageframe.html的dom结构中窥探一二。
那么,既然每一个视图层页面由pageframe模板生成,那么小程序每一个页面独有的页面内容如dom和样式等如何生成呢,这主要是利用nw.js的executeScript方法来执行一段js脚原本注入只与当前页面相关的代码,包括当前页面的配置,注入当前页的css以及当前页面的virtual dom的生成,注入的代码以下:
最终生成的js代码(拿pages/index/index为例)以下图:
其中:
history.pushState('','', 'http://127.0.0.1:59524/__pageframe__/pages/index/index')
这句代码的做用修改当前webview的src,由于视图层的webview的src为pageframe.html,经过这句代码将其变动为具体的页面地址。
另外,须要注意的是nw.js的executeScript方法注入的代码是须要时机的,须要等到视图层的初始化工做准备ready以后才行,那么这个时机如何知道呢?细心的读者可能发现,在pageframe模板的最后一个script的内容:
<script>alert("DOCUMENT_READY")</script>
这个从字面意思能够看出此时应该是页面dom ready的一个时机,经过alert来进行通知。
alert能通知消息?固然能够的,在nw.js的webview中alert、prompt对应的弹框是被会阻止的,那么经过为webview绑定dialog事件来知道是那种弹框类型,以及提示内容,具体能够查看这篇文章。例如小程序开发者工具绑定事件部分代码(便于查看有修改):
this.webview.on('dialog', (a) => { a.preventDefault(); const b = a.messageType || '', c = a.messageText, d = a.dialog; if ('alert' === b) { c === 'DOCUMENT_READY' && (this.documentReady = !0, this.loadPage()) } ... })
这样方法loadPage
就会触发nw注入并执行页面相关的代码。最终生成的页面视图对应dom结构以下图:
能够看出,视图页面生成的dom结构中,document.body已无pageframe.html模板中对应body中的script内容,这是由于视图层的WAWebview.js在经过virtual dom生成真实dom过程当中,它会挂载到页面的document.body上,覆盖掉pageframe.html模板中对应document.body的内容。
小程序将全部业务代码置于同一个线程中运行,小程序开发者工具是在一个webview中执行;webview中的appservice.html引入业务代码js以外,还有后台服务内嵌的一些基础功能代码,appservice.html对应的模板内容以下:
通过后台服务的处理,模板中的各类占位符就被对应的js内容注入,下面就来简单说几个重要的注入内容:
<!-- wxconfig -->
: 小程序的配置项,包括用户自定义与系统默认的整合结果。在控制台输入__wxConfig
能够看出打印结果<!--devtoolsconfig-->
:小程序开发者配置,包括开发者工具版本,设置的请求域名、默认开发者工具的设置以及访问Native方法须要permission的方法。控制台输入__devtoolsconfig
能够看到其对应的信息<!-- wxmlxcjs -->
: 调用wcc
可执行命令生成的小程序注册全部页面wxml对应的js脚本内容,js脚本提供$gwx方法。<!-- asdebug -->
: 提供业务逻辑层的WeixinJSBridge模拟以及一些针对开发者工具的接口,如在控制台输入help能够看到提供的接口。其内容为extensions/appservice/index.js.<!-- vendorlist -->
: 为业务逻辑层注入WAService.js,为业务逻辑层提供小程序底层基础库的功能此外,开发者工具服务还在appservice.html的body注入一段脚本,脚本的做用是将业务逻辑代码经过script动态注入到head中执行,这段代码以下:
最终生成的appservice.html中的head状况以下图所示:
经过上图能够看出,咱们写的页面逻辑都引入到页面中,而且分别从app.js开始一一执行;小程序代码调用Page构造器的时候,小程序基础库会记录页面的基础信息,如初始数据(data)、方法等。须要注意的是,若是一个页面被屡次建立,并不会使得这个页面所在的JS文件被执行屡次,而仅仅是根据初始数据多生成了一个页面实例(this),在页面JS文件中直接定义的变量,在全部这个页面的实例间是共享的。