微信開放標簽喚起App

目錄

  • 相關(guān)配置說明文檔
  • 先了解一下webcomponent
  • 我們自己實現(xiàn)一個“微信開放標簽”
  • 項目中引入開放標簽的思路
  • 完整參考代碼
  • 微信開放標簽調(diào)試
  • 異常場景
  • 優(yōu)化建議

相關(guān)配置說明文檔

H5開發(fā)文檔:微信開放標簽

安卓開發(fā)文檔:安卓介入官方文檔 備注sdk包要6.6.4以上:sdk資源

iOS開發(fā)文檔:ios介入官方文檔

先了解一下webcomponent

微信開放標簽的實質(zhì)就是webcomponent
了解webcomponent的概念晒奕,有助于我們更好的使用微信開放標簽芦圾。
Using custom elements
Using shadow DOM
Using templates and slots

  • Custom elements(自定義元素):一組JavaScript API,允許您定義custom elements及其行為,然后可以在您的用戶界面中按照需要使用它們晃择。
  • Shadow DOM(影子DOM):一組JavaScript API,用于將封裝的“影子”DOM樹附加到元素(與主文檔DOM分開呈現(xiàn))并控制其關(guān)聯(lián)的功能游岳。通過這種方式身隐,您可以保持元素的功能私有,這樣它們就可以被腳本化和樣式化蝴光,而不用擔心與文檔的其他部分發(fā)生沖突她渴。
  • HTML templates(HTML模板): <template><slot> 元素使您可以編寫不在呈現(xiàn)頁面中顯示的標記模板。然后它們可以作為自定義元素結(jié)構(gòu)的基礎(chǔ)被多次重用蔑祟。

Shadow DOM 允許將隱藏的 DOM 樹附加到常規(guī)的 DOM 樹中——它以 shadow root 節(jié)點為起始根節(jié)點趁耗,在這個根節(jié)點的下方,可以是任意元素疆虚,和普通的 DOM 元素一樣苛败。


image.png

這里满葛,有一些 Shadow DOM 特有的術(shù)語需要我們了解:

Shadow host:一個常規(guī) DOM節(jié)點,Shadow DOM 會被附加到這個節(jié)點上罢屈。
Shadow tree:Shadow DOM內(nèi)部的DOM樹嘀韧。
Shadow boundary:Shadow DOM結(jié)束的地方,也是常規(guī) DOM開始的地方缠捌。
Shadow root: Shadow tree的根節(jié)點锄贷。

我們自己實現(xiàn)一個“微信開放標簽”

customElements.define('wx-open-launch-app',
      class extends HTMLElement {
        constructor () {
          super()
          console.log('this', this)
          // console.log('template', this.getElementsByTagName('template'))
          const shadowContent = document.createElement('a')
          shadowContent.innerHTML = '喚起App'
          shadowContent.addEventListener('click', function () {
            alert('launch app')
          })
          const shadowRoot = this.attachShadow({mode: 'open'}) // 微信把mode設置為了closed,外界無法獲取到shadow內(nèi)部的元素
            .appendChild(shadowContent)
          console.log('shadowRoot:', shadowRoot)
        }
      }
    )
    let dom = document.createElement('wx-open-launch-app')
    let box = document.createElement('div')
    box.style.width = 100 + 'px'
    box.style.height = 100 + 'px'
    box.style.position = 'fixed'
    box.style.top = '0'
    box.style.left = '0'
    box.style.zIndex = '999'
    box.style.overflow = 'hidden'
    box.appendChild(dom)
    document.body.appendChild(box)

項目中引入開放標簽思路:

  1. 微信jssdk初始化
  2. 開放標簽創(chuàng)建曼月。把開放標簽當做普通標簽處理谊却。
    (由于公司ios端使用了openInstall喚起的方案,所以只對安卓做了處理)
image.png

創(chuàng)建開放標簽哑芹,模板內(nèi)容樣式做特殊處理

