route.ts

/**
 * 路由跳转方法,该方法相对于直接使用uni.xxx的好处是使用更加简单快捷
 * 并且带有路由拦截功能
 */
import { deepClone, page, deepMerge } from '@/utils'

type ToPageType =
| 'navigateTo'
| 'to'
| 'redirectTo'
| 'redirect'
| 'switchTab'
| 'tab'
| 'reLaunch'
| 'launch'
| 'navigateBack'
| 'back'

interface conFig {
    type: ToPageType,
    url: string,
    delta: number,
    params: initBody,
    animationType: string,
    animationDuration: number,
    intercept: boolean,
    routeIntercept: Function
}

interface routerObj {
    url: string,
    type?: ToPageType,
    params?: initBody,
    delta?: number
}

class Router {
    public config: conFig
    constructor() {
        // 原始属性定义
        this.config = {
            type: 'navigateTo',
            url: '',
            delta: 1, // navigateBack页面后退时,回退的层数
            params: {}, // 传递的参数
            animationType: 'pop-in', // 窗口动画,只在APP有效
            animationDuration: 300, // 窗口动画持续时间,单位毫秒,只在APP有效
            intercept: false, // 是否需要拦截
            routeIntercept: (mergeConfig: any, resolve: any) => {
                // 处理或判断参数
                resolve(true)
            } // 拦截器
        }

        // 因为route方法是需要对外赋值给另外的对象使用,同时route内部有使用this,会导致route失去上下文
        // 这里在构造函数中进行this绑定
        this.route = this.route.bind(this)
    }

    // 判断url前面是否有"/",如果没有则加上,否则无法跳转
    addRootPath(url: string) {
        return url[0] === '/' ? url : `/${url}`
    }

    // 整合路由参数
    mixinParam(url: string, params: initBody = {}) {
        url = url && this.addRootPath(url)

        // 使用正则匹配,主要依据是判断是否有"/","?","="等,如“/page/index/index?name=mary"
        // 如果有url中有get参数,转换后无需带上"?"
        let query = ''
        
        // if (/.*\/.*\?.*=.*/.test(url)) {
        //     // object对象转为get类型的参数
        //     query = queryParams(params, false)
        //     // 因为已有get参数,所以后面拼接的参数需要带上"&"隔开
        //     return url += `&${query}`
        // }
        // // 直接拼接参数,因为此处url中没有后面的query参数,也就没有"?/&"之类的符号
        // query = queryParams(params)
        // return url += query

        if(Object.prototype.toString.call(params).slice(8, -1) === 'Object') {
            query = encodeURIComponent(JSON.stringify(params))
            if (/.*\/.*\?.*=.*/.test(url)) {
                // 因为已有get参数,所以后面拼接的参数需要带上"&"隔开
                return url += Object.keys(params).length > 0 ? `&params=${ query }` : ''
            }
            // 直接拼接参数,因为此处url中没有后面的query参数,也就没有"?/&"之类的符号
            return url += Object.keys(params).length > 0 ? `?params=${ query }` : ''
        } else {
            throw Error('params type must be object')
        }
    }

    // 对外的方法名称
    async route(options: string | routerObj, params: any = {}) {
        // 合并用户的配置和内部的默认配置
        let mergeConfig: any = {}
        if (typeof options === 'string') {
            // 如果本次跳转的路径和本页面路径一致,不执行跳转,防止用户快速点击跳转按钮,造成多次跳转同一个页面的问题
            if (options === page()) return
            // 如果options为字符串,则为route(url, params)的形式
            mergeConfig.url = this.mixinParam(options, params)
            mergeConfig.type = 'navigateTo'
        } else {
            mergeConfig = deepClone(options)
            // 如果本次跳转的路径和本页面路径一致,不执行跳转,防止用户快速点击跳转按钮,造成多次跳转同一个页面的问题
            if (mergeConfig.url === page()) return console.log('鱼鱼路由跳转提示:请不要重复点击~~~')
            // 否则正常使用mergeConfig中的url和params进行拼接
            mergeConfig.url = this.mixinParam(options.url, options.params)
        }
        if (params.intercept) {
            this.config.intercept = params.intercept
        }
        // 合并内外部参数
        mergeConfig = deepMerge(this.config, mergeConfig)
        // 判断用户是否定义了拦截器
        if (this.config.intercept && typeof this.config.routeIntercept === 'function') {
            // 定一个promise,根据用户执行resolve(true)或者resolve(false)来决定是否进行路由跳转
            const isNext = await new Promise((resolve, reject) => {
                this.config.routeIntercept(mergeConfig, resolve)
            })
            // 如果isNext为true,则执行路由跳转
            isNext && this.openPage(mergeConfig)
        } else {
            this.openPage(mergeConfig)
        }
    }
    

