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那样做一下错误处理。

相关推荐
前端小趴菜0532 分钟前
React - 组件通信
前端·react.js·前端框架
Amy_cx1 小时前
在表单输入框按回车页面刷新的问题
前端·elementui
dancing9991 小时前
cocos3.X的oops框架oops-plugin-excel-to-json改进兼容多表单导出功能
前端·javascript·typescript·游戏程序
后海 0_o2 小时前
2025前端微服务 - 无界 的实战应用
前端·微服务·架构
Scabbards_2 小时前
CPT304-2425-S2-Software Engineering II
前端
小满zs2 小时前
Zustand 第二章(状态处理)
前端·react.js
程序猿小D2 小时前
第16节 Node.js 文件系统
linux·服务器·前端·node.js·编辑器·vim
萌萌哒草头将军2 小时前
🚀🚀🚀Prisma 发布无 Rust 引擎预览版,安装和使用更轻量;支持任何 ORM 连接引擎;支持自动备份...
前端·javascript·vue.js
狼性书生2 小时前
uniapp实现的简约美观的星级评分组件
前端·uni-app·vue·组件
书语时2 小时前
ES6 Promise 状态机
前端·javascript·es6