export const createOpenApp = (root, schema) => {
  root.setAttribute('launch-box-created', true)
  const box = document.createElement('div')
  // 創(chuàng)建開放標簽
  const clickDom = document.createElement('wx-open-launch-app')
  box.appendChild(clickDom)
  // 設置Shadow host節(jié)點css樣式炎辨,是其可以完全覆蓋在目標元素上。注意設置overflow:hidden;屬性
  box.style.width = root.clientWidth + 'px'
  box.style.height = root.clientHeight + 'px'
  box.style.position = 'absolute'
  box.style.top = '0'
  box.style.left = '0'
  box.style.zIndex = '999'
  box.style.overflow = 'hidden'
  box.setAttribute('class', 'launch-app-box')
  // 模板內(nèi)容樣式特殊處理為width: 1000px; height: 1000px;目的是能完全覆蓋
  clickDom.innerHTML =
    `<template>
      <style>
        .btn {
          width: 1000px;
          height: 1000px;
        }
      </style>
      <div class="btn"><div>
    </template>`
  clickDom.setAttribute('extinfo', schema)
  clickDom.setAttribute('appid', '********')
  if (window.getComputedStyle(root, null).position === 'static') {
    // 為目標元素設置position屬性
    root.style.position = 'relative'
  }
  root.appendChild(box)
  clickDom.addEventListener('ready', function () {
    console.log('dom ready', new Date().getTime() - domReadyTime)
    removeLoading() // 移除loading遮罩
  })
  clickDom.addEventListener('click', function (e) {
    e.preventDefault()
  })
  clickDom.addEventListener('launch', function (e) {
    console.log('success', e)
  })
  clickDom.addEventListener('error', function (e) {
    console.log('fail', e.detail)
  })
}

遍歷所有需要點擊添加開放標簽的dom節(jié)點

export const domTraverse = function (rootElement) {
  const startTime = new Date().getTime()
  // console.log('==domTraverse start==', startTime)
  let domList = []
  if (rootElement) {
    domList = rootElement.getElementsByClassName('root-wx-open-launch-app')
  } else {
    domList = document.getElementsByClassName('root-wx-open-launch-app')
  }
  let j = 0
  for (let i = 0; i < domList.length; i++) {
    const item = domList[i]
    const schema = item.getAttribute('schema')
    const launchCreated = item.getAttribute('launch-box-created')
    if (!launchCreated) {
      j++
      createOpenApp(item, schema)
    }
  }
  console.log('開放標簽數(shù)量:', j)
  console.log('所有開放標簽創(chuàng)建花費時間:', (new Date().getTime() - startTime) + 'ms')
}

完整參考代碼

