转:https://blog.csdn.net/CameloHuang/article/details/64476385
从html5打开本地的app–如果本地没有app就跳转到下载页面,大家都会认为这是一项很简单的操作。网上的教程也很多,但是可行性都不高。因为手机系统和浏览器型号各不相同,所以兼容性会是让各个前端工程师头疼的问题。我们不妨看一下京东是如何解决的。京东的原代码已经混淆过了,我只能一点点反混淆并注释。
网上的文章千篇一律 都是采用window.location.href
的方式打开的,但是这种方法的兼容性非常的渣。在ios的safari浏览器中无法使用,会出现还未打开app就自动跳转到下载页面的情况,影响用户的使用。那么我们来看一下京东是如何解决兼容性的问题。
下面附上我的代码翻译和注释。
京东打开app的js代码链接
1 (function(){ 2 // 判断浏览器 3 var Navigator = navigator.userAgent; 4 var ifChrome = Navigator.match(/Chrome/i) != null && Navigator.match(/Version\/\d+\.\d+(\.\d+)?\sChrome\//i) == null ? true : false; 5 var ifAndroid = (Navigator.match(/(Android);?[\s\/]+([\d.]+)?/)) ? true : false; 6 var ifiPad = (Navigator.match(/(iPad).*OS\s([\d_]+)/)) ? true : false; 7 var ifiPhone = (!ifiPad && Navigator.match(/(iPhone\sOS)\s([\d_]+)/)) ? true : false; 8 var ifSafari = (ifiPhone || ifiPad) && Navigator.match(/Safari/); 9 var version = 0; 10 ifSafari && (version = Navigator.match(/Version\/([\d\.]+)/)); 11 12 version = parseFloat(version[1], 10); 13 // 是否从微信打开 14 var ifWeixin = navigator.userAgent.indexOf("MicroMessenger") >= 0; // weixin 15 var j = false; 16 var iframe = "plugIn_downloadAppPlugIn_loadIframe"; 17 var t = false; 18 var i = 0; 19 var B = {}; 20 var b = {}; 21 var selector = null; 22 var Hquery = {}; 23 // 判断当前使用的js框架是zepto还是jquery 24 var Query = window.Zepto || window.jQuery ? true : false; 25 var g = []; 26 // 是否存在html5的localStorage 存储 27 var v = window.localStorage ? true : false; 28 var o = "mdownloadAppPlugInskip"; 29 var p = null; 30 31 function m() { // 打印时间 例如:2016-5-18 32 var M = new Date(); 33 var N = M.getFullYear(); 34 var O = M.getMonth() + 1; 35 var L = M.getDate(); 36 strDate = N + "-" + O + "-" + L; 37 return strDate 38 } 39 // 微信相关操作 40 function r() { // weixin api 41 WeixinJSBridge.invoke("getInstallState", { 42 packageName: "com.jingdong.app.mall", 43 packageUrl: "openApp.jdMobile://" 44 }, function(M) { 45 var N = M.err_msg, 46 L = 0; 47 if (N.indexOf("get_install_state:yes") > -1) { 48 j = true 49 } 50 }) 51 } 52 // 根据是否存在js框架进行dom和时间的绑定 53 function bind(dom, event, fun) { // bind event 54 if (Query) { 55 selector("#" + dom).bind(event, fun) 56 } else { 57 selector("#" + dom).addEventListener(event, fun, !1) 58 } 59 } 60 61 function z(L) { 62 var M = (L || "mGen") + (++i); 63 return M 64 } 65 // 微信操作 66 if (ifWeixin) { // if navigitor is weixin 67 if (window.WeixinJSBridge && WeixinJSBridge.invoke) { 68 r() 69 } else { 70 document.addEventListener("WeixinJSBridgeReady", r, !1) 71 } 72 } 73 74 // 如果存在js框架 75 if (Query) { 76 selector = window.$; 77 Hquery = window.$ 78 } else { 79 selector = function(obj) { 80 if (typeof obj == "object") { 81 return obj 82 } 83 return document.querySelector(obj); 84 }; 85 if (!window.$) { 86 window.$ = Hquery = selector 87 } else { 88 Hquery = window.$ 89 } 90 } 91 window.onblur = function() { 92 for (var L = 0; L < g.length; L++) { 93 clearTimeout(g[L]) 94 } 95 }; 96 // 设置cookie。 97 function e(N) { 98 var M = document.cookie.indexOf(N + "="); 99 if (M == -1) { 100 return "" 101 } 102 M = M + N.length + 1; 103 var L = document.cookie.indexOf(";", M); 104 if (L == -1) { 105 L = document.cookie.length 106 } 107 return document.cookie.substring(M, L) 108 } 109 // 设置cookie 110 function l(N, P, L, Q, O) { 111 var R = N + "=" + escape(P); 112 if (L != "") { 113 var M = new Date(); 114 M.setTime(M.getTime() + L * 24 * 3600 * 1000); 115 R += ";expires=" + M.toGMTString() 116 } 117 if (Q != "") { 118 R += ";path=" + Q 119 } 120 if (O != "") { 121 R += ";domain=" + O 122 } 123 document.cookie = R 124 } 125 126 // 打开的链接集合 127 function F(L) { 128 var url = { 129 downAppURl: "http://h5.m.jd.com/active/download/download.html?channel=jd-m", 130 downAppIos: "http://union.m.jd.com/download/go.action?to=http%3A%2F%2Fitunes.apple.com%2Fcn%2Fapp%2Fid414245413&client=apple&unionId=12532&subunionId=m-top&key=e4dd45c0f480d8a08c4621b4fff5de74", 131 downWeixin: "http://a.app.qq.com/o/simple.jsp?pkgname=com.jingdong.app.mall&g_f=991850", 132 downIpad: "https://itunes.apple.com/cn/app/jing-dong-hd/id434374726?mt=8", 133 inteneUrl: "openApp.jdMobile://360buy?type=1", 134 inteneUrlParams: null, 135 openAppBtnId: "", 136 closePanelBtnId: "", 137 closePanelId: "", 138 closeCallblack: null, 139 closeCallblackSource: null, 140 cookieFlag: null, 141 noRecord: false, 142 sourceType: "JSHOP_SOURCE_TYPE", 143 sourceValue: "JSHOP_SOURCE_VALUE", 144 openAppEventId: "MDownLoadFloat_OpenNow", 145 closePanelEventId: "MDownLoadFloat_Close" 146 }; 147 if (L) { 148 for (var M in L) { 149 if (M && L[M]) { 150 url[M] = L[M] 151 } 152 } 153 } 154 return url 155 } 156 // 敲黑板 重点内容。看京东是怎么解决兼容问题的。 157 function openApp(N, L) { // openApp 158 var R = h(N); //获取相对应的url 159 var O = null; 160 if (ifWeixin) { // 如果是微信端 161 var M = null; 162 if (j) { 163 M = R 164 } else { 165 M = N.downWeixin 166 } 167 location.href = M; // 直接使用location.href打开 168 return 169 } 170 if (ifiPad) { // 如果是ipad 171 O = N.downIpad 172 } else { 173 if (ifiPhone) { // 如果是iphone 174 O = N.downAppIos 175 } else { 176 O = N.downAppURl 177 } 178 } 179 180 if (ifChrome) { // 如果是chrome 181 if (ifAndroid) { //安卓浏览器 182 var Q = R; 183 R = y(Q); 184 // 延后50毫秒 185 setTimeout(function() { 186 window.location.href = R 187 }, 50) 188 } 189 } 190 if (ifSafari && version >= 9) { // 判断safari版本 如果大于9 191 setTimeout(function() { // 必须要使用settimeout 192 var S = document.createElement("a"); //创建a元素 193 S.setAttribute("href", R), S.style.display = "none", document.body.appendChild(S); 194 var T = document.createEvent("HTMLEvents"); // 返回新创建的 Event 对象,具有指定的类型。 195 T.initEvent("click", !1, !1)// 初始化新事件对象的属性, S.dispatchEvent(T) // 绑定事件 196 }, 0) 197 } else { 198 document.querySelector("#" + iframe).src = R // 将iframe增加src 199 } 200 var P = Date.now(); 201 setTimeout(function() { 202 if (L) { 203 var S = setTimeout(function() { 204 x(P, O) 205 }, 1500); 206 g.push(S) 207 } 208 }, 100) 209 } 210 // x方法 211 function x(N, downUrl) { 212 var L = Date.now(); 213 if (N && (L - N) < (1500 + 200)) { 214 window.location.href = downUrl 215 } 216 } 217 218 function h(N) { 219 var V = []; 220 var P = N.inteneUrlParams; 221 var T = { 222 category: "jump", 223 des: "productDetail" 224 }; 225 if (N.sourceType && N.sourceValue) { 226 T.sourceType = N.sourceType; 227 T.sourceValue = N.sourceValue; 228 if (P && !P.sourceType && !P.sourceValue) { 229 P.sourceType = N.sourceType; 230 P.sourceValue = N.sourceValue 231 } 232 } 233 if (P) { 234 for (var U in P) { 235 if (U && P[U]) { 236 V.push('"' + U + '":"' + P[U] + '"') 237 } 238 } 239 } else { 240 for (var U in T) { 241 if (U && T[U]) { 242 V.push('"' + U + '":"' + T[U] + '"') 243 } 244 } 245 } 246 try { 247 var Q = MPing.EventSeries.getSeries(); 248 if (Q) { 249 var W = JSON.parse(Q); 250 W.jdv = encodeURIComponent(e("__jdv")); 251 W.unpl = encodeURIComponent(e("unpl")); 252 W.mt_xid = encodeURIComponent(e("mt_xid")); 253 W.mt_subsite = encodeURIComponent(e("mt_subsite")) 254 } 255 var S = { 256 mt_subsite: encodeURIComponent(e("mt_subsite")), 257 __jdv: encodeURIComponent(e("__jdv")), 258 unpl: encodeURIComponent(e("unpl")), 259 __jda: encodeURIComponent(e("__jda")) 260 }; 261 Q = JSON.stringify(W); 262 V.push('"m_param":' + Q); 263 V.push('"SE":' + JSON.stringify(S)) 264 } catch (R) { 265 V.push('"m_param":null') 266 } 267 var M = "{" + V.join(",") + "}"; 268 var O = N.inteneUrl.split("?"); 269 var L = null; 270 if (O.length == 2) { 271 L = O[0] + "?" + O[1] + "¶ms=" + M 272 } else { 273 L = O[0] + "?params=" + M 274 } 275 return L 276 } 277 278 function y(L) { 279 return "intent://m.jd.com/#Intent;scheme=" + L + ";package=com.jingdong.app.mall;end" 280 } 281 282 function n(L) { 283 if (L.openAppBtnId) { 284 B[L.openAppBtnId] = L; 285 G(L.openAppBtnId, L.openAppEventId); 286 bind(L.openAppBtnId, "click", function() { 287 var P = this.getAttribute("id"); 288 var M = B[P]; 289 if (!t) { 290 var N = document.createElement("iframe"); 291 N.id = iframe; 292 document.body.appendChild(N); 293 document.getElementById(iframe).style.display = "none"; 294 document.getElementById(iframe).style.width = "0px"; 295 document.getElementById(iframe).style.height = "0px"; 296 t = true 297 } 298 var O = M.cookieFlag ? "downloadAppPlugIn_downCloseDate_" + M.cookieFlag : "downloadAppPlugIn_downCloseDate"; 299 l(O, Date.now() + "_2592000000", 60, "/", "m.jd.com"); 300 l(O, Date.now() + "_2592000000", 60, "/", "m.jd.hk"); 301 openApp(M, true) 302 }) 303 } 304 } 305 306 function D(M) { 307 if (M.closePanelBtnId && M.closePanelId) { 308 B[M.closePanelBtnId] = M; 309 G(M.closePanelBtnId, M.closePanelEventId); 310 var Q = M.cookieFlag ? "downloadAppPlugIn_downCloseDate_" + M.cookieFlag : "downloadAppPlugIn_downCloseDate"; 311 var O = e(Q); 312 var P = null; 313 if (O) { 314 P = O.split("_"); 315 if (P.length == 2) { 316 P[0] = parseInt(P[0], 10); 317 P[1] = parseInt(P[1], 10) 318 } else { 319 P = null 320 } 321 } 322 var L = Date.now(); 323 if (Html5Plus() || (!M.noRecord && P && P.length == 2 && (L - P[0]) < P[1])) { 324 document.querySelector("#" + M.closePanelId).style.display = "none"; 325 if (M.closeCallblack) { 326 var N = M.closeCallblackSource ? M.closeCallblackSource : null; 327 M.closeCallblack.call(N) 328 } 329 return 330 } else { 331 document.querySelector("#" + M.closePanelId).style.display = "block" 332 } 333 bind(M.closePanelBtnId, "click", function() { 334 var U = this.getAttribute("id"); 335 var R = B[U]; 336 var T = R.cookieFlag ? "downloadAppPlugIn_downCloseDate_" + R.cookieFlag : "downloadAppPlugIn_downCloseDate"; 337 if (!R.noRecord) { 338 l(T, Date.now() + "_259200000", 60, "/", "m.jd.com"); 339 l(T, Date.now() + "_259200000", 60, "/", "m.jd.hk") 340 } 341 document.querySelector("#" + R.closePanelId).style.display = "none"; 342 if (R.closeCallblack) { 343 var S = R.closeCallblackSource ? R.closeCallblackSource : null; 344 R.closeCallblack.call(S) 345 } 346 }) 347 } 348 } 349 350 function Html5Plus() { // htmlplus 351 if (Navigator.indexOf("Html5Plus") >= 0) { 352 return true 353 } else { 354 return false 355 } 356 } 357 358 function G(P, M) { 359 try { 360 var O = document.getElementById(P); 361 var L = O.className; 362 if (L) { 363 L = L + " J_ping" 364 } else { 365 L = "J_ping" 366 } 367 O.className = L; 368 O.setAttribute("report-eventid", M) 369 } catch (N) {} 370 } 371 372 function C(L) { 373 var M = F(L); 374 n(M); 375 D(M) 376 } 377 Hquery.downloadAppPlugIn = C; 378 Hquery.downloadAppPlugInOpenApp = function(L) { 379 var M = F(L); 380 openApp(M); 381 } 382 });