news 2026/5/19 11:24:14

vue和React路由、history、hash模式,缓存activated、keep-alive

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vue和React路由、history、hash模式,缓存activated、keep-alive

目录

安装

原理

popstate 事件监听 URL 变化

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

后端路由

前端路由

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

刷新页面会发起请求,404

hash 模式:createHashRouter

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

本地开发和打包

路由切换

1.配置路由表的字段:path、element/component、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)

带样式的声明式路由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:保留历史记录,可返回

默认复用缓存的组件(触发 activated)

配置

keep-alive

动态加载的组件注册 name

activated:配合恢复滚动位置等上次状态

replace:不保留,适合登陆页面

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

物理导航 vs Router

应用:导航守卫处理权限或数据保存


安装

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

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

npm install vue-router

npm i react-router-dom

原理

popstate事件监听 URL 变化

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

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

后端路由

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

前端路由

作为单页面应用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()事件变化,查找对应的路由规则
  • 在单页应用SPA中,#后面的部分是前端路由
  • 如果参数放在hash后面,后端无法直接获取这些参数
  • 只有前端JavaScript可以通过window.location.hash获取

错误:参数在hash后面#/path?params- 后端收不到参数

http://www.xxx.com/#/conversationList?&date=2025-08-07&roleId=0

正确:参数在hash前面?params#/path- 前后端都能正常处理

http://www.xxx.com.com/?date=2025-08-07&roleId=0#/conversationList

//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 })

本地开发和打包

http://0.0.0.0:8081为本地启动,http://www.baidu.com.index.html为打包的地址

路由一个是加/#/selectRobot,一个是加#/selectRobot,本地地址多了一个/ http://0.0.0.0:8081/#/selectRobot

http://www.baidu.com.index.html#/selectRobot

这两个URL都带#,说明它们都是Hash模式。它们的核心区别不在于模式,而在于服务器上的文件路径结构

本地服务器把index.html放在了根路径(/),而你的打包文件xxxx.index.html本身就是一个放在服务器根路径下的、名字很长的HTML文件。

当你访问http://0.0.0.0:8081时,本地服务器默认会返回根目录下的index.html文件。
等价于访问http://0.0.0.0:8081/index.html#/selectRobot,只是省略了index.html

特性Hash 模式History 模式
URL 示例http://www.baidu.com.index.html#/selectRobothttp://0.0.0.0:8081/selectRobot
符号使用#(hash)使用/(真实路径)
原理利用#后的内容变化不会触发浏览器刷新或向服务器发送请求的特性。利用 HTML5 History API (pushState,replaceState) 来改变URL,不会真正刷新页面
服务器要求无特殊要求。因为#后的内容服务器会忽略,始终返回index.html需要特殊配置。因为像/selectRobot这样的路径是一个真实的URL,如果服务器没有正确配置,访问它会返回404错误
美观度不美观,URL中带#美观,看起来和普通的URL一样。
部署难度简单,非常适合静态资源服务器。稍复杂,需要在服务器端做回退处理(Fallback),即所有未匹配到静态资源的请求都返回index.html

HTTP协议和Web服务器(发送请求自动补全)使空路径默认指向根路径

如url末尾带/,不会影响

两种模式最简单的路由区别举例

假设我们有一个项目,有两个页面:首页(/)和一个关于页(/about)。

1. Hash 模式 (默认模式)

  • 首页:http://yourdomain.com/your-project/#/

  • 关于页:http://yourdomain.com/your-project/#/about

浏览器实际请求的始终是http://yourdomain.com/your-project/index.html这个文件。#/about的变化由前端的 Vue Router 自己处理,用于决定具体显示哪个组件。

2. History 模式

  • 首页:http://yourdomain.com/your-project/

  • 关于页:http://yourdomain.com/your-project/about

浏览器在首次加载时,会请求http://yourdomain.com/your-project/index.html。当点击跳转到“关于页”时,Vue Router 会通过 History API无刷新地将地址栏变成/about
但是,如果你直接在这个地址栏输入http://yourdomain.com/your-project/about并回车,浏览器会向服务器发起一个真实的 HTTP 请求,请求/about这个路径下的资源。如果服务器(如 Nginx, Apache)没有配置好,找不到这个路径对应的文件,就会返回404 Not Found