import Vue from 'vue'
import axios from 'axios'
import $ from 'jquery'
let wxConfidReady = false
let logInstance = new Vue()
let loadingDom = null
let asyncStartTime = 0 // 記錄微信jssdk加載到wx.ready觸發(fā)的時長
let domReadyTime = 0 // 記錄微信開放標簽的初始化時長
// 判斷ios聪姿、android
export const getPlatform = () => {
  if (new Date('2016-11-11 11:11:11').getTime() > 0) {
    return 'android'
  }
  return 'ios'
}
// 異步加載微信jssdk
export const asyncWeiXinJS = (cb) => {
  asyncStartTime = new Date().getTime()
  if (isWeixinFn()) {
    const script = document.createElement('script')
    script.src = '//res.wx.qq.com/open/js/jweixin-1.6.0.js'
    document.body.appendChild(script)
    if (cb) {
      script.addEventListener('load', cb)
    }
  }
}
// 判斷微信內(nèi)瀏覽器環(huán)境
export const isWeixinFn = () => { // 判斷是否是微信
  const ua = navigator.userAgent.toLowerCase()
  return ua.indexOf('micromessenger') !== -1
}
// 頁面loading遮罩層
const addLoading = function () {
  const vm = new Vue({
    data () {
      return {
        show: true
      }
    },
    template: `
    <div v-if="show" style="position: fixed;
      top: 0;
      left: 0;
      z-index: 9999;
      width: 100%;
      height: 100%;
      background-color: rgba(0,0,0,.6);
    ">
      <div style="display: flex;
        align-items: center;
        justify-content: center;
        height: 100%;">
        <van-loading type="spinner" />
      </div>
    </div>`
  }).$mount()
  loadingDom = vm.$el
  document.body.append(loadingDom)
}
// 移除loading
const removeLoading = function () {
  try {
    setTimeout(() => {
      if (loadingDom) {
        document.body.removeChild(loadingDom)
        loadingDom = null
      }
    }, 300)
  } catch (e) {
    console.log(e)
  }
}
// 微信jssdk config校驗
export const wxConfig = async () => {
  if (wxConfidReady) return
  addLoading()
  const { msg: { wxConf } } = await axios.post('https://xxx.com/conf')
  wxConf.openTagList = ['wx-open-launch-app']
  console.log('wxConf:', wxConf)
  // wxConf.debug = true
  wxConf.timestamp = wxConf.timeStamp
  delete wxConf.timeStamp
  window.wx.config(wxConf)
  window.wx.ready(function () {
    domReadyTime = new Date().getTime()
    console.log('wx.ready時間', (new Date().getTime() - asyncStartTime) + 'ms')
    wxConfidReady = true
    // removeLoading()
  })
  window.wx.error(function (res) {
    console.log('==wx.error==', res)
    wxConfidReady = true
    // removeLoading()
  })
  setTimeout(() => {
    removeLoading()
  }, 10000)
}
export const createOpenApp = (root, schema) => {
  root.setAttribute('launch-box-created', true)
  const box = document.createElement('div')
  // 創(chuàng)建開放標簽
  const clickDom = document.createElement('wx-open-launch-app')
  box.appendChild(clickDom)
  // 設置Shadow host節(jié)點css樣式碴萧,是其可以完全覆蓋在目標元素上。注意設置overflow:hidden;屬性
  box.style.width = root.clientWidth + 'px'
  box.style.height = root.clientHeight + 'px'
  box.style.position = 'absolute'
  box.style.top = '0'
  box.style.left = '0'
  box.style.zIndex = '999'
  box.style.overflow = 'hidden'
  box.setAttribute('class', 'launch-app-box')
  // 模板內(nèi)容樣式特殊處理為width: 1000px; height: 1000px;目的是能完全覆蓋
  clickDom.innerHTML =
    `<template>
      <style>
        .btn {
          width: 1000px;
          height: 1000px;
        }
      </style>
      <div class="btn"><div>
    </template>`
  clickDom.setAttribute('extinfo', schema)
  clickDom.setAttribute('appid', '********')
  if (window.getComputedStyle(root, null).position === 'static') {
    // 為目標元素設置position屬性
    root.style.position = 'relative'
  }
  root.appendChild(box)
  clickDom.addEventListener('ready', function () {
    console.log('dom ready', new Date().getTime() - domReadyTime)
    removeLoading() // 移除loading遮罩
  })
  clickDom.addEventListener('click', function (e) {
    e.preventDefault()
  })
  clickDom.addEventListener('launch', function (e) {
    console.log('success', e)
  })
  clickDom.addEventListener('error', function (e) {
    console.log('fail', e.detail)
  })
}
export const domTraverse = function (rootElement) {
  const startTime = new Date().getTime()
  // console.log('==domTraverse start==', startTime)
  let domList = []
  if (rootElement) {
    domList = rootElement.getElementsByClassName('root-wx-open-launch-app')
  } else {
    domList = document.getElementsByClassName('root-wx-open-launch-app')
  }
  let j = 0
  for (let i = 0; i < domList.length; i++) {
    const item = domList[i]
    const schema = item.getAttribute('schema')
    const launchCreated = item.getAttribute('launch-box-created')
    if (!launchCreated) {
      j++
      createOpenApp(item, schema)
    }
  }
  console.log('開放標簽數(shù)量:', j)
  console.log('所有開放標簽創(chuàng)建花費時間:', (new Date().getTime() - startTime) + 'ms')
}

