web-launch-app

launch app from web page


Keywords
deeplink,scheme,universal link,applink,invoke,launch app, scheme, copy, launch, download, intent, detector, applink, deeplink, appstore, invoke, callapp, universallink, launchapp
License
MIT
Install
npm install web-launch-app@2.2.8

Documentation

web-launch-app

Intro

  • 唤起App到指定页、通过Scheme调用端能力、下载安装包、到应用商店

Install

  • npm install web-launch-app --save

Usage

import { LaunchApp, detector, copy, ua, isAndroid, isIos, inWeixin, inWeibo, supportLink } from 'web-launch-app';

const lanchApp = new LaunchApp();
// 简单唤起(参数含义参见Api部分)
lanchApp.open({
    scheme: 'app://path?k=v',
    url: 'https://link.domain.com/path?k=v',
    param:{
        k2: 'v2'
    }
});

// 复杂唤起
lanchApp.open({
    useYingyongbao: inWeixin && isAndroid,
    launchType: {
        ios: inWeixin ? 'store' : 'link',
        android: inWeixin ? 'store' : 'scheme',
    },
    autodemotion: false,
    scheme: 'app://path?k=v',
    url: 'https://link.domain.com/path?k=v',
    param:{
        k2: 'v2'
    },
    timeout: 2000,
    pkgs:{
        android: 'https://cdn.app.com/package/app20190501.apk',
        ios: 'https://itunes.apple.com/cn/app/appid123?mt=8',
        yyb: 'http://a.app.qq.com/o/simple.jsp?pkgname=com.app.www&ckey=CK123'
    }
}, (s, d, url) => {
        console.log('callbackout', s, d, url);
        s != 1 && copy(url);
        return 2;
    });

// 下载
lanchApp.down();

// 全局默认配置(参见Config部分)
const lanchApp2 = new LaunchApp(config);
lanchApp2.open({
    page: 'pagenameInConfig',
    param:{
        k: 'v'
    }
});
lanchApp2.down();

/* 必知:
- H5页面并不知道用户是否已安装App
- Scheme,一般格式为protocol://path?param=value&**,通过判断是否离开当前页面来推断是否唤起app
- Link,和正常url格式一样,尝试唤起App,未唤起时会像一个正常页面一样访问(一般会通过server转发到url中参数指定页面)
    - iOS上的Link叫UniversalLink,iOS9开始支持,各浏览器支持比较好
    - Android上的Link叫Applink,Chrome、三星、宙斯等浏览器支持(各版本支持情况也可能不一样)
- 微信等App及浏览器会尽可能封禁scheme或link,避免用户流出(2019.7.16发布的iOS7.0.5支持ulink)
*/

/* export:
- LaunchApp:唤起类,核心逻辑所在,通过不同方案实现唤起App及下载
- detector:宿主环境对象(含os及browser信息)
- copy:复制方法(浏览器安全限制,必须由用户行为触发)
- ua:=navigator.userAgent + " " + navigator.appVersion + " " + navigator.vendor
- isAndroid、isIos、inWeixin、inWeibo:字面含义,Boolea值
- supportLink:是否支持universal link或applink(uc&qq浏览器不支持ulink,chrome、三星、宙斯及基于chrome的浏览器支持applink),供参考
*/

API

open(options, callback)

Param Notes
options useGuideMethod 是否使用引导提示,优先级高于launchType指定的方案(适用于微信、微博等受限环境),默认false
guideMethod 引导提示方法,默认蒙层文案提示
updateTipMethod 版本升级提示方法,scheme指定版本要求时使用,默认alert提示
useYingyongbao launchType为store方案时(应用宝归为应用商店),控制微信中是否走应用宝,默认false
launchType 【1】.link:iOS9+使用universal link,Android6+使用applink,可配置指定link无法使用时自动降级为scheme。【2】.scheme:scheme协议,通过唤起超时逻辑进行未唤起处理,同时适用于app内打开页面调用native功能。【3】.store:系统应用商店(去应用宝需要指定useYingyongbao为true)
autodemotion 是否支持link方案不可用时自动降级为scheme方案,(注意参数配置:使用page时要有同page下的link和scheme配置,或同时指定url及scheme参数),默认false
scheme 指定scheme
callback scheme的回调方法
url 指定link url(iOS的universal link值或Android的applink值)
page 在config中配置的页面名称(用来替代scheme和url参数,方便管理)
param scheme或link的参数
paramMap 参数映射(适用于iOS与Android同scheme功能但参数名不同的情况,真实世界就是有这么多坑orz)
clipboardTxt 复制到剪贴板内容(针对未安装或环境受限等唤起中断情况使用,在打开app或下载app后可以通过剪贴板内容进行交互衔接或统计),浏览器安全限制需要用户动作触发才能生效
timeout scheme/store方案中超时时间,默认2000毫秒,<0表示不走超时逻辑
landPage 落地页面(异常或未知情况均会进入此页面)
pkgs {android:'',ios:'',yyb:'',store:{...}},指定子项会覆盖基础配置
callback (s, d, url) => { return 0;} ,launchType为scheme或store方案时默认有超时逻辑,可通过设置tmieout为负值取消或根据callback中的返回值进行超时处理。s表示唤起结果(0失败,1成功,2未知), d为detector,url为最终的scheme或link值。无返回值默认下载apk包或去appstore,1不处理,2落地页,3应用市场(百度春晚活动时引导去应用市场下载分流减压)

