Vue3最新Router带来哪些颠覆性变化?
1 前后端开发模式的演变
jQuery时对大部分Web项目,前端不能控制路由,要依赖后端项目的路由系统。通常,前端项目也部署在后端项目的模板里,项目执行示意图:
jQuery前端都要学会在后端模板如JSP里写代码。此时,前端工程师无需了解路由。对每次的页面跳转,都由后端负责重新渲染模板。
前端依赖后端,且前端无需负责路由,有很多
优点
如开发速度快、后端也承担部分前端任务,所以至今很多内部管理系统还这样。
缺点
如:
- 前后端项目无法分离
- 页面跳转由于需重新刷新整个页面、等待时间较长,让交互体验下降
为提高页面交互体验,很多前端做不同尝试。前端开发模式变化,项目结构也变化。目前前端开发中,用户访问页面后代码执行的过程:
- 用户访问路由后,无论URL地址,都直接渲染一个前端的入口文件index.html,然后在index.html文件中加载JS、CSS
- 之后,js获取当前页面地址及当前路由匹配的组件
- 再去动态渲染当前页面
用户在页面上点击时,也不需刷新页面,而直接通过JS重新计算出匹配的路由渲染。
前后两个示意图中,绿色的部分表示的就是前端负责的内容。后面这架构下,前端获得路由的控制权,在js中控制路由系统。也因此,页面跳转时就不需刷新页面,网页浏览体验提高。 这种所有路由都渲染一个前端入口文件的方式,是单页面应用程序(SPA)的雏形。
通过js动态控制数据去提高用户体验的方式并不新奇,Ajax让数据获取不需刷新页面,SPA应用让路由跳转也不需要刷新页面。这种开发模式在jQuery时代就出来,浏览器路由的变化可以通过pushState来操作,这种纯前端开发应用的方式,以前称Pjax (pushState+ Ajax)。之后,这种开发模式在MVVM框架时代放异彩,现在大部分使用Vue/React/Angular应用都这种架构。
SPA应用相比于模板的开发方式,对前端更友好,如:
- 前端对项目控制权更大
- 交互体验更丝滑
- 前端项目终于可独立部署
完成了前后端系统完全分离。
2 前端路由的实现原理
通过URL区分路由的机制实现:
- hash模式,通过URL中#后面的内容做区分,hash-router
- history模式,路由看起来和正常URL一致
对应vue-router的函数:
- createWebHashHistory
- createWebHistory
2.1 hash 模式
单页应用在页面交互、页面跳转上都是无刷新的,极大提高用户访问网页的体验。 为实现单页应用,前端路由的需求也变重要。
类似服务端路由,前端路由实现也简单,就是匹配不同 URL 路径,进行解析,然后动态渲染出区域 HTML 内容。但URL每次变化都会造成页面的刷新。解决思路:改变 URL 时保证页面的不刷新。
2014年前,大家通过 hash 实现前端路由,URL hash 中的 # 类似下面这种 # :
http://www.xxx.com/#/login
之后,在进行页面跳转操作时,hash 值变化并不会导致浏览器页面刷新,只会触发hashchange事件。在下面的代码中,通过对hashchange事件的监听,就可在fn函数内部进行动态地页面切换。
window.addEventListener('hashchange',fn)
2.2 history 模式
2014年后HTML5标准发布,浏览器多API:pushState 和 replaceState。可改变 URL 地址,并且浏览器不会向后端发送请求,就能用另外一种方式实现前端路由。
监听popstate事件,可监听到通过pushState修改路由的变化。并且在fn函数中,我们实现了页面的更新
window.addEventListener('popstate', fn)
3 手写vue-router
- src/router新建grouter文件夹
- 在grouter文件夹新建index.js
手写Vuex的基础,在index.js写代码。
先用Router类去管理路由,并用createWebHashHistory返回hash模式相关的监听代码及返回当前URL和监听hashchange事件的方法
import {ref,inject} from 'vue' const ROUTER_KEY = '__router__' function createRouter(options){ return new Router(options) } function useRouter(){ return inject(ROUTER_KEY) } function createWebHashHistory(){ function bindEvents(fn){ window.addEventListener('hashchange',fn) } return { bindEvents, url:window.location.hash.slice(1) || '/' } } class Router { constructor(options) { this.history = options.history this.routes = options.routes this.current = ref(this.history.url) this.history.bindEvents(()=>{ this.current.value = window.location.hash.slice(1) }) } // 通过Router类install方法注册Router实例 install(app) { app.provide(ROUTER_KEY,this) } } // 暴露createRouter方法创建Router实例 // 暴露useRouter方法,获取路由实例 export {createRouter,createWebHashHistory,useRouter}
回到src/router/index.js:
import {createRouter, createWebHashHistory} from './grouter/index' const router = createRouter({ history: createWebHashHistory(), // 使用routes作为页面参数传递给createRouter函数 routes })
在createRouter创建的Router实例上,current返回当前路由地址,并用ref包裹成响应式数据。
注册两个内置组件:
- router-view:就是current变化时,去匹配current地址对应组件,然后动态渲染到router-view。
- router-link
实现RouterView组件
grouter下新建RouterView.vue。
<template> 4. 在template内部使用component组件动态渲染 <component :is="comp"></component> </template> <script setup> import {computed } from 'vue' import { useRouter } from '../grouter/index' // 1. 先用useRouter获取当前路由的实例 let router = useRouter() // 3. 最后通过计算属性返回comp变量 const comp = computed(()=>{ // 2. 通过当前的路由,即router.current.value值,在用户路由配置route中计算出匹配的组件 const route = router.routes.find( (route) => route.path === router.current.value ) return route?route.component : null }) </script>
实现router-link组件
grouter下新建RouterILink.vue。template是渲染个a标签,只是将其href属性前加个#, 实现hash的修改。
<template> <a :href="'#'+props.to"> <slot /> </a> </template> <script setup> import {defineProps} from 'vue' let props = defineProps({ to:{type:String,required:true} }) </script>
回到grouter/index.js,注册router-link、router-view组件,hash模式mini-vue-router就实现了。
import {ref,inject} from 'vue' import RouterLink from './RouterLink.vue' import RouterView from './RouterView.vue' class Router{ .... install(app){ app.provide(ROUTER_KEY,this) app.component("router-link",RouterLink) app.component("router-view",RouterView) } }
vue-router还需处理路由懒加载、路由的正则匹配等。
4 vue-router实战路由匹配
vue-router支持动态路由。某用户页面使用User组件,但每个用户信息不一,需给每个用户配置单独的路由入口,就可按下面代码样式配置路由。
冒号开头的id就是路由的动态部分,同时匹配/user/dasheng和/user/javaedge, 详见 官方文档的路由匹配语法部分。
const routes = [ { path: '/users/:id', component: User }, ]
有些页面,仅管理员可访问,普通用户访问提示无权限。得用vue-router的 路由守卫功能 ,即访问路由页面之前进行权限认证,做到页面级控制,只允许某些用户访问。
项目庞大后,如果首屏加载文件太大,就可能影响性能。可用vue-router的 动态导入功能,把不常用的路由组件单独打包,当访问到这个路由的时候再进行加载,这也是vue项目常见优化方式。
5 总结
前后端开发模式演进:前端项目经历的从最初的嵌入到后端内部发布,再到如今前后端分离,也见证了前端SPA发展。
前端路由实现的两种方式,即通过监听不同的浏览器事件,实现hash、history模式。之后,根据这原理,手写vue-router,通过createRouter创建路由实例,并在app.use函数内部执行router-link和router-view组件的注册,最后在router-view组件内部动态的渲染组件。
Vue Router 路由实现步骤
路由配置:
import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/login', name: 'Login', component: () => import('@/views/user/login.vue') }, { path: '/', name: 'Home', component: () => import('@/views/Home.vue'), meta: { requiresAuth: true } // 需要登录权限 } ] const router = createRouter({ history: createWebHistory(), routes })
路由守卫:
router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.meta.requiresAuth && !token) { // 需要登录但未登录,重定向到登录页 next({ path: '/login', query: { redirect: to.fullPath } }) } else { next() } })
登录组件中使用路由:
<script setup> import { useRouter, useRoute } from 'vue-router' const router = useRouter() const route = useRoute() const login = async () => { try { await doLogin() // 登录成功后跳转 const redirect = route.query.redirect || '/' router.push(redirect) } catch (error) { handleError(error) } } const logout = async () => { localStorage.removeItem('token') router.push('/login') } </script>
主应用挂载路由:
import { createApp } from 'vue' import router from './router' import App from './App.vue' const app = createApp(App) app.use(router) app.mount('#app')
路由视图渲染:
<template> <router-view></router-view> </template>
路由流程:
- 配置路由表
- 设置路由守卫
- 组件中注入路由
- 应用挂载路由
- 视图渲染组件
使用方式:
- 声明式:
<router-link to="/login">
- 编程式:
router.push('/login')
FAQ
60行代码实现hash模式的迷你vue-router,支持history模式的迷你vue-router咋实现?
实现支持 history 模式 的迷你 Vue Router 的核心是利用 HTML5 提供的 pushState
和 replaceState
API,以及监听 popstate
事件来响应浏览器的回退、前进等操作。以下是支持 history 模式的迷你 Vue Router 的实现步骤:
实现 history 模式的 createWebHistory 方法
在 src/router/grouter/index.js
中修改或新增以下代码,用于返回 history 模式相关的监听逻辑:
function createWebHistory() { function bindEvents(fn) { window.addEventListener('popstate', fn); } function push(url) { history.pushState(null, '', url); // 修改浏览器地址但不刷新页面 } return { bindEvents, push, url: window.location.pathname || '/', // 获取当前路径 }; }
修改 Router 类
扩展 Router 类,支持 history 模式的路由变化处理:
class Router { constructor(options) { this.history = options.history; this.routes = options.routes; this.current = ref(this.history.url); this.history.bindEvents(() => { this.current.value = window.location.pathname; }); } // 编程式导航(例如 router.push('/path')) push(url) { this.history.push(url); this.current.value = url; } install(app) { app.provide(ROUTER_KEY, this); app.component('router-link', RouterLink); app.component('router-view', RouterView); } }
修改 RouterLink 组件
支持 history
模式的 RouterLink 组件不需要 #
前缀,使用编程式导航:
<template> <a @click.prevent="navigate">{{ $slots.default() }}</a> </template> <script setup> import { defineProps, inject } from 'vue'; const props = defineProps({ to: { type: String, required: true }, }); const router = inject('__router__'); function navigate() { router.push(props.to); } </script>
注册 Vue Router
在 src/router/index.js
中注册使用 createWebHistory
的路由实例:
import { createRouter, createWebHistory } from './grouter/index'; const routes = [ { path: '/', component: Home }, { path: '/about', component: About }, ]; const router = createRouter({ history: createWebHistory(), routes, }); export default router;
配置 Web 服务
要支持 history 模式,需要配置服务器以处理所有的路径。以 Nginx 为例,配置如下:
server { listen 80; server_name yourdomain.com; location / { root /path/to/your/app; index index.html; try_files $uri /index.html; } }
hash V.S history模式
| 特点 | Hash 模式 | History 模式 | | -------------- | --------------------------- | ------------------------- | | URL 格式 | http://example.com/#/path
| http://example.com/path
| | 浏览器刷新处理 | 不需后端额外支持 | 需服务器配置支持 | | SEO | 不友好 | 更友好 | | 实现复杂度 | 简单 | 较复杂 |、
本文已收录在Github,关注我,紧跟本系列专栏文章,咱们下篇再续!
作者简介:魔都架构师,多家大厂后端一线研发经验,在分布式系统设计、数据平台架构和AI应用开发等领域都有丰富实践经验。
各大技术社区头部专家博主。具有丰富的引领团队经验,深厚业务架构和解决方案的积累。
负责:
- 中央/分销预订系统性能优化
- 活动&券等营销中台建设
- 交易平台及数据中台等架构和开发设计
- 车联网核心平台-物联网连接平台、大数据平台架构设计及优化
- LLM Agent应用开发
- 区块链应用开发
- 大数据开发挖掘经验
- 推荐系统项目
目前主攻市级软件项目设计、构建服务全社会的应用系统。
参考:
本文由博客一文多发平台 OpenWrite 发布!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
2024 源创会年终盛典“昇腾 AI 大模型与应用开发”分论坛顺利举行
在科技发展的浪潮中,每一次创新都预示着行业的变革。12月28日,2024 源创会年终盛典在珠海隆重举行。其中,“昇腾 AI 大模型与应用开发”分论坛聚焦昇腾在AI 大模型领域的技术、生态、应用等等多方面的创新进展。 充分发挥硬件算力,昇思MindSpore成为首选AI框架 华为昇思生态总监王神迪博士以《昇思 MindSpore Al 框架使能大模型原生创新》为题发表演讲,重点分享了昇思 MindSpore Al 框架在技术上的突破。 他表示,在文本生成、视频生成等AI场景中,序列长度突破百万tokens,超长序列成为主流标配,大模型从数据驱动走向算力驱动。大算力成为支撑更高质量大模型创新落地的最关键基础。 然而,大模型开发难度大,常稳高效训练挑战大,推理部署成本高,算力及开发迎来挑战。在这一背景下,昇思MindSpore赋能用户开发大模型能力,充分发挥硬件澎湃算力,成为了大模型领域的首选AI框架。 尤其是全新发布的昇思MindSpore 2.4版本,引入了多维混合并行技术,大规模分布式训练的性能实现最优。此外,昇思MindSpore在动静统一方面也取得了突破,提供了静态图的全栈优化和...
- 下一篇
推动云原生中间件持续进化 东方通&openEuler Meetup北京站成功举办!
中间件+云原生+开源=? 这一组合并非寻求唯一答案,而是推动持续的进化。 12月27日,由东方通与OpenAtom openEuler(简称"openEuler")社区携手主办的openEuler社区云原生开源中间件Meetup北京站圆满落幕。活动围绕开源与云原生中间件,吸引了技术爱好者、行业从业者及高校学生的关注与参与。本次Meetup以共享云原生开源中间件的技术动态与前沿信息为主旨,提升专业技术人才对云原生中间件技术的兴趣与参与度,促进生态进一步繁荣,携手为中间件云原生发展注入“开源”动力。 随着云与大数据的加速推进,中间件作为数字化基础设施建设的基石,云原生转型升级已是必然趋势。这一趋势也驱动着云原生中间件开源生态深度融合、技术的迅速普及与持续优化。 作为openEuler社区轻舟与云翼项目的贡献方,东方通持续关注项目进度,投入研发力量,积极推动项目技术完善、迭代、改进与进一步创新。东方通开发一部经理王普代表东方通致开场辞,他表示,东方通一直致力于推动云原生中间件的创新与发展,依托openEuler社区强大的生态影响力和技术支持,共同推动了开源项目的优化与创新,为技术人员解锁了更...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装Docker,最新的服务器搭配容器使用
- Linux系统CentOS6、CentOS7手动修改IP地址
- 2048小游戏-低调大师作品
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8编译安装MySQL8.0.19
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- MySQL8.0.19开启GTID主从同步CentOS8