Home Reference Source

src/wx.js

import extend from 'extend';
import {
    appendUrl,
    invokePageMethod
} from './util.js';

/**
 * 如果跳转失败, 则尝试降级(degrade)到降级的方法(例如 `switchTab`)来完成跳转
 * 
 * @param {object} degradeFunctionName 降级的方法名
 * @return {function} function(options)
 */
function failThenTryDegrade(degradeFunctionName) {
    return function(options) {
        var originalFail = options.fail;
        var originalUrl = options.url;
        function tryDegrade() {
            var _options = extend({}, options);

            // 调用过 `wx` 的跳转方法后, 会改写 `options.url` 参数
            // 例如 `url` 原始值为 `/pages/home/index?a=1`
            // 会被改写为 `pages/home/index.html?a=1`
            _options.url = originalUrl;

            // 如果降级的方法也调用失败了, 才触发失败回调
            if (originalFail) {
                _options.fail = originalFail;
            } else {
                // 删除用于降级的 fail 回调
                delete _options.fail;
            }

            wx[degradeFunctionName](_options);
        }

        options.fail = function(result) {
            // XXX 微信小程序没有提供错误码用于判定是想跳转到 tabbar 页面却用错了方法
            // 例如: `navigateTo:fail can not navigateTo a tabbar page`
            console.warn(result.errMsg);
            if (originalUrl) {
                // 标记尝试使用降级的方法来跳转页面
                options._tryDegrade = degradeFunctionName;
                tryDegrade();
            }
        };
    };
}

var failThenTrySwitchTab = failThenTryDegrade('switchTab');
var failThenTryRedirectTo = failThenTryDegrade('redirectTo');

// 包装 wx 的方法

/**
 * 封装原生的 navigateTo 方法
 * 
 * - 扩展 `_urlParams` 用于在 URL 上追加参数
 * - 调用失败时尝试降级为 `switchTab` 来完成跳转
 * - 解决微信小程序只允许跳转 10 层路由的问题, 超过限制后自动变为 `redirectTo` 跳转页面
 * 
 * @param {object} options
 *                 options._urlParams {object} 需要追加到 URL 上的参数
 */
export function navigateTo(options) {
    var _options = extend({}, options);

    if (_options._urlParams) {
        _options.url = appendUrl(_options.url, _options._urlParams);
    }

    failThenTrySwitchTab(_options);

    // 最多 10 次路由(首页本身也算一次)
    if (getCurrentPages().length < 10) {
        return wx.navigateTo(_options);
    } else {
        return wx.redirectTo(_options);
    }
};

/**
 * 封装原生的 redirectTo 方法
 * 
 * - 扩展 `_urlParams` 用于在 URL 上追加参数
 * - 调用失败时尝试降级为 `switchTab` 来完成跳转
 * 
 * @param {object} options
 *                 options._urlParams {object} 需要追加到 URL 上的参数
 */
export function redirectTo(options) {
    var _options = extend({}, options);

    if (_options._urlParams) {
        _options.url = appendUrl(_options.url, _options._urlParams);
    }

    failThenTrySwitchTab(_options);
    return wx.redirectTo(_options);
};

/**
 * 封装原生的 `switchTab` 方法
 * 
 * - 调用失败时尝试降级为 `redirectTo` 来完成跳转
 * 
 * @param {object} options
 */
export function switchTab(options) {
    failThenTryRedirectTo(options);
    return wx.switchTab(options);
};

/**
 * 封装原生的 navigateBack 方法
 * 
 * - 回退页面时调用回退页的特定方法
 * - 判断预期返回的页面
 * 
 * @param {object} options
 *                 options._triggerOnNavigateBack {boolean|string} 激活时默认调用回退页中的 _onNavigateBack 方法, 如果传入的是字符串则表示方法名, 指定调用回退页面中的该方法
 *                 options._onNavigateBackArgs {array<any>} 传入 _onNavigateBack 方法的参数
 *                 options._expectedBackUrl {string} 预期返回的页面 URL
 */
export function navigateBack(options = {}) {
    var _options = extend({}, options);

    // 包装成功方法
    _options.success = options.success;
    if (_options._triggerOnNavigateBack) {
        _options.success = function() {
            options.success && options.success();

            var methodName = typeof _options._triggerOnNavigateBack === 'boolean' ?
                             '_onNavigateBack' : _options._triggerOnNavigateBack;
            invokePageMethod(methodName, [_options._onNavigateBackArgs]);
        };
    }

    // 预期返回的页面
    if (_options._expectedBackUrl) {
        var pages = getCurrentPages();

        if (pages.length === 1) { // 没有上一页
            _options.url = _options._expectedBackUrl;
            // XXX tabbar 的页面算是第一个页面
            return navigateTo(_options);
        } else {
            var delta = isNaN(parseInt(_options.delta)) ? 1 : parseInt(_options.delta);
            if (delta >= pages.length) { // 首页
                delta = pages.length - 1;
            } else if (delta <= 0) { // 上一页
                delta = 1;
            }

            // 需要排掉当前页
            var backPage = pages[pages.length - (delta + 1)];

            // 回退的页面符合预期直接回退
            if (backPage.route.indexOf(_options._expectedBackUrl) != -1) {
                return wx.navigateBack(_options);
            } else { // 不符合预期则跳转到预期页面
                _options.url = _options._expectedBackUrl;
                return navigateTo(_options);
            }
        }
    } else { // 兜底
        return wx.navigateBack(_options);
    }
};