h5企微插件之WeixinJSBridge、iframe通过cors-anywhere加载微信文章实践

项目背景

公司有一个运营同事使用的素材库,是h5开发的企微插件,原本素材主要是公司域名的文章,点击文章可以进入详情页查看文章内容,并且可以转发客户、群发客户、发朋友圈等。现在希望可以支持非公司域名的文章,主要以微信公众号的文章为主,兼顾一些其他文章,还需要统计分享出去的链接访问量,根据分享OA人员进行统计

原本设计方案

详情页使用iframe展示文章,底部是功能按钮,相当简单

开搞

需求到手后,想着这不很简单吗,拿着键盘就要开撸,先让后端配几个微信公众号文章,结果一试傻眼了,居然报了iframe的错误
在这里插入图片描述

百度之后发现,原来这是微信防盗链导致的

但是,虽说微信的防盗链很牛逼,但是也是有解决方案的,那就是cors-anywhere,但是目前该项目是停运的了,需要手动下载代码,起一下服务即可

https://github.com/Rob–W/cors-anywhere

在这里插入图片描述

接下来就可以通过代理和一系列字符串操作加载微信公众号文章了

主要就几个点

  1. 设置X-Requested-With为XMLHttpRequest,否则会请求报错
  2. 将图片的data-src属性转成src属性
  3. 将css文件中的href="//res.wx.qq.com xxx"改成带href="https://res.wx.qq.com xxx"前缀的,不然会请求相对路径
  4. 微信拦截所有图片访问受限,会出现图片加载不了问题:添加一下代码解决
  5. 解决背景图片不展示问题
  6. 主体内容不可见:获取rich_media_content元素,将元素的visibility设置为可见