down(options)

Param Notes
options 未指定项使用实例配置中的默认值
yyb 应用宝地址,在微信中使用
android android apk包下载地址
ios appstore地址
landPage 落地页地址,非iOS/Android平台使用

Config

// 针对各种环境及方案参数有点多,需要使用者了解scheme及link本身的区别
// 虽然config中很多参数可以在使用api时指定,还是建议在实例时全局配置,减少使用api时传参
{
    inApp: false,   // 是否是app内(在app内使用了指定version的scheme会进行版本检测)
    appVersion: '', // 对具体scheme链接进行版本检测时使用
    pkgName:'', // 应用商店使用
    deeplink:{
        // 配置scheme方案具体页面及参数,生成请求格式为"protocol://path?param&param"
        scheme: {
            android: {
                // 指定android的scheme协议头
                protocol: 'appname',
                index: {    // 页面名称(默认页面请设置为:index)
                    protocol: 'appname', // 可选,如无会读取上一级protocol,一般不需要配置
                    path: 'path',
                    param: {},	// 生成scheme或linkurl时的固定参数
                    paramMap: {},// 参数映射,解决不同平台参数名不一至情况
                    version: '4.9.6'  // 版本要求
                },
                ...
            },
            ios: {
                ...
            }
        },
        // 配置univerlink或applink方案中具体页面url及参数
        link: {
            pagename: {
                url: 'https://link.app.com/p/{forumName}',	// 支持占位符
                param: {
                },
                paramMap: {
                },
                version: 0
            },
            ...
        }
    },
    // 下载包配置
    pkgs: { 
        yyb: '',
        android: 'https://cdn.app.com/package/app-default.apk',
        ios: '',
        store: {    // 手机商店匹配
            xphone: {
                reg: /\(.*Android.*\)/,
                scheme: 'market://details?id=packagename'
            }
        }
    }, 
    useUniversalLink: true, // 是否为ios9+使用universallink方案,默认true
    useAppLink: true,       // 是否为android6+使用applink方案,默认true
    autodemotion: false,    // 不支持link方案时自动降级为scheme方案,默认false
    useYingyongbao: false,   // 在微信中store方案时是否走应用宝,默认false
    useGuideMethod: false,   // 使用guide方案
    guideMethod: ()=>{},  // 引导方法,默认蒙层文案提示
    updateTipMethod: ()=>{},    // scheme版本检测时升级提示
    searchPrefix: '?',  // scheme或univerlink生成请求中参数前缀,默认为"?"
    timeout: 2000   // scheme/store方案中超时时间,默认2000毫秒,<0表示不走超时逻辑
    landPage:'',   // 兜底页
}

Demo

// ----------------------------------------------------
// launch-app.ts(业务使用的基础文件,多模块使用建议提npm包)
// ----------------------------------------------------
import { LaunchApp, detector, ua, isAndroid, isIos, supportLink, inWeixin, inWeibo, copy } from 'web-launch-app';
const inApp = /appname(.*)/.test(ua);
const appVersion = inApp ? /appname\/(\d+(\.\d+)*)/.exec(ua)[1] : '';
// 微信iOS7.0.5支持ulink(20190716)
const wxSupportLink = isIos && inWeixin && detector.browser.fullVersion > '7.0.4';
const lanchIns = new LaunchApp({
    inApp: inApp,
    appVersion: appVersion,
    pkgName: 'com.app.www',
    deeplink: {
        scheme: {
            android: {
                protocol: 'app',
                index: {
                    path: '/',
                },
                frs: {
                    protocol: 'app',
                    path: 'forum/detail',
                    param: {from:'h5'},
                    paramMap: {
                        forumName: 'kw'
                    }
                }
            },
            ios: {
                protocol: 'app',
                index: {
                    path: '/',
                },
                frs: {
                    path: 'forum/detail'
                }
            }
        },
        link: {
            index: {url: 'https://link.app.com'},
            frs: {url: 'https://link.app.com/p/{forumName}'}
        },
    },
    pkgs: {
        android: 'https://cdn.app.com/package/app-defult.apk',
        ios: 'https://itunes.apple.com/app/apple-store/appid123?pt=328057&ct=MobileQQ_LXY&mt=8',
        yyb: 'http://a.app.qq.com/o/simple.jsp?pkgname=com.app.www&ckey=123',
    },
    useUniversalLink: supportLink,
    useAppLink: supportLink,
    autodemotion: true,
    useYingyongbao: inWeixin,
    useGuideMethod: inWeixin && !wxSupportLink, // 受限情况下使用引导方案
    landPage: 'http://www.app.com/download'
});

