位置: IT常识 - 正文
目录
一、什么是路由?
二、在vue中如何使用路由?
三、vue-router的搭建
3.1 什么是hash和history模式?
四、路由的基本使用
2.1 router-view
2.1.1 命名视图
2.2 router-link
三、路由的参数
3.1 query参数
3.2 params参数
四、编程式导航
4.1 $router:路由跳转
4.1.1 push方法
4.1.2 replace方法
4.1.3 back方法
4.1.4 forward方法
4.1.5 go方法
4.2 $route:获取路由参数
4.2.1 路由的props配置
五、路由重定向
六、路由别名:alias
七、命名路由
八、路由的懒加载
九、路由元信息
十、路由守卫
10.1 全局守卫
10.1.1 全局前置守卫
10.1.2 全局后置守卫
10.2 路由独享守卫
10.3 组件内守卫
我们想要构建一个完整的Vue项目,路由是必不可少的。
一、什么是路由?路由分为前端路由和后端路由。
前端路由:
浏览器的路径就代表着一个页面(在vue中是一个SFC),这个路径也就是我们所说的路由。当路径改变时,会渲染对应的页面内容,这是前端路由。
后端路由:
后端路由就是:当我们请求/提交数据时,所触发的那个方法就是后端路由。当服务器收到一个请求时,后端会根据路径找到匹配的方法来处理请求,进而返回对应的数据。
总结:前端路由用于切换页面,后端路由用于处理请求、返回数据。
二、在vue中如何使用路由?我们使用vue-router这个官方库来对项目的路由进行搭建,具体下载方法点击连接查看官网。
注意:我接下来最开始会使用vue2的写法来演示,因为vue3的改变并不是很多,但框架是vue3的框架。
三、vue-router的搭建在下载模块之后(例如使用npm),我们在项目的src目录下创建一个router文件夹,在这个文件夹中创建一个index.js文件:
接下来就是在这个文件中开始搭建路由:
引入创建路由和创建历史记录的方法:import { createRouter,createWebHashHistory } from "vue-router"注意:我这里使用的是createWebHashHistory,还有一个叫做createWebHistory,这是两种不同的路由模式:hash模式和history模式。
3.1 什么是hash和history模式?我们在浏览器的地址栏应该是见过两种形式的路径:一种是带有#的,一种是不带有#的,带有#的就是hash模式,另一个就是history模式了。
hash模式中,#及其后面的内容就是hash值。hash值不会带给服务器。
hash模式:
地址中永远带有#,不美观;若地址被第三方手机app分享,若是app校验严格的话,则地址会被标记为不合法;兼容性较好;history模式:
地址干净,美观;兼容性和hash模式相比较差;应用部署上线时,需要后端人员支持,以解决刷新页面的时候服务器返回404的问题;2. 创建一个变量,该变量是一个数组,用于保存路由:
import { createRouter,createWebHashHistory } from "vue-router"import Home from "../views/home.vue"const routers=[{path:"/home",component:Home}]这个数组中,保存的是一个对象形式的数据,在这个对象中有着两个基本配置:path和component。
path:用于路由跳转以及浏览器地址栏显示时;component:表示要跳转到path所写的路径,要使用哪个SFC(或者说是返回哪个SFC的内容),并且要在文件开始,将这个组件进行导入;推荐整理分享Vue-Router的使用(vue–router),希望有所帮助,仅作参考,欢迎阅读内容。
文章相关热门搜索词:vue- router,vue router routes,router.vue,vue $route $router,vue $route $router,vue-router作用,vue-router教程,vue-router教程,内容如对您有帮助,希望把文章链接给更多的朋友!
3. 创建一个变量,该变量就代表着后续项目所需要使用的路由对象:
import { createRouter,createWebHashHistory } from "vue-router"import Home from "../views/home.vue"const routers=[{path:"/home",component:Home}]// 要被导出的变量const router=createRouter({ routes:routers, history:createWebHashHistory()})在这里使用了createRouter,并传入一个对象参数,表示真正的创建了一个路由对象:
routes:表示要使用的路由数组;history:表示要使用的路由模式;4. 将router导出,并在main.js文件中进行全局使用:
/ router下的index.js文件中import { createRouter,createWebHashHistory } from "vue-router"import Home from "../views/home.vue"const routers=[{path:"/home",component:Home}]// 要被导出的变量const router=createRouter({ routes:routers, history:createWebHashHistory()})export default router// main.js中import { createApp } from 'vue'import './style.css'import App from './App.vue'// 引入路由文件import router from "./router/index.js"const app=createApp(App)// 使用use使用路由app.use(router).mount('#app')按照以上步骤,就可以完成项目的路由环境搭建了。
四、路由的基本使用在搭建完成后,我们该如何通过使用路由并展示对应SFC中的内容?
在vue中,提供了两个内置组件:<router-view></router-view>和<router-link></router-link>。
2.1 router-viewrouter-view用于展示“通过路由切换的组件”。
router-view写在哪,组件就会被展示在哪里:
<template> <div><!-- SFC要展示的位置 --> <router-view></router-view> </div></template>这里有个问题:如果说我想在一个页面展示多个组件的内容该怎么办?
有的人或许会说:直接写多个router-view。如果真的这样做的话,编译器就会变的非常混乱---他不知道该把哪个组件往哪里插入,因为程序员并没有明确的告诉他。如何解决?
2.1.1 命名视图通过命名视图可以解决之前提出的问题:
<!-- 这是一个footer组件 --><template> <div><!-- 给试图命名为xx 如果不命名,则默认为default --> <router-view name="xx"></router-view> </div></template>接下来就要在路由文件(index.js)中进行一些改变:
// router下的index.js文件中import { createRouter,createWebHashHistory } from "vue-router"import Home from "../views/home.vue"import Footer from "../views/footer.vue"const routers=[{path:"/home",components:{ default:Home, footer:Footer, }}]// 要被导出的变量const router=createRouter({ routes:routers, history:createWebHashHistory()})export default router原来的component变成了components;组件以键值对的形式存在;为什么要这么改变?
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。
Vue-Router官网
既然是多个组件,原来的component就要变成复数。default表示没有命名的视图(也就是默认视图)使用什么组件;footer就表示自己给视图取的名字,后面的值就是要给这个命名的视图使用哪个组件;此时在当前的home组件中,就变成了:
<!-- 这是一个footer组件 --><template> <div><!-- 默认视图在这 --> <router-view></router-view><!-- 命名视图在这 --> <router-view name="xx"></router-view> </div></template>其余命名router-view的使用,可以查看官网:命名视图。
2.2 router-linkrouter-link的作用就是点击跳转到指定路由。
要成功使用router-link的话,前提是路由配置成功并且全局使用。
<!-- 某个不知名组件内 --><template> <div> <router-link to="/home">跳转到home</router-link> </div></template>router-link中有一个to属性,其值表示要跳转到哪个路由;是个双标签;标签中可以写文本,点击文本可实现跳转;三、路由的参数路由是可以传递参数的,在某些时候需要这些参数来进行某些操作。
路由的参数分为两种:query和params。
3.1 query参数query参数在地址栏的形式是:127.0.0.1:3000/xxx?age=1&score=100。
问号后面的就是query参数,那么在开发时我们因该怎么写呢?
<template> <!-- 跳转路由并携带query参数, 在引号中使用模板字符串的形式进行参数的传递,如果不使用模板字符串,则传递的只是普通字符串。 --><!-- 写法1:to的字符串写法 --> <router-link :to=`路由?id=${参数1}&name=${参数2}` >点我跳转并传递参数</router-link><!-- 写法2:to的对象写法 --> <router-link :to="{ path:'路由', query:{ id:m.id, name:m.name } }" >点我跳转并传递参数</router-link></template>3.2 params参数params参数在地址栏中的样子是:127.0.0.1:3000/xxx/age=1/score=100。
<template><!-- 写法1:params的字符串写法 --> <router-link :to="`路由/${参数1}/${参数2}`" >点我跳转并传递参数</router-link><!-- 写法2:params的对象写法 --> <router-link :to="{ name:'给路由取的名', params:{ id:m.id, name:m.name } }" >点我跳转并传递参数</router-link></template>除此之外,要使用params参数,需要在router的index.js文件下,进行额外的配置:
// router下的index.js文件中import { createRouter,createWebHashHistory } from "vue-router"import Home from "../views/home.vue"const routers=[{ // 要使用params参数,需要在后面加上/:参数名path:"/home/:id/:name",component:Home}]// 要被导出的变量const router=createRouter({ routes:routers, history:createWebHashHistory()})export default router注意:
在传递params参数并且使用对象的写法时,不再是path配置项了,而是name配置项,而且必须是name,如果是path配置项,会报错;name的值是在创建路由时,给路由取的一个名字,也叫做”命名路由“,我打算后面再说,可以暂时不用理解,如果看到这就想要搞明白的,可以去官网提前了解:命名路由。要使用params参数,需要在router下的index.js文件中进行额外配置;四、编程式导航什么是编程式导航?
通过自定义事件,触发路由的切换,即所谓的编程式导航。
为什么会有编程式导航?
因为router-link并不能够满足我们开发时所遇到的各种需求,当我们需求类似于”点击button跳转页面“的话,router-link就会显得没有用武之地。
4.1 $router:路由跳转在vue的实例上,有一个叫做”$router“的属性,该属性上有很多方法,我们常用的总共有五个方法:push、replace、back、forward。
4.1.1 push方法push方法的作用是进行指定路由的跳转:
<template> <button @click="changeRouter">点我跳转路由</button></template><script> export default { methods:{ changeRouter(){ this.$router.push("/home") //通过push方法跳转到home组件(页面) // 同样的可以传递参数 } } }</script>4.1.2 replace方法replace方法也是实现的指定路由跳转:
<template> <button @click="changeRouter">点我跳转路由</button></template><script> export default { methods:{ changeRouter(){ this.$router.replace("/home") //通过replace方法跳转到home组件(页面) // 同样的可以传递参数 } } }</script>push方法和replace方法的区别是?
在知道它们的区别之前,首先要知道浏览器历史记录的两种模式:push和replace。
在浏览器中,浏览历史记录默认是以栈的方式进行存储的。当有一条新的历史记录时,就会添加在最上方,也就是所谓的“压栈”,此时有一个指针指向当前最上方的历史记录。当我们点击浏览器左上方的左右箭头时,就是改变指针的指向。
push就是在栈顶添加新的历史记录,那么replace就是直接替换栈顶的记录。路由跳转的时候,默认开启的是push方式。
编程式路导航可以使用replace开启替换模式,那么之前所提到的router-link如何开启replace模式呢?
在router-link上添加一个replace属性即可开启。
<template><!-- 直接写replace,默认的值就是true --><router-link replace to="xxx">xxxx</router-link><!-- <router-link :replace="true" to="xxx">xxxx</router-link> --></template>4.1.3 back方法back方法的作用是返回上一个页面,也就是控制浏览器后退一步:
<script>export default { methods:{ // 回退 back(){ this.$router.back() } } }</script>4.1.4 forward方法forward方法的作用是控制浏览器前进一步:
<script>export default { methods:{ // 前进 forward(){ this.$router.forward() } } }</script>注意:要使用该方法,前提是你之前有执行过后退操作。
4.1.5 go方法go方法的作用是控制浏览器的前进或者后退,根据传入的值有关:
<script>export default { methods:{ go(){ /*正数代表着前进,负数代表着后退*/ this.$router.go(3) //传入3代表着前进3下 } } }</script>4.2 $route:获取路由参数$route能够获取路由跳转时传递的参数。
注意:该属性和$router有着细微的区别,就是少了个r,使用的时候不要写错了。
获取query参数:<script>export default { methods:{ getQuery(){ console.log(this.$route.query) } } }</script>获取params参数:<script>export default { methods:{ getParams(){ console.log(this.$route.params) } } }</script>$route既然能在自定义事件中获取,那么肯定在模板中也能获取到了:
<template><!-- 注意:在模板中获取时,不用加this --> {{$route.params}}</template>4.2.1 路由的props配置在跳转目标内,我们有时会需要接收传递的params传递的参数,就有可能写多次$route.params,为了避免代码的臃肿,就给路由设置props配置:
{ path:"/demo", component:Demo, children:[ { path:"test", component:Test, children:[ { name:"hello" //给路由命名 path:"welcome", component:Hello, //props的第一种写法:一般不用,因为传递的都是固定数据 //props:{a:1,b:"hello"} //props的第二种写法 //值为布尔值,若为真, // 就会把该路由组件中所有params参数以props的形式传递给当前路由组件 //props:true //props的第三种写法:值为函数,最终结果也是以props形式传递 props($route){ return { id:$route.query.id, name:$route.query.name} } } ] } ]}既然是props传递,那么在页面内接收也是以props接收:
<script>export default { props:["参数1名","参数2名"] }</script>五、路由重定向在用户输入网页访问地址的时候,可能会出现地址输入错误等情况,如果要访问的网站没有做处理的话,浏览器就会显示错误页面。
路由重定向的作用?
在网址出错的情况下,不显示浏览器的错误页面,而是自动的跳转到写好的404页面或者网站的某个页面,进而提升用户体验。
vue-router和实现重定向?
在路由配置中,配置一项redirect。
import { createRouter,createWebHashHistory } from "vue-router"import Home from "../views/home.vue"const routers=[ { // 如果域名后面什么都不写的话 path:"/", redirect:"/home" //重定向到home页 }, { path:"/home", component:Home }]// 要被导出的变量const router=createRouter({ routes:routers, history:createWebHashHistory()})export default router当然,这个例子只是展示了“只写协议+域名+端口”的情况,如果后面的子路由拼写错误的话,就要用官网推荐的方法了:pathMatch。
pathMatch的作用就是使用正则进行路径匹配,当匹配的结果没有一个和已经定义好的路由相同的话,就会进行重定向,当然,也还是需要redirect:
import { createRouter,createWebHashHistory } from "vue-router"import Home from "../views/home.vue"const routers=[ { // 如果域名后面什么都不写或者没有和已经定义好的路由匹配的话 path:"/:pathMatch(.*)*", redirect:"/home" //重定向到home页 }, { path:"/home", component:Home }]// 要被导出的变量const router=createRouter({ routes:routers, history:createWebHashHistory()})export default router具体信息可以查看官网:捕获所有路由或 404 Not found 路由。
重定向的其他写法:重定向。
在vue3中对于$router和$route进行了改变。
在vue3中,由于无法在setup中使用this,自然也就无法使用$router和$route了,官方提供了两个hook函数:useRouter和uerRoute。
具体使用可以查看我的另外一篇文章:useRoute和useRouter。
或者查看官网:useRoute和useRoouter。
六、路由别名:alias给路由取别名的作用:
通过别名,你可以自由地将 UI 结构映射到一个任意的 URL,而不受配置的嵌套结构的限制。
上面这句话是官网的说法,直白的说,就是访问一个路由时,通过别名,访问到的却是另一个组件的内容。
说再多还不如看代码来的直白:
// router下的index.js文件中import { createRouter,createWebHashHistory } from "vue-router"import Home from "../views/home.vue"import Login from "../views/login.vue"const routers=[{path:"/home",component:Home, // 给路由取别名,当你想要访问/home时,结果输出的却是login的内容 alias:"/login"}, { path:"/login", component:Login }]const router=createRouter({ routes:routers, history:createWebHashHistory()})export default router这里有个小问题:
既然能够通过别名访问别的路由的内容,那么地址栏显示的是我原本访问的路径,还是别名的路径呢?
答案是:还是原本的路径。
要想知道别名的其他写法请查看官网:别名。
七、命名路由之前在说到通过router-link传递params参数的对象写法时,有提到过命名路由,如果已经看过官网并且掌握了的小伙伴可以跳过这一部分了。
命名路由的作用主要就是简化部分的路由跳转,特别是路由嵌套的时候。
在路由配置时,通过name配置项给路由命名:
import { createRouter,createWebHashHistory } from "vue-router"import Home from "../views/home.vue"import Goods from "../views/goods.vue"const routers=[ { path:"/home", component:Home, // children可以定义当前组件下的子路由,是一个数组 children:[ { // 注意。子路由前面不要加/,底层遍历时会自动加上,如果你手动加上了路由会识别失败 path:"goods", component:Goods, // 给home下的子路由命名为hg name:"hg" } ] }]// 要被导出的变量const router=createRouter({ routes:routers, history:createWebHashHistory()})export default router命名路由可以在在router-link中使用:<template> <router-link :to="{name:'hg'}"> 我是命名路由进行跳转,目的地是home下的Goods组件 </router-link></template>命名路由也可以在编程式导航中使用:
<script>export default { methods:{ push(){ // this.$router.push("/路由名") this.$router.push({ // 这里使用path或者name配置都可以 name:"hg", //这里填query或者params参数都可以 query:{ id:xxx, name:xxx } }) }, } }</script>注意:命名路由只能在对象的写法中使用。
八、路由的懒加载在之前的例子中,我们都是以这样的方式在路由文件中进行组件引入的:
// 直接引入好组件,然后在需要的地方使用import Home from "../views/home.vue"这样引入如果是小项目还好,当你在一个庞大的项目中使用这种方式的话,整个项目会变得很卡,因为当用户进入网站的那一刻,会从后端加载所有与项目有关的资源,从而可能会出现网站打开速度很慢很慢等情况。
官方推荐使用“路由懒加载”方式,用于解决上述问题。
一般来说,对所有的路由都使用动态导入是个好主意。
上面这句话提到了一个关键词:动态导入。
什么是动态导入?
动态 import 是 JavaScript ES2020 规范中引入的功能之一。
动态导入就是:在需要/进入页面的时候才会进行导入,不需要是,它就只是一段未执行的普通代码。
下面上代码展示实现懒加载的方法:
const Home = ()=>import("../views/home.vue")取一个变量名;变量名后直接跟一个箭头函数;箭头函数的return简写形式,返回一个import;还可以在路由配置中使用动态引入:
import { createRouter,createWebHashHistory } from "vue-router"// const Home=()=>import("../views/home.vue")const routers=[ { path:"/home", // component:Home, // 直接在配置项中动态引入 component:()=>import("../views/home.vue") , }]// 要被导出的变量const router=createRouter({ routes:routers, history:createWebHashHistory()})export default router更多信息请查看官网:路由懒加载。
九、路由元信息路由元信息,其实就是当前路由的额外的信息。元信息的主要作用个人认为是在路由守卫以及在路由渲染视图的时候。(路由守卫和路由渲染视图后面会说)
如何给路由添加元信息?
直接在路由配置时,添加一项:meta。
import { createRouter,createWebHashHistory } from "vue-router"// const Home=()=>import("../views/home.vue")const routers=[ { path:"/home", component:()=>import("../views/home.vue") , // 配置路由元信息 meta:{ // 这里写路由的额外信息 title:"home" } }]// 要被导出的变量const router=createRouter({ routes:routers, history:createWebHashHistory()})export default router元信息的使用就是这么简单,但它很有用。
十、路由守卫什么是路由守卫?
就好比公主的护卫一样,一旦有人想要靠近公主,那么首先就会被护卫拦下来,通过验证/得到允许后才可以靠近。
路由也是一样的。我们在使用其他网站时,在没有登录或者其他情况下,如果想要访问某些信息,往往是需要跳转到登录页,在登录之后才可以进行访问。这其中就是路由守卫在起作用。
路由守卫在vue-router官网中也叫做导航守卫。
路由守卫的分类?
路由守卫分为三类:全局守卫、路由独享守卫、组件内守卫。
其中,全局守卫分为全局前置守卫和全局后置守卫。
10.1 全局守卫全局守卫的书写位置是在router文件夹下的index.js文件中。
10.1.1 全局前置守卫全局前置守卫,顾名思义:纵观全局,只要有路由的切换,就会进行守卫校验:
import { createRouter,createWebHashHistory } from "vue-router"const routers=[ { path:"/home", component:()=>import("../views/home.vue") , }]const router=createRouter({ routes:routers, history:createWebHashHistory()})// 全局前置守卫router.beforeEach((to,from,next)=>{})export default router前置守卫接收三个参数:to方法、from方法、next方法。
to方法:表示要到哪里去,存储着目的地的相关信息;from方法:表示从哪里来,存储着来源的相关信息;next方法:表示放行,如果传入路由,则表示跳转到指定路由;import { createRouter,createWebHashHistory } from "vue-router"let islogin=falseconst routers=[ { path:"/home", component:()=>import("../views/home.vue") , meta:{ isAuth:true //表示当前路由是否需要守卫验证 } }, { path:"/login", component:()=>import("../views/login.vue") }]const router=createRouter({ routes:routers, history:createWebHashHistory()})// 全局前置守卫router.beforeEach((to,from,next)=>{ // 这里使用元信息来判断进入一个路由是否需要验证 if(to.meta.isAuth){ if(islogin){ // 如果islogin为true,表示用户已经登录了,可以放行 next() }else{ // 验证不通过,不能放行 next("/login") //跳转到login页面 } }else{ // 如果不需要守卫验证,则直接放行 next() }})export default router全局前置守卫的使用场景?
初识化的时候被调用;每一次路由切换之前被调用;10.1.2 全局后置守卫全局后置守卫只接收两个参数:to和from,他们的作用和意思都和前置守卫相同,包括后续的路由独享守卫和组件内守卫。
后置守卫只接收两个参数的原因是:因为他的使用场景是在路由切换后才执行的,此时有没有next已经没有任何意义了:
import { createRouter,createWebHashHistory } from "vue-router"let islogin=falseconst routers=[ { path:"/home", component:()=>import("../views/home.vue") , meta:{ isAuth:true //表示当前路由是否需要守卫验证 } }, { path:"/login", component:()=>import("../views/login.vue") }]const router=createRouter({ routes:routers, history:createWebHashHistory()})// 全局前置守卫router.afterEach((to,from)=>{})export default router全局后置路由守卫的使用场景?
初始化的时候被调用;每次路由切换之后被调用;应用场景:在路由切换成功之后修改网页页签标题;import { createRouter,createWebHashHistory } from "vue-router"let islogin=falseconst routers=[ { path:"/home", component:()=>import("../views/home.vue") , meta:{ isAuth:true, //表示当前路由是否需要守卫验证 title:"首页", //title可以表示网页页签标题 } }, { path:"/login", component:()=>import("../views/login.vue") }]const router=createRouter({ routes:routers, history:createWebHashHistory()})// 全局前置守卫router.afterEach((to,from)=>{ // 在路由切换成功后,修改页签标题 document.title=to.meta.title})export default router10.2 路由独享守卫顾名思义,路由独享守卫就是某一个路由的专享,其他路由使用不了,同样是接收to、from、next三个参数:
import { createRouter,createWebHashHistory } from "vue-router"let islogin=falseconst routers=[ { path:"/home", component:()=>import("../views/home.vue") , meta:{ isAuth:true, //表示当前路由是否需要守卫验证 title:"首页", //title可以表示网页页签标题 }, beforeEnter:(to,from,next)=>{ // 这里的to其实就是当前路由 if(to.meta.isAuth){ if(islogin){ // 如果islogin为true,表示用户已经登录了,可以放行 next() }else{ // 验证不通过,不能放行 next("/login") //跳转到login页面 } }else{ // 如果不需要守卫验证,则直接放行 next() } } }, { path:"/login", component:()=>import("../views/login.vue") }]const router=createRouter({ routes:routers, history:createWebHashHistory()})export default router注意:
路由独享守卫没有后置守卫;但是可以搭配全局后置守卫来更改网页标题;10.3 组件内守卫写在组件内的路由守卫,共有三个配置项:
beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave<template> <div> </div></template><script> import sideNavibar from '@/components/sideNavibar.vue' export default{ data(){ return{ } }, components:{ sideNavibar }, // 在渲染该组件的对应路由被验证前调用 // 不能获取组件实例 `this` ! // 因为当守卫执行时,组件实例还没被创建! beforeRouteEnter(to,from,next){ if(to.meta.isAuth){ if(localStorage.getItem("school")==="atguigu"){ next() }else{ console.log("你不是这个学校的"); } }else{ next() } }, beforeRouteUpdate(to, from) { // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候, // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this` }, beforeRouteLeave(to, from,next) { // 在导航离开渲染该组件的对应路由时调用 // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this` console.log("我离开了"); }, }</script><style scoped></style>具体信息查看官网:导航守卫。
上一篇:Vue中,一个组件调用其他组件的方法(非父子组件)(vue中的组件)
下一篇:手把手带你写一份优秀的开发求职简历(五)技术能力如何凸显优势(手把手怎么写)
友情链接: 武汉网站建设