web发展历程
1.后端实现路由
在这个阶段,前端基本上只写界面,也就是html,css,js那些东西,然后在界面中挖槽用来接后端数据,包括路由也由后端负责,在这个阶段中,web开发非常依赖后端,常见的后端框架有python中的flask。
2.前后端分离阶段
在这个阶段,前端和后端的耦合性很低,它不像flask一样,后端传过来的数据需要在前端对应好,这样前后端需要时时刻刻对接,非常不方便(写flask应该只能前后端全干?)。在前后端分离阶段,前端一般用react,angular,vue等前端框架部署页面,这时我们只需要向服务器发送请求获取数据,然后把数据转化为前端能使用的数据,比如vue中请求的json文件可以进行解析然后再模板中渲染,这时前后端就不需要一一对应,只需要后端发送的数据符合格式,那么前端就能正确渲染。所以,前端可以更关注前端的逻辑,后端可以更关注后端的业务逻辑和数据库申请等后端操作。这就实现了前后端分离。
3.SPA(single page application)
SPA与前后端分离的区别主要在于SPA可以在不重新刷新界面的同时切换url。
使用vue-router实现SPA
环境配置
进入项目后,在终端中(切到项目目录下,因为vue-router这种第三方插件需要打包)
键入 pnpm install vue-router -D
在实现SPA前,我们先要了解一下SPA是如何实现的:
SPA原理
要实现SPA,本质上就是实现在不刷新界面的同时切换掉url
url的hash
URL的hash也就是锚点(#), 本质上是改变window.location的href属性,我们可以通过直接赋值location.hash来改变href, 但是页面不发生刷新。我们也可以使用histroy接口实现。
vue-router使用
接下来我会结合一个案例来讲讲vue-router是如何使用的。
src/Views 组件文件夹
Home.vue
<template> <div class="sub-nav"> <ul class="nav-bar"> <li @click="recommendLiClick">推荐</li> <li @click="rankingLiClick">排行榜</li> <li>歌单</li> <li>主播电台</li> </ul> </div> <router-view></router-view> </template> <script setup> import { useRouter } from 'vue-router'; const router = useRouter() function recommendLiClick(){ router.push({ path:"/home/recommend", query:{} }) } function rankingLiClick(){ router.push({ path:"/home/ranking", query:{} }) } </script> <style lang="less" scoped> .sub-nav { .nav-bar{ list-style-type: none; display: flex; margin: 0; padding: 0; text-align: center; height: 44px; width: 100%; line-height: 44px; li{ flex: 1; background-color: rgb(173, 29, 29); color: whitesmoke; font-size: 12px; font-weight: bold; cursor: pointer; } } } </style>
About.vue
<template> <div class="about"> <div class="container"> <div class="header"><span>关于我们</span></div> <div class="content"> <div class="info"> <p> Name: {{ $route.query.name }}</p> <p> Age: {{ $route.query.age }}</p> <p> Sex: {{ $route.query.sex }}</p> </div> </div> </div> </div> <button @click="backBtnClick">back</button> </template> <script setup> import { useRouter } from 'vue-router'; const router = useRouter() function backBtnClick(){ router.back() } </script> <style lang="less" scoped> .about{ background-color: #f5f5f5; height: 900px; .container{ background-color:#ffff; height: 900px; width: 55%; margin: 0 auto; padding: 55px 40px; letter-spacing: 2px; .header{ font-size: 22px; font-weight: bold; width: 100%; border-bottom: 2px solid rgb(173, 29, 29); } .content{ width:100%; height: 200px; border: 2px solid black; margin-top: 20px; .info{ padding: 20px 30px; } } } } </style>
Ranking.vue
<template> <div class="ranking"> <div class="aside"> <div class="header">云音乐特色榜</div> <div class="items"> <ul class="item"> <li>飙升榜</li> <li>新歌榜</li> <li>原创榜</li> <li>热歌榜</li> </ul> </div> <div class="header">全球媒体榜</div> <div class="items"> <ul class="item"> <li>云音乐说唱榜</li> <li>云音乐古典榜</li> <li>云音乐电音榜</li> <li>云音乐ACG榜</li> </ul> </div> </div> <div class="content"> 内容区域 </div> </div> </template> <script setup> </script> <style lang="less" scoped> .ranking{ display: flex; width: 60%; height: 1000px; margin: 0 auto; background-color:whitesmoke; .aside{ margin-left: 30px; flex: 1; .header{ padding-top: 20px; font-size: 16px; font-weight: bold; } .items{ .item{ list-style-type: none; li{ margin-top: 20px; } } } } .content{ border: 2px solid black; margin-top: 1px; flex: 3; color: red; font-size: 30px; line-height: 1000px; text-align: center; } } </style>
Recommend.vue
<template> <div class="home-recommend"> <div class="rollplay"> 轮播图区域 </div> <div class="recommended-list"> <div class="title">⭕热门推荐</div> <div class="category"> <ul class="category-list"> <li><span>华语</span></li> <li><span>流行</span></li> <li><span>摇滚</span></li> <li><span>民谣</span></li> </ul> </div> </div> <div class="content"> 内容区域 </div> </div> </template> <script setup> </script> <style lang="less" scoped> .home-recommend{ .rollplay{ margin: 1px 0 0 0; width: 100%; height: 255px; font-size: 26px; text-align: center; line-height: 255px; border: 2px solid black; } .recommended-list{ height: 100px; line-height: 100px; display: flex; border-bottom: 2px solid rgb(173,29, 29) ; .title{ padding-left: 33px; font-size: 20px; font-weight: 500px; flex: 1; } .category{ flex: 5; .category-list{ margin:0 5px; list-style-type: none; display: flex; color: #727272; li{ span{ border-right: 1px solid #727272; padding: 0 10px; font-size: 12px; } } } } } .content{ margin: 1px 0 0 0; width: 100%; height: 400px; font-size: 26px; text-align: center; line-height: 400px; border: 2px solid black; } } </style>
App.vue
<template> <div class="app"> <div class="main-nav"> <!-- <router-link to="/home" replace>首页</router-link> replace 替换路径,不会记录历史路径--> <!-- <router-link to="/home" active-class="link-class">首页</router-link> active-class指定选中的元素附带的className --> <ul class="nav-bar"> <li @click="homeLiClick">首页</li> <li @click="aboutLiClick">关于</li> <li @click="userLiClick">用户</li> </ul> </div> <router-view></router-view> </div> <!-- 编程式跳转页面 --> </template> <script setup> import { useRouter } from 'vue-router'; const router = useRouter() function homeLiClick(){ router.push({ path:"/home", query:{} }) } function aboutLiClick(){ router.push({ path:"/about", query:{ name:"Lisman", age:"18", sex:"male" } }) } function userLiClick(){ router.push({ path:"/user", query:{} }) } </script> <style lang="less" scoped> .main-nav { .nav-bar{ list-style-type: none; display: flex; margin: 0; padding: 0; text-align: center; height: 44px; width: 100%; line-height: 44px; li{ flex: 1; background-color: #454545; color: whitesmoke; font-size: 14px; font-weight: bold; cursor: pointer; } } } </style>
src/router/index.js 路由配置文件
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router' //导入组件 // import Home from '@/Views/Home.vue' // import About from '@/Views/About.vue' //以下为路由懒加载,import导入可以做到分包处理,webpackChunkName可以给包指定名字 // const Home = import(/* webpackChunkName: 'Home' */"../Views/Home.vue") // const About = import(/* webpackChunkName: 'About' */"../Views/About.vue") //创建路由:映射关系 const router = createRouter({ //选择模式(Hash) // history: createWebHashHistory(), history: createWebHashHistory(), routes: [ { path: "/home", component: () => import("../Views/Home.vue"), //嵌套路由 children: [ { path: "recommend",//相当于/home/recommend component: () => import("../Views/Recommend.vue") }, { path: "ranking", component: () => import("../Views/Ranking.vue") }, { path: "", redirect: "/home/recommend" } ] }, { path: "/about", component: () => import("../Views/About.vue") }, { path: "/user/:id", component: () => import("../Views/User.vue") },//动态路由 { path: "/", redirect: "/home" }, ] }) //导出路由 export default router
main.js
import { createApp } from 'vue' import App from './App.vue' import router from './router/index.js' const app = createApp(App) app.use(router) app.mount("#app")
实现效果如下:
2024-09-02 23-53-59
主要流程
要建立组件与url的关系,局部分为三步:创建组件,创建路由,构建路由和组件的关系
创建组件自不必多说,创建路由和创建组件关系才是重点。
index.js
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router' //导入组件 // import Home from '@/Views/Home.vue' // import About from '@/Views/About.vue' //以下为路由懒加载,import导入可以做到分包处理,webpackChunkName可以给包指定名字 // const Home = import(/* webpackChunkName: 'Home' */"../Views/Home.vue") // const About = import(/* webpackChunkName: 'About' */"../Views/About.vue") //创建路由:映射关系 const router = createRouter({ //选择模式(Hash) // history: createWebHashHistory(), history: createWebHashHistory(), routes: [ { path: "/home", component: () => import("../Views/Home.vue"), //嵌套路由 children: [ { path: "recommend",//相当于/home/recommend component: () => import("../Views/Recommend.vue") }, { path: "ranking", component: () => import("../Views/Ranking.vue") }, { path: "", redirect: "/home/recommend" } ] }, { path: "/about", component: () => import("../Views/About.vue") }, { path: "/user/:id", component: () => import("../Views/User.vue") },//动态路由 { path: "/", redirect: "/home" }, ] }) //导出路由 export default router
通常我们会在src下创建一个router文件夹,然后在router文件夹下写路由的逻辑。
首先,我们导入vue-router中的createRouter()方法创建路由对象,我们传入一个对象作为参数,在这个对象中写入histroy,routes两个成员(当然还有更多)。
History: 可以选择url的模式,如果为createWebHashHistroy的话,那么url路径会以/*/分隔,如果为createWebHistroy的话就不会有/*/分隔,这样更容易阅读。
比如有路径
localhost:8080/*/home/recommend(Hash模式)
localhost:8080/home/recommend (History模式)
routes:这个就是用来配置url与组件关系的东西,比较简单,就是指定好path,然后再为其绑定一个组件,当在浏览器中访问该路径时会在合适的地方渲染这个组件。redirect为重定向,当浏览器访问目标路径时自动将其导向新的路径。关于children的话,这个涉及路由嵌套,可以这么认为:
这个在之后了解完route-view应该更好理解。可以这样理解:在根组件app.vue中,我们使用route-view来占位,当进入目标路径时,路由会根据这个url找到对应的组件并将其渲染在route-view中,而嵌套路由其实就是重复这个过程。在渲染的组件中在写入route-view,在进入下一级路径时再次找到对应的组件将其渲染在组件中的route-view中。
最后,返回路由。到此为止,路由创建和url和组件间的绑定就告一段落了。
接下来,我们要开始配置路由(使用返回的router)
main.js
import { createApp } from 'vue' import App from './App.vue' import router from './router/index.js' const app = createApp(App) app.use(router) app.mount("#app")
使用app.use(router)使用该路由就可以了。
本文没有使用router-link,而是使用script来实现跳转。