具体代码如下:

   function processLoadWeChatUrl(url) {
    const instance = Axios.create({
      headers: {
        "X-Requested-With": "XMLHttpRequest",
      },
    })
    url = `http://127.0.0.1:8090/${url}`
    instance.get(url).then(response => {
      let html = response.data
      // 将data-src属性转成src
      html = html.replace(/data-src/g, "src")
        .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/g, '')
        .replace(/https/g, 'http')
        .replace(/href="\/\/res.wx.qq.com\//g, 'href="https://res.wx.qq.com/')
      //根据微信传回的html中的特殊路径data-src转为src等
      let html_src = html
      let iframe = document.getElementById('iFrame-id');
      iframe.src = html_src
      //这时候就成功拿到了微信公众号,这个html_src是个静态网页
      let doc = iframe.contentDocument || iframe.document
      //添加meta展示图片不加这个图片出不来
      let htmlArr = html_src.split('</head>')
      let html_src_add = htmlArr[0] + '<meta name="referrer" content="never"></head>' + htmlArr[1]
      let backgroundUrlReg = /url[(]&quot;(\S*)&quot;/g
      //获取背景图
      let backgroundImgs = html_src_add.match(backgroundUrlReg)
      if (backgroundImgs && backgroundImgs.length) {
        backgroundImgs.forEach(item => {
          let url = item.replace(/url[(]&quot;/g, '').replace(/&quot;/g, '')
          let img = document.createElement('img')
          img.src = url
          doc.querySelector("body").appendChild(img)
        })
      }
      //配置无效img绕过背景图片路径限制
      doc.write(html_src_add)
      setLoadWeChatSuccess(true)
      // 设置主体内容为可见
      const rich_media_content = doc.querySelector(".rich_media_content")
      rich_media_content.style.visibility = 'unset'
      const images = doc.querySelectorAll('img')
      images.forEach(function (image) {
        image.style.maxWidth = '100%'
      })
    }).catch(function (error) { // 请求失败处理
      setLoadWeChatSuccess(false)
      console.log(error)
    })
  }

<Iframe url={webViewUrl}
  width="100%"
  id="iFrame-id"
  className="myClassname"
  height="87%"
  styles={{height: "87%"}}
  security="restricted"
  sandbox=""
/>

至此,已经可以实现在iframe绕过微信防盗链加载微信文章了
在这里插入图片描述

到这里以为就很简单了,剩下的就是埋点统计访问量的问题了,写一个中间页,转发分享时候把中间页地址带上要跳转的url、分享用户信息等分享出去,在中间页埋点,埋点成功后通过重定向到文章地址就解决问题了

然后就写下了这样几行代码,其中神策的pageview事件在setTitle完成,url的公共参数在神策js已经处理,这里省略

function UrlMiddlePage() {
  useEffect(() => {
    const search = new URLSearchParams(window.location.search)
    const url = search.get('jumpUrl')
    const title = search.get('title')
    setTitle(title)
    window.location.replace(url)
  }, [])

  return <Loading key='loading' show />
}

本以为这样已经没问题了,其实在浏览器上确实没有问题了

然后,坑来了

当我使用微信打开中间页时候,可以成功跳转微信公众号文章,但是当我返回上一页的时候,居然是展示了中间页,微信浏览器重定向居然不起作用??重定向之后的中间页的页面栈还在,所以导致重定向到微信文章后,有两个页面,这样就会出现死循环,看了文章返回中间页,然后触发useEffect,又跳转微信公众号文章

解决方案

既然已经知道是微信浏览器不支持重定向,那就想加个状态判断,如果是已经跳转过微信文章,就关闭微信浏览器退出即可,这时候首先想到的是加个state存一下状态即可,但是后来发现,在微信文章返回中间页,中间页是重新加载的,所以不能用state保存状态了,只能用sessionStorage进行状态存储,跳转之前设置sessionStorage,后续判断sessionStorage有值的话,直接关闭微信浏览器即可,其中使用window.WeixinJSBridge进行浏览器的关闭

注意:本来关闭微信浏览器是想用wx.closeWindow api的,但是发现该api只能是手动触发,否则不起作用,所以转成使用window.WeixinJSBridge.call(“closeWindow”)

function UrlMiddlePage() {

  useEffect(() => {
    if (sessionStorage.getItem(KEY)) {
      closeWindow()
      return
    } else if (isInWechatBrowser() || isInWeComBrowser()) {
      sessionStorage.setItem(KEY, KEY)
    }
    const search = new URLSearchParams(window.location.search)
    const url = search.get('jumpUrl')
    const title = search.get('title')
    setTitle(title)
    window.location.replace(url)
  }, [])

  // 兼容微信,微信执行window.location.replace,页面栈还是存在的,关闭微信的页面,退回聊天页面
  const closeWindow = () => {
    if (window.WeixinJSBridge) {
      window.WeixinJSBridge.call("closeWindow")
    } else if (document.addEventListener) {
      document.addEventListener("WeixinJSBridgeReady", () => {
        window.WeixinJSBridge && window.WeixinJSBridge.call("closeWindow")
      })
    } else if (document.attachEvent) {
      document.attachEvent("onWeixinJSBridgeReady", () => {
        // 关闭ios的页面
        window.WeixinJSBridge && window.WeixinJSBridge.call("closeWindow")
      })
    }
  }

  return <Loading key='loading' show />
}

还有高手

到这里,本以为已经完美解决问题了(测试机是安卓手机),上到ios测试的时候,又出现问题了,在ios的微信浏览器中,在微信文章返回中间页时候,是不会触发页面的重新加载、也不会触发useEffect的(企微对微信聊天框也会这样),导致没有触发判断sessionStorage的时机,最终,发现在返回中间页的时候,会触发window.onpageshow,只要判断是ios并且是微信的情况下,在window.onpageshow判断sessionStorage即可

最终代码如下:

function UrlMiddlePage() {

  useEffect(() => {
    processWeChatSpecialScenario()
    // 安卓微信,在其他域名页面返回本页面时候会触发useEffect
    if (sessionStorage.getItem(KEY)) {
      closeWindow()
      return
    } else if (isInWechatBrowser() || isInWeComBrowser()) {
      sessionStorage.setItem(KEY, KEY)
    }
    const search = new URLSearchParams(window.location.search)
    const url = search.get('jumpUrl')
    const title = search.get('title')
    setTitle(title)
    window.location.replace(url)
  }, [])

  /**
   * 处理微信浏览器、企微浏览器的特定场景
   * IOS微信或者企微对微信聊天框 ,在window.location.replace跳转页面后,返回不会触发useEffect,但是会触发window.onpageshow
  */
  const processWeChatSpecialScenario = () => {
    if (!isInWechatBrowser() && !isInWeComBrowser()) { return }
    window.onpageshow = (event) => {
      if (!event.persisted || !sessionStorage.getItem(KEY)) { return }
      closeWindow()
    }
  }

  // 兼容微信,微信执行window.location.replace,页面栈还是存在的,关闭微信的页面,退回聊天页面
  const closeWindow = () => {
    if (window.WeixinJSBridge) {
      window.WeixinJSBridge.call("closeWindow")
    } else if (document.addEventListener) {
      document.addEventListener("WeixinJSBridgeReady", () => {
        window.WeixinJSBridge && window.WeixinJSBridge.call("closeWindow")
      })
    } else if (document.attachEvent) {
      document.attachEvent("onWeixinJSBridgeReady", () => {
        // 关闭ios的页面
        window.WeixinJSBridge && window.WeixinJSBridge.call("closeWindow")
      })
    }
  }

  return <Loading key='loading' show />
}

总结

到这里,已经成功完整需求,包括:

  1. 通过cors-anywhere实现在iframe展示微信文章
  2. 兼容微信浏览器不能重定向问题,使用WeixinJSBridge.call(“closeWindow”)关闭微信浏览器
  3. 兼容ios、android的微信返回中间页的触发事件和机制问题
  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值