ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

vue-router HTML5 History 模式完全指南:配置、服务器回退与部署实战

vue-router HTML5 History 模式完全指南:配置、服务器回退与部署实战 前端路由【免费下载链接】vue-router The official router for Vue 2项目地址https://gitcode.com/gh_mirrors/vu/vue-router点击查看免费下载导读本文基于 vue-router 官方文档 docs-gitbook/fr/essentials/history-mode.md法语版英文对照见 docs/guide/essentials/history-mode.md系统讲解 vue-router 的history 模式HTML5 History Mode它是什么、与默认 hash 模式的本质区别、如何启用、为什么必须配置服务器 fallback、如何在 Apache/nginx/Node.js/Express/IIS/Caddy/Firebase Hosting 等主流服务器上落地配置以及如何在前端应用内兜底 404。读完本文你将掌握 history 模式从原理到部署的全链路实战方案并能依据仓库源码解释其内部行为。一、从 hash 模式到 history 模式为什么要去#1.1 默认的 hash 模式及其工作原理vue-router的默认模式是hash 模式。它利用 URL 的 hash#之后的部分来模拟一个完整的 URL从而在 URL 变化时不触发页面重新加载。在 src/history/hash.js 中可以看到 hash 模式的实现细节它通过getHash()读取window.location.href中#之后的内容作为路由路径注意源码注释特别说明不能直接使用window.location.hash因为 Firefox 会提前对其做 URL 解码导致各浏览器行为不一致并通过pushHash/replaceHash更新 hash 部分完成导航。hash 模式 URL 形如http://oursite.com/#/user/id。hash 部分不会作为 HTTP 请求发送给服务器因此服务器天然无需任何额外配置这就是 hash 模式开箱即用的原因。1.2 history 模式基于history.pushState的干净URL为了去掉 URL 中的#可以使用history 模式它借助 HTML5 的history.pushStateAPI 实现无刷新的 URL 导航。启用方式只需在创建路由时指定mode: historyconst router new VueRouter({ mode: history, routes: [...] })启用后URL 将和普通网站一样例如http://oursite.com/user/id。从源码看history 模式由 src/history/html5.js 中的HTML5History类实现。其核心行为包括push()在导航完成后调用pushState(cleanPath(this.base route.fullPath))更新地址栏对应 src/util/push-state.js 中的pushState内部调用window.history.pushState并对 Safari 的 DOM Exception 18限制 100 次 pushState 调用做了 try/catch 兜底回退到window.location.assignreplace()对应replaceStatesetupListeners()监听window上的popstate事件——当用户点击浏览器前进/后退按钮时通过popstate回调触发transitionTo完成无刷新路由切换getLocation()负责从window.location.pathname解析出当前路径并正确处理base前缀与查询参数、hash。1.3 关键区别对比维度hash 模式默认history 模式URL 形态http://oursite.com/#/user/idhttp://oursite.com/user/id底层 API修改 location.hashhistory.pushState/replaceState服务器配置不需要必须配置 fallback见下文兼容性支持所有 Vue 支持浏览器包括不支持 HTML5 History API 的旧浏览器需要支持 HTML5 History API 的现代浏览器监听事件popstate支持 pushState 时或hashchange见 src/history/hash.jspopstate注意上述不需要/必须的结论有前提——hash 模式下 hash 变化不会向服务器发起新请求所以刷新、直接访问都不会 404而 history 模式的路由路径会真实地发到服务器直接访问/user/id时服务器若不知道这个路径就会返回 404。二、核心问题为什么 history 模式会 404以及解决思路2.1 问题根源history 模式下的应用本质是单页应用SPA路由切换完全由前端 JS 控制页面上只有一份index.html。问题在于当用户在地址栏直接输入http://oursite.com/user/id或刷新该页面时浏览器会向服务器发起一个针对/user/id的真实 HTTP 请求。而服务器上并不存在/user/id这个静态资源只有/index.html于是返回404 错误页。2.2 解决思路服务器 catch-all fallback解决办法并不复杂为服务器添加一条兜底规则——当请求的 URL 不匹配任何静态文件且不是目录时一律返回应用入口页index.html。这样无论用户访问什么深链接deep link浏览器都会先拿到index.html随后由前端路由接管并渲染出对应页面。而真正的静态资源JS/CSS/图片等仍然按原路径正常返回。该策略的前提是应用部署在服务器根目录。如果你的应用部署在子目录下则需要使用构建工具如 Vue CLI的publicPath配置资源路径配合路由的base选项下文详细介绍把下面各服务器的示例配置中的根目录路径改为你的子目录路径例如把 Apache 的RewriteBase /改为RewriteBase /name-of-your-subfolder/。三、六种主流服务器的 fallback 配置实例以下配置均假设应用部署在服务器根目录。3.1 Apache使用mod_rewrite模块将不存在的文件/目录请求重写回index.htmlIfModule mod_rewrite.c RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] /IfModule逐行解读RewriteEngine On启用重写引擎RewriteBase /设置重写基准路径为站点根目录RewriteRule ^index\.html$ - [L]对已经请求index.html本身的 URL 直接放行-表示不重写[L]表示这是最后一条规则RewriteCond %{REQUEST_FILENAME} !-f请求的路径不是一个真实存在的文件RewriteCond %{REQUEST_FILENAME} !-d请求的路径不是一个真实存在的目录RewriteRule . /index.html [L]上述条件都满足时把任意请求重写到/index.html。另一种方案不用mod_rewrite可以改用 Apache 的FallbackResource指令效果类似但配置更简洁。3.2 nginx在location /中使用try_files指令location / { try_files $uri $uri/ /index.html; }含义依次尝试按原样提供文件→按目录提供→都不行就回退到/index.html。3.3 Node.js 原生不使用框架一个最小可用的原生 Node.js 服务器示例const http require(http) const fs require(fs) const httpPort 80 http.createServer((req, res) { fs.readFile(index.html, utf-8, (err, content) { if (err) { console.log(We cannot open index.html file.) } res.writeHead(200, { Content-Type: text/html; charsetutf-8 }) res.end(content) }) }).listen(httpPort, () { console.log(Server listening on: http://localhost:%s, httpPort) })这段代码对所有请求都返回index.html内容。注意它只演示了回退的核心逻辑没有区分静态资源实际项目中应先用静态资源中间件/路由匹配真实文件未命中再回退到index.html。3.4 Node.js Express对于 Express 应用官方文档推荐使用connect-history-api-fallback 中间件它专门处理 SPA 的 history 模式回退逻辑区分真实请求、Accept头、带.的文件等场景用法如下const express require(express) const history require(connect-history-api-fallback) const serveStatic require(serve-static) const app express() app.use(history()) app.use(serveStatic(__dirname /dist)) app.listen(3000)仓库佐证本仓库的 examples/server.js 使用 Express express-urlrewrite把/exampleName/*重写为对应示例目录的index.html正是把深链接回退到入口页这一思路的实践样板。3.5 IISInternet Information Services安装 IIS UrlRewrite 模块在站点根目录创建web.config内容如下?xml version1.0 encodingUTF-8? configuration system.webServer rewrite rules rule nameHandle History Mode and custom 404/500 stopProcessingtrue match url(.*) / conditions logicalGroupingMatchAll add input{REQUEST_FILENAME} matchTypeIsFile negatetrue / add input{REQUEST_FILENAME} matchTypeIsDirectory negatetrue / /conditions action typeRewrite url/ / /rule /rules /rewrite /system.webServer /configuration规则解读匹配所有 URL当请求不是文件IsFile取反且不是目录IsDirectory取反时重写为根路径/即返回首页index.html。3.6 Caddy 与 Firebase HostingCaddy v2新版本使用内置try_files指令try_files {path} /Caddy v1旧版本使用 rewrite 块rewrite { regexp .* to {path} / }Firebase Hosting在firebase.json中添加rewrites规则将所有请求重写到/index.html{ hosting: { public: dist, rewrites: [ { source: **, destination: /index.html } ] } }四、mode 与 base 的源码级解析4.1mode选项的取值与默认值根据 docs/api/README.md 的官方 API 文档mode选项定义如下type:string默认值:hash浏览器环境 /abstractNode.js 环境可选值:hash | history | abstracthash使用 URL hash 路由兼容所有 Vue 支持的浏览器包括不支持 HTML5 History API 的旧浏览器history需要 HTML5 History API 且必须配合服务器配置abstract适用于所有 JavaScript 环境如 Node.js 服务端渲染。当浏览器 API 不存在时路由会被自动强制切换到该模式。在 src/router.js 中可以看到模式的实际决策逻辑let mode options.mode || hash this.fallback mode history !supportsPushState options.fallback ! false if (this.fallback) { mode hash } if (!inBrowser) { mode abstract }即显式传入mode: history但浏览器不支持history.pushStatesupportsPushState为 false且未把fallback设为false时路由会自动回退到 hash 模式在非浏览器环境如 Node.js下则强制使用 abstract 模式。supportsPushState的定义见 src/util/push-state.js它检测window.history.pushState是否存在并额外排除了 Android 2.x / Android 4.0 的旧版 Mobile Safari 内核浏览器这类浏览器虽实现了 API 但行为有缺陷。4.2base选项子目录部署的关键当整个 SPA 被部署在子路径例如/app/下时应通过base选项告知路由const router new VueRouter({ mode: history, base: /app/, routes: [...] })官方文档对base的说明docs/api/README.mdtype 为string默认值为/表示应用的基准 URL。例如整个应用挂在/app/下base就应设为/app/。从源码看base在 src/history/base.js 的normalizeBase中被规范化优先读取页面base标签的href剥离掉协议和域名部分确保以/开头并去掉尾部/。随后在 src/history/html5.js 的getLocation中会按base前缀剥离出真正的路由路径源码注释专门提到base/a时不能把/app误判为/a/pp这类边界 case因此会在匹配时补上尾斜杠。在 src/router.js 的createHref中生成链接时也会把base拼回 URL。文档还提示history 模式下使用base后router-link的to属性无需再包含base前缀。4.3fallback选项fallbacktype:boolean默认true见 docs/api/README.md控制当浏览器不支持history.pushState但配置了mode: history时是否回退到 hash 模式。若设为false在 IE9 等旧浏览器中每次router-link导航都会变成整页刷新这一设置在服务端渲染SSR且需兼容 IE9的场景下有用因为 hash 模式的 URL 不适用于 SSR。仓库中的 examples/basic/app.js 与 examples/hash-mode/app.js 分别演示了mode: history与mode: hash的完整用法可作为对比参考。五、历史模式下的 404 兜底前端 catch-all 路由5.1 副作用服务器不再报告 404配置了服务器的 catch-all 回退后会出现一个新的副作用服务器对任何不存在的路径都会返回index.htmlHTTP 404 状态码不再出现。因为所有未命中的请求都被回退到了应用入口页。5.2 解法在 Vue 应用内实现 catch-all 路由正确的做法是在 Vue 应用内部添加一条匹配所有路径的兜底路由用来渲染 404 页面const router new VueRouter({ mode: history, routes: [ { path: *, component: NotFoundComponent } ] })其中path: *是 vue-router 中经典的通配符wildcard路径用于匹配所有未匹配到的路径。仓库的 test/unit/specs/create-map.spec.js 中就有{ path: *, name: wildcard, component: Baz }这样的测试用例来验证通配符匹配行为。版本提示本项目为 Vue 2 的 vue-router*语法对应path-to-regexp旧版通配符写法。在 Vue Router 4 中通配符语法已改为具名参数正则形式{ path: /:pathMatch(.*)*, ... }。本文以当前仓库Vue 2的实现为准。需要注意通配符路由通常会放在路由表最后避免它提前拦截掉其他正常路由的匹配。5.3 进阶方案Node.js 服务端路由做 404如果你的服务器是 Node.js还可以在服务端实现同样的回退逻辑用服务端路由匹配每个进来的 URL如果匹配不到任何路由就明确返回 404 响应。这种方案的优势在于深链接首次访问就能得到正确的 404 状态码利于 SEO未匹配路径不会白白返回index.html再让前端二次判断。这一做法正是**服务端渲染SSR**的标准路径可参阅 Vue 官方 SSR 文档了解详情。六、为什么无刷新——源码层面的行为链路为了深入理解 history 模式的原理这里把点击链接 → URL 变化 → 视图更新的完整链路串起来拦截点击router-link组件拦截默认跳转行为改为调用router.push()组件实现在 src/components/link.js。官方 API 文档docs/api/README.md也说明history 模式下router-link会拦截点击事件避免浏览器整页刷新。执行导航router.push()委托给 history 实例的push()src/history/html5.js先执行transitionTo完成路由匹配与守卫guard流程见 src/history/base.js 的transitionTo与confirmTransition。更新地址栏导航确认后调用pushState(cleanPath(this.base route.fullPath))通过window.history.pushState把新 URL 写入地址栏——该操作不会触发页面刷新这正是无刷新导航的关键。更新视图updateRoute把新 route 写入this.current并通知 Vue 响应式更新router-view渲染对应组件。响应浏览器前进/后退用户点击前进/后退按钮时浏览器触发popstate事件不刷新页面HTML5History.setupListeners中注册的回调捕获到该事件后调用transitionTo完成反向导航src/history/html5.js。由此可以得出一个重要结论history 模式下的 URL 完全由前端 JS 维护服务器端并不存在/user/id这样的真实文件。这正是直接访问深链接必须依赖服务器 fallback的根本原因也是本文所有服务器配置要解决的唯一问题。七、常见问题与部署清单7.1 常见问题速查现象原因解决方案直接访问/user/id返回 404服务器未配置 fallback按第三节为你的服务器添加 catch-all 重写规则刷新后 404但站内点击导航正常同上同上部署在子目录后链接错乱未配置base设置base: /app/并调整服务器根路径规则旧浏览器下 URL 出现#浏览器不支持 pushState触发了自动 fallback属正常降级若需 SSR 场景可设fallback: false不存在的路径显示了首页而非 404 页服务器不再返回 404前端添加path: *通配符路由渲染 404 组件7.2 上线前检查清单路由创建时是否设置了mode: history服务器是否已配置对应平台Apache/nginx/Node/Express/IIS/Caddy/Firebase的 fallback 规则若部署在子目录base与服务器重写基准是否一致是否在路由表末尾添加了通配符 404 兜底路由静态资源是否仍能正常返回fallback 规则应只拦截非文件、非目录的请求。参考文档与源码索引本文主体文档docs-gitbook/fr/essentials/history-mode.md英文版 docs/guide/essentials/history-mode.mdmode / base / fallback 选项说明docs/api/README.mdhistory 模式实现src/history/html5.jshash 模式实现src/history/base.jshash 实现见 src/history/hash.jspushState 能力检测与调用src/util/push-state.js模式选择逻辑src/router.js通配符路由测试test/unit/specs/create-map.spec.js示例history 模式 examples/basic/app.js、hash 模式 examples/hash-mode/app.js赞分享前端路由【免费下载链接】vue-router The official router for Vue 2项目地址https://gitcode.com/gh_mirrors/vu/vue-router点击查看免费下载相关推荐Vue Router 2 的 HTML5 History 模式从配置到服务器回退的完整实战指南Vue Router 2 的 HTML5 History 模式从配置到服务器回退的完整实战指南 导读 vue router 默认使用 Hash 模式URL前端路由electerm 使用指南SSH、桌面与文件传输一个窗口全搞定electerm 使用指南SSH、桌面与文件传输一个窗口全搞定 这篇 electerm 使用教程以先跑起来的思路展开讲清安装、首次配置和连上第一台 S前端路由vue-router HTML5 History 模式实战指南原理、服务端配置与 404 兜底方案vue router HTML5 History 模式实战指南原理、服务端配置与 404 兜底方案 vue router 默认使用 hash 模式通过 UR前端路由上一篇Roc 编译器快照测试深度解析关联块内类型别名引用嵌套类型nominal associated alias within block下一篇Next.js App Router 数据获取革命Suspense 与 use Hook 的完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表