towry / n

Lots of notes here, check out the issues.

Home Page:http://git.io/vEsyU

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

h5 打开小程序 wx-open-launch-weapp 踩坑

towry opened this issue · comments

关键词:"wx-open-launch-weapp 安卓不显示"

对于一些问题,请直接看最下面的问题汇总。

首先,官方文档在这里,之前一直就没看到过。https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html

目前我测试的情况是:

  1. 支持 js 动态插入 wx-open-launch-weapp 标签。
  2. 自定义样式还是可以的,我的解决方案是将这个自定义标签用一个 div 包着,然后 absolute 定位在某个元素上面透明显示。

封装的 vue 基础组件,这个组件会以 position: absolute 的形式,悬浮在其他元素上面,透明看不到,但是可以点击到。在侦测设备不支持的时候,可以设置 disabled=false,这样该组件就会不显示。:

/**
 * @description 展示一个透明的打开微信小程序的按钮,悬浮在其他元素上面。
 *
 * @see https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html
 *
 * @example
 * ```html
 * <open-weapp-plast :disabled="false" appid="123456" path="your_path" />
 * ```
 */
import uniqueId from "lodash/uniqueId";
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
import { convertToUnit } from "../../vue/utils/helper";

/**
 * @emits warn - 警告,组件未准备好。
 * @emits click - 用户点击了打开小程序的按钮。
 * @emits ready - 组件OK,用户可以准备点击了。
 * @emits error - 错误发生了。
 */
@Component
export default class OpenWeappPlast extends Vue {
  /**
   * 是否被禁用。
   */
  @Prop(Boolean) disabled!: boolean;

  /**
   * 小程序原始账号 ID(gh_ 开头的)
   */
  @Prop(String) appid!: string;

  /**
   * 要跳转到的页面路径
   */
  @Prop(String) path!: string;

  /**
   * 是否要阻止事件的默认行为。
   */
  @Prop(Boolean) preventDefault!: boolean;

  /**
   * 因兼容性问题无法使用标签的。
   */
  disabledLazy = false;
  isReady = false;
  uniqueid = uniqueId("wxopen_");

  get isDisabled() {
    return this.disabled || this.disabledLazy;
  }

  @Watch("isDisabled")
  onDisabledChange(newVal: boolean) {
    if (!newVal) {
      this.$nextTick(() => {
        this.onUpdate();
      });
    }
  }

  created() {
    document.addEventListener("WeixinOpenTagsError", (e: any) => {
      let errMsg = e.detail || "";
      errMsg = e.detail.errMsg
        ? e.detail.errMsg
        : typeof e.detail === "string"
        ? e.detail
        : "Unknown";

      this.$emit("error", new Error(errMsg));
      this.disabledLazy = true;
    });
  }

  mounted() {
    this.onUpdate();
  }

  private getSelfSize() {
    const el = this.$el;
    if (!el) {
      return {
        width: 0,
        height: 0,
      };
    }
    const box = el.getBoundingClientRect();
    return {
      width: box.width,
      height: box.height,
    };
  }

  private onUpdate() {
    this.inject();
    this.addEvent_();
  }

