vue和React路由、history、hash模式

11 篇文章 0 订阅
4 篇文章 0 订阅

目录

安装

原理

popstate 事件监听 URL 变化

Route 组件来匹配当前的 URL 路径

前端路由

history 模式:前进后退、隐藏额外字符、无#、需服务器支持、html5

刷新页面会发起请求,404

hash 模式:createHashRouter

未指定路由模式:自动选择

后端路由

路由切换

1.配置路由表的字段:path、element、children 

Vue

命名路由(name)

命名视图(同级展示)

路由元信息(meta,任意附加信息,如授权)

2.根据路由表生成路由对象:createBrowserRouter

3.在主入口/src/router/index.js 结合路由配置文件:RouterProvider router={router}

传参

query(显式)携带辅助信息

params(显式/隐式(刷新页面会消失))差异化界面 

?:分隔URL和参数

#:锚点(Anchor)标识文档中的特定位置或元素,由浏览器处理,不发送到服务器

指示浏览器滚动到具有 id="section1" 的元素处

location属性值

window.location.href:获取/设置 url

window.location.orgin:协议、主机名和端口号部分

window.location.protocol: 协议http

window.location.host:主机+端口(host:8080)/IP地址(127.123.32.1唯一)/域名(www.example.com助记)

window.location.hostname:主机host

window.location.port:端口8080

react:useLocation()

路由分类

Link标签和a标签区别

Link to="/"首页/Link

a 原生超链接标签,触发页面刷新:不适合单页应用 (SPA) 

动态路由(不同parmas,访问同一个组件)

编程式路由(非link模式)

默认路由(没有匹配成功 )、重定向路由(redirect/Navigate )、404(*/errorElement)

Vue

React

loader回调函数(路由前触发)

主入口

守卫(权限、路由拦截)

导航解析流程

全局

Vue

React

/src/components/BeforeEach.jsx

局部路由(相比组件路由,更推荐局部,更好地逻辑分块)

组件路由

浏览器输入URL展示路由对应组件

Vue占位router-view\router-link

React占位Outlet\Link

导航重复(修改原型push、replace方法)

路由:根据不同的url地址展示不同的内容,SPA(单页应用)的路径管理器

安装

Vue3搭配的是Vue Router4,其他用法上和vue2没有太大变化

//Vue.use() 方法进行安装和注册VueRouter插件
//自动调用插件对象中的 install 方法
Vue.use(VueRouter);

npm install vue-router

npm i react-router-dom

原理

popstate 事件监听 URL 变化

Route 组件来匹配当前的 URL 路径

前端路由

作为单页面应用SPA首次请求会把前端所需的页面下载下来(不利于 SEO 优化),所有页面的跳转都是在客户端进行操作,监听URL变化,从而对页面进行渲染,不会刷新页面,即不会http 请求

hash 本质是 锚点定位, 跳到已加载页面的指定位置

history会对浏览器进行history栈操作

history 模式:前进后退、隐藏额外字符、无#、需服务器支持、html5

  • history 模式使用浏览器的 History API,可以在 URL 中隐藏额外的字符,适合大多数常规的浏览器环境。
  • 不使用哈希符号 #,路由更加美观。
  • 可以充分利用浏览器的前进和后退功能。
  • 需要服务器配置支持,以确保在直接访问路由时返回正确的页面。
  • HTML5history API监听URL变化,所以有浏览器兼容问题
//Vue 2.x 或 3.x  Options API
const router = new VueRouter({
  mode: 'history',
  routes
});

// Vue 3.x  Composition API
import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
  history: createWebHistory(),
  routes
})

刷新页面会发起请求,404

原因:当用户在浏览器中刷新页面时,浏览器会向服务器发送请求,但服务器不知道如何处理这个特定的路由,因为它可能对应的是前端应用中的某个路由路径而不是实际的文件路径。

解决:所有的路由请求(url变化)都被指向前端应用的入口文件index.html

对比:hash模式不会出现url变化,是因为#后的部分不会发送到服务端

//服务器是 Nginx
server {
    listen 80;
    server_name yourdomain.com;

    location / {
        try_files $uri $uri/ /index.html; # 尝试查找实际文件,如果没有找到则返回index.html
    }

    # 可以根据实际情况配置其他路由
    # location /about {
    #     try_files $uri $uri/ /index.html;
    # }

    # 可以根据实际情况配置静态文件路径
    # location /static {
    #     alias /path/to/your/static/files;
    # }
}