路由切换

1.配置路由表的字段:path、element/component、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 参数Params 参数 (未定义在 path)
表现形式留在 URL 中 (?url=xxx)存在内存中,URL 不可见
按 F5 刷新页面保留(从地址栏重新解析)丢失(内存被清空)
组件不重载时的复用需依靠watch:key驱动更新需依靠watch驱动更新

Vue Router 4 (Vue 3 )取消 无path匹配的 params 传参 特性,是因为这种“纯内存参数”破坏了 Web 网页的核心原则——状态可预测性

正常的 URL 应该做到“所见即所得”:你把当前地址栏的链接复制发给你的同事,他打开后看到的界面应该和你一模一样。但如果允许隐式传递内存 params,你同事打开这个链接时因为内存里没有数据,页面就会白屏或报错。这种“刷新就崩、分享就死”的特性成了很多前端新手的 Bug 重灾区。

参数变更-组件更新

当 URL 里的 query 从?id=1变成?id=2时,组件实例是同一个。如果你希望组件对这个变化做出响应,标准姿势有两个:

  • 姿势 A:全量刷新(最省心)<router-view>上绑定一个唯一的 key,逼迫 Vue 只要路由变了就重新实例化组件。

    <router-view :key="$route.fullPath"></router-view>
  • 姿势 B:定点监听(性能好)使用watch捕获特定参数的变化,手动触发局部数据的重新请求。

    watch: { '$route.query.id'(newId, oldId) { // 只要 id 变了,我就重新拉取接口,不用整页销毁 this.fetchData(newId); } }

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 }) } // 1. 传参跳转 ➡️ 生成的 URL 为: /user?url=https://abc.com&type=1 this.$router.push({ name: 'User', query: { url: 'https://abc.com', type: 1 } }); // 2. 显式清除某个参数 ➡️ 地址栏会抹除 url,只留下 /user?type=1 this.$router.push({ query: { ...this.$route.query, url: undefined } });

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

$route.params

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

隐式:path: '/user/',

query显式 /workbenchConfiguration/upload?wpReleId=59

params显式 /workbenchConfiguration/upload/59

如果不想让数据挂在 URL 上,又想刷新不丢失的敏感参数,最标准的做法是配合sessionStorage或状态管理

