admin管理员组文章数量:1633989
目录
安装
原理
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.locationin:协议、主机名和端口号部分
window.location.protocol: 协议http
window.location.host:主机+端口(host:8080)/IP地址(127.123.32.1唯一)/域名(www.example助记)
window.location.hostname:主机host
window.location.port:端口8080
react:useLocation()
路由分类
Link标签和a标签区别
Link to="/"首页/Link
a 原生超链接标签,触发页面刷新:不适合单页应用 (SPA)
带样式的声明式路由NavLink
动态路由(不同parmas,访问同一个组件)
编程式路由(非link模式)
navigate = useNavigate()
默认路由(没有匹配成功 )、重定向路由(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 中隐藏额外的字符,适合大多数常规的浏览器环境。- 不使用哈希符号
#
,路由更加美观。 - 可以充分利用浏览器的前进和后退功能。
- 需要服务器配置支持,以确保在直接访问路由时返回正确的页面。
- HTML5的history 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;
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/page?param1=value1¶m2=value2#section1
#:锚点(Anchor)标识文档中的特定位置或元素,由浏览器处理,不发送到服务器
指示浏览器滚动到具有 id="section1"
的元素处
location属性值
window的全局对象,表示当前页面http://www.example/path/index.html
window.location.href:获取/设置 url
window.locationin:协议、主机名和端口号部分
//https://www.example:8080/page.html
// :// :
//https%3A%2F%2Fwww.example%3A8080。
encodeURIComponent(window.location.origin)
//encodeURIComponent用于将字符串中的特殊字符(空格、&、+、= 、?)转换为编码形式,确保URL中不包含任何无效字符
//查询参数时 或者 动态参数时 需要encodeURIComponent
const url = 'https://example/api?param=' + encodeURIComponent(queryParam);
window.location.href =`https://www.example/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助记)
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)
带样式的声明式路由NavLink
动态路由(不同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模式)
navigate = useNavigate()
//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,登录后都能访问,有一个页面需要额外认证
导航解析流程
-
导航被触发。
-
在失活的组件里调用
beforeRouteLeave
守卫。 -
调用全局的
beforeEach
守卫。 -
在重用的组件里调用
beforeRouteUpdate
守卫(2.2+)。 -
在路由配置里调用
beforeEnter
-
解析异步路由组件。
-
在被激活的组件里调用
beforeRouteEnter
。 -
调用全局的
beforeResolve
守卫(2.5+)。 -
导航被确认。
-
调用全局的
afterEach
钩子。 -
触发 DOM 更新。
-
调用
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 的原型方法 push
和 replace
,用来捕获导航重复错误并进行处理,
而不会在控制台中抛出错误,从而避免了不必要的错误提示和潜在的问题。
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;
版权声明:本文标题:vue和React路由、history、hash模式 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dongtai/1729175770a1188694.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论