vue-router如何实现修改url但不加载HTML

一、背景和意义

vue-router中vue中的常用组件,其在切换页面时,不会重新请求获取新页面的HTML代码,与常见的页面重定向不一样。本文参照相关源码简要介绍其实现方式。

二、vue-router的使用

在vue中,使用vue-router的方法为:

html 复制代码
<template>
...
<button @click="toUserInfoPage()" ...>跳转到个人中心</button>
...
</template>

<script>
import { useRouter } from 'vue-router'

const router = useRouter();
...
function toUserInfoPage() {
  router.push({ name: "userInfo" });
}
...
</script>

当点"跳转到个人中心"的按钮之后,地址栏上的URL会变成/userInfo,但浏览器并没有请求/userInfo页面的html代码,看上去很流畅。

三、vue-router相关源码解读

创建Router对象的代码是在node_modules\vue-router\dist\vue-router.mjs文件中的createRouter方法,需要注意的是它是在vue初始化插件的时候调用,而不是在const router = useRouter()这段代码中调用。createRouter函数的调用在前,const router = useRouter()只是获取之前创建Router对象。

createRouter方法的大致结构如下:

javascript 复制代码
function createRouter(options) {
  ...
  function push(to) {
    return pushWithRedirect(to);
  }
  function pushWithRedirect(to, redirectedFrom) {
    // pushWithRedirect的实现代码比较复杂,这里先省略
  }
  ...
  const router = {
    ...
    push,
    ...
  };
  return router;
}

接下来在前面的例子中,当点"跳转到个人中心"的按钮之后,调用router.push({ name: "userInfo" })方法,这里的push方法也就是createRouter中定义的push方法。

在调用push方法时,push方法会调用pushWithRedirect方法,该方法的大致结构为:

javascript 复制代码
function pushWithRedirect(to, redirectedFrom) {
    ...
    return (failure ? Promise.resolve(failure) : navigate(toLocation, from))
            .catch((error) => ...)
            .then((failure) => {
            if (failure) {
                ...
            }
            else {
                // if we fail we don't finalize the navigation
                failure = finalizeNavigation(toLocation, from, true, replace, data);
            }
            ...
            return failure;
        });
}

中pushWithRedirect中的Promise是先写catch再写then,这与很多人的习惯写法不太一样。这里调用了一个finalizeNavigation方法,该方法的大致结构为:

javascript 复制代码
function finalizeNavigation(toLocation, from, isPush, replace, data) {
    ...
    // change URL only if the user did a push/replace and if it's not the initial navigation because
    // it's just reflecting the url
    if (isPush) {
        // on the initial navigation, we want to reuse the scroll position from
        // history state if it exists
        if (replace || isFirstNavigation)
            routerHistory.replace(toLocation.fullPath, assign({
                scroll: isFirstNavigation && state && state.scroll,
            }, data));
        else
            routerHistory.push(toLocation.fullPath, data);
    }
    ...
}

前面调用router.push({ name: "userInfo" })方法时,会走routerHistory.push(toLocation.fullPath, data)这个代码分支。routerHistory则是useHistoryStateNavigation这个方法创建的一个对象,该方法的结构为:

javascript 复制代码
function useHistoryStateNavigation(base) {
    const { history, location } = window;
    ...
    function changeLocation(to, state, replace) {
        ...
        try {
            // BROWSER QUIRK
            // NOTE: Safari throws a SecurityError when calling this function 100 times in 30 seconds
            history[replace ? 'replaceState' : 'pushState'](state, '', url);
            ...
        } catch (err) {
            ...
        }
    }
    function push(to, data) {
        ...
        changeLocation(to, state, false);
        ...
    }
    return {
        ...
        push,
        ...
    };
}

也就是push方法又调用了一个changeLocation方法,第三个参数replace传false。那么changeLocation就会调用window.history.pushState修改浏览器的url地址,使用该方法修改url地址时则不会请求相关页面的HTML代码。

另外这里有一行注释:// NOTE: Safari throws a SecurityError when calling this function 100 times in 30 seconds,也就是在Safari浏览器中,如果频繁调用window.history.pushState可能会报错,vue-router加了一个try...catch错误处理,我们自己在使用window.history.pushState实现其他功能时,也可以像vue-router那样做一下错误处理。

相关推荐
hqk7 分钟前
鸿蒙项目实战:手把手带你实现 WanAndroid 布局与交互
android·前端·harmonyos
Lee川22 分钟前
解锁 JavaScript 的灵魂:深入浅出原型与原型链
javascript·面试
是糖糖啊36 分钟前
OpenClaw 从零到一实战指南(飞书接入)
前端·人工智能·后端
Despupilles43 分钟前
第三篇、基本骨架结构
前端
swipe1 小时前
从原理到手写:彻底吃透 call / apply / bind 与 arguments 的底层逻辑
前端·javascript·面试
踩着两条虫1 小时前
从设计稿到代码:VTJ.PRO 的 AI 集成系统架构解析
前端·vue.js·人工智能
Mapmost1 小时前
从“雕琢”到“生成”:AIGC正在重塑数字孪生世界
前端
掘金一周1 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了 | 掘金一周 3.5
前端·人工智能·agent
JasonYin1 小时前
ViewModel 知识体系思维导图
前端
幸福小宝2 小时前
uniapp 抽屉实现左滑
前端