大众点评点餐小程序开发经验 - 源码解析

做者介绍:周中坚,美团点评工程师,4年 Web 前端开发经验,主要负责过会员卡、外卖、预订、商家平台等业务的前端开发,如今是美团点评点餐团队的一员。css

咱们团队的小程序开发经验系列文章已经发布了4篇,这些文章主要介绍了小程序开发概述小程序的视图层小程序的逻辑层小程序开发中碰到的坑(几个设计实例)。相信你们看了这些文章,再结合官方文档已经能够毫无压力地开发小程序了,可是为何有这些坑,是否是能够绕过去,怎么排查问题,咱们还想从源头——小程序的源码的角度来尝试分析,所以有了这篇源码解析。html

代码结构

以 mac 电脑为例,首先进入应用程序文件夹,再右键微信开发者工具显示包内容,最后让咱们进入 ./Contents/Resources/app.nw 目录下就能够查看小程序的源码了,代码结构如图:
前端

文件夹看起来不少,但命名还算清晰,如今让咱们先从开发者工具界面的角度来看下都用到了哪些文件吧。web

开发者工具

首页


首页的不少信息能够和这个项目中的 package.json对应起来,好比name, icon, version等。

代理


代理的设置在 ./app/dist/components/setting/setting.js,而用户设置的保存(包括后面要说的模拟器设备、网络等信息)是调用了 ./app/dist/stores/*.js方法。

菜单


上图能够看到我对菜单作的一些定制。
菜单的设置在 ./app/dist/common/menu/menu.js,动做在 ./app/dist/common/actions/actions.js,你们能够自行到代码中查看文件的 require进一步分析。

设备及网络


上图能够看到我本身添加了一个设备以及一个网络类型。
模拟器的设备配置在 ./app/dist/config/DeviceModules.js,网络配置在 ./app/dist/common/jssdk/osInfoSdk.js

调试工具


调试工具是这一节最核心的内容了,乍一看微信的调试工具和 chrome 的 DevTools 长的很像,查看源代码发现果真就是借助 chrome 的 DevTools 实现的。

其中 Console, Sources, Network 就是直接使用的 DevTools, 而 Storage, AppData, Wxml, Sensor 是本身实现的。chrome

参照 Storage, AppData, Wxml, Sensor 这些调试工具,这些咱们要本身添加一个其实很是简单,只要在./app/dist/extensions目录下新建一个文件夹,用html/css/js完成这个工具的功能,再改devtools.html将这个工具引入进来chrome.devtools.panels.create()便可,如图:
json

有趣的是,在0.15.150201这个测试版中已经发现了一个名为Bluetooth的开发工具。
小程序

weapp

上面一节主要讲的是小程序开发者工具的源码,咱们借助分析源码能够搞清楚代理是怎么设置的,模拟器的设备和网络如何添加,怎样开发一个知足本身特定需求的 DevTool。微信小程序

这一节主要介绍咱们写的微信小程序的代码是如何变成页面在用户的终端运行的:api

  • tpl 文件夹下是页面模板。
  • onlinevendor/wcc 在编译时把 wxml 文件 转为 js,onlinevendor/wcsc 在编译时把 wxss 文件转化为 js,这也是编译包比代码库要大很多的重要缘由。
  • trans 文件夹下有五个方法,其中 transConfigToPf 将配置转成pageFrame,trans/transWxmlToHtml 将 wxml 转成 dom tree, 再进一步用 webview 渲染,trans/transWxssToCss 将 wxss 转成 css,提供 view 层样式。
  • onlinevendor/WAService.js 提供了service 层几乎一切功能。

pageFrame

首先是看一下刚才提到的 pageFrame,对应的 transConfigToPf 主要用字符串替换的方式完成转换。微信

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <link href="https://res.wx.qq.com/mpres/htmledition/images/favicon218877.ico" rel="Shortcut Icon">
  <meta http-equiv="Content-Security-Policy" content="script-src 'self' *.qq.com 'unsafe-inline' 'unsafe-eval'">
  <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 -->

  <!--{{appconfig}}-->

  <!--{{pageconfig}}-->

  <!--{{WAWebview}}-->

  <!--{{reportSDK}}-->

  <!--{{webviewSDK}}-->

  <!--{{exparser}}-->

  <!--{{components_js}}-->

  <!--{{virtual_dom}}-->

  <!--{{components_css}}-->

  <!--{{allWXML}}-->

  <!--{{eruda}}-->

  <!--{{style}}-->

  <!--{{currentstyle}}-->

  <!--{{generateFunc}}-->

</head>

<body>
  <div></div>
</body>

</html>复制代码

appservice 页面模板

开发者工具提供了封装过的 wxml pannel, 咱们并不能从中看到页面完整的 dom 结构,可是用 $('*')选择器咱们能够看到页面的 appservice 模板,看这段代码咱们能够分析出小程序是如何使用 wxml, wxss, js 将页面生成出来的。

<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline' 'unsafe-eval'">
  <link href="https://res.wx.qq.com/mpres/htmledition/images/favicon218877.ico" rel="Shortcut Icon">
  <script>
  var __wxAppData = {}
  var __wxRoute
  var __wxRouteBegin
  global = {}
  </script>
  <script></script><!-- 加载一堆script标签 -->
</head>

<body>
  <p>
    开发者工具使用 nwjs 来模拟小程序的实现,帮助你们来开发和调试微信小程序,因此这里是一个 webview,但真实
    的手机端是运行在 jscore 中的,因此请不要使用任何 bom 对象。
  </p>
  <p>
    咱们建议你先完整阅读该开发文档,这将有助于更快地完成开发。若是发现咱们的文档有任何错漏,
    或者开发过程当中有任何疑问或者你有更好的建议,欢迎经过下列邮箱联系咱们

    weixin_developer@qq.com

    或者访问微信小程序开发者社区提交问题:

    https://developers.weixin.qq.com
  </p>
  <script>
    window._____sendMsgToNW({
      sdkName: 'APP_SERVICE_COMPLETE'
    })
  </script>
</body>

</html>复制代码

WAService.js

WAService.js 是小程序页面运行的核心方法,主要有几大功能:

  • 内置的 report 方法定义
  • 微信小程序 API 封装
  • WeixinJSBridge 封装
  • appServiceEngine 模块
    // 内置的 report 方法定义,用于内部 API 的调用日志 & 报错记录等。
    var Reporter = {
      surroundThirdByTryCatch,
      slowReport,
      speedReport,
      reportKeyValue,
      reportIDKey,
      thirdErrorReport,
      errorReport,
      log,
      submit,
      registerErrorListener,
      unRegisterErrorListener,
      triggerErrorMessage
    }
    // 微信小程序 API 封装,全部文档中的 [API](https://mp.weixin.qq.com/debug/wxadoc/dev/api/)都在这里封装了,以showModal为例简单分析一下
    showModal: function() {
      var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}
        , t = {
          title: "",
          content: "",
          confirmText: "肯定",
          cancelText: "取消",
          showCancel: !0,
          confirmColor: "#3CC51F",
          cancelColor: "#000000"
      };// 默认值,此处比文档准确
      if (t = (0,
      f.extend)(t, e),
      a("showModal", t, {// 调用 jsbridge,见下方代码
          title: "",
          content: "",
          confirmText: "",
          cancelText: "",
          confirmColor: "",
          cancelColor: ""
      }))
          return t.confirmText.length > 4 ? void B("showModal", e, "showModal:fail confirmText length should not large then 4") : t.cancelText.length > 4 ? void B("showModal", e, "showModal:fail cancelText length should not large then 4") : void (0, // 各类校验
          u.invokeMethod)("showModal", t, {
              beforeSuccess: function(e) {
                  e.confirm = Boolean(e.confirm)// 返回值处理
              }
          })
    }
    // 此处调用 WeixinJSBridge,此外还对每一个 API 调用记 log,方便微信小程序的问题排查
    function a() {
      var e = Array.prototype.slice.call(arguments)
        , t = e[1];
      e[1] = function(e, n) {
          var o = e.data
            , r = e.options
            , i = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {}
            , a = r && r.timestamp || 0
            , s = Date.now();
          "function" == typeof t && t(o, n),
          Reporter.speedReport({
              key: "webview2AppService",
              data: o || {},
              timeMark: {
                  startTime: a,
                  endTime: s,
                  nativeTime: i.nativeTime || 0
              }
          })
      }
      ,
      WeixinJSBridge.subscribe.apply(WeixinJSBridge, e)
    }
    // WeixinJSBridge 封装,底层是调用 WeixinJSCore
    e.WeixinJSBridge = {
      invoke: d,
      invokeCallbackHandler: p,
      on: h,
      publish: v,
      subscribe: g,
      subscribeHandler: y
    }
    // 内置的 jsbridge core
    WeixinJSCore = {
      invokeHandler,
      publishHandler
    }
    // setData 方法定义,逻辑层经过 setData 方法改变 virtual dom,改变 dom tree,从而改变视图层
    // appServiceEngine 模块,提供 App 和 Page 相关的接口复制代码

总结

若是是为了源码分析而进行源码分析,我以为大可没必要,在小程序的场景下,源码分析的价值在于:

  • 官方文档不必定和实际状况是对齐的,开发时碰到不一致的状况能够查阅源码,以此为准。
  • 熟悉源码结构能够快速定位问题,提高开发效率,甚至给本身开发合适的 DevTool。
  • 小程序能够认为是前端的一个子集,并且相对封闭,开发时会有各类约束,查阅源码能够有助于小程序的设计。

本文对你有帮助?欢迎扫码加入前端学习小组微信群:

相关文章
相关标签/搜索