Vue3 使用路由 Router

Vue3 使用路由 Router

之前几篇博文说了一下 vue 的基本语法和 vue 的传参,今天这篇博文稍微说一下 vue3 里面使用路由。

介绍

众所周知,vue 是用来构建单页面应用的前端框架,大于大多数此类型应用来讲,都推荐使用官方支持的 vue Router,在单页面应用,客户端的 JavaScript 可以连接页面跳转请求,动态获取数据,然后无需重新加载页面的情况下,更新当前页面数据,这样可以带来更加丝滑的用户体验,因为这类场景下的用户通常会在很长的一段时间中做出多次交互,路由是更新在客户端执行的。

vue Router 是 vue 官方路由,他与 vue 核心深度集成,让 vue 构建单页面应用变得更加轻而易举。

  • 嵌套路由映射
  • 动态路由选择 模块化、基于组件的路由配置
  • 路由参数、查询、通配符
  • 展示由 Vue.js 的过渡系统提供的过渡效果
  • 细致的导航控制
  • 自动激活 CSS 类的链接
  • HTML5 history 模式或 hash 模式
  • 可定制的滚动行为
  • URL 的正确编码

router 安装

安装 vue Router 只需要一个简单的命令即可实现安装:

npm install vue-router -S

执行完成之后,只需要静待安装完成即可。

安装完成之后,我们可以看到已经装了 4 版本的 router,如果是 vue2 的项目,则需要安装 3 版本的。

在这里插入图片描述

因为这两个版本他们是不互相兼容的,代码是不一样的,切记。

router 初始化

首先我们在 src 文件夹下创建一个 router 文件夹,在内部创建一个 index.ts 文件。

首先我们需要在这个 index.ts 文件中引入 router:

import { createRouter } from "vue-router";

然后我们初始化一下路由:

import { RouteRecordRaw, createWebHistory, createRouter } from "vue-router";const routes: Array<RouteRecordRaw> = [{path: '/',component: () => import('../components/HelloWorld.vue')  // 首页组件}, {path: '/about',component: () => import('../components/About.vue')  // 关于我们组件}
]const router = createRouter({history: createWebHistory(), // 路由类型routes // short for `routes: routes`
})export default router

然后,我们需要在 main.ts 文件中注册一下子:

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')

这样的话,我们就把路由集成进项目了。

router-view

我们刷新一下页面,发现并没有任何效果,为啥子呢?

其实到这一步,我们已经将路由添加到项目里面去了,但是没有效果,是因为我们还没有写一个容器来引入我们的路由。

接下来我们写一个容器,在 App.vue 项目里面:

<script setup>
</script><template><h1 class="ed-t">我是ed. vue3+router</h1><router-view></router-view>
</template><style scoped>
.ed-t {padding: 10px;margin: 5px;color: hotpink;
}
</style>

像 vue2 项目一样,使用 <router-view></router-view> 插入路由。

这样的话我们刷新页面,可以看到我们能够根据路由变化切换组件更新显示内容:

在这里插入图片描述

注意: <router-view></router-view> 可以卸载任何位置,这个根据实际业务的排版来就可以。

router-link

接下来说一下 router-link ,这个是和 vue2 完全一样的,我们在 App.vue 文件编写 router-link

<template><h1 class="ed-t">我是ed. vue3+router</h1><div><router-link class="ed-rl" to="/">首页</router-link><router-link class="ed-rl" to="/about">关于我们</router-link></div><router-view></router-view>
</template>

这样的话,我们点击 router-link 的时候,可以快速实现组件切换,注意 router-link 必须有一个 to 属性,to 属性的值必须与初始化的 router 里面的 path 对应,意味着去哪个页面。

在这里插入图片描述

我们点击之后看到,下面的组件切换了,同时地址栏的地址也修改掉了。这就是路由最简单的使用方式。

路由模式

接下来说一下路由模式:

vue2vue3
historycreateWebHistory
hashcreateWebHashHistory
abstactcreateMemoryHistory

上面是 vue2 和 vue3 路由类型的对比,其中 vue2 配置类型使用的属性是 mode, vue3 里面更新为 history

createWebHashHistory

上面的案例我们使用了 createWebHistory 模式:

在这里插入图片描述

看到访问的路由就是正常类似于多页面的地址。

但是如果使用了 createWebHashHistory 模式之后:

const router = createRouter({history: createWebHashHistory(),routes // short for `routes: routes`
})

我们看一下:

在这里插入图片描述

地址中间使用了 # 连接。

他是通过 location.hash 去匹配路由的:

在这里插入图片描述

比如我们让他跳转到首页:

在这里插入图片描述

就是这个样子。监听浏览器左右箭头,是使用一个回调函数实现的:

在这里插入图片描述

我们切换浏览器左右箭头就会触发打印:

在这里插入图片描述

createWebHistory

使用 createWebHistory 在地址栏是没有 # 号的。

它是基于 H5history 实现的:

在这里插入图片描述

它监听浏览器左右箭头是通过 popstate 实现的:

在这里插入图片描述

这时候,我们切换浏览器前后箭头,就可以打印出数据:

在这里插入图片描述

好的,就是这个样子。跳转的话是使用 pushState 实现跳转的:

在这里插入图片描述

使用这个切换了之后,你会发现页面地址栏地址已经变了,但是页面并没有修改,这是因为,你这种方式切换并不会监听到,还是需要手动刷新页面。

编程式导航

path 跳转

<template><h1 class="ed-t">我是ed. vue3+router</h1><div><router-link class="ed-rl" to="/">首页</router-link><router-link class="ed-rl" to="/about">关于我们</router-link></div><router-view></router-view>
</template>

上面的案例,我们是使用 router-link 标签通过 path 方式实现的路由跳转,除了使用 path 实现路由跳转之外,我们还可以使用 name 的方式进行路由的跳转。

name 跳转

比如我们给 routes 列表的路由配置添加一个名字:

const routes: Array<RouteRecordRaw> = [{path: '/',name: "index",component: () => import('../components/HelloWorld.vue')}, {path: '/about',name: "about",component: () => import('../components/About.vue')}
]

注意哈,这个 name 不要起重复了嗷!

然后我们修改一下 router-link 标签,由 path 跳转改为 name 跳转:

<div><router-link class="ed-rl" to="/">首页</router-link><router-link class="ed-rl" :to="{name: 'about'}">关于我们</router-link>
</div>

我把 关于我们 改成通过 name 跳转了,可以对比一下子,效果一样一样滴!

在这里插入图片描述

a 标签跳转

我们还可以使用另一种方式,就是直接是 a 标签:

<a href="/">首页</a>
<a href="/about">关于我们</a>

但是这个和 path 跳转还是有区别的:

在这里插入图片描述

这个动图可能看不太清楚,我说一下吧,注意浏览器刷新按钮,我们切换页面的时候,按钮编程叉号一段时间,所以可以说明,使用这个方式实现页面跳转的话,会看到页面整体闪烁了一下子,他是整个页面给你刷新,而不是其中一部分刷新。因此不建议使用这种方式,只是知道就可以了。

编程式跳转

编程式跳转就是不通过便签实现路由的跳转,而是使用 js 代码的方式实现,用于我们点击按钮,手动进行跳转,或者是点击按钮,进行一些逻辑处理后在进行跳转。

那么我们可以在之前写 a 标签的地方改成两个按钮吧:

    <button class="ed-rl">首页</button><button class="ed-rl">关于我们</button>

然后呢,我们给按钮添加个点击事件:

    <button @click="toPage('/')" class="ed-rl">首页</button><button @click="toPage('/about')" class="ed-rl">关于我们</button>

然后我们写一下这个 toPage 事件:

<script setup lang="ts">
// 引入 hook
import { useRouter } from 'vue-router'
// 初始化一下
const router = useRouter()
// 页面跳转方法
const toPage = (url: string) => {// 跳转页面router.push(url)
}
</script>

这样的话,我们就是先了一个简单的编程式路由跳转:

在这里插入图片描述

效果是一样的。

router.push(url) 不仅仅可以传路由,他还可以传递一个对象,比如说用来跳转传参:

router.push({path: url})   // path 跳转

也可以是用 name 进行跳转:

router.push({name: 'about'})   // name 跳转

效果一样就不截图了。

历史纪录

上面的案例哈,我们点击按钮跳转完之后,我们可以通过浏览器的前进、后退按钮实现对应的操作。

在这里插入图片描述

因为通过 vue 路由的操作,会把历史纪录给存储起来。

但是,有时候嘞,我操作完,也就是页面跳转完成之后,我不想把历史纪录给存储起来,就比如说,我登录完成之后,我不想点击浏览器后退按钮在进入登录页面,这是后怎么办呢?

router-link 标签

首先我们看 router-link 标签:

<router-link replace class="ed-rl" to="/">首页</router-link>

如果是 router-link 标签的话,我们可以直接使用一个 replace 设置这个路由不被保存到历史记录。

编程式开发

如果是使用编程式开发的话也很简单,就是把 push 改为 replace 即可:

router.replace(url)   // path 跳转

效果是一样的,也是没有历史纪录,效果一样就不截图了。

历史纪录逻辑操作

关于历史纪录的逻辑处理也很简单。

    <router-link class="ed-rl" to="/">首页</router-link><router-link class="ed-rl" :to="{ name: 'about' }">关于我们</router-link><button @click="prev()" class="ed-rl">向前</button><button @click="next()" class="ed-rl">向后</button>

我们不用浏览器,点击自己的自定义按钮实现向前、向后切换功能:

const prev = () => {// router.go(-1)  // 参数是后退几个历史,比如1个,2个。router.back()  // 后退
}const next = () => {router.go(1)  // 参数是前进几个历史,比如1个,2个。
}

看一下效果:

在这里插入图片描述

都是没有问题的!

路由传参

路由传参是项目里面肯定会用的功能,所以说这个得好好整一下,下面这一节,主要说一下关于路由传参的部分。

案例准备

先准备一个案例,我随便写的,咱就不要细说了关于这个案例,首先准备一个电影列表的 json 文件:

{"data": [{"name": "流浪地皮","price": 29.9,"msg": "五星"},{"name": "我就是潘金莲","price": 19.9,"msg": "四星"},{"name": "水壶传","price": 9.9,"msg": "三星"}]
}

然后编写一个组件展示一下:

<template><p class="ed-title">电影列表</p><p class="ed-item" @click="toPage(item, index)" v-for="item, index in data" :key="index">No.{{ index + 1 }} -{{ item.name }}</p>
</template><script setup lang="ts">
import { data } from '../json/data.json'type Film = {name: string,msg: string,price: number
}
// 点击电影项事件
const toPage = (item: Film , index: number) => {// todo: 跳转到新的页面,展示详细数据
}
</script>

样式我就不粘贴了,我们现看一下效果:

在这里插入图片描述

我们点击电影名称,跳转到 about 页面,展示详细信息,这时候,我们在点击事件里面需要实现两个功能,第一个是跳转,第二个是传参:

首先我们需要引入 router :

import { useRouter} from 'vue-router'

因为引入进来的是 hook,我们需要调用一下:

const router = useRouter()

好的,我们实现页面跳转:

const toPage = (item: film, index: number) => {router.push({path: '/about',})
}

好的,这样就实现了页面的跳转:

在这里插入图片描述

query 传参

然后,是传递参数,和 vue2 其实是一样一样的:

const toPage = (item: Film, index: number) => {router.push({path: '/about',query: item,})
}

注意,query 只能设置对象。上面我们是使用的 query 进行参数传递,

在这里插入图片描述

我们看到我们再次点击的时候,就会在地址栏显示我们传递的参数。

然后我们就可以在详情页面去取一下数据:

<template><p class="ed-title">电影详情</p><button @click="router.back()">返回</button><p class="ed-t">名称:{{ route.query.name }}</p><p class="ed-t">价格:¥{{ route.query.price }}</p><p class="ed-t">备注:{{ route.query.msg }}</p>
</template>
<script setup lang="ts">
import { useRoute} from 'vue-router';
import { useRouter } from 'vue-router';
const route = useRoute()
const router = useRouter()</script>
<style scoped>
.ed-title {font-size: 20px;font-weight: 550;color: rgb(6, 221, 236);padding: 10px;margin: 5px;
}.ed-t {font-size: 16px;padding: 10px;margin: 5px;
}button {margin: 15px;
}
</style>

然后,我们看一下效果:

在这里插入图片描述

效果实现了,可以传参并且展示出数据。

params 传参

注意 params 不能使用 path 进行参数传递,只能使用 name 进行传参。

所以修改上面页面跳转的代码:

const toPage = (item: Film, index: number) => {// router.push({//   path: '/about',//   query: item,// })router.push({name: 'about',params: item,})
}

上面代码就已经修改成 params 的方式进行参数传递了。params传参有一个特点,就是他传递的参数不会显示在地址栏:

在这里插入图片描述

看,点击之后,通过 params 传递参数的时候,地址栏已经不会显示传递的参数是什么了。

然后我们需要修改一下接受参数的地方,同样也是改为 params 接收参数:

  <p class="ed-t">名称:{{ route.params.name }}</p><p class="ed-t">价格:¥{{ route.params.price }}</p><p class="ed-t">备注:{{ route.params.msg }}</p>

这样就可以实现数据显示了。

但是,注意一个问题:

在这里插入图片描述
啥意思哈,就是从 4.1.4 版本之后,修改了route.params.name 之后也显示不出来,会出问题:

在这里插入图片描述

因为新版本把这个功能给砍掉了,怎么办呢?看下面官方提供的平替解决方案:

在这里插入图片描述

嵌套路由

官网

直接一个案例,和 vue2 完全一样,就不重复了:

import { RouteRecordRaw, createWebHashHistory, createWebHistory, createRouter } from "vue-router";const routes: Array<RouteRecordRaw> = [{path: '/',name: "footer",component: () => import('../components/Footer.vue'),children: [{path: '',name: "index",component: () => import('../components/HelloWorld.vue')}, {path: '/about',name: "about",component: () => import('../components/About.vue')}]},]const router = createRouter({history: createWebHistory(),routes // short for `routes: routes`
})export default router

命名视图

有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar (侧导航)main (主内容) 两个视图,这个时候命名视图就派上用场了。

你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default

<router-view name="LeftSidebar"></router-view>
<router-view></router-view>
<router-view name="RightSidebar"></router-vie

一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置 (带上 s):

const router = createRouter({history: createWebHashHistory(),routes: [{path: '/',components: {default: Home,// LeftSidebar: LeftSidebar 的缩写LeftSidebar,// 它们与 `<router-view>` 上的 `name` 属性匹配RightSidebar,},},],
})

不常用,了解即可。

路由重定向 redirect

重定向比较简单一笔带过:

const routes: Array<RouteRecordRaw> = [{path: '/',name: "footer",redirect: '/about',children: [{path: '/index',name: "index",component: () => import('../components/HelloWorld.vue')}, {path: '/about',name: "about",component: () => import('../components/About.vue')}]},
]

主要是使用的 redirect: '/about', 这一段代码实现重定向功能。

除了直接设置,还可以使用对象的方式实现重定向:

redirect: {path: '/about'
},

可以设置 path ,当然设置 name 也是一样的:

redirect: {name: 'about'
},

除了上面两种方式,还可以设置一个回调:

    redirect: to => {console.log(to)return '/about'},

回调的话,我们可以接受一个参数 to,我们打印了 to 的信息,同时他需要返回一个路径:

在这里插入图片描述

打印出了他父路由的信息,ok,没问题!除了返回一个路径之外,同样也是可以返回一个对象实现传参:

    redirect: to => {console.log(to)return {path: '/about',query: {name: "我是ed."}}},

也是没有任何问题的,效果都一样。

在这里插入图片描述

OKOK,没有问题,参数也传递过去了!

路由别名 alias

alias 就是给我们的路由起多个名字,别名可以随便起,甚至可以取多个。

const routes: Array<RouteRecordRaw> = [{path: '/',name: "footer",alias: ['/footer', '/footer1', '/footer2'],component: () => import('../components/Footer.vue')},
]

我们给这个路由设置了多个别名,我们访问哪一个别名之后呢,都可以访问到这个路由:

在这里插入图片描述

上面我们测试了一下别名,没有任何问题。

导航守卫

官网

router.beforeEach((to, from, next) => {// to 要前往的页面;from 从哪个页面来;next() 设置到哪个页面if(localStorage.getItem('token')) {next(to.path)}else {next("/login")}
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/595216.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

宋仕强论道之华强北后山寨手机时代(三十六)

今天继续讲华强北山寨手机&#xff0c;跟手机配套周边产品。华强北&#xff0c;作为中国电子产品的集散地和创新中心&#xff0c;一直以来都是电子产品和数码产品的聚集地。在早期&#xff0c;赛格市场以其走私、翻新的电脑和电脑周边产品而闻名。赛格大厦以前5楼以上都是做电脑…

乒乓球廉价底板评测之五F勒布伦打法讨论

菲利克斯勒布伦的直拍打法让直板又焕发了青春&#xff0c;那他的打法又有什么特点呢&#xff1f;和中国众多直板选手的区别在哪呢&#xff1f;这篇微博我们简单分一下。 首先说下他的器材&#xff0c;纤维板中置碳&#xff0c;淘宝上的版本是碳在大芯两侧&#xff0c;是七层板&…

Unity中URP下统一不同平台下的z值

文章目录 前言一、ComputeFogFactor 来计算雾效混合因子二、UNITY_Z_0_FAR_FROM_CLIPSPACE 来统一计算不同平台下的Z值1、DirectX平台2、GL平台下&#xff08;在Unity.2022.LTS下&#xff0c;该功能没有完善)3、Opengl下 前言 在之前的文章中&#xff0c;我们实现了URP下的雾效…

电动汽车BMS PCB制板的技术分析与可制造性设计

随着电动汽车行业的迅猛发展&#xff0c;各大厂商纷纷投入巨资进行技术研发和创新。电动汽车的核心之一在于其电池管理系统&#xff08;Battery Management System, BMS&#xff09;&#xff0c;而BMS的心脏则是其印刷电路板&#xff08;PCB&#xff09;。通过这篇文章探讨电动…

Graphics Control

Graphics Control提供了一个易于使用的图形设置管理解决方案,帮助您加快开发。它附带了一个常用设置库,如分辨率、垂直同步、全屏模式、光晕、颗粒、环境光遮挡等。我们的可自定义设置面板UI预制件为您提供了一个可用的UI面板,支持完整的游戏手柄和键盘输入。图形控制还附带…

Spark---RDD介绍

文章目录 1.Spark核心编程2.RDD介绍2.1.RDD基本原理2.2 RDD特点1.弹性2.分布式 &#xff1a;数据存储在大数据集群的不同节点上3.数据集 &#xff1a;RDD封装了计算逻辑&#xff0c;并不保存数据4.数据抽象 &#xff1a;RDD是一个抽象类&#xff0c;具体实现由子类来实现5. 不可…

CCF模拟题 202312-1 仓库规划

问题描述 试题编号&#xff1a; 202312-1 试题名称&#xff1a; 仓库规划 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 512.0MB 问题描述&#xff1a; 输入格式 输出格式 样例输入 4 2 0 0 -1 -1 1 2 0 -1样例输出 3 1 0 3样例解释 Java实现代码&#xff1a; import …

macbook录屏快捷键大全,教你快速录制视频

“有人知道macbook电脑有录屏快捷键吗&#xff0c;现在录屏的速度太慢了&#xff0c;每次打开都要浪费不少时间&#xff0c;要是有录屏快捷键&#xff0c;应该会快很多&#xff0c;有哪位大佬知道吗&#xff1f;教教我&#xff01;” 无论是在工作还是生活中&#xff0c;电脑已…

生活中危险的气体:一氧化碳与二氧化碳中毒的症状及安全预防措施

一氧化碳和血红蛋白亲和力超过氧气&#xff0c;会占用血红蛋白&#xff0c;导致缺氧。 二氧化碳会和血浆结合&#xff0c;导致血液pH值不正常&#xff0c;抑制呼吸&#xff0c;导致窒息。 通俗点说&#xff1a;一氧化碳是中毒&#xff0c;二氧化碳则是窒息。 一氧化碳中毒 …

通过Vue自定义指令实现前端埋点

在营销活动中&#xff0c;通过埋点可以获取用户的喜好及交互习惯&#xff0c;从而优化流程&#xff0c;进一步提升用户体验&#xff0c;提高转化率。 在之前的埋点方案实现中&#xff0c;都是在具体的按钮或者图片被点击或者被曝光时主动通过事件去上报埋点。这种方法在项目中…

K8Spod组件

一个pod能包含几个容器 一个pause容器(基础容器/父容器/根容器&#xff09; 一个或者多个应用容器(业务容器) 通常一个Pod最好只包含一个应用容器&#xff0c;一个应用容器最好也只运行一个业务进程。 同一个Pod里的容器都是运行在同一个node节点上的&#xff0c;并且共享 net、…

javaweb学习笔记

JSP 动态网页&#xff0c;指的是随时间、地点、用户操作改变的网页 架构 CS架构 client-server 缺点&#xff1a;每一台客户端都需要安装客户端软件&#xff0c;如果升级全要升级&#xff0c;如果坏了就得维护 优点&#xff1a;响应快&#xff0c;界面美观 BS架构 browser-…

以STM32为例,实现按键的短按和长按

以STM32为例&#xff0c;实现按键的短按和长按 目录 以STM32为例&#xff0c;实现按键的短按和长按1 实现原理2 实现代码3 测试结束语 1 实现原理 简单来说就是通过设置一个定时器来定时扫描几个按键的状态&#xff0c;并分别记录按键按下的持续时间&#xff0c;通过时间的长短…

后端杂七杂八系列篇一

后端杂七杂八系列篇一 ① MySQL选择合适的数据类型① Char与Varchar② Text与Blob ② EqualsAndHashCode(callSuper true)的作用③ mybatis-plus 相关① 主键生成策略② 使用Model实现CRUD③ Wrapper的用法① Wrapper的继承关系② 项目中最常用的warpper [LambdaQueryWrapper]…

[NISACTF 2022]level-up

[NISACTF 2022]level-up wp level 1 robots.txt 通过目录爆破&#xff0c;发现存在 robots.txt 文件&#xff08;或者说查看源码&#xff0c;源码中有 disallow 提示&#xff0c;说明存在 robots.txt 文件&#xff09;&#xff1a; dirsearch -u "http://node5.anna.n…

Docker入门教程(详解)

Docker容器化 一 入门 1. 引言 &#xff08;1&#xff09;单机部署 场景&#xff1a; 将多个应用部署一台服务器上。 问题 每个应用软件&#xff0c;都会消耗物理资源&#xff0c;共用计算机资源&#xff0c;彼此之间会形成竞争关系。 &#xff08;2&#xff09;多机部署 …

python django 生鲜商城管理系统

python django 生鲜商城管理系统,包含用户端和管理端 功能&#xff1a; 用户端&#xff1a;商城主页展示&#xff0c;登录&#xff0c;注册&#xff0c;用户中心&#xff0c;购物车&#xff0c;我的订单&#xff0c;购物车结算 管理端&#xff1a;登录&#xff0c;商品&…

SpringBoot: 通过MyBatis访问ClickHouse

一、ClickHouse中建表&#xff0c;添加数据 二、SpringBoot项目添加mybatis、clickhouse、druid相关依赖 <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.6</version></dependency>…

opencv入门到精通——Canny边缘检测

目录 理论 OpenCV中的Canny Edge检测 附加资源 在本章中&#xff0c;我们将学习 Canny边缘检测的概念 OpenCV函数: cv.Canny() 理论 Canny Edge Detection是一种流行的边缘检测算法。它由John F. Canny发明 1.这是一个多阶段算法&#xff0c;我们将经历每个阶段。 2.降…

原生JS调用OpenAI GPT接口并实现ChatGPT逐字输出效果

效果&#xff1a; 猜你感兴趣&#xff1a;springbootvue实现ChatGPT逐字输出打字效果 附源码&#xff0c;也是小弟原创&#xff0c;感谢支持&#xff01; 没废话&#xff0c;上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><me…