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

微信小程序架构分析 (中)相关推荐

  1. 微信小程序架构分析 (上)

    作者:赵启明 链接:https://zhuanlan.zhihu.com/p/22754296 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 相信不少上手试用了微信 ...

  2. 微信小程序架构分析 (下)

    作者:赵启明 链接:https://zhuanlan.zhihu.com/p/22932309 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 这一篇拖了一段时间,原 ...

  3. android 微信小程序原理,微信小程序架构原理

    前言 昨日看朋友圈,据说有人花钱买star,不知道真的假的?以前以为只是大家开玩笑.今日早读文章由百度EUX@田光宇投稿分享. 正文从这开始~~ 微信小程序 微信小程序包含下面四种文件: js jso ...

  4. 微信小程序在开发中遇到的问题与解决方法

    微信小程序在开发中遇到的问题与解决方法 参考文章: (1)微信小程序在开发中遇到的问题与解决方法 (2)https://www.cnblogs.com/zjjDaily/p/8032142.html ...

  5. 微信小程序云函数中的数据处理后返回

    微信小程序云函数中的数据处理后返回 本文主要演示的是,在微信云函数中调用数据库后,如何对获取的内容在云函数内处理后返回. 主函数 exports.main = async (event, contex ...

  6. 微信小程序框架分析思维导图

    微信小程序框架分析思维导图

  7. 微信小程序input框中加入小图标的实现方法

    最近入坑小程序,要求在小程序的输入框中展示一个小图标,页面如下: 然后按照,html页面中的做法,在input框中添加了background-image属性,出乎意料的事,小程序报了下边这样一个错误: ...

  8. 微信小程序 使用vant-weapp中的索引栏右侧点不动,滚定索引失效问题

    微信小程序 使用vant-weapp中的索引栏右侧点不动,滚定索引失效问题 vant的索引栏只要使用了定位,或者自定义头部,那么vant的索引栏就会出现各种BUG,基本不能用,这里我是基本重新写了一个 ...

  9. 微信小程序app.js中的全局变量globalData改变,如何通知其它页面界面显示改变,小程序中如何在其他页面中监听到globalData中值的变化

    微信小程序app.js中的全局变量globalData改变,如何通知其它页面界面显示改变,小程序中如何在其他页面中监听到globalData中值的变化 在小程序中,我们一般在app.js的onLaun ...

最新文章

  1. ECHO.js 纯javascript轻量级延迟加载
  2. 机器学习系列19:将核函数应用于支持向量机
  3. 绝不能错过的10款最新OpenStack网络运维 监控工具
  4. 浅谈Python http库 httplib2
  5. VTK:可视化之Cursor3D
  6. markdown学习记录
  7. python慢为什么用的人还很多_为什么是所有人比python标准慢得多吗?
  8. HttpHandler和HttpModule 心得介绍
  9. P3372 【模板】线段树 1
  10. python下载大文件mp4_python 实现视频流下载保存MP4的方法
  11. OOD1---wangwenmin
  12. PandoraBox 路由器 IPv6穿透
  13. 固件版本区别:alpha版、beta版、rc版、stable版
  14. 360 和 qq 之争
  15. 实现开启和关闭android移动网络
  16. php表白情话,唯美表白情话短句 八字古风情话
  17. 橡胶接头分类和型号规格选择
  18. 推荐收藏 | 掌握这些步骤,机器学习模型问题药到病除
  19. c++超级简单的计算器
  20. word中利用题注实现公式图表自动编号及引用

热门文章

  1. 小学计算机教师德育工作计划,小学教师个人德育工作计划
  2. 警告: 程序集绑定日志记录被关闭。解决方法
  3. Linux下安装Docker,报错docker: unrecognized service的两种解决方案
  4. docker部署服务实现文件上传到本地问题解决
  5. docker load tar.gz包失败解决方法
  6. 卸载软件后,win10应用与功能中仍有残留,解决方案。
  7. 非受检异常(运行时异常)和受检异常的区别等
  8. Mybatis异常There is no getter for property named ‘XXX‘ in ‘class java.lang.String‘
  9. 如何在Windows上的Git Bash中退出'git diff'的结果? [重复]
  10. Android网络库的比较:OkHTTP,Retrofit和Volley [关闭]