微信小程序架构分析 (中)
2019独角兽企业重金招聘Python工程师标准>>>
本文探讨一下小程序的 view 模块和 service 模块是如何构成的。 你可以在app.nw/app/dist/weapp/tpl/pageFrameTpl.js 和app.nw/app/dist/weapp/tpl/appserviceTpl.js 文件内找到页面的模板。
打开微信 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 vtreevar rootNodedocument.addEventListener("generateFuncReady", function(e) {var generateFunc = e.detail.generateFunc;wx.onAppDataChange(function(obj) {// 合并 data 到现有 dataDataStore.setData(obj.data)// 生成 virtual dom 的 javascript plain objectvar 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_vtreedocument.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 __wxRoutevar __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 标签之内。
下篇介绍如何自己动手建立一个可以运行小程序的环境,欢迎关注。
登录乐搏学院官网http://www.learnbo.com/
或关注我们的官方微博微信,还有更多惊喜哦~
作者:赵启明
链接:https://zhuanlan.zhihu.com/p/22765476
转载于:https://my.oschina.net/learnbo/blog/822902
微信小程序架构分析 (中)相关推荐
- 微信小程序架构分析 (上)
作者:赵启明 链接:https://zhuanlan.zhihu.com/p/22754296 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 相信不少上手试用了微信 ...
- 微信小程序架构分析 (下)
作者:赵启明 链接:https://zhuanlan.zhihu.com/p/22932309 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 这一篇拖了一段时间,原 ...
- android 微信小程序原理,微信小程序架构原理
前言 昨日看朋友圈,据说有人花钱买star,不知道真的假的?以前以为只是大家开玩笑.今日早读文章由百度EUX@田光宇投稿分享. 正文从这开始~~ 微信小程序 微信小程序包含下面四种文件: js jso ...
- 微信小程序在开发中遇到的问题与解决方法
微信小程序在开发中遇到的问题与解决方法 参考文章: (1)微信小程序在开发中遇到的问题与解决方法 (2)https://www.cnblogs.com/zjjDaily/p/8032142.html ...
- 微信小程序云函数中的数据处理后返回
微信小程序云函数中的数据处理后返回 本文主要演示的是,在微信云函数中调用数据库后,如何对获取的内容在云函数内处理后返回. 主函数 exports.main = async (event, contex ...
- 微信小程序框架分析思维导图
微信小程序框架分析思维导图
- 微信小程序input框中加入小图标的实现方法
最近入坑小程序,要求在小程序的输入框中展示一个小图标,页面如下: 然后按照,html页面中的做法,在input框中添加了background-image属性,出乎意料的事,小程序报了下边这样一个错误: ...
- 微信小程序 使用vant-weapp中的索引栏右侧点不动,滚定索引失效问题
微信小程序 使用vant-weapp中的索引栏右侧点不动,滚定索引失效问题 vant的索引栏只要使用了定位,或者自定义头部,那么vant的索引栏就会出现各种BUG,基本不能用,这里我是基本重新写了一个 ...
- 微信小程序app.js中的全局变量globalData改变,如何通知其它页面界面显示改变,小程序中如何在其他页面中监听到globalData中值的变化
微信小程序app.js中的全局变量globalData改变,如何通知其它页面界面显示改变,小程序中如何在其他页面中监听到globalData中值的变化 在小程序中,我们一般在app.js的onLaun ...
最新文章
- ECHO.js 纯javascript轻量级延迟加载
- 机器学习系列19:将核函数应用于支持向量机
- 绝不能错过的10款最新OpenStack网络运维 监控工具
- 浅谈Python http库 httplib2
- VTK:可视化之Cursor3D
- markdown学习记录
- python慢为什么用的人还很多_为什么是所有人比python标准慢得多吗?
- HttpHandler和HttpModule 心得介绍
- P3372 【模板】线段树 1
- python下载大文件mp4_python 实现视频流下载保存MP4的方法
- OOD1---wangwenmin
- PandoraBox 路由器 IPv6穿透
- 固件版本区别:alpha版、beta版、rc版、stable版
- 360 和 qq 之争
- 实现开启和关闭android移动网络
- php表白情话,唯美表白情话短句 八字古风情话
- 橡胶接头分类和型号规格选择
- 推荐收藏 | 掌握这些步骤,机器学习模型问题药到病除
- c++超级简单的计算器
- word中利用题注实现公式图表自动编号及引用
热门文章
- 小学计算机教师德育工作计划,小学教师个人德育工作计划
- 警告: 程序集绑定日志记录被关闭。解决方法
- Linux下安装Docker,报错docker: unrecognized service的两种解决方案
- docker部署服务实现文件上传到本地问题解决
- docker load tar.gz包失败解决方法
- 卸载软件后,win10应用与功能中仍有残留,解决方案。
- 非受检异常(运行时异常)和受检异常的区别等
- Mybatis异常There is no getter for property named ‘XXX‘ in ‘class java.lang.String‘
- 如何在Windows上的Git Bash中退出'git diff'的结果? [重复]
- Android网络库的比较:OkHTTP,Retrofit和Volley [关闭]