hash 模式:createHashRouter

  • hash 模式在 URL 中使用哈希值( #,可以通过监听哈希变化来实现路由导航)
  • 不需要服务器配置支持(哈希值不会发送到服务器),即不会发送请求
  • 适用于不支持 History API 或需要兼容旧版浏览器的情况。
  • 不充分利用浏览器的前进和后退功能,只有一个历史记录入口。
  • 通过window.addEventListener监听浏览器的onhashchange()事件变化,查找对应的路由规则
//Vue 2.x 或 3.x  Options API
const router = new VueRouter({
  mode: 'hash',
  routes
});

// Vue 3.x  Composition API
import { createRouter, createWebHashHistory} from 'vue-router';
const router = createRouter({
  history:  createWebHashHistory(),
  routes
})

未指定路由模式:自动选择

如果浏览器支持 HTML5 History API(即支持 History 模式),并且应用部署在支持此模式的服务器上(能够处理单页应用的所有路径请求),Vue Router 会自动选择使用 History 模式。这样可以创建更具语义的 URL,而且在切换路由时不会显示 # 符号。

不支持 HTML5 History API 或者部署环境不适用 History 模式: 如果浏览器不支持 HTML5 History API 或者应用部署在无法处理 History 模式的服务器上,Vue Router 会自动回退到使用 Hash 模式。

const router = new VueRouter({
  routes
})

后端路由

路由分类前端路由后端路由
页面位置SPA(单页面应有)服务器端
更改url不会重新请求,局部刷新重新请求
SEO(搜索引擎优化)大部分内容 JS 动态生成的更利于 SEO

路由切换

1.配置路由表的字段:path、element、children 

  • path:指定路径

  • element(React)/component(Vue):对应组件

  • children:嵌套路由

  • export default dataConfirmChildren

Vue

命名路由(name)

 params 的自动编码/解码

{
        path: '/about/foo/:id',
        name: 'foo',
        component: Foo
}

{ name: 'foo', params: {id: 123} }
命名视图(同级展示)

路由元信息(meta,任意附加信息,如授权)

meta: { auth: false }
this.$route.meta.auth

import { useLocation, matchRoutes, Navigate } from 'react-router-dom'
import { routes } from '../../router';
export default function BeforeEach(props) {
  const location = useLocation();
  const matchs = matchRoutes(routes, location)
  const meta = matchs[matchs.length-1].route.meta
  if(meta.auth){
    return <Navigate to="/login" />
  }
  else{
    return (
      <div>{ props.children }</div>
    )
  }
}

2.根据路由表生成路由对象:createBrowserRouter

import { createBrowserRouter, createHashRouter } from 'react-router-dom'
//路由表
export const routes = [];
//路由对象
const router = createBrowserRouter(routes);
export default router;

3.在主入口/src/router/index.js 结合路由配置文件:RouterProvider router={router}

import { RouterProvider } from 'react-router-dom'
import router from './router';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <RouterProvider router={router}></RouterProvider>
  </React.StrictMode>
);

传参

query(显式)携带辅助信息

path: '/user/',

$route.query

query显式    /workbenchConfiguration/upload?wpReleId=59

import { useSearchParams } from 'react-router-dom'

  const [searchParams, setSearchParams] = useSearchParams()
  console.log( searchParams.get('age') );
  const handleClick = () => {
	setSearchParams({ age: 22 })
  }

params(显式/隐式(刷新页面会消失))差异化界面 

$route.params

显式:path: '/user/:id',

隐式:path: '/user/',

query显式    /workbenchConfiguration/upload?wpReleId=59

params显式 /workbenchConfiguration/upload/59

?:分隔URL和参数

http://example.com/page?param1=value1&param2=value2#section1

#:锚点(Anchor)标识文档中的特定位置或元素,由浏览器处理,不发送到服务器

指示浏览器滚动到具有 id="section1" 的元素处

location属性值

window的全局对象,表示当前页面http://www.example.com/path/index.html

window.location.href:获取/设置 url

window.location.orgin:协议、主机名和端口号部分