const routes = [ { // :id 就是占位符,如果加个 ? (如 :id?) 代表这个参数是可选的 path: '/user/:id', name: 'UserDetails', component: UserDetailsComponent } ] // 1. 传参跳转 ➡️ 生成的 URL 为: /user/9527 (数据绑在 URL 中,刷新绝不丢) // 【注意】使用 params 必须搭配 name 属性跳转,不能直接写 path this.$router.push({ name: 'UserDetails', params: { id: '9527' } }); // 2. 目标组件中接收 console.log(this.$route.params.id); // 输出: '9527'

?:分隔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)

带样式的声明式路由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,登录后都能访问,有一个页面需要额外认证

导航解析流程

  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:保留历史记录,可返回

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

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

默认复用缓存的组件(触发 activated)

只有当你使用了 <keep-alive> 标签包裹了 <router-view>,并且在 SPA(单页应用)内部切走又切回来时,组件没有被销毁,才会触发 activated()(激活)和 deactivated()(停用)。

配置
{ path: '/main/meet', name: 'meet', component: (resolve: any) => require(['@/views/serviceCenter/index.vue'], resolve), // iframeComponent: meet },
keep-alive

main/index.vue

<keep-alive include="todoTask,serviceCenter"> <router-view></router-view> </keep-alive>

<keep-alive include>是根据组件的name选项来匹配的,而不是路由的name

动态加载的组件注册name

如果组件是动态加载的(如() => import('@/views/meet')),确保导出的组件有name属性:

export default { name: 'serviceCenter',
activated:配合恢复滚动位置等上次状态
activated() { this.focusInput('delay'); this.restoreScrollPosition(); }, beforeRouteLeave(to, from, next) { next(); this.scrollTop = { history: document.querySelector('.chat_history_box').scrollTop || 0, panel: document.querySelector('.drawer_body').scrollTop || 0, }; },

replace:不保留,适合登陆页面

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

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

可能不会触发 activated,特别是如果key变化或组件未正确缓存

导航重复(修改原型push、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;

物理导航vs Router

维度浏览器按钮键盘/移动端手势:前进后退Router 前端路由操作
本质浏览器行为应用内行为
控制权浏览器历史记录栈控制前端路由库(Vue Router/React Router)控制
URL 变化URL 改变,并向服务端发起请求(若未处理)仅改变URL 的路径部分无服务端请求
页面动作完整页面刷新(默认行为,会重建DOM)SPA 组件切换(仅更新部分视图,无刷新)
数据状态页面状态丢失(JS 内存被清空)页面状态保留(在 SPA 内)
性能差(网络请求、重新解析渲染)好(本地JS执行,瞬时切换)
开发者干预通过popstate事件监听,可阻止默认行为完全可控,可添加导航守卫、动画等
  • 底层API:操作的是window.history对象。

  • 核心事件popstate事件。当历史记录条目改变时(用户执行前进/后退),会触发此事件。

// 监听浏览器前进后退 window.addEventListener('popstate', (event) => { console.log('位置变了:', window.location.pathname); // 可以在这里同步前端路由的状态,但无法阻止手势本身 });
  • 触发条件:在应用内点击<router-link>或调用router.push()/router.go()

  • 底层原理:通过history.pushState()/history.replaceState()API修改历史栈,但不触发页面刷新。

// Vue Router 示例 this.$router.push('/next-page'); // 添加一条记录并跳转 this.$router.go(-1); // 向前移动一条记录(等同于浏览器后退) this.$router.go(1); // 向后移动一条记录(等同于浏览器前进)

应用:导航守卫处理权限或数据保存

// Vue Router 示例:在离开页面前保存数据 beforeRouteLeave(to, from, next) { if (this.formChanged) { if (confirm('内容未保存,确定离开?')) { next(); } else { next(false); // 取消导航 } } else { next(); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/19 11:23:13

Tower.js 客户端开发实战:模板渲染和指令系统详解

Tower.js 客户端开发实战&#xff1a;模板渲染和指令系统详解 【免费下载链接】tower UNMAINTAINED - Small components for building apps, manipulating data, and automating a distributed infrastructure. 项目地址: https://gitcode.com/gh_mirrors/tow/tower Tow…

作者头像 李华
网站建设 2026/5/19 11:22:08

pyperclip测试策略:如何确保跨平台剪贴板功能的稳定性

pyperclip测试策略&#xff1a;如何确保跨平台剪贴板功能的稳定性 【免费下载链接】pyperclip Python module for cross-platform clipboard functions. 项目地址: https://gitcode.com/gh_mirrors/py/pyperclip pyperclip是一个强大的Python跨平台剪贴板模块&#xff0…

作者头像 李华
网站建设 2026/5/19 11:21:20

Vaadin Framework客户端开发揭秘:GWT集成与JavaScript扩展终极指南

Vaadin Framework客户端开发揭秘&#xff1a;GWT集成与JavaScript扩展终极指南 【免费下载链接】framework Vaadin 6, 7, 8 is a Java framework for modern Java web applications. 项目地址: https://gitcode.com/gh_mirrors/framework225/framework 想要掌握现代Java…

作者头像 李华
网站建设 2026/5/19 11:20:08

1Panel面板下ghcr.io镜像加速全攻略:以Open WebUI为例

1. 为什么你的Open WebUI镜像总是下载失败&#xff1f; 每次在1Panel面板里拉取Open WebUI镜像时&#xff0c;看着进度条卡住不动&#xff0c;是不是特别想砸键盘&#xff1f;我刚开始用ghcr.io的时候也经常遇到这个问题&#xff0c;后来才发现这根本不是技术问题&#xff0c;而…

作者头像 李华