Appearance
Vue路由基础
学习目标
- 【掌握】Vue-router入门
- 【掌握】Vue-router传参
- 【掌握】编程式导航
- 【掌握】路由重定向和404
- 【掌握】路由组件传参
- 【掌握】历史记录模式
通过之前的学习我们应该了解Vue创建的是一个单页应用,但我们使用的大型Vue项目仍然能够实现页面内容的切换,这是如何实现的呢?这就用到了我们接下来要学习的内容——路由,Vue中的路由跟生活中理解的路由概念其实非常类似,可以理解为是视图和URL的一种匹配关系,它是Vue全家桶生态中一个非常重要的插件,我们会通过学习最新的V4版本来掌握路由中的一些重要概念。
1.路由入门
1.1 安装依赖
在我们的Vite项目中,通过命令来安装Vue-router,vue3版本使用的是最新的Vue-router@4版本。
npm install vue-router
可以在package.json中确认成功安装了依赖。
1.2 配置路由
我们现在有这样一个需求:在页面上有2个button,点击button搜索,可以跳转到一个搜索页面,点击button列表,可以跳到一个列表页面。
稍微分析下,就应该知道这个需求要用路由来实现,我们现在需要2个路由页面(搜索 列表),路由页面其实就是组件。
我们首选需要来在项目中配置一下路由:
在项目的src目录中新建一个router目录,再建立一个index.js来存放我们的路由配置文件。
js
import { createRouter, createWebHashHistory } from 'vue-router'
import Search from '../components/Search.vue'
import List from '../components/List.vue'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/search',
name: 'search',
component: Search,
},
{
path: '/list',
name: 'list',
component: List,
},
],
})
export default router
然后需要在主入口中引入路由配置文件,作用到整个项目中。
js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index.js'
createApp(App).use(router).mount('#app')
1.3 内置路由组件使用
在App.vue也就是项目的根组件中,通过Vue-router内置的2个组件和,最终实现页面的正确跳转。
vue
可以理解为路由页面的出口,它可以根据路径来决定具体展示哪个组件 可以理解一个a标签,to属性决定它最终跳转到哪个路径<template>
<div>
<h1>这是首页</h1>
<router-link to="search"><button>搜索页</button></router-link>
<router-link to="list"><button>列表页</button></router-link>
<router-view></router-view>
</div>
</template>
此时我们已经实现了路由页面的正确跳转。
2.路由传参
当路由跳转到一个页面的时候,往往需要携带一些信息,然后通过这些信息可以向服务端请求数据渲染页面,那如何来实现路由传参呢,Vue给我们提供了2种常见的形式。
2.1 query传参
利用查询字符串的形式来进行参数传递。
在刚才的搜索页中,现在希望点击搜索按钮能够跳转到详情页面,此时我们在做路由跳转的时候必须思考一个问题:我必须将我在搜索页种输入的文本内容传递到详情页面。
在路由配置文件中,新增一项配置信息。
js
routes: [
{
path: '/search',
name: 'search',
component: Search,
},
{
path: '/list',
name: 'list',
component: List,
},
{
path: '/detail',
name: 'detail',
component: detail,
},
],
然后在搜索按钮那里通过来实现跳转,此时在路径中通过查询字符串的形式来携带参数
vue
<template>
<div>
<input type="text" placeholder="请输入搜索内容" v-model="text" />
<router-link :to="`/detail?text=${text}`"><button>点击搜索</button></router-link>
</div>
</template>
<script setup>
import { ref } from 'vue'
const text = ref('')
</script>
最终在详情页中来接受这个传递过来的数据。
在url中可以直接观测到传递的数据。
通过Vue-router提供了一个hook函数useRoute可以获取到传递过来的query参数。
vue
<template>
<h1>这是搜索的内容:{{ text }}</h1>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
let text = ref('')
onMounted(() => {
text.value = useRoute().query.text
})
</script>
最终页面也能正确显示传递过来的参数。
2.2 params传参
刚才的需求同样可以使用另一种方式来实现,我们称之为params动态传参。
使用这种方式实现传参的时候在配置路由信息时需要额外处理。
js
{
path: '/detail/:text', //这里添加了一个:text的占位符 这个占位符名称可以任意指定
name: 'detail',
component: Detail,
},
然后是通过搜索按钮进行跳转时传递参数。注意区分和query传参的格式区别。
vue
<template>
<div>
<input type="text" placeholder="请输入搜索内容" v-model="text" />
<!--query传参 <router-link :to="`/detail?text=${text}`"><button>点击搜索</button></router-link> -->
<router-link :to="`/detail/${text}`"><button>点击搜索</button></router-link>
</div>
</template>
最终在详情页中来接受参数。
vue
<template>
<h1>这是搜索的内容:{{ text }}</h1>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
let text = ref('')
onMounted(() => {
// text.value = useRoute().query.text 这是query传参的接受方式
text.value = useRoute().params.text
})
</script>
页面同样能正确显示参数内容。
3.编程式导航
3.1 基本使用
除了使用 <router-link>
创建 a 标签来定义导航链接,我们还可以通过获取router的方式来控制页面的跳转。
上面示例中的跳转都是通过<router-link>
渲染的a标签实现的,如果我们希望页面能够自动跳转呢?具体来说,希望进入到首页后过3S自动进入到搜索页,这种场景下,使用<router-link>
显然无法实现,因为a标签必须手动去点击,我们现在希望通过代码能完成点击,这里就需要使用编程式导航了。
在App.vue中通过useRouter来获取整个router,然后开启一个3s的定时器,通过push方法进行路径的跳转。
vue
<script setup>
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
onMounted(() => {
const router = useRouter()
setTimeout(() => {
router.push('/search')
}, 3000)
})
</script>
<template>
<div>
<h1>这是首页</h1>
<router-link to="/search"><button>搜索页</button></router-link>
<router-link to="/list"><button>列表页</button></router-link>
<router-view></router-view>
</div>
</template>
当你点击 <router-link>
时,Vue内部其实仍然会调用push这个方法,所以点击 <router-link :to="...">
相当于调用 router.push(...)
,可以根据实际需求来选用更合理的方式。
3.2 路由传参
和声明式导航类似,编程式导航也可以采用query和params2种方式传递参数。
js
// 字符串路径
router.push('/users')
// 带有路径的对象
router.push({ path: '/users' })
// 命名的路由,并加上动态参数 同样需要对应地配置路由信息
router.push({ name: 'user', params: { username: 'eduardo' } })
// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })
3.3 replace,push和go
刚才详细说明了push方法可以实现编程式导航跳转,replace方法同样可以,唯一不同的是,它在导航时不会向 history 添加新记录,正如它的名字所暗示的那样——它取代了当前的条目。
go方法采用一个整数作为参数,表示在history历史中前进或后退多少步,类似于 window.history.go(n)
。
4.路由重定向和404
4.1 重定向
当我们匹配到某个路径进行页面跳转的时候,希望能够再次跳转到另一个路由地址,可以采用重定向来实现。
在上面的示例中,比如进入首页就希望能够自动跳转到搜索页:
js
routes: [
{
path: '/',
redirect: '/search', //配置首页的路由信息时通过redirect属性来实现重定向
},
{
path: '/search',
name: 'search',
component: Search,
},
{
path: '/list',
name: 'list',
component: List,
},
{
path: '/detail/:text?',
name: 'detail',
component: Detail,
},
],
4.2 404
当我们访问一些不存在的路径时,一般都会给出一个404的的页面提示,404也是通过重定向来实现的。
首先可以新建一个404页面组件,当路由路径没有正确匹配的时候,统一重定向到404这个页面中,需要注意path路径的格式。
js
import { createRouter, createWebHashHistory } from 'vue-router'
import Search from '../components/Search.vue'
import List from '../components/List.vue'
import Detail from '../components/Detail.vue'
import NotFound from '../components/404.vue'
const router = createRouter({
history: createWebHashHistory(),
routes: [
...
{
path: '/404',
name: '404',
component: NotFound,
},
{
path: '/:catchAll(.*)',
redirect: '/404',
},
],
})
export default router
5.路由组件传参
我们之前已经深入学习了如何进行路由组件的参数传递,但之前通过useRoute来获取路由参数的方式耦合度太强,我们希望用我们最习惯的方式props来获取这个参数。
5.1 布尔模式
当 props
设置为 true
时,route.params
将被设置为组件的 props。
回到之前的示例中来实现一下,首先在路由配置信息中添加props这个属性,并设置为true。
js
const router = createRouter({
history: createWebHashHistory(),
routes: [
...
{
path: '/detail/:text?',
name: 'detail',
component: Detail,
props:true
},
...
],
})
在detail页面中,便可以通过props形式来接受这个参数并且放到模板中使用了。
vue
<template>
<h1>这是搜索的内容:{{ props.text }}</h1>
</template>
<script setup>
import { onMounted, ref } from 'vue'
let text = ref('')
onMounted(() => {
// text.value = useRoute().query.text 这是query传参的接受方式
// text.value = useRoute().params.text 这是params传参的接受方式
})
const props = defineProps(['text'])
</script>
5.2 对象模式
当希望给路由组件传递一些静态属性的时候,使用对象形式是更好的选择。
js
const router = createRouter({
history: createWebHashHistory(),
routes: [
...
{
path: '/list',
name: 'list',
component: List,
props:{
a:5,
b:6
}
},
...
],
})
vue
//List.vue中
<template>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>{{ a }}</li>
<li>{{ b }}</li>
</ul>
</template>
<script setup>
defineProps(['a', 'b'])
</script>
5.3 函数模式
这种模式大家也可以了解一下,props还可以是一个函数,返回的对象最终会被并入到传递给路由组件的props中。
js
const routes = [
{
path: '/detail',
component: Detail,
props: route => ({ query: route.query.q })
}
]
URL /detail?q=vue
将传递 {query: 'vue'}
作为 props 传给 Detail组件。
6.历史记录模式
在创建路由器实例时,history
配置允许我们在不同的历史模式中进行选择。
6.1 Hash模式
hash 模式是用 createWebHashHistory()
创建的,我们上面的示例便是采用了这种模式。
js
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes
})
它在内部传递的实际 URL 之前使用了一个哈希字符(#
)。由于这部分 URL 从未被发送到服务器,所以它不需要在服务器层面上进行任何特殊处理。不过,它在 SEO 中确实有不好的影响。如果你担心这个问题,可以使用 HTML5 模式。
6.2 HTML5 模式
用 createWebHistory()
创建 HTML5 模式,推荐使用这个模式:
js
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
//...
],
})
当使用这种历史模式时,URL 会看起来很 "正常",没有了那个多余的#。
不过,问题来了(这个问题一般都是后端运维来解决)。由于我们的应用是一个单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问 https://example.com/user/id
,就会得到一个 404 错误。这就丑了。
不用担心:要解决这个问题,你需要做的就是在你的服务器上添加一个简单的回退路由。如果 URL 不匹配任何静态资源,它应提供与你的应用程序中的 index.html
相同的页面。漂亮依旧!