微信小程序架构分析 (中)
【引自第九程序的博客】本文探讨一下小程序的 view 模块和 service 模块是如何构成的。
打开微信 web 开发者工具,然后输入 openVendor() 便会打开 WeappVendor这个目录,这里包含了 view 模块和 service 模块使用的几个核心文件:
- wcc 可执行程序,用于将 wxml 转为 view 模块使用的 js 代码,使用方式为wcc xxx.wxml
- wcsc 可执行程序,用于将 wxss 转为 view 模块使用的 css 代码,使用方式为 wcsc xxx.wxss
- WAService.js 提供 service 模块大部分功能,下面会有详细介绍
- WAWebview.js 提供 view 模块大部分功能,下面会有详细介绍
view 页面详解
view 页面的 template 如下:
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <link href="https://res.wx.qq.com/mpres/htmledition/images/favicon218877.ico" rel="Shortcut Icon">
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
- <script>
- var __webviewId__;
- </script>
- <!-- percodes -->
- <!--{{WAWebview}}-->
- <!--{{reportSDK}}-->
- <!--{{webviewSDK}}-->
- <!--{{exparser}}-->
- <!--{{components_js}}-->
- <!--{{virtual_dom}}-->
- <!--{{components_css}}-->
- <!--{{allWXML}}-->
- <!--{{eruda}}-->
- <!--{{style}}-->
- <!--{{currentstyle}}-->
- <!--{{generateFunc}}-->
- </head>
- <body>
- <div></div>
- </body>
- </html>
其中 <!-- percodes --> 会在 dev 模式开启后被替换为一个时间锚点,例如:
- <script>var pageFrameStartTime = new Date();</script>
<!--{{WAWebview}}--> 会被 WAWebview.js 内代码替换
<!--{{WAWebview}}--> 到 <!--{{generateFunc}}--> 之间暂时没有被使用到
<!--{{generateFunc}}--> 会被 wcc 命令生成后的 js 代码替换
除了上面这些,页面上还会被插入页面和应用的 style 标签,如:
- <link rel="stylesheet" type="text/css" href="index.wxss">
这里的 wxss 文件包含的是原始 wxss 文件转换后的 css
以及生成 DOM 的启动脚本:
- <script>
- document.dispatchEvent(new CustomEvent("generateFuncReady", {
- detail: {
- generateFunc: $gwx('./page/index.wxml')
- }
- }))
- </script>
WAWebview.js 文件中的各个模块(行号为 jsbeautify 之后代码行号,开发者工具版本:092300):
- 1-77 行: WeixinJSBridge 对象兼容层,这个大概只会在调试时用到,因为开发时和运行时页面都会被后台以注入的方式添加 WeixinJSBridge 这个对象。我们可以通过这段代码看到它暴露的方法: invoke invokeCallbackHandleron publish subscribe subscribe subscribeHandler。
- 78-235 行:Reporter 对象,它的作用就是发送错误和性能统计数据给后台
- 236-596 行:wx 对象,页面的核心之一,一方面封装 WeixinJSBridge 的 invokeMethod 方位为易于调用的形式(例如 redirectTo, navigateTo等),另一方面封装 WeixinJSBridge 回调方法,调用者可以使用wx.onAppDataChange(callback) 添加数据变更的回调函数,最后提供wx.publishPageEvent 发送页面事件到后台
- 607-1267 行:wxparser 对象,提供 dom 到 wx element 对象之间的映射操作,提供元素操作管理和事件管理功能
- 1268-1285 行:转发 window 上的 animation 和 transition 相关的动画事件到 exparser
- 1286-1313 行:订阅并转发 WeixinJSBridge 提供的全局事件到 exparser
- 1324-1345 行:转发 window 上的 error 以及各种表单事件到 exparser
- 1347-3744 行:使用 exparser.registerBehavior 和exparser.registerElement 方法注册各种以 wx- 做为标签开头的元素到 exparser
- 3744-4498 行:virtual dom 渲染算法实现,提供 diff apply render 等方法,该模块接口基本与 virtual-dom 一致,这里特别的地方在于它所 diff 和生成的并不是原生 DOM,而是各种模拟了 DOM 接口的 wx element 对象
- 4599-4510 行:插入默认样式到页面
从页面 data 到 dom 的主要流程如下:
- var vtree
- var rootNode
- document.addEventListener("generateFuncReady", function(e) {
- var generateFunc = e.detail.generateFunc;
- wx.onAppDataChange(function(obj) {
- // 合并 data 到现有 data
- DataStore.setData(obj.data)
- // 生成 virtual dom 的 javascript plain object
- var props = generateFunc(DataStore.getData())
- // 第一次渲染
- if (obj.options.firstRender) {
- vtree = createVirtualTree(props, true)
- rootNode = vtree.render()
- rootNode.replaceDocumentElement(document.body)
- wx.initReady()
- } else {
- var other_vtree = createVirtualTree(props, false)
- var patches = vtree.diff(other_vtree)
- patches.apply(rootNode)
- vtree = other_vtree
- document.dispatchEvent(new CustomEvent("pageReRender", {}));
- }
- })
- })
上面的 DataStore 对象提供合并和获取当前页面 data 对象的功能,其实现如下:
- var DataStore = (function() {
- var data = {}
- return {
- getData: function() {
- return data
- },
- setData: function(e) {
- for (var t in e) {
- for (var n = (0, parsePath)(t), o = data, a = void 0, s = void 0, c = 0; c < n.length; c++) Number(n[c]) === n[c] && Number(n[c]) % 1 === 0 ? Array.isArray(o) || (a[s] = [], o = a[s]) : "[object Object]" !== Object.prototype.toString.call(o) && (a[s] = {}, o = a[s]), s = n[c], a = o, o = o[n[c]];
- a && (a[s] = e[t])
- }
- }
- }
- })()
- // 解析 key 为 data 内对象的路径字符串
- function parsePath(e) {
- for (var t = e.length, n = [], i = "", r = 0, o = !1, a = !1, s = 0; s < t; s++) {
- var c = e[s];
- if ("\\" === c) s + 1 < t && ("." === e[s + 1] || "[" === e[s + 1] || "]" === e[s + 1]) ? (i += e[s + 1], s++) : i += "\\";
- else if ("." === c) i && (n.push(i), i = "");
- else if ("[" === c) {
- if (i && (n.push(i), i = ""), 0 === n.length) throw new Error("path can not start with []: " + e);
- a = !0, o = !1
- } else if ("]" === c) {
- if (!o) throw new Error("must have number in []: " + e);
- a = !1, n.push(r), r = 0
- } else if (a) {
- if (c < "0" || c > "9") throw new Error("only number 0-9 could inside []: " + e);
- o = !0, r = 10 * r + c.charCodeAt(0) - 48
- } else i += c
- }
- if (i && n.push(i), 0 === n.length) throw new Error("path can not be empty");
- return n
- }
可以看到,每次 data 变化之后,小程序就会开始整个页面的 diff patch 过程。
对于原生实现的组件, exparser 会在监视到数据变化后发送对应事件到 WeixinJSBridge。
service 页面详解
service 页面会被被拼接为以下的样子:
- <!DOCTYPE html>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <link href="https://res.wx.qq.com/mpres/htmledition/images/favicon218877.ico" rel="Shortcut Icon">
- <script>
- var __wxAppData = {}
- var __wxRoute
- var __wxRouteBegin
- </script>
- <script>var __wxConfig = {"pages":["page/index"],
- // app 相关各种配置
- }</script>
- <script src="http://70475629.appservice.open.weixin.qq.com/asdebug.js"></script>
- <script src="http://70475629.appservice.open.weixin.qq.com/WAService.js"></script>
- <script src="http://70475629.appservice.open.weixin.qq.com/app.js"></script>
- <script>
- __wxRoute = 'page/index';
- __wxRouteBegin = true
- </script>
- <script src="http://70475629.appservice.open.weixin.qq.com/page/index.js"></script>
- </head>
- <body>
- <script>
- window._____sendMsgToNW({
- sdkName: 'APP_SERVICE_COMPLETE'
- })
- </script>
- </body>
- </html>
除了配置和开发者编写的页面、app.js,页面还在加载了 asdebug.js 和 WAService.js 两个文件。
asdebug.js 文件位于 nwjs 项目目录下,路径为app/dist/weapp/appservice/asdebug.js。 它包含了两个部分,一个是 WeixinJSBridge 针对 service 模块的实现,另一块是一些方便命令使用的接口, 例如:help() 会告诉你一些可用的函数:
该文件只会在开发者工具内被引入,如果小程序在微信内运行,应该会由微信底层提供 WeixinJSBridge。
WAService 负责 service 模块的一些核心逻辑,它包含以下部分 (行号为 jsbeautify 之后代码行号,开发者工具版本:092300):
- 1-78 行: 跟 WAWebview.js 一样的 WeixinJSBridge 兼容模块
- 79-245 行: 跟 WAWebview.js 一样的 Reporter 模块
- 246-1664 行:比 WAWebview.js 中 wx 功能更为丰富 wx 接口模块
- 1665-2304 行:appServiceEngine 模块,提供 Page,App,GetApp 接口
- 2305-2360 行: 为 window 对象添加 AMD 接口 require define
现在的 WAService 还有有很多地方依赖 window 对象,所以很有可能它在微信中和开发者工具内一样,依然运行于 webview 标签之内。
作者:第九程序
来源:51CTO
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
6个用于大数据处理分析的最好工具
大数据是一个含义广泛的术语,是指数据集,如此庞大而复杂的,他们需要专门设计的硬件和软件工具进行处理。该数据集通常是万亿或EB的大小。这些数据集收集自各种各样的来源:传感器,气候信息,公开的信息,如杂志,报纸,文章。大数据产生的其他例子包括购买交易记录,网络日志,病历,军事监控,视频和图像档案,及大型电子商务。 大数据和大数据分析,他们对企业的影响有一个兴趣高涨。大数据分析是研究大量的数据的过程中寻找模式,相关性和其他有用的信息,可以帮助企业更好地适应变化,并做出更明智的决策。 一、Hadoop Hadoop 是一个能够对大量数据进行分布式处理的软件框架。但是 Hadoop 是以一种可靠、高效、可伸缩的方式进行处理的。Hadoop 是可靠的,因为它假设计算元素和存储会失败,因此它维护多个工作数据副本,确保能够针对失败的节点重新分布处理。Hadoop 是高效的,因为它以并行的方式工作,通过并行处理加快处理速度。Hadoop 还是可伸缩的,能够处理 PB 级数据。此外,Hadoop 依赖于社区服务器,因此它的成本比较低,任何人都可以使用。 Hadoop是一个能够让用户轻松架构和使用的分布式计...
- 下一篇
从零开始搭建论坛(二):Web服务器网关接口
【引自selfboot的博客】在 从零开始搭建论坛(一):Web服务器与Web框架 中我们弄清楚了Web 服务器、Web 应用程序、Web框架的概念。对于 Python 来说,越来越多的 Web 框架面世,在给我们更多选择机会的同时,也限制了我们对于 Web Server 的选择。同样是有着很多 Web 框架的Java,因为有着 servlet API 的存在,任何Java Web框架写的应用程序都可以运行在任意一个 Web Server 上。 Python 社区当然也需要这样一套 API,来适配Web服务器和应用程序,这套 API 就是 WSGI(Python Web Server Gateway Interface),在 PEP 3333 里有详细的说明。简单来说,WSGI是连接Web服务器和Web应用程序的桥梁,一方面从Web server 拿到原始 HTTP 数据,处理成统一格式后交给 Web 应用程序,另一方面从应用程序/框架这边进行业务逻辑处理,生成响应内容后交给服务器。 Web服务器和框架通过 WSGI 来进行耦合的详细过程如下图所示: WSGI Server 适配 具体...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2全家桶,快速入门学习开发网站教程
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS8编译安装MySQL8.0.19
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2配置默认Tomcat设置,开启更多高级功能