背景
线上的一个项目因为嵌套路由过深,客户觉得复制分享的链接地址太长,所以需要增加一个分享短链接的功能。
方案
在网上搜索短链接跳转的方法大部分都是使用node服务实现的。
虽然实现并不复杂,但是考虑到多维护一个服务就多一份加班的可能性。我还是想要一个更加简单的方法。
而VueRouter就可以实现短链接跳转的功能。
直接往路由表插入一组短链接路由,redirect指向对应的路由。示例如下:
javascript
// shortLinks.js - 短链接配置
export const shortRoutes = [
{
path: '/sbKLEy/:id',
redirect: '/consectetur/adipiscing/elit/:id',
name: 'sbKLEy',
meta: { title: '页面1' },
},
];
javascript
import { shortRoutes } from './shortLinks';
const routes = [
...shortRoutes,
{
path: '/consectetur/adipiscing/elit/:id',
name: 'consectetur',
meta: { title: '页面1' },
component: () => import('@/views/consectetur/adipiscing/elit.vue'),
},
];
const router = new VueRouter({
routes,
mode: 'history',
});
这样的访问/sbKLEy/:id
路径的时候,直接就会跳转到/consectetur/adipiscing/elit/:id
。
query、params也都能正常重定向到源页面。
这个方案缺点就是只能缩短URL的 路径 部分。如果你的页面上有大量的查询参数部分,那这个方法能缩短的字符很有限。
好了,跳转功能没有问题,接下来就可以实现分享功能了。
分享功能
我们分享的逻辑是,用当前的路由地址去 短链接路由表 查询有无对应地址。有则使用短链接为分享路径,无则继续使用当前路由。然后将分享路径写入到粘贴板。
在实现分享功能之前,先准备两个工具函数:
javascript
/**
* 根据给定的路径和参数对象,生成一个包含占位符的路径字符串
* @param {Object} route - 包含路径和参数的对象
* @param {string} route.path - 路径字符串
* @param {Object} route.params - 参数对象
* @returns {string} - 替换参数后的路径字符串,其中参数被替换为占位符
* @example
* 输入: "/users/123", { id: "123" }
* 输出: "/users/:id"
*/
function getSourcePath(path, params) {
let sourcePath = path;
// 按占位符名称的长度排序,从长到短,避免替换错误
const keys = Object.keys(params).sort((a, b) => b.length - a.length);
for (const key of keys) {
if (!Object.hasOwnProperty.call(params, key)) continue;
const value = params[key];
const regex = new RegExp(value, 'g'); // 全局匹配该值
sourcePath = sourcePath.replace(regex, `:${key}`);
}
return sourcePath.replace(//+$/, "");
}
/**
* 根据给定的路径和参数对象,生成一个包含实际参数值的路径字符串
* @param {string} path - 路径模板,其中包含占位符,如 ":id"
* @param {Object} params - 参数对象,包含占位符对应的值
* @returns {string} - 替换占位符后的实际路径字符串
* @example
* 输入: "/users/:id", { id: "123" }
* 输出: "/users/123"
*/
function getParamsPath(path, params) {
let paramsPath = path;
// 遍历占位符,确保每个占位符都能被替换
for (const key in params) {
if (!Object.hasOwnProperty.call(params, key)) continue;
const value = params[key];
const regex = new RegExp(`:${key}(?=\/|$)`, 'g'); // 确保只替换占位符
paramsPath = paramsPath.replace(regex, value);
}
return paramsPath.replace(//+$/, "");
}
这两个函数是相反的功能,一个是将/users/123
转为/users/:id
,另一个是将/users/:id
转回/users/123
。
getSourcePath
方法是按值查找替换,在两个param
值相同的情况下可能会替换错误。这里有需要的可以深入再完善一下。
因为我项目内没有多个param
的路由,所以就不再继续完善该函数了。
接下来就是分享函数 handleShare
javascript
export function handleShare (route) {
// 待分享的路由路径
let sharePath = route.path
// 源路径
const sourcePath = getSourcePath(route.path, route.params)
// 映射的短链接路由
const redirectMapRoute = redirectMap.find(item => item.redirect === sourcePath)
if (redirectMapRoute) {
sharePath = getParamsPath(redirectMapRoute.path, route.params)
}
// 将完整路径中的路径替换为分享路径-省去处理 query 部分
sharePath = route.fullPath.replace(route.path, sharePath)
// 复制内容
const shareUrl = `【${document.title}】 ${window.location.origin + sharePath}`
// 将分享路径复制到剪切板,并且处理兼容问题
try {
navigator.clipboard.writeText(shareUrl)
.then(() => {
Message({
message: '分享链接复制成功',
type: 'success'
})
})
} catch (err) {
var copyInput = document.createElement('input')
copyInput.setAttribute('value', shareUrl)
document.body.appendChild(copyInput)
copyInput.select()
document.execCommand('copy')
document.body.removeChild(copyInput)
Message({
message: '分享链接复制成功',
type: 'success'
})
}
}
功能大致就是:从短链接路由表中查找是否有对应映射的路由,有就使用该短链接路由作为分享地址,然后复制。
随后只需要在页面分享按钮的上添加上这个方法调用就ok了。
javascript
// vue2
<div @click="handleShare($route)">
分享
</div>
// vue3
<div @click="handleShare(route)">
分享
</div>
...
import { useRoute } from 'vue-router'
const route = useRoute()
短链接路由生成
原先的页面路由如果很多的话。我们就再写个一次性的工具函数,遍历页面路由然后生成短链接路由表。
javascript
/**
* 路径中提取参数部分
* @param {string} path - 包含参数的路径字符串
* @returns {string} - 提取出的参数部分,以斜杠分隔
*/
function getParams (path) {
const params = path.split('/').filter(i => i.startsWith(':'))
if (params.length)
return '/' + params.join('/')
return ''
}
// 生成分享短链接路由表
function getShortRoutes () {
const shortRoutes = []
function getRedirect(currentRoutes, parentPath = '') {
currentRoutes.forEach(route => {
let path = route.path
// 相对路径需要拼接父路径
if (!route.path.startsWith('/'))
path = parentPath + '/' + route.path
// 随机生成一个短链接
const shortPath = nanoid(6)
shortRoutes.push({
path: `/${shortPath}${getParams(path)}`,
redirect: path,
name: shortPath,
props: route.props,
meta: route.meta
})
// 继续递归子路由
if (route.children)
getRedirect(route.children, path)
})
}
getRedirect(routes)
return shortRoutes
}
console.log(getShortRoutes())
很简单的函数,执行后到控制台取生成的结果就好了。这里有两点需要说一下。
- 相对路径需要拼接上父路径才是完整的
redirect
路径。 - 生成短链接路径使用的是
nanoid
随机生成,nanoid
默认使用URL友好字符,不需要担心字符问题。