@ray-js/router-mp
Version:
Ray Core
210 lines (199 loc) • 5.85 kB
JavaScript
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import { navigateBack, navigateTo, reLaunch, redirectTo, switchTab } from '@ray-js/api';
import { url } from '@ray-js/library';
import { RouterScheduler, currentPage } from './RouterScheduler';
function pathRelative(fromPath, toPath) {
const from = fromPath.split('/');
const to = toPath.split('/');
let i = 0;
// 找到两个路径第一个不同的目录
while (from[i] === to[i]) {
i++;
}
let result = '';
// 在 from 中添加 '..' 直到达到相同的目录
for (let j = i; j < from.length - 1; j++) {
result += '../';
}
// 在 result 中添加 to 中剩余的目录
for (let j = i; j < to.length; j++) {
result += to[j];
if (j < to.length - 1) {
result += '/';
}
}
return result;
}
export class Router {
// 只有作为其他小程序的子包时,才设置urlPrefix
urlPrefix = '';
/**
* 功能页模式为 functional,使用相对路径模式
* 如果是绝对路径,表示跳转到宿主小程序
*
* @private
*/
mode = 'miniprogram'; // functional
/**
* setUrlPrefix
*/
setUrlPrefix(prefix) {
this.urlPrefix = prefix;
}
setMode(mode) {
this.mode = mode;
}
scheduler = (() => new RouterScheduler())();
/**
* 标准化路由路径,将 web 标准的地址栏转化到 小程序地址
*/
normalizeRoute(params) {
let {
pathname,
query,
hash
} = url.parse(params.to);
const subPackage = params.subpackage;
const currentRoute = this.scheduler.getCurrentRoute();
const isRelativePath = pathname.startsWith('.');
if (isRelativePath) {
const currentPathname = currentRoute.route;
// 如果是相对路径,修正为绝对路径
const absolutePathSegments = (currentPathname + '/' + pathname).split('/');
const normalizedPathSegments = [];
for (let i = 0; i < absolutePathSegments.length; i++) {
const segment = absolutePathSegments[i];
if (segment === '..') {
normalizedPathSegments.pop();
} else if (segment !== '.') {
normalizedPathSegments.push(segment);
}
}
pathname = normalizedPathSegments.join('/');
}
// 如果是相对路径,修正为绝对路径
const matchedPage = subPackage ? this.scheduler.getMatchedSubPackagePage(pathname, subPackage) : this.scheduler.getMatchedPage(pathname);
if (!matchedPage) {
console.warn('try match page', {
pathname,
query,
hash
});
return Promise.reject('can not find page by path: ' + params.to);
}
// 作为其他小程序时不能作为tabBar
if (this.urlPrefix) {
matchedPage.isTabBar = false;
}
let finalPath = matchedPage.path;
// FIXME: tabBar.list里的页面不能有query,也不能有hash
if (!matchedPage.isTabBar) {
finalPath = url.format({
pathname: matchedPage.path,
query: _objectSpread(_objectSpread(_objectSpread({}, query), matchedPage.params), {}, {
// 模式匹配得到的参数,如/xxx/:id 可以匹配路径 /xxx/123 得参数id: 123
____h_a_s_h____: hash.slice(1) // 小程序中不能传递hash参数,用query辅助传递
})
});
}
/**
* 坑点之一
* 微信小程序: app.json 里的页面配置不能以`/`开头,wx.navigatorTo 却需要
*/
if (this.urlPrefix) {
finalPath = ('/' + this.urlPrefix + '/' + finalPath).replace(/\/+/g, '/');
}
matchedPage.path = finalPath;
// 因为可能是功能页模式,必须使用相对路径进行跳转
if (isRelativePath) {
// 将路径基于 currentRoute 进行绝对路径转成相对路径
matchedPage.path = pathRelative(currentRoute.path, matchedPage.path);
}
return Promise.resolve(matchedPage);
}
/**
* 跳转到指定路由
* @param to - routes.config.ts 中配置的路由地址
* @param options
* @param options.subpackage - 分包名
*
* @example
* router.push('/cat', { subpackage: 'packageA' });
*/
push(to, options) {
const subpackage = options === null || options === void 0 ? void 0 : options.subpackage;
this.normalizeRoute({
to,
subpackage
}).then(route => {
const path = route.path;
if (route.isTabBar) {
switchTab({
url: path
});
} else {
navigateTo({
url: path
});
}
});
}
/**
* 替换当前页到指定路由
* @param to - routes.config.ts 中配置的路由地址
* @param options
* @param options.subpackage - 分包名
*
* @example
* router.replace('/cat', { subpackage: 'packageA' });
*/
replace(to, options) {
const subpackage = options === null || options === void 0 ? void 0 : options.subpackage;
this.normalizeRoute({
to,
subpackage
}).then(route => {
if (route.isTabBar) {
switchTab({
url: route.path
});
} else {
redirectTo({
url: route.path
});
}
});
}
reload() {
const page = currentPage();
reLaunch({
url: url.params(page.route, page.options)
});
}
go() {
throw new Error('Method not implemented.');
}
back() {
navigateBack({
delta: 1
});
}
// 获取宿主环境上的 href ,小程序端需要通过 path 进行转换
get href() {
const page = currentPage();
const reg = RegExp('^' + this.urlPrefix + '/');
const r = page.route.replace(reg, '');
const path = url.params(`/${r}`, page.options);
const {
pathname,
query,
hash
} = url.parse(path);
return this.scheduler.getHrefByPath({
path: pathname,
query,
hash
});
}
}
export const router = new Router();