多端复用:不同版本JSSDK使用探究

271 阅读6分钟

场景:

将企微侧边栏的H5可以平迁到小程序的webview中使用,做到一套代码多端使用。

同时要保证在小程序使用的H5可以跟原生小程序进行交互。即:小程序可以通过点击事件进入到H5页面,H5页面也可以通过点击事件跳转到小程序页面。即可以使用**`wx.miniProgram`**相关的接口。

企微侧边栏示例:

可以发现:企微侧边栏的能力大部分是独立模块,如果在同服务的情况下内嵌到小程序webview可以极大的提高代码复用。一次开发多端使用。

问题:

研发需求:

在这个过程中遇到了问题,多个不同版本的JSSDK不能同时共存

其实这也不会是个很大问题,毕竟我们可以在构建打包的时候使用不同的脚本去动态插入不同版本的JSSDK。但是这样做就会存在需要构建两次的问题,会增加构建时长。

期待结果:不使用不同的脚本区分环境。解决项目构建时长的问题。毕竟一套代码基于不同的脚本打包两次,构建时长是翻倍的!!!

官方限制:

  1. 企微微信使用JSSDK:developer.work.weixin.qq.com/document/pa…

  2. 微信小程序webview使用JSSDK: developers.weixin.qq.com/miniprogram…

重点:小程序使用的JSSDK同时还要满足可以跟原生通信:即小程序可以跳转到webview,内嵌的webview可以跳转到小程序

微信JSSDK 1.6.0与1.2.0版本区别:

版本定位与使用场景:

  • 1.6.0版本:主要服务于微信公众号(如服务号、订阅号),支持更新的接口和功能,例如分享接口的升级(如updateAppMessageShareDataupdateTimelineShareData),并优化了部分功能兼容性。

  • 1.2.0版本:主要用于企业微信环境,因企业微信客户端未完全同步微信公众号的JS-SDK更新,导致部分高版本功能无法兼容。例如,企业微信可能不支持1.6.0新增的接口。

接口差异与兼容性:

  • 分享接口

    • 1.6.0版本推荐使用updateAppMessageShareDataupdateTimelineShareData等新接口,而1.2.0版本仍依赖旧接口如onMenuShareAppMessageonMenuShareTimeline。若在企业微信中使用1.6.0的新接口,可能导致分享内容显示异常。

    • 例如,分享到企业微信群可能显示链接而非图文卡片,需回退到旧版接口适配。

  • 图像接口

    • 1.2.0版本修复了iOS系统下chooseImage返回的localId无法直接预览的问题(需通过getLocalImgData获取Base64数据),而1.6.0可能进一步优化了跨平台兼容性。
  • 其他接口

    • 1.6.0可能引入更多新功能或参数扩展(如checkJsApi的增强支持),而1.2.0功能相对固定。

签名与安全配置:

  • 签名算法要求:两个版本的签名逻辑(基于jsapi_ticketnonceStrtimestampurl的SHA-1加密)一致,但实际应用中,企业微信可能因客户端环境差异导致相同签名在微信公众号正常、企业微信报错(如invalid signature),需检查URL处理或版本兼容性。

  • 安全域名配置:微信公众号和企业微信需分别配置独立的JS接口安全域名,不可混用19。

版本升级与适配建议:

  • 从1.2.0升级到1.6.0

    • 必要性:若需使用新版分享接口或修复特定平台问题(如iOS图片预览),建议升级。

    • 风险:直接替换JS文件可能导致企业微信环境报错,需同时调整接口调用方式并测试兼容性。

  • 混合使用场景

    • 若同一页面需同时支持微信公众号和企业微信,需根据环境动态加载不同版本的JS文件(如1.6.0用于微信,1.2.0用于企业微信),并通过接口降级策略处理功能差异。

备注:

基于以上对不同版本差异性的对比,不太敢贸然将JSSDK升级到1.6.0版本。存在不确定以及测试没办法全量回归测试一遍。

环境变量的方案:

当前使用的可行方案如下:

但是这个方案需要构建两次,不是很理想。理想的方案是可以基于环境动态加载不同版本的JSSDK。

构建脚本:

 "build:prod": "vite build --mode prod",    
 "build:mini": "vite build --mode prodmini",

vite.config.ts配置:

