前言
在 Web 前端开发中,路由是非常重要的一环,但是路由到底是什么呢?
-
从路由的用途上讲
路由是指随着浏览器地址栏的变化,展示给用户不同的页面。
-
从路由的实现原理上讲
路由是URL到函数的映射。它将 URL 和应用程序的不同部分映射到特定的处理程序或控制器上。
路由本身也经历了不同的发展阶段:
-
服务端路由
服务端路由也称后端路由。
服务器根据用户访问的 URL 路径返回不同的响应结果。当我们在一个传统的服务端渲染的 web 应用中点击一个链接时,浏览器会从服务端获得全新的 HTML,然后重新加载整个页面。
对于最简单的静态资源服务器,可以认为所有URL的映射函数就是一个文件读取操作。对于动态资源,映射函数可能是一个数据库读取操作,也可能是进行一些数据处理等等。
-
好处
安全性好、利于 SEO(Search Engine Optimization,搜索引擎优化。是指为了增加网页在搜索引擎自然搜索结果中的收录数量以及提升排序位置而做的优化行为)
-
缺点
加大服务器的压力,不利于用户体验,代码冗余
正式服务端路由得缺点,让客户端路由开始展露头角。
-
-
客户端路由
客户端路由也称前端路由。
当用户通过客户端访问不同的路径时,路由的映射函数利用诸如 History API 或是
hashchange
事件这样的浏览器 API 来管理应用当前应该渲染的视图,其根本就是操作 DOM 的显示和隐藏。-
基于 hash 的实现
早期的前端路由的实现就是基于location.hash来实现的。实现原理也很简单就是:监听#后面的内容来发起Ajax请求来进行局部更新,而不需要刷新整个页面。
location.hash的值就是URL中#后面的内容。
例如:https://www.happy.com#me中的 location.hash = ‘#me’
hash也存在下面几个特性:
1.URL中的hash值只是客户端的一种状态,也就是说当向服务器发出请求时,hash部分不会被发送。
2.hash值的改变,都会在浏览器的访问历史中增加一个记录,因此我们能通过浏览器的回退,前进按钮控制hash 的切换。
3.我们可以使用hashchange事件来监听URL的变化
触发hash变化的方式也有两种:
1.通过a标签,并设置href属性,当用户点击这个标签后,URL就会发生变化,也就会触发hashchange事件了 例如:<a href="#hahha">hahha</a> 2.直接使用js来对location.hash进行赋值,从而改变URL 触发hashchange事件 例如: location.hash = '#hahha' 3.浏览器前进后退改变 URL,触发hashchange事件
-
基于 history API 的实现
-
切换历史状态
包括 back() 、forword() 、go(n) 三个方法,分别对应浏览器的前进,后退,跳转操作
history.go(-2) //后退两次 + 刷新 history.go(2) //前进两次 history.back() //后退 (不刷新) history.forword() //前进
back() 方法可加载历史列表中的前一个 URL(如果存在)。调用该方法的效果等价于点击后退按钮或调用 history.go(-1)
forward() 方法可加载历史列表中的下一个 URL。调用该方法的效果等价于点击前进按钮或调用 history.go(1)
-
修改历史状态
包括 pushState() 、replaceState() 两个方法,都接收三个参数:stateObj 、title、url。它们可以在不刷新页面的情况下,操作浏览器的历史记录。
- pushState() 新增一个历史记录
- replaceState() 替换当前的历史记录
- popstate() 事件。用于监听历史记录的变化
参数:
- 状态对象(state object):一个JavaScript对象,与用 pushState() 方法创建的新历史记录条目关联。无论何时用户导航到新创建的状态,会触发popstate 事件,并能在事件中使用该对象
- 标题(title):一般浏览器会忽略,传 null 即可
- 地址(URL):需要新增的历史记录地址,浏览器不会去直接加载改地址,但后面也可能会去尝试加载该地址。此外需要注意的是,传入的URL与当前URL应该是同源的。
window.onpopstate = function(event) { alert("location: " + document.location + ", state: " + JSON.stringify(event.state)); }; history.pushState({page: 1}, "title 1", "?page=1"); history.pushState({page: 2}, "title 2", "?page=2"); history.replaceState({page: 3}, "title 3", "?page=3"); history.back(); // 弹出 "location: http://example.com/example.html?page=1, state: {"page":1}" history.back(); // 弹出 "location: http://example.com/example.html, state: null history.go(2); // 弹出 "location: http://example.com/example.html?page=3, state: {"page":3}
-
-
**总结:**无论是客户端路由还是服务端路由,都有一定的应用场景和适用范围。对于需要提供优秀用户体验和动态交互的 Web 应用,客户端路由更为适用;而对于需要提供更好的
SEO优化
和网站性能的网站,服务端路由更为适用。但之后的出现了nuxt.js
和next.js
,很大程度上弥补了传统的客户端路由不利于SEO优化的问题。
一、Vue 中的路由
Vue 属于单页面应用,而单页面应用中“路由”是在客户端执行的。客户端的 JavaScript 可以拦截页面的跳转请求,动态获取新的数据,然后在无需重新加载的情况下更新当前页面。这样通常可以带来更顺滑的用户体验,尤其是在更偏向“应用”的场景下,因为这类场景下用户通常会在很长的一段时间中做出多次交互。
如果你只需要一个简单的页面路由,而不想为此引入一整个路由库,你可以通过动态组件的方式,监听浏览器 hashchange
事件或使用 History API 来更新当前组件。
<script setup>
import { ref, computed } from 'vue'
import A from './A.vue'
import B from './B.vue'
import NotFound from "./NotFound.vue"
const routes = {
'/': A,
'/b': B
}
const currentPath = ref(window.location.hash)
window.addEventListener('hashchange', () => {
currentPath.value = window.location.hash
})
const currentView = computed(() => {
return routes[currentPath.value.slice(1) || '/'] || NotFound
})
</script>
<template>
<div>
<div>
<a href="#/">A</a>
<a href="#/b" style="margin-left: 20px">B</a>
</div>
<br>
<Transition name="fade">
<component :is="currentView" style="margin-top:50px" tag="div"></component>
</Transition>
</div>
</template>
<style scoped lang="less">
.fade-enter-from {
opacity: 0;
}
.fade-enter-active {
transition: opacity 1s 0.5s ease;
}
.fade-leave-to {
opacity: 0;
}
.fade-leave-active {
transition: opacity .5s ease;
}
</style>
如果是做项目的话,路由库就是必须的了。针对 Vue 项目,官方也推出自己的路由库 Vue Router,为 Vue.js 提供富有表现力、可配置的、方便的路由。
二、Vue Router
Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。功能包括:
- 嵌套路由映射
- 动态路由选择
- 模块化、基于组件的路由配置
- 路由参数、查询、通配符
- 展示由 Vue.js 的过渡系统提供的过渡效果
- 细致的导航控制
- 自动激活 CSS 类的链接
- HTML5 history 模式或 hash 模式
- 可定制的滚动行为
- URL 的正确编码
2.1 安装与使用
2.2.1 安装
CDN下载地址:https://unpkg.com/vue-router@4.2.2/dist/vue-router.global.js
项目集成,安装 vue-router
# yarn 方式
yarn add vue-router@4
# npm 方式
npm install vue-router@4
安装成功后的 package.json
2.2.2 组件映射到路由
安装好 Vue Router 之后,我们需要做的就是将组件映射到路由上,让它知道应该在哪里渲染这些组件。
-
编写映射代码(目录及文件如果没有,请创建)
# src/router/index.js // 引入路由相关方法 import { createRouter, createWebHistory } from 'vue-router' // 引入登录组件 Login import Login from '../views/Login.vue' // 定义一些路由:每个路由都需要映射到一个组件。路由过多时,根据情况可以分离到单独的文件中 const routes = [ { path:'/', redirect: '/login' }, // redirect 重定向。这里访问根目录时,跳转到登录页 { path: '/login', name: 'login', component: Login, meta: { title: '登录' } }, // meta 定义路由页的元信息 { path: '/home', name: 'home', component: () => import('../views/Home.vue'),// 这里组件采用惰性加载,打包时从路由层面拆分代码,可生成一个单独的块 Home.[hash].js children: [ // 嵌套路由:定义子路由信息 { name: 'welcome', path: "/welcome", component: () => import ('../views/Welcome.vue'), meta: { title: "欢迎页" } }, { name: 'user', path: "/user", component: () => import ('../views/BaseData/User.vue'), meta: { title: "用户管理" } } ] } ] // 创建路由实例并传递 routes 配置 const router = createRouter({ // 函数参数:路由配置项 // 默认提供 history 模式实现。可通过引入 createWebHashHistory 配置 hash 模式。 history: createWebHistory(import.meta.env.BASE_URL), // history: createWebHashHistory() // 配置路由 routes }) // 导出路由实例 export default router
-
将路由配置文件挂载到 Vue 应用上
# src/main.js import { createApp } from 'vue' import App from './App.vue' import router from './router' // 默认查找当前路径下的 index.js,同 './router/index' const app = createApp(App) app.use(router) app.mount('#app')
-
在组件中使用路由
组件中使用路由,涉及到**“路由跳转”和“路由出口”**两个概念:
-
router-link
自定义组件,用于创建链接。可以使
Vue Router
在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码。 -
router-view
路由出口,用于显示与 url 对应的组件(路由映射组件),可以放到任何位置。
<div id="app"> <h1>Hello App!</h1> <p> <!--使用 router-link 组件进行导航 --> <!--通过传递 `to` 来指定链接 --> <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签--> <router-link to="/">Go to Home</router-link> <router-link to="/about">Go to About</router-link> </p> <!-- 路由出口 --> <!-- 路由匹配到的组件将渲染在这里 --> <router-view></router-view> </div>
-