  /**
   * 监听元素显示.
   */
  private addVisibleObserver() {
    if (!window.IntersectionObserver || !this.$el) {
      return;
    }

    const observer = new IntersectionObserver(
      (entries, ob) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            if (entry.intersectionRatio >= 0.75) {
              ob.disconnect();
              this.onUpdate();
            }
          }
        });
      },
      {
        root: null,
        rootMargin: "0px",
        threshold: [0.0, 0.75],
      }
    );

    observer.observe(this.$el);
  }

  private addEvent_() {
    if (this.isDisabled) {
      return;
    }

    const el = document.getElementById(this.uniqueid);
    if (!el) {
      return;
    }
    el.addEventListener("error", (e: any) => {
      e = e || {};
      const eDetail = e.detail || {};
      let errMsg = e.detail || "";
      errMsg = eDetail.errMsg
        ? eDetail.errMsg
        : typeof e.detail === "string"
        ? e.detail
        : "Unknown";

      console.error(errMsg);

      this.$emit("error", new Error(errMsg));
      this.disabledLazy = true;
    });
    el.addEventListener("launch", (event: any) => {
      console.log(`${this.uniqueid} is launched`);
      this.$emit("click", event);
    });
    el.addEventListener("ready", () => {
      console.log(`${this.uniqueid} is ready`);
      this.$emit("ready");
      this.isReady = true;
    });
  }

  /**
   * 能否渲染成功,和微信 jssdk 有关系。
   * 注意!如果元素刚开始是 hide 的,因为无法拿到容器的宽高,导致微信的开放按钮的宽高为0的话,
   * 会出现点击没反应。
   */
  private inject() {
    if (this.isDisabled) {
      return;
    }

    const mainText = `<button class="invisible-weapp-button">...</button>`;

    const boxSize = this.getSelfSize();
    const swidth = convertToUnit(boxSize.width || "100%");
    const sheight = convertToUnit(boxSize.height || "100%");
    if (!boxSize.width && !boxSize.height && window.IntersectionObserver) {
      this.addVisibleObserver();
      return;
    }

    let styleText = `.invisible-weapp-button {
      display: block; outline: none; border: none; background-color: transparent; color: transparent; width: ${swidth}; height: ${sheight};
    }
    `;
    const script = this.$refs.weapp as HTMLDivElement;

    const style = `<style>${styleText.replace(/\\n/g, "")}</style>`;
    if (!script) {
      return;
    }

    const content = style + mainText;

    const all =
      `
      <wx-open-launch-weapp id="${
        this.uniqueid
      }" style="display: block; width: 100%;height: 100%;" username="${
        this.appid
      }" ${this.path ? 'path="' + this.path + '"' : ""}>` +
      '<script type="text/wxtag-template">' +
      content +
      "</sc" +
      "ript></wx-open-launch-weapp>";

    script.innerHTML = all;
  }

  render() {
    const h = this.$createElement;

    if (this.isDisabled) {
      return null;
    }

    return h(
      "div",
      {
        staticClass: "OpenWeappPlast",
        style: {
          width: "100%",
          height: "100%",
          overflow: "hidden",
          position: "absolute",
          top: 0,
          bottom: 0,
          right: 0,
          left: 0,
          zIndex: 1,
        },
        on: {
          click: (e: MouseEvent) => {
            if (e?.stopPropagation) {
              e.stopPropagation();
            }
            if (this.preventDefault && e?.preventDefault) {
              e.preventDefault();
            }

            if (!this.isReady) {
              this.$emit("warn");
            }
          },
        },
      },
      [
        h("div", {
          style: {
            width: "100%",
            height: "100%",
          },
          ref: "weapp",
        }),
      ]
    );
  }
}

问题汇总

jssdk config 里记得加上 openTagList: ["wx-open-launch-weapp"]

如果点击没反应,检查微信开放按钮样式的宽高是否有被设置,如果宽高为0是不会显示的,因此点击也会没反应的。

CSP

https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html

对于有CSP要求的页面,需要添加白名单 frame-src https://*.qq.com webcompt:,才能在页面中正常使用开放标签。

检查你页面的 response 里,是否有一个字段:"Content-Security-Policy",如果有,说明你的页面要求了 CSP,则修改此字段的值,添加上面说的白名单。

这个问题会导致 ios 下按钮正常显示,但是安卓下不显示,且无事件无报错。

jsApiList 里不能为空。

在微信的社区很多类似答案里贴了这个页面的链接,希望其他人能少踩坑。

另外请注意检查是否复制了官方文档中的代码:

openTagList: [] // 可选,需要使用的开放标签列表,例如['wx-open-launch-app']

上面官方文档中关于 wx-open-launch-weapp 的拼写是错的,排查了半天才发现,就离谱 🙃

commented

最后这个很关键

检查你页面的 response 里,是否有一个字段:"Content-Security-Policy",如果有,说明你的页面要求了 CSP,则修改此字段的值,添加上面说的白名单。这个问题会导致 ios 下按钮正常显示,但是安卓下不显示,且无事件无报错。

调了两天没搞出来 情况一模一样 ios能显示 正常 微信开发者工具调试也正常 安卓不显示 最后是nginx配置了这个加白名单就解决了