Vue-Router

268 阅读2分钟

zhuanlan.zhihu.com/p/337694767

History API

  • window对象通过history对象提供了对浏览器的会话历史的访问;
  • 它暴露了很多有用的方法和属性,允许你在用户的浏览历史中向前和向后跳转;
  1. 使用back()、forward()和go()方法来完成在用户历史记录中向后和向前的跳转
  2. 用go()方法跳转到history中指定的一个点
  3. window.history.length确定历史堆栈中页面的数量
  • 从HTML5开始提供了对history栈中内容的操作。
  1. history.pushState()添加历史记录条目
  2. history.replaceState()修改历史记录条目
  3. window.onpopstate

不同的历史模式

在创建路由器实例时,history配置允许我们在不同的历史模式中进行选择。

Hash模式

hash模式是用createWebHashHistory()创建的

import { createRouter, createWebHashHistory } from 'vue-router'

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    //...
  ],
})

HTML5模式

HTML5模式是通过createWebHistory创建的

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    //...
  ],
})

原理

  • 改变url,页面不刷新
  • 改变url时,我们可以监听到路由的变化并能够做出一些处理(如更新DOM)

如何实现改变url,页面不刷新?

  1. HTML5模式,使用replaceState或pushState改变url
  2. 浏览器不支持replaceState和pushState,使用hash模式,再通过window.location.replace()或window.location.assign()替换url
hash模式举例:
    如当前url为http://127.0.0.1:9090/Test#/chart
    执行window.location.replace('#/uploadFile')
    url改变为http://127.0.0.1:9090/Test#/uploadFile
    页面不刷新

如何监听路由变化,触发DOM更新?

  1. createRouter时创建currentRoute对象为响应式
    const START_LOCATION_NORMALIZED = {
        path: '/',
        name: undefined,
        params: {},
        query: {},
        hash: '',
        fullPath: '/',
        matched: [],
        meta: {},
        redirectedFrom: undefined,
    };
    
    const currentRoute =shallowRef(START_LOCATION_NORMALIZED)
  1. 当路由导航被点击,修改currentRoute的value,触发视图重新渲染
 onClick: link.navigate,
 function navigate(e = {}) {
        if (guardEvent(e))
            return router[unref(props.replace) ? 'replace' : 'push'](unref(props.to));
        return Promise.resolve();
    }
  function push(to) {
        return pushWithRedirect(to);
    }
  function pushWithRedirect(to, redirectedFrom) {
       failure = finalizeNavigation(toLocation, from, true, replace, data);
  }
    function finalizeNavigation(toLocation, from, isPush, replace, data) {
        const error = checkCanceledNavigation(toLocation, from);
        if (error)
            return error;
        const isFirstNavigation = from === START_LOCATION_NORMALIZED;
        const state = !isBrowser ? {} : history.state;
        if (isPush) {
            if (replace || isFirstNavigation)
                routerHistory.replace(toLocation.fullPath, assign({
                    scroll: isFirstNavigation && state && state.scroll,
                }, data));
            else
                routerHistory.push(toLocation.fullPath, data);
        }
        currentRoute.value = toLocation; //currentRoute被修改,触发响应
        handleScroll(toLocation, from, isPush, isFirstNavigation);
        markAsReady();
    }
  1. router-view组件定义watch,监听路由的变化
        watch(() => [viewRef.value, matchedRouteRef.value, props.name], ([instance, to, name], [oldInstance, from, oldName]) => {
            if (to) {
                to.instances[name] = instance;
                if (from && instance === oldInstance) {
                    to.leaveGuards = from.leaveGuards;
                    to.updateGuards = from.updateGuards;
                }
            }
            if (instance &&
                to &&
                (!from || !isSameRouteRecord(to, from) || !oldInstance)) {
                (to.enterCallbacks[name] || []).forEach(callback => callback(instance));
            }
        }, { flush: 'post' });
  1. router-view组件渲染 router-view组件拿到当前路由,利用全局matcher维护的匹配器匹配到当前路由对应的组件并渲染它。