企業微信 JS-SDK 自建應用踩坑指南

前言

最近一直在用企業微信 JS-SDK 來開發企業微信的側邊欄,用得特別不爽。主要原因是在官方文檔的客戶端那一塊沒有講特別詳細,在服務端那裏講了,搞得我一開發客戶端的一臉懵逼,而且官方文檔也很久沒有維護更新了,要排查問題簡直難上加難。

這裏特別感謝和我一起開發的後端大哥,很多問題都幫我解決了,麼麼。

什麼是側邊欄

企業微信的側邊欄其實就是類似於我們微信的聊天小工具。

而企業微信將其增強了,可以自定義裏面的工具,並能調用企業微信應用自己的 API,比如獲取用戶信息,選取外部聯繫人等等。其本質上就是 Hybird。

下面就是側邊欄的樣例。

可以想象得到這個小工具的頁面就是前端 HTML 嘛,因此,在這基礎上加上企業微信提供的 JS-SDK 我們就可以愉快地調用應用的 API 了。

但是。。。真的愉快麼?

文檔

企業微信開發文檔第一步就卡死了很多人。開發文檔是在這裏:

https://work.weixin.qq.com/api/doc/90000/90136/90512

爲什麼要單獨說,因爲點擊去客戶端API這個 Tab,直接顯示是的展開的小程序菜單,很容易就看到小程序那塊去了。。。而且翻來翻去找不到 JS-SDK 的文檔。

你來告訴我,JS-SDK 的文檔在哪?

其實你不知道的是這個“小程序”菜單項收起來就可以看到 JS-SDK 的文檔的。哦?怪我不折疊咯?

自建應用

首先你得有個企業,然後才能在裏面自建應用,並配置到企業員工的側邊欄。具體怎麼配置可以參考:

掘金:https://juejin.im/post/6844904132122247182 裏的“搭建自建應用”部分。

步驟如下:

  1. https://work.weixin.qq.com/wework_admin/frame#apps
  2. 點擊應用管理 -> 創建應用 -> 填寫應用信息 -> 點擊應用 -> 配置到聊天工具欄的配置 -> 配置頁面 -> 輸入你的域名地址
  3. 最最最重要的一步:在應用配置的頁面 -> 啓用網頁授權及JS-SDK,一定要做!不然用不了 JS-SDK

引入

首先我們來看怎麼引入 JS-SDK。你可能會想:就這還用你來教?那我只能說 too young too naive。

這裏先放一下官方文檔是教我們怎麼引入的:

https://work.weixin.qq.com/api/doc/90001/90144/90547

NPM 包

現在畢竟都是 2020 年了,NPM 包用得多舒服啊,還有版本鎖定,而且 NPM 包很多都帶 TypeScript 類型聲明文件的,用起來都不需要去看文檔了。CDN 和 NPM 兩個選擇,我肯定選 NPM嘛。

因此我在網上找到了這個:

https://www.npmjs.com/package/wxwork-js-sdk,經檢驗,沒什麼用。

又找到這個:

https://npm.io/package/wxwork-jsapi,經檢驗,可用。

但是!這個包在 Windows 下不能正常調用 wx.config,導致所有其他功能都不能使用。而且這個包還在不斷更新中,觀察了幾周,其版本就更新了幾次,非常不穩定。

不過,這裏也非常感謝這位作者的貢獻,雖然目前他的這個包還不能投放到生產環境中,但是這種勇於創新的精神還是值得肯定的。希望有一天可以用得上這個穩定的包,畢竟我還爲這個包寫了個 TS 類型聲明文件。

cdn 引入

說實話,Windows 上不能使用這個坑我找了4天的Bug才發現是這個包的問題。其實呢,官方也是不推薦使用 NPM 包的,只推薦使用 CDN 方式引入。好吧,算你狠。

按照官方文檔:

<script src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>

如果你覺得這就完事了,那你就想得太簡單了。在某些情況下,會出現 wx.agentConfig is not a function 的報錯,如果不能正常調用 agentConfig,不好意思,你還是不能使用企業微信的 API。