//https://www.example.com:8080/page.html
//     ://               :
//https%3A%2F%2Fwww.example.com%3A8080。
encodeURIComponent(window.location.origin)
//encodeURIComponent用于将字符串中的特殊字符(空格、&、+、= 、?)转换为编码形式,确保URL中不包含任何无效字符

//查询参数时 或者 动态参数时 需要encodeURIComponent
const url = 'https://example.com/api?param=' + encodeURIComponent(queryParam);
window.location.href =`https://www.example.com/path/to/resource.html/domain=${location.host}&req=${encodeURIComponent(location.pathname)}&protocol=https${location.hash}`

window.location.protocol: 协议http

window.location.host:主机+端口(host:8080)/IP地址(127.123.32.1唯一)/域名(www.example.com助记)

window.location.hostname:主机host

window.location.port:端口8080

window.location.pathname: 资源路径path/index.html,资源index.html

window.location.hash:

window.location.search: 搜索

var searchParams = new URLSearchParams(window.location.search);
console.log(searchParams.get('name')); // 输出 "John"

react:useLocation()

import { useLocation } from 'react-router-dom'

  • hash:哈希值

  • key:唯一标识

  • pathname:路径

  • search:query值(需要把字符串解析成对象。)

  • state:隐式数据

路由分类

Link标签和a标签区别

Link to="/"首页/Link