/* 應對簡單場景頁面末购,直接調(diào)用該方法即可破喻,
 * 由于是對dom進行操作,如果復雜異步渲染dom的場景可以酌情組合使用asyncWeiXinJS招盲,wxConfig低缩,domTraverse等方法
*/
export const createWXOpenTag = function (schema) {
  if (isWeixinFn() && (getPlatform() === 'android')) {
    domTraverse()
    asyncWeiXinJS(() => {
      wxConfig()
    })
  }
}

vue框架中使用演示

<template>
  <div class="app">
    <div class="root-wx-open-launch-app">點擊熱區(qū)</div>
  </div>
</template>
<script>
import { createWXOpenTag } from './util.js'
export default {
  mounted () {
    createWXOpenTag()
  }
}
</script>

微信開放標簽調(diào)試

調(diào)試分為兩種:微信開發(fā)者工具、真機調(diào)試

3.1用微信開發(fā)者工具調(diào)試:需要添加公眾號開發(fā)者權(quán)限

注意:微信開發(fā)者工具無法模擬喚起應用彈框曹货,只能看dom元素中開放標簽是否添加成功咆繁。

image.png

3.2測試環(huán)境下真機調(diào)試

本地電腦開代理,host切為測試環(huán)境(如:Charles)顶籽。檢查電腦所有vpn并確保關(guān)閉玩般,安卓手機配置并連接代理。微信中打開測試頁面地址即可礼饱。

注意:由于微信公眾號安全域名限制坏为,本地服務手機無法彈起跳轉(zhuǎn)彈框,如本地服務 localhost:8080/xxx是無法正常彈起跳轉(zhuǎn)應用彈框的)

image.png

備注:跳轉(zhuǎn)應用的彈框只能是線上環(huán)境或者測試環(huán)境下真機镊绪。

異常場景

如果真機調(diào)試出現(xiàn)下圖報錯: [WXTAG][JSCORE]匀伏。

image.png

出現(xiàn)原因:手機鏈接代理、測試環(huán)境下無法加載微信js腳本導致

優(yōu)化

開放標簽數(shù)量增多蝴韭,導致開放標簽初始化時間增長够颠。

開放標簽新增ready事件。標簽初始化完畢榄鉴,可以進行點擊操作履磨。

減少首屏創(chuàng)建開放標簽數(shù)量蛉抓,非首屏延遲異步創(chuàng)建開放標簽,可以緩解等待ready事件觸發(fā)時間過久剃诅。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巷送,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子矛辕,更是在濱河造成了極大的恐慌笑跛,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件聊品,死亡現(xiàn)場離奇詭異堡牡,居然都是意外死亡,警方通過查閱死者的電腦和手機杨刨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來擦剑,“玉大人妖胀,你說我怎么就攤上這事』堇眨” “怎么了赚抡?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長纠屋。 經(jīng)常有香客問我涂臣,道長,這世上最難降的妖魔是什么售担? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任赁遗,我火速辦了婚禮,結(jié)果婚禮上族铆,老公的妹妹穿的比我還像新娘岩四。我一直安慰自己,他們只是感情好哥攘,可當我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布剖煌。 她就那樣靜靜地躺著,像睡著了一般逝淹。 火紅的嫁衣襯著肌膚如雪耕姊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天栅葡,我揣著相機與錄音茉兰,去河邊找鬼。 笑死妥畏,一個胖子當著我的面吹牛邦邦,可吹牛的內(nèi)容都是我干的安吁。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼燃辖,長吁一口氣:“原來是場噩夢啊……” “哼鬼店!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起黔龟,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤妇智,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后氏身,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巍棱,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年蛋欣,在試婚紗的時候發(fā)現(xiàn)自己被綠了航徙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡陷虎,死狀恐怖到踏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情尚猿,我是刑警寧澤窝稿,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站凿掂,受9級特大地震影響伴榔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜庄萎,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一踪少、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惨恭,春花似錦秉馏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锉罐,卻和暖如春帆竹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脓规。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工栽连, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓秒紧,卻偏偏與公主長得像绢陌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子熔恢,可洞房花燭夜當晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容