/**
 * 唤起app到指定页面
 * @param options 
 * @param callback 
 */
export function launch(options?: any, callback?: (status, detector, scheme) => number) {

    // pkgs处理
    options.pkgs = options.pkgs || {};
    if(options.param && options.param.pkg){
        options.pkgs.android = options.pkgs.android || `https://cdn.app.com/download/app-${pkg}.apk`;
    }
    if(options.param && options.param.ckey){
        options.pkgs.android = options.pkgs.android || `http://a.app.qq.com/o/simple.jsp?pkgname=com.app.www&ckey=${ckey}`;
    }
    
    // 针对scheme情况处理剪贴板口令(纯link方案不需要)
    if (options.clipboardTxt === undefined) {
        let paramStr = options.param ? stringtifyParams(options.param) : '';
        if (options.scheme) {
            options.clipboardTxt = '#' + options.scheme + (paramStr ? ((options.scheme.indexOf('?') > 0 ? '&' : '?') + paramStr) : '') + '#';
        } else if (options.page) {
            // schemeConfig为实例化时参数中scheme配置
            options.clipboardTxt = '#' + schemeConfig['protocol'] + '://' + schemeConfig[options.page].path + (paramStr ? '?' + paramStr : '') + '#';
        }
    }
    lanchIns.open(options, callback);
}

/**
 * 端内H5页面调用端能力
 */
export function invoke(options: any) {
    options.launchType = {
        ios: 'scheme',
        android: 'scheme'
    };
    options.timeout = -1;
    lanchIns.open(options);
}

/**
 * 下载安装包
 * @param opt 
 */
export function download(opt) {
    lanchIns.download(opt);
}

// ----------------------------------------------------
// demopage.ts(业务代码部分)
// ----------------------------------------------------
import {launch, invoke, download} from 'launch-app'
// 唤起(使用默认唤起方案,指定page参数会根据配置自动生成scheme和url)
launch({
    page: 'frs',
    param:{
        forumName: 'jawidx'
    }
});

// 唤起(使用默认唤起方案,直接指定scheme和url)
launch({
    scheme: 'app://path?k=v',
    url: 'https://link.domain.com/path?k=v',
    param:{
        forumName: 'jawidx'
    }
});

// 唤起(使用scheme唤起)
launch({
    launchType: {
        ios: 'scheme',
        android: 'scheme'
    },
    page: 'frs',
    param:{
        forumName: 'jawidx'
    }
});

// 唤起(使用link唤起,适用于不阻断用户继续去h5页体验场景)
launch({
    launchType: {
        ios: 'link',
        android: 'link'
    },
    url: 'https://link.domain.com/path?k=v',
    param:{
        k2: 'v2'
    }
});

// 唤起(ios使用link,android使用scheme,微信中受限时使用引导)
launch({
    // useGuideMethod: inWeixin && !wxSupportLink, // 使用默认配置,微信中受限时使用引导
    launchType: {
        ios: 'link',
        android: 'scheme'
    },
    page: 'frs',
    param: {
        k: 'v',
        target: 'https://www.app.com/download', // 未唤起app时,server处理跳转到此页面
    },
    timeout: 2000,
    pkgs: {
        android: 'https://cdn.app.com/package/app20190502.apk'
    }
});

// 唤起(微博出引导提示,ios微信去appstore,android微信去应用宝,同时指定超时处理及下载包)
launch({
    useGuideMethod: inWeibo,
    useYingyongbao: inWeixin && isAndroid,
    launchType: {
        ios: inWeixin ? 'store' : 'link',
        android: inWeixin ? 'store' : 'scheme'
    },
    page: 'frs',
    param: {
        k: 'v',
        ckey: '123',
        pkg: '20190502',
        target: 'https://www.app.com/download',
    },
    // scheme:'',
    // url:'https://link.app.com/path',
    // guideMethod: () => {
    //     alert('出去玩~');
    // },
    timeout: 2000,
    // clipboardTxt: '#key#', // launch-app中自动生成
    // pkgs: {
        // android: 'https://cdn.app.com/package/app-20190502.apk',  // 通过pkg参数处理
        // ios: 'https://itunes.apple.com/cn/app/appid123?mt=8',    // 不传使用默认值
        // yyb: 'http://a.app.qq.com/o/simple.jsp?pkgname=com.app.www&ckey=123' // 通过ckey参数处理,不传使用默认值
    // }
}, (s, d, url) => {
    console.log('callbackout', s, d, url);
    // s != 1 && copy(url);
    return 0;
});

/**
 * 端内H5页面调用端能力
 */
invoke({
    scheme:'app://copy',
    param:{
        context:'copycontent'
    }
});

// 下载
download();

// 下载指定包(不指定平台使用全局配置)
download{
    pkgs:{
        ios:'',
        android:''
        yyb:'',
        landPage:''
    }
};

Who use?

百度贴吧、伙拍小视频、好看视频