大衆點評點餐小程序開發經驗 - 源碼解析

做者介紹:週中堅,美團點評工程師,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。
  • 小程序能夠認爲是前端的一個子集,並且相對封閉,開發時會有各類約束,查閱源碼能夠有助於小程序的設計。

本文對你有幫助?歡迎掃碼加入前端學習小組微信羣:

相關文章
相關標籤/搜索