const h5v3Scripts = `
<script src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
<script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script>
`
const miniScripts = `
<script src="//res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
`
// 区分环境 
const isMini = env.mode === 'prodmini' ? true : false

createHtmlPlugin({
  inject: {
    data: {
      injectScript: isMini ? miniScripts : h5v3Scripts,
    },
  },
}),

动态加载方案:

动态加载JSSDK代码:

const isEnterpriseWechat = () => {
  const ua = navigator.userAgent.toLowerCase();
  return ua.indexOf('wxwork') !== -1; // 企业微信的UA中包含"wxwork"
}

const loadScript = (src:string, callback:any) =>  {
  const script = document.createElement('script');
  script.src = src;
  script.onload = callback; // 加载完成后执行回调
  document.head.appendChild(script);
}

export const loadJSSDK = (callback:any) => {
  const isEnterprise = isEnterpriseWechat();
  const jssdkUrl = isEnterprise
      ? '//res.wx.qq.com/open/js/jweixin-1.2.0.js' // 企业微信使用1.2.0
      : '//res.wx.qq.com/open/js/jweixin-1.6.0.js'; // 微信公众号使用1.6.0
  const jsworkUrl = 'https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js'
  if (isEnterprise) {
    loadScript(jssdkUrl, () => {
      console.log('jweixin-1.2.0.js 加载完成');
      loadScript(jsworkUrl, () => {
        console.log('jwxwork-1.0.0.js 加载完成', wx, wx.agentConfig);
        callback(true)
      })
    })
  } else {
    loadScript(jssdkUrl, callback)
  }
} 

动态加载问题1:

当前这种动态加载JSSDK的方案,可以满足小程序webview与原生页面交互即可以调用:`wx.miniProgram`,但是在企微侧边栏会报错,报错信息如下:`wx.agentconfig is not a function`

报错信息如图:

针对这个问题,在企微社区搜索找到了一个官方回答:

可以理解为启动的时候在html文件直接加载,尝试了一下这样是没问题的。

const h5v3Scripts = `
<script src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
<script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script>
`
createHtmlPlugin({
  inject: {
    data: {
      injectScript: h5v3Scripts,
    },
  },
}),

动态加载问题2:

基于问题1调试出来的报错,需要调整动态加载的方案:

  1. 默认加载企微侧边栏的JSSDK版本,即加载`jweixin-1.2.0.js``jwxwork-1.0.0.js`

  2. 在判断非企微侧边栏的场景,删除已加载JSSDK,然后加载`jweixin-1.6.0.js`

按照这个思路实现的代码如下:

const removeScript = (id: string) => {
  const script = document.getElementById(id);
  if (script && script.parentNode) {
    script.parentNode.removeChild(script);
  }
}

export const loadJSSDK = (callback:any) => {
  const isEnterprise = isEnterpriseWechat();
  const jssdkUrl = '//res.wx.qq.com/open/js/jweixin-1.6.0.js'
  if (!isEnterprise) {
    removeScript('h5v3Scriptsjs')
    removeScript('h5v3Scriptsjswork')
    loadScript(jssdkUrl, callback)
  } else {
    callback(true)
  }
} 

测试结果显示在小程序场景下无法调用**`wx.miniProgram`接口,即便是已经动态加载`jweixin-1.6.0.js`。通过打印`window.wx`发现该对象还是保留`jweixin-1.2.0.js`**版本的信息,未更新。

那么接下来我们需要解决动态加载JSSDK版本,同时更新`window.wx`

动态加载问题3:

基于问题2调试问题,对`loadJSSDK`方法做完善,如下:动态加载JSSDK之前,先删除`window.wx`

export const loadJSSDK = (callback:any) => {
  const isEnterprise = isEnterpriseWechat();
  const jssdkUrl = '//res.wx.qq.com/open/js/jweixin-1.6.0.js'
  if (!isEnterprise) {
    removeScript('h5v3Scriptsjs')
    removeScript('h5v3Scriptsjswork')
    if (window && window.wx) {
      window.wx = null
    }
    if (window && window.jWeixin) {
      window.jWeixin = null
    }
    loadScript(jssdkUrl, callback)
  } else {
    callback(true)
  }
} 

测试结果显示:

  • 使用这种动态加载JSSDK方式,可以同时满足侧边栏、小程序webview的场景

  • 同时可以删除环境变量的构建方式,实现构建时长减半

总结:

  • 一次有意义也许没意义的调试探究。