认识前端路由
路由的概念在软件工程中出现,最早是在后端路由中实现的,原因是web的发展主要经历了这样一些阶段:
- 后端路由阶段。
- 前后端分离阶段。
- 单页面富应用(SPA)。 下面我们就来介绍一下这些阶段
后端路由阶段
早期的网站开发整个HTML页面是由服务器来渲染的。服务器直接生产渲染好对应的HTML页面, 返回给客户端进行展示。
但是, 一个网站, 这么多页面服务器如何处理呢?
每个页面都有对应的一个网址(url),用户在浏览器中输入对应的url,服务器将返回对应的完整页面。这种情况下渲染好的页面, 不需要单独加载任何的js和css, 可以直接交给浏览器展示, 这样也有利于SEO的优化。
后端路由的缺点:
- 一种情况是整个页面的模块由后端人员来编写和维护的。
- 另一种情况是前端开发人员如果要开发页面, 需要通过PHP和Java等语言来编写页面代码。
- 而且通常情况下HTML代码和数据以及对应的逻辑会混在一起, 编写和维护都是非常糟糕的事情。
前后端分离阶段
这个阶段就是后端人员写数据接口,前端人员写页面。拿到数据接口渲染对应的页面。
前端渲染的理解:
每次请求涉及到的静态资源都会从静态资源服务器获取,这些资源包括HTML+CSS+JS,然后在前端对这些请求回来的资源进行渲染。需要注意的是,客户端的每一次请求,都会从静态资源服务器请求文件。同时可以看到,和之前的后端路由不同,这时后端只是负责提供API了。
前后端分离阶段:
随着Ajax的出现, 有了前后端分离的开发模式。后端只提供API来返回数据,前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面中。这样做最大的优点就是前后端责任的清晰,后端专注于数据上,前端专注于交互和可视化上。并且当移动端(iOS/Android)出现后,后端不需要进行任何处理,依然使用之前的一套API即可。目前比较少的网站采用这种模式开发(jQuery开发模式)。
SPA(single page application)单页面应用阶段
一个应用有多个页面,需要切换展示时,我们前端自己维护一个路由映射表。我们通过改变hash或者通过HTML5提供的history模式。这两种模式修改url。不会向服务器请求资源。就可以映射到不同的页面了。
改变hash,实现前端路由
通过hashchange
监听hash的变化(location.href来改变),来改变占位内容。
了解更多location的内容,请访问mdn: developer.mozilla.org/zh-CN/docs/…
<div id="app"> <a href="#/home">home</a> <a href="#/about">about</a> <div id="content">Default</div> </div> <script> const contentEl = document.getElementById("content") window.addEventListener("hashchange", () => { switch(location.hash) { case "#/home": contentEl.innerHTML = "Home"; break; case "#/about": contentEl.innerHTML = "About"; break; default: contentEl.innerHTML = "Default"; } }) </script>
hash的优势就是兼容性更好,在老版IE中都可以运行,但是缺陷是有一个#,显得不像一个真实的路径。
history模式实现前端路由
history接口是HTML5新增的, 它有六种模式改变URL而不刷新页面:
- replaceState:替换原来的路径。
- pushState:使用新的路径。
- popState:路径的回退。
- go:向前或向后改变路径。
- forward:向前改变路径。
- back:向后改变路径。 下面就来简单实现以下
<a href="/zh">zh</a> <a href="/llm">llm</a> <div id="container">我是默认页面</div> <script> const container = document.getElementById("container"); const aEls = document.getElementsByTagName("a"); const changeContent = () => { switch (location.pathname) { case "/zh": container.innerHTML = "我是zh页面" break; case "/llm": container.innerHTML = "我是llm页面" break; default: container.innerHTML = "我是其他页面" break; } } for (let a of aEls) { a.addEventListener("click", (e) => { e.preventDefault(); const href = a.getAttribute("href"); // 将路由保存到对应栈中 history.pushState({}, "", href) changeContent() }) } // 监听出栈状态 addEventListener("popstate", changeContent)
vue-router
通过上面的介绍,我们现在来介绍一下vue-router吧。
Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得非常容易。目前Vue路由最新的版本是4.x版本。
vue-router是基于路由和组件的。路由用于设定访问路径, 将路径和组件映射起来。在vue-router的单页面应用中, 页面的路径的改变就是组件的切换。
安装Vue Router:npm install vue-router@4
。
路由的基本使用步骤
- 创建路由组件的组件。
- 配置路由映射: 组件和路径映射关系的routes数组。
- 通过
createRouter
创建路由对象,并且传入routes和history模式。
- 使用路由通过
<router-link>
(切换到指定的组件) 和<router-view>
(切换路径,他就自动匹配到对应的组件)。
- 并将router对象在app.use中注册。 下面我们来介绍一下routes(路由组件映射表)
路由的映射,只会匹配到第一个准确的路径,当匹配到多个路径时,只会展示第一个匹配到的路径的组件
path
: 配置路由路径
redirect
: 重定向。参数重定向的路由路径。
{ path: "/", redirect: "/home" }
children
: 配置子路由。一个数组。每个数组元素都是一个route。
alias
: 配置路由别名。string | string[]
。可以简化牵头路由的路径。
{ path: "/home", name: "home", component: () => import("../pages/Home.vue"), children: [ { path: "message", component: () => import("../pages/HomeMessage.vue"), // 配置别名 /home/message = /zh = '/home/message' alias: ['/zh', 'zh'] } } }
name
: 该路由匹配的名称。(唯一)
beforeEnter
: 当匹配到该路由时,调用的钩子。在进入特定于此记录的守卫之前。注意如果记录有重定向
属性,则beforeEnter
无效。
props
: 传入一个Boolean, 用来给由router-view
渲染的组件传递动态路由值。以至于不需要使用$route.params.参数
。
const User = { template: '<div>User {{ $route.params.id }}</div>' } const routes = [{ path: '/user/:id', component: User }]
替换成
const User = { props: ['id'], template: '<div>User {{ id }}</div>' } const routes = [{ path: '/user/:id', component: User, props: true }]
meta
: 在记录上附加自定义数据。
const routes = [ { path: '/user/:id', component: User, meta: {name: 'zh'} } ]
一个简单的路由映射
//route/index.js import { createRouter, createWebHistory } from "vue-router" import About from '../components/About.vue' const Home = () => import("../components/Home.vue") const routes = [ { path: "/", redirect: "/home" }, { path: "/home", name: "home", component: Home }, { path: "/about", name: "about", component: About } ] export default router = createRouter({ history: createWebHistory(), routes: routes })