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

相关推荐
阿蒙Amon5 小时前
TypeScript学习-第7章:泛型(Generic)
javascript·学习·typescript
睡美人的小仙女1275 小时前
Threejs加载环境贴图报错Bad File Format: bad initial token
开发语言·javascript·redis
fanruitian5 小时前
uniapp android开发 测试板本与发行版本
前端·javascript·uni-app
rayufo5 小时前
【工具】列出指定文件夹下所有的目录和文件
开发语言·前端·python
RANCE_atttackkk5 小时前
[Java]实现使用邮箱找回密码的功能
java·开发语言·前端·spring boot·intellij-idea·idea
摘星编程6 小时前
React Native + OpenHarmony:Timeline垂直时间轴
javascript·react native·react.js
2501_944525547 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 支出分析页面
android·开发语言·前端·javascript·flutter
jin1233227 小时前
React Native鸿蒙跨平台完成剧本杀组队详情页面,可以复用桌游、团建、赛事等各类组队详情页开发
javascript·react native·react.js·ecmascript·harmonyos
李白你好7 小时前
Burp Suite插件用于自动检测Web应用程序中的未授权访问漏洞
前端
经年未远8 小时前
vue3中实现耳机和扬声器切换方案
javascript·学习·vue