a 原生超链接标签,触发页面刷新:不适合单页应用 (SPA

动态路由(不同parmas,访问同一个组件)

​import { Outlet, Link } from 'react-router-dom'
export default function About() {
	return (
        <div>
            <Link to="/about/foo/123">foo 123</Link> | <Link to="/about/foo/456">foo 456</Link>
        </div>
   	)
}
//...

{
    path: 'foo/:id',
    element: <Foo />
}

//...

import { useParams } from 'react-router-dom'
export default function Foo() {
  const params = useParams()
  return (
    <div>Foo, { params.id }</div>
  )
}
​
//Vue与React唯一不同
this.$route.params.id 

编程式路由(非link模式)

//vue
this.$router.push({
      path: `/about/foo/${id}`
 })
//react
import {useNavigate } from 'react-router-dom'

const navigate = useNavigate()
const handleClick= () => {
        navigate('/about/foo/123')
}

默认路由(没有匹配成功 )、重定向路由(redirect/Navigate )、404(*/errorElement)

Vue
import VueRouter from 'vue-router'

import Home from '@/views/Home.vue'
const About={template:'<div>About</div>'}

const routes: Array<any> = [
  {
    path: '/',
    redirect: '/workbenchConfiguration'
  },
  {
    path: '/404',
    meta: { title: '404' },
    component: () => import('@/views/404.vue')
  },
  { path: '*', redirect: '/404' }
]

const router = new VueRouter({
  routes
})
React
import { createBrowserRouter, createHashRouter } from 'react-router-dom'
//路由表
export const routes = [
// 默认路由
    {
        index: true,
        //重定向
        element: <Navigate to="/about/foo/123" />,
       //自带errorElement
       errorElement: <div>404</div>,
    },
//*局部404
  {
    path: '*',
 	element: <div>404</div>
  }
];
//路由对象
const router = createBrowserRouter(routes);
export default router;
loader回调函数(路由前触发)

默认同步,配合redirect做权限拦截。

{
    path: 'bar',
    element: <Bar />,
        //async,await异步,Promise用于表示一个异步操作的最终完成(或失败)及其结果值。
    loader: async() => {
        let ret = await new Promise((resolve)=>{
            setTimeout(()=>{
                resolve({errcode: 0})
            }, 2000)
        })
        return ret; 
    }
}

useLoaderData()获取loader函数返回的数据

import { useLoaderData } from 'react-router-dom'
export default function Bar() {
  const data = useLoaderData()
  console.log(data)
  return (
    <div>Bar</div>
  )

loader函数中是没有办法使用<Navigate>组件进行重定向操作的

{
    path: 'bar',
    element: <Bar />,
    loader: async() => {
        let ret = await new Promise((resolve)=>{
            setTimeout(()=>{
                resolve({errcode: Math.random() > 0.5 ? 0 : -1})
            }, 2000)
        })
        if(ret.errcode === 0){
            return ret;
        }
        else{
            return redirect('/login')
        }
    }
}
主入口
//index.js
import { RouterProvider } from 'react-router-dom'
import router from './router';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <RouterProvider router={router}></RouterProvider>
  </React.StrictMode>
);

守卫(权限、路由拦截)

权限/拦截:一个页面很多路由SPA,登录后都能访问,有一个页面需要额外认证

导航解析流程

  1. 导航被触发。

  2. 在失活的组件里调用 beforeRouteLeave 守卫。

  3. 调用全局的 beforeEach 守卫。

  4. 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。

  5. 在路由配置里调用 beforeEnter

  6. 解析异步路由组件。

  7. 在被激活的组件里调用 beforeRouteEnter

  8. 调用全局的 beforeResolve 守卫(2.5+)。

  9. 导航被确认。

  10. 调用全局的 afterEach 钩子。

  11. 触发 DOM 更新。

  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

全局

Vue

//to
router.beforeEach((to, from, next)=>{
  if(to.meta.auth){
    next('/');
  }
  else{
    next();
  }
})
//vue+ts
router.beforeEach((to: any, from: any, next: any) => {
  const metaTitlt = (to.meta && to.meta.title) || ''
  document.title = `${metaTitlt} - 默认模版`
//是否从根路径而来,当前路由的来源路径和即将进入的路由的路径是否相同
  if (from.path !== '/' && from.matched[0].path !== to.matched[0].path) {
    message.destroy()
  }
  next()
})

React

/src/components/BeforeEach.jsx
import React from 'react'
import { Navigate } from 'react-router-dom'
import { routes } from '../../router';
export default function BeforeEach(props) {
  if(true){
    return <Navigate to="/login" />
  }
  else{
    return (
      <div>{ props.children }</div>
    )
  }
}
export const routes = [
  {
    path: '/',
    element: <BeforeEach><App /></BeforeEach>//包裹根组件APP
  }
]

局部路由(相比组件路由,更推荐局部,更好地逻辑分块)

const routes = [
    {
        name: 'bar',
        component: Bar,
        beforeEnter(to, from, next){
            if(to.meta.auth){
                next('/');
            }
            else{
                next();
            }
        }
    }
];

组件路由

<script>
  export default {
    name: 'FooView',
    beforeRouteEnter(to, from, next){
      if(to.meta.auth){
        next('/');
      }
      else{
        next();
      }
    }
  }
</script>

浏览器输入URL展示路由对应组件

没有占位的话,默认整个页面

Vue占位router-view\router-link

<template>
  <div>
    <router-link to="/">首页</router-link> | 
    <router-link to="/about">关于</router-link>
    <router-view></router-view>
  </div>
</template>

React占位Outlet\Link

​import React from "react";
import { Outlet, Link } from 'react-router-dom'
function App() {
  return (
    <div className="App">
      <h2>hello react</h2>
      <Link to="/">首页</Link> | <Link to="/about">关于</Link>
      <Outlet />
    </div>
  );
}
export default App;

带样式的声明式路由NavLink

导航重复(修改原型push、replace方法)

push:将新的路由添加到浏览器的历史记录中,这样用户就可以通过浏览器的后退按钮回到之前的路由。

this.$router.push('/about')

replace:不会在浏览器的历史记录中留下新的条目,而是直接替换当前的历史记录条目。

this.$router.replace('/contact')

比如在处理登录页面时,登录成功后可能会用replace方法替换当前路由,以防止用户通过后退按钮回到登录页面。

修改 VueRouter 的原型方法 pushreplace,用来捕获导航重复错误并进行处理,

不会在控制台中抛出错误,从而避免了不必要的错误提示和潜在的问题。

import Vue from 'vue';
import VueRouter from 'vue-router';

Vue.use(VueRouter);

const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => {
    if (err.name !== 'NavigationDuplicated') {
      throw err;
    }
  });
};

const originalReplace = VueRouter.prototype.replace;
VueRouter.prototype.replace = function replace(location) {
  return originalReplace.call(this, location).catch(err => {
    if (err.name !== 'NavigationDuplicated') {
      throw err;
    }
  });
};

const router = new VueRouter({
  // 路由配置...
});

export default router;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值