首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

vue-router 如何实现支持外部链接

前言

vue项目中 不少场景会遇到外部链接的情况 vue-router官方 提供了扩展 RouterLink 的方式 封装成一个组件AppLink.vue. 但是这种扩展方案 存在以下问题

  • 写法上 由 <router-link> 转变为 <AppLink>
  • 由于是封装的组件 就可能涉及到 style 样式作用域 不一样,可能会发生样式 失效
  • 项目需要额外 维护 AppLink.vue

于是就想到采取另一种方案 扩展源码 来解决以上问题 , 实现 扩展版vue-router ,同时还可以增强一下 vue-router,使其 支持 restful 风格的链接

以下为修改的核心源码

function queryToString(query) {
    return ('?' +
        Object.keys(query)
            .map(key => {
            const value = query[key];
            if (value == null)
                return '';
            if (Array.isArray(value)) {
                return value
                    .map(val => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`)
                    .join('&');
            }
            return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
        })
            .filter(Boolean)
            .join('&'));
}
function paramsToHref(to) {
    const { path, params } = to;
    const pathParams = Object.keys(params)
        .reduce((acc, key) => {
        acc[key] = params[key];
        return acc;
    }, {});
    const pathWithParams = path.replace(/\:(\w+)/g, (_, key) => {
        const value = pathParams[key];
        if (value == null)
            return ':' + key;
        if (Array.isArray(value)) {
            return value
                .map(val => encodeURIComponent(val))
                .join('/');
        }
        return encodeURIComponent(value);
    });
    return `${pathWithParams}`;
}
function checkExternalLink(to) {
    if (typeof to === 'string' && to.startsWith('http')) {
        return { isExternalLink: true, href: to, isJavascript: false };
    }
    else if ((typeof to === 'string' && to.startsWith('javascript:')) || (typeof to.path === 'string' && to.path.startsWith('javascript:'))) {
        return { isExternalLink: false, href: to, isJavascript: true };
    }
    else if (typeof to === 'object' && typeof to.path === 'string' && to.path.startsWith('http')) {
        let path = typeof to.params === 'object' ? paramsToHref(to) : to.path;
        let queryString = typeof to.query === 'object' ? queryToString(to.query) : '';
        return { isExternalLink: true, href: path + queryString + (to.hash ? to.hash : ''), isJavascript: false };
    }
    return { isExternalLink: false, href: '', isJavascript: false };
}
const RouterLinkImpl = /*#__PURE__*/ defineComponent({
    name: 'RouterLink',
    props: {
        to: {
            type: [String, Object],
            required: true,
        },
        replace: Boolean,
        activeClass: String,
        // inactiveClass: String,
        exactActiveClass: String,
        custom: Boolean,
        ariaCurrentValue: {
            type: String,
            default: 'page',
        },
    },
    useLink,
    setup(props, { slots }) {
        const { options } = inject(routerKey);
        const { isExternalLink, href, isJavascript } = checkExternalLink(props.to);
        const link = !isExternalLink && !isJavascript ? reactive(useLink(props)) : { href: href, isActive: false, isExactActive: false, route: '', navigate: () => Promise.resolve() };
        const elClass = computed(() => ({
            [getLinkClass(props.activeClass, options.linkActiveClass, 'router-link-active')]: link.isActive,
            // [getLinkClass(
            //   props.inactiveClass,
            //   options.linkInactiveClass,
            //   'router-link-inactive'
            // )]: !link.isExactActive,
            [getLinkClass(props.exactActiveClass, options.linkExactActiveClass, 'router-link-exact-active')]: link.isExactActive,
        }));
        return () => {
            const children = slots.default && slots.default(link);
            return props.custom
                ? children
                : !isExternalLink ? h('a', {
                    'aria-current': link.isExactActive
                        ? props.ariaCurrentValue
                        : null,
                    href: link.href,
                    // this would override user added attrs but Vue will still add
                    // the listener so we end up triggering both
                    onClick: link.navigate,
                    class: elClass.value,
                }, children) : h('a', {
                    href: link.href,
                    target: !isJavascript ? "_blank" : null,
                    class: elClass.value,
                }, children);
        };
    },
});

扩展版 vue-router

vue2.0 的项目 详解可见 @npm-pkg/vue-router vue3.0 的项目 详解可见 @npkg/vue-router@next

扩展版 vue-router

扩展支持自动跳转到外部链接

快速上手

  • 通过 CDN: <script src="https://unpkg.com/@npkg/vue-router@next"></script>
  • 将其添加到现有的 Vue 项目中:
  npm install @npkg/vue-router@next
  |
  yarn add @npkg/vue-router@next

用法

将所有引用 vue-router 的地方用 @npkg/vue-router 去替代

创建路由实例

//# /src/router/index.js

/*
 * 原代码
 */
import {
  createRouter,
  createWebHistory,
} from "vue-router";

// 创建路由实例
export const router = createRouter({
  history: createWebHistory(),
  routes: [{
      ...
  }]
  }
});


//----------------
// 替换为以下代码
//----------------


/*
 * 新代码
 */
import {
  createRouter,
  createWebHistory,
} from "@npkg/vue-router";

//  创建路由实例
export const router = createRouter({
  history: createWebHistory(),
  routes: [{
       ...
  }]
  }
});

/*
 * 其他使用
 */

 import { useRoute, useLink } from "@npkg/vue-router";
 let router = useRouter()
 router.push({path:'/'})

除了 Vue Router 原有用法,它还支持以下扩展写法


// 基础使用

<router-link to="/"></router-link>

<router-link to="/list"></router-link>

<router-link to="https://github.com/npm-pkg/vue-router"></router-link>

<router-link to="https://github.com/npm-pkg/vue-router?author=five-great"></router-link>

<router-link to="https://github.com/npm-pkg/vue-router/tree/v4.0.15#readme"></router-link>

//高级使用 restful 风格

<router-link :to="{path: '/'}"></router-link>

<router-link :to="{path: '/list'}"></router-link>

<router-link :to="{path:'https://github.com/npm-pkg/vue-router'}"></router-link>

<router-link :to="{path:'https://github.com/npm-pkg/vue-router', query:{author: 'five-great'}}"></router-link>

<router-link :to="{path:'https://github.com/npm-pkg/vue-router/tree/v4.0.15',hash:'#readme'}"></router-link>

<router-link :to="{path:'https://github.com/:org/:repo',params:{org:'npm-pkg',repo: 'vue-router'}}"></router-link>

<router-link :to="{path:'https://github.com/:org/:repo/tree/:v',query:{author: 'five-great'},params:{org:'npm-pkg',repo: 'vue-router',v:'v4.0.15'},hash:'#readme'}"></router-link>
  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/0a8a19044031d8f679c853213
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券