    // 执行路由跳转
    openPage(config: any) {
        // 解构参数
        const {
            url,
            type,
            delta,
            animationType,
            animationDuration
        } = config
        if (type == 'navigateTo' || type == 'to') {
            uni.navigateTo({
                url,
                animationType,
                animationDuration
            })
        }
        if (type == 'redirectTo' || type == 'redirect') {
            uni.redirectTo({
                url
            })
        }
        if (type == 'switchTab' || type == 'tab') {
            uni.switchTab({
                url
            })
        }
        if (type == 'reLaunch' || type == 'launch') {
            uni.reLaunch({
                url
            })
        }
        if (type == 'navigateBack' || type == 'back') {
            uni.navigateBack({
                delta
            })
        }
    }

    // 修改默认配置
    setConfig(func: Fn) {
        this.config = func(this.config)
    }
}

const _router = new Router()
// _router.setConfig((config: any)=> {
//     config.intercept = true // 开启路由拦截
//     config.routeIntercept = (params: any, resolve: any) => {
//         resolve(false)
//     }
//     return config
// })

export default _router.route

/**
 * @description 深度克隆
 * @param {object} obj 需要深度克隆的对象
 * @returns {*} 克隆后的对象或者原值(不是对象)
 */
 export function deepClone(obj: any) {
	// 对常见的“非”值,直接返回原来值
	if ([null, undefined, NaN, false].includes(obj)) return obj
	if (typeof obj !== 'object' && typeof obj !== 'function') {
		// 原始类型直接返回
		return obj
	}
	const o: any = test.array(obj) ? [] : {}
	for (const i in obj) {
		if (obj.hasOwnProperty(i)) {
			o[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
		}
	}
	return o
}

/**
 * @description JS对象深度合并
 * @param {object} target 需要拷贝的对象
 * @param {object} source 拷贝的来源对象
 * @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象)
 */
 export function deepMerge(target: any = {}, source: any = {}) {
	target = deepClone(target)
	if (typeof target !== 'object' || typeof source !== 'object') return false
	for (const prop in source) {
		if (!source.hasOwnProperty(prop)) continue
		if (prop in target) {
			if (typeof target[prop] !== 'object') {
				target[prop] = source[prop]
			} else if (typeof source[prop] !== 'object') {
				target[prop] = source[prop]
			} else if (target[prop].concat && source[prop].concat) {
				target[prop] = target[prop].concat(source[prop])
			} else {
				target[prop] = deepMerge(target[prop], source[prop])
			}
		} else {
			target[prop] = source[prop]
		}
	}
	return target
}
/**
 * @description 获取当前页面路径
 */
 export function page() {
	const pages = getCurrentPages()
	// 某些特殊情况下(比如页面进行redirectTo时的一些时机),pages可能为空数组
	return `/${pages[pages.length - 1]?.route ?? ''}`
}
/**
 * @description 页面跳转获取参数
 * @returns Object
 */
export function transQuery(params: string | undefined): initBody{
    if(params) {
        return JSON.parse(decodeURIComponent(params))
    } else {
        return {}
    }
}

使用方法

const toPage = () => {
  // _route('/pages/index/xiaoai')
  _route({
      url: '/pages/index/xiaoai'
  })
}
解析数据
onLoad((options)=>{
  const query = transQuery(options.query)
  userName.value = query.name
  age.value = query.age
})
Logo

前往低代码交流专区

更多推荐