前言
最近一直在用企業微信 JS-SDK 來開發企業微信的側邊欄,用得特別不爽。主要原因是在官方文檔的客戶端那一塊沒有講特別詳細,在服務端那裏講了,搞得我一開發客戶端的一臉懵逼,而且官方文檔也很久沒有維護更新了,要排查問題簡直難上加難。
這裏特別感謝和我一起開發的後端大哥,很多問題都幫我解決了,麼麼。
什麼是側邊欄
企業微信的側邊欄其實就是類似於我們微信的聊天小工具。
而企業微信將其增強了,可以自定義裏面的工具,並能調用企業微信應用自己的 API,比如獲取用戶信息,選取外部聯繫人等等。其本質上就是 Hybird。
下面就是側邊欄的樣例。
可以想象得到這個小工具的頁面就是前端 HTML 嘛,因此,在這基礎上加上企業微信提供的 JS-SDK 我們就可以愉快地調用應用的 API 了。
但是。。。真的愉快麼?
文檔
企業微信開發文檔第一步就卡死了很多人。開發文檔是在這裏:
爲什麼要單獨說,因爲點擊去客戶端API這個 Tab,直接顯示是的展開的小程序菜單,很容易就看到小程序那塊去了。。。而且翻來翻去找不到 JS-SDK 的文檔。
你來告訴我,JS-SDK 的文檔在哪?
其實你不知道的是這個“小程序”菜單項收起來就可以看到 JS-SDK 的文檔的。哦?怪我不折疊咯?
自建應用
首先你得有個企業,然後才能在裏面自建應用,並配置到企業員工的側邊欄。具體怎麼配置可以參考:
掘金:https://juejin.im/post/6844904132122247182 裏的“搭建自建應用”部分。
步驟如下:
- 去 https://work.weixin.qq.com/wework_admin/frame#apps
- 點擊應用管理 -> 創建應用 -> 填寫應用信息 -> 點擊應用 -> 配置到聊天工具欄的配置 -> 配置頁面 -> 輸入你的域名地址
- 最最最重要的一步:在應用配置的頁面 -> 啓用網頁授權及JS-SDK,一定要做!不然用不了 JS-SDK
引入
首先我們來看怎麼引入 JS-SDK。你可能會想:就這還用你來教?那我只能說 too young too naive。
這裏先放一下官方文檔是教我們怎麼引入的:
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 function2、引入https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js
企業微信Mac版:報錯:window.wx未定義
企業微信手機版:通過wx.config、wx.agentConfig3、引入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
- 從服務端獲取 ticket(企業 ticket 和 應用 ticket)
- 生成簽名,注意:企業 ticket 對應的
wx.config
裏面的簽名,應用 ticket 對應的是wx.agentConfig
裏的簽名 - 最後
wx.config
再wx.agentConfig
但是,文檔裏面有一行小字
注意:從企業微信3.0.24及以後版本(可通過企業微信UA判斷版本號),無須先調用wx.config,可直接
wx.agentConfig
。
這裏可以做個小優化,不過你要是想偷懶,可以直接 config
和 agentConfig
都用上,反正不報錯的。
文檔裏還提到了 wx.checkJsApi(fn)
,wx.ready(fn)
和 wx.error(fn)
,這三個玩意,官方文檔裏說是必須要寫上的,其實用處不大,可以不寫。因爲 checkJsApi
的功能可以通過 agentConfig
就可以知道哪些 API 是可以使用的。而 wx.ready
和 wx.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,我們來反推一下你就知道爲什麼這個流程要這麼長了。
首先服務端是要通過這個接口來獲取 userId 的。其中用到
?suite_access_token=SUITE_ACCESS_TOKEN&code=CODE
。suite_access_token 一般是緩存到服務端的,不管。那就要看 code 是怎麼來的。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 路由模式,就會有點問題了,請欣賞:
- https://developers.weixin.qq.com/community/develop/doc/000a448b7d06207a6e39f023d5b400?highLine=hash
- https://developers.weixin.qq.com/community/develop/doc/00082cde184dc8d0c8b9d9fe151800?highLine=hash
其實我也遇到了上面重定向之後 code=xxx 接到別的地方去的問題。試過 /#/user
,/#user
,#/user
,#user
都不行,反正就是不行,MD。
最後我妥協了,用 ?page=user
這種方式去切換路由,即:根據 page 的值切換不同的組件,如 UserPage
,LoginPage
等,有點 Low,但是能用。
最後
遇到問題多嘗試,多去開放社區 找答案和問問題。你問爲什麼是去這裏不是 Google?因爲 JS-SDK 只有這個地方纔能找到答案
祝你好運!