# 「大众点评点餐」小程序开发经验 05：解析开发工具

- 作者：知晓程序
- 发布时间：2017-04-12 11:56
- 原文：https://www.ifanr.com/minapp/817760

![](https://s3.ifanr.com/wp-content/uploads/2017/03/Featured-12.jpg)

文 \| 周中坚

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

知晓程序（微信号 zxcx0101）已经为大家带来 4 篇来自大众点评的小程序开发文章，相信大家看了这些文章、再结合官方文档已经可以毫无压力地开发小程序了。

关注知晓程序（微信号 zxcx0101），回复「点评」，获取该系列所有文章。

但是为什么有这些坑，是不是可以绕过去，怎么排查问题，**我们还想从源头——小程序的源码的角度来尝试分析**，因此有了这篇源码解析。

### 代码结构

以 macOS 系统为例，首先进入应用程序文件夹，再右键微信开发者工具显示包内容，最后让我们进入 `./Contents/Resources/app.nw` 目录。

接下来，我们就可以查看小程序的源码了。代码结构如图：

![](https://s3.ifanr.com/wp-content/uploads/2017/04/Artboard.jpg)

文件夹看起来很多，但命名还算清晰。现在，让我们从开发者工具入手，来看下都用到了哪些文件吧。

### 开发者工具

#### 首页

![](https://s3.ifanr.com/wp-content/uploads/2017/04/036bd2901c83e177d3c9.jpg)

这个页面里的很多信息，可以和这个项目中的 `package.json` 对应起来，比如 name、icon、version 等。

#### 代理

![](https://s3.ifanr.com/wp-content/uploads/2017/04/51363e82a7ef5b0418f3.jpg)

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

#### 菜单

![](https://s3.ifanr.com/wp-content/uploads/2017/04/Artboard-1.jpg)

上图可以看到我对菜单做的一些定制。

菜单的设置在 `./app/dist/common/menu/menu.js`，动作在 `./app/dist/common/actions/actions.js`，大家可以自行到代码中查看文件的 `require` 进一步分析。

#### 设备及网络

![](https://s3.ifanr.com/wp-content/uploads/2017/04/Artboard-2.jpg)

上图可以看到我自己添加了一个设备以及一个网络类型。

模拟器的设备配置在 `./app/dist/config/DeviceModules.js`，网络配置在 `./app/dist/common/jssdk/osInfoSdk.js`。

#### 调试工具

![](https://s3.ifanr.com/wp-content/uploads/2017/04/5bc65d9e1e6af2b55b65.jpg)

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

![](https://s3.ifanr.com/wp-content/uploads/2017/04/Artboard-3.jpg)

其中 Console、Sources、Network 就是直接使用的 DevTools，而 Storage、AppData、Wxml、Sensor，都是微信自己实现的。

参照这些调试工具，我们自己弄一个自己的调试工具就很简单了。

只要在 `./app/dist/extensions` 目录下新建一个文件夹，用  
`html/css/js` 完成这个工具的功能，再将 `devtools.html` 这个工具引入  
`chrome.devtools.panels.create()`。

![](https://s3.ifanr.com/wp-content/uploads/2017/04/ce8b71c6cbac7d4138af.jpg)

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

![](https://s3.ifanr.com/wp-content/uploads/2017/04/Artboard-4.jpg)

（知晓程序注：最新版微信，已支持小程序蓝牙接口。）

### WeApp

上面一节主要讲的是小程序开发者工具的源码。我们借助分析源码可以搞清楚代理是怎么设置的，模拟器的设备和网络如何添加，怎样开发一个满足自己特定需求的 DevTool。

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

- `tpl` 文件夹下是页面模板。
- `onlinevendor/wcc` 在编译时把 WXML 文件转为 JS 文件，`onlinevendor/wcsc` 在编译时把 wxss 文件转化为 js，这也是编译包比代码库要大不少的重要原因。
- `trans` 文件夹下有五个方法，其中 `transConfigToPf` 可以将配置转成 `pageFrame`，`trans/transWxmlToHtml` 将 WXML 转成 DOM 树，再进一步用 WebView 渲染，`trans/transWxssToCss` 将 WXSS 转成 CSS，提供 View 层样式。
- `onlinevendor/WAService.js` 提供了service 层几乎一切功能。

#### pageFrame

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

```

<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 将页面生成出来的。**

```

<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。
- 小程序可以认为是前端的一个子集，而且相对封闭，开发时会有各种约束，查阅源码可以有助于小程序的设计。

**本文由知晓程序授权转载，关注微信号 zxcx0101，在微信后台回复「点评」，获取大众点评小程序开发系列文章。**

![zxcx&#95;0208](https://s3.ifanr.com/wp-content/uploads/2017/02/zxcx_0208.png)
