企业微信 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 只有这个地方才能找到答案

祝你好运!

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