這個問題我找了一圈,終於找到解決方案:

https://developers.weixin.qq.com/community/develop/article/doc/00022417118c78d4448af86625b413

官方文檔提到,需要引入http://res.wx.qq.com/open/js/jweixin-1.2.0.js,但是經過大量測試驗證,得出如下解決方案:

1、引入http://res.wx.qq.com/open/js/jweixin-1.2.0.js
企業微信Mac版:通過wx.config、wx.agentConfig
企業微信手機版:報錯:wx.agentConfig is not a function

2、引入https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js
企業微信Mac版:報錯:window.wx未定義
企業微信手機版:通過wx.config、wx.agentConfig

3、引入https://res.wx.qq.com/wwopen/js/jsapi/jweixin-1.0.0.js
企業微信Mac版:通過wx.config、wx.agentConfig
企業微信手機版:通過wx.config、wx.agentConfig

總結:
在進行企業微信JS SDK對接時,正確引用的JS文件應該是https://res.wx.qq.com/wwopen/js/jsapi/jweixin-1.0.0.js,而這個文件鏈接在官方文檔中根本就找不到,是筆者通過F12 Network標籤反覆比對出來的。與大家共享,希望少走彎路。

驚不驚喜?意不意外?你猜都猜不對怎麼才能正常引入這個 SDK。

起步

引入了之後呢,就得按官方的步驟來了。https://work.weixin.qq.com/api/doc/90001/90144/90547

  1. 從服務端獲取 ticket(企業 ticket 和 應用 ticket)
  2. 生成簽名,注意:企業 ticket 對應的 wx.config 裏面的簽名,應用 ticket 對應的是 wx.agentConfig 裏的簽名
  3. 最後 wx.configwx.agentConfig

但是,文檔裏面有一行小字

注意:從企業微信3.0.24及以後版本(可通過企業微信UA判斷版本號),無須先調用wx.config,可直接 wx.agentConfig

這裏可以做個小優化,不過你要是想偷懶,可以直接 configagentConfig 都用上,反正不報錯的。

文檔裏還提到了 wx.checkJsApi(fn)wx.ready(fn)wx.error(fn),這三個玩意,官方文檔裏說是必須要寫上的,其實用處不大,可以不寫。因爲 checkJsApi 的功能可以通過 agentConfig 就可以知道哪些 API 是可以使用的。而 wx.readywx.error 可以通過回調來做監聽:

const onFail = async (res: any) => {
  if (res.errMsg.indexOf('function not exist') > -1) {
    message.error('版本過低請升級');
  } else {
    message.error(res.errMsg);
  }
};

const init = (meta: ISetupMeta, url: string, cb: Function) => {
  // 調用普通 API 的配置
  const corpConfig: IConfig = {
    ...
    fail: onFail,
  };

  // 調用需要權限的 API 的配置
  const appConfig: IAgentConfig = {
    ...
    success: cb,
    fail: onFail,
  };

  // 企業微信 < 3.0.24 需要先調用 wx.config 再調用 agentConfig
  // 企業微信 >= 3.0.24 可以直接調用 agentConfig
  window.wx.config(corpConfig);
  window.wx.agentConfig(appConfig);
};

假如我們要使用獲取當前外部聯繫人userid這個功能,就可以這樣:

const onGetCurExternalContact = () => {
  setLoading(false);
  if (res.err_msg === 'getCurExternalContact:ok') {
    localStorage.setItem('userId', res.userId);
    fetchUser(res.userId);
  } else {
    message.error(res.err_msg);
  }
}

useEffect(() => {
  init(setupMeta, currentUrl, () => {
    window.wx.invoke('getCurExternalContact', {}, onGetCurExternalContact);
  });
}, []);

agentConfig 成功之後,直接調用 getCurExternalContact。

參考官方文檔的小字部分:

接口調用說明
所有接口通過wx對象(也可使用jWeixin對象)來調用,參數是一個對象,除了每個接口本身需要傳的參數之外,還有以下通用參數:

success:接口調用成功時執行的回調函數。
fail:接口調用失敗時執行的回調函數。
complete:接口調用完成時執行的回調函數,無論成功或失敗都會執行。
cancel:用戶點擊取消時的回調函數,僅部分有用戶取消操作的api纔會用到。
trigger: 監聽Menu中的按鈕點擊時觸發的方法,該方法僅支持Menu中的相關接口。

如果你不仔細看文檔的小字,就會錯過很多《最佳實踐》哦。

獲取自己 UserId 的流程

流程圖如下:

是不是覺得怎麼這麼麻煩?OK,我們來反推一下你就知道爲什麼這個流程要這麼長了。

  1. 首先服務端是要通過這個接口來獲取 userId 的。其中用到?suite_access_token=SUITE_ACCESS_TOKEN&code=CODE。suite_access_token 一般是緩存到服務端的,不管。那就要看 code 是怎麼來的。

  2. code 其實是通過重定向這個方法來獲取的。所以剛進入你的網頁就要判斷是否有緩存 userId?沒有,那就構造URL並重定向,然後獲取問號後面的 code的值,再發請求到後端換取 userId。

正推過來就是上面的流程圖。

調試

側邊欄的調試,你可能會用 VConsole 來打 log,或者看 Network,畢竟這東西就沒法給你 F12。但是。。。真的沒法 F12 麼?

參考: https://work.weixin.qq.com/api/doc/90001/90148/90457#%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%B0%83%E8%AF%95

這上面寫了 Windows 和 Mac 兩種調試的方法,其中 Windows 的朋友在你最新的企業微信下已經有 devtools_resources.pak 這個玩意了,不需要你去下載再 Ctrl-C Ctrl-V。直接打開調試模式就可以了。

這裏也是有坑的地方的,比如網絡請求獲取不全:你打開側邊欄,再打開調試工具會看到前面的請求沒了。所以你最好,先打開側邊欄,打開調試工具,再刷新,這樣的 log 和請求顯示比較全。

上面如果操作正確的話,應該是可以快樂使用企業微信的 JS-SDK 了,但是其實在我使用的時候還會有一些坑。

緩存

文檔裏不止一次說到要做緩存,服務端可以使用 Redis 來緩存 access token,客戶端可能會想到 localStorage,但是不好意思,側邊欄的 localStorage 就是個擺設。

經過我多次實驗, localStorage 每次進來都會被重置掉。而 IndexedDB 確不會被重置,所以我的緩存都是放到 IndexedDB 裏的。

這裏有人就會說了,啊這,我看 IndexedDB 用起來好麻煩呀,又監聽這又監聽那的,就不能像 localStorage 那樣 key-value 直接存和取麼?答案是是可以的,可以用 idb-keyval 這個庫,然後可以這樣做一個簡單的封裝:

import { clear, del, get, set } from 'idb-keyval';

const Storage = {
  setItem: async (key: string, value: string, expires: string) => {
    await set(key, value);
  },
  getItem: async (key: string): Promise<null | string> => {
    return get<string>(key);
  },
  clear: async () => {
    await clear();
  },
  del: async (key: string) => {
    await del(key);
  },
};

重定向路徑

上面說到了獲取 UserId 是需要重定向的,假如你的應用用到了 Hash 路由模式,就會有點問題了,請欣賞:

  1. https://developers.weixin.qq.com/community/develop/doc/000a448b7d06207a6e39f023d5b400?highLine=hash
  2. https://developers.weixin.qq.com/community/develop/doc/00082cde184dc8d0c8b9d9fe151800?highLine=hash

其實我也遇到了上面重定向之後 code=xxx 接到別的地方去的問題。試過 /#/user/#user#/user#user 都不行,反正就是不行,MD。

最後我妥協了,用 ?page=user 這種方式去切換路由,即:根據 page 的值切換不同的組件,如 UserPageLoginPage 等,有點 Low,但是能用。

最後

遇到問題多嘗試,多去開放社區 找答案和問問題。你問爲什麼是去這裏不是 Google?因爲 JS-SDK 只有這個地方纔能找到答案

祝你好運!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章