现在将向您展示如何使用vuerouter重建以前的应用程序,替代vuex以更方便管理应用程序的状态。 当然你肯定同时使用vuex和vue-router。 这里决定使用bootstrap来设计这个应用程序的样式,主要是大部人对它更熟悉,而且要更快地构建这个新版本。 点击在这里 。
在单页应用程序中使用路由器库的第一个原因是让用户能访问浏览器的导航按钮。这是在以前的应用程序中非常缺乏。 要获得此行为,必须将路由附加到URL上。在传统应用程序中,路由表示服务器上的html视图。 而在单页应用中,路由是在JavaScript中,并不是后端服务器,并且每个路由被访问时将匹配到应该渲染呈现的组件。
import Vue from 'vue' import VueRouter from 'vue-router' import SearchResults from '../components/SearchResults' import ResultDetail from '../components/ResultDetail'
Vue.use(VueRouter)
export default new VueRouter({ mode: 'hash', base: __dirname, routes: [ { path: '/:type/detail/:id', component: ResultDetail }, { path: '/:type/:query?', component: SearchResults }, { path: '*', redirect: '/Artist/' } ] })
|
这里我定义我的两个用例的路由。 第一个路由用于详细视图,它将搜索类型和搜索结果ID作为路由参数。 第二个路由代表搜索结果视图,它包含搜索类型和用户输入的查询字符串作为路由参数。 这些路由参数将有关如何管理应用程序的状态。 当路由更新时,组件将根据这些参数呈现自身。 要使用我在这里定义的路由,需要将它们添加到主vue实例。
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router'
import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap/dist/css/bootstrap-theme.css'
/* eslint-disable no-new */ new Vue({ el: 'app', router, render: h => h(App) }) view raw
|
无论是否使用路由我总是要渲染显示应用程序组件,因此vue实例将在div中呈现输出。当路由改变时,应用组件将负责切换其他组件。 为了实现这一点,App组件需要对由vue-router库提供的路由器视图组件router-view进行渲染。
<template lang="pug"> divapp search-box router-view </template>
import SearchBox from './components/SearchBox' export default { name: 'app', components: { SearchBox } }
<style> body { padding-top: 4em; padding-bottom: 2em; scroll-behavior: smooth; } </style>
|
随着路由代码的到位,下一步将是确定何时路由改变,以及组件如何对路由改变做出反应。
搜索框是用户与应用程序交互的主要方式。 因此,当点击搜索按钮或改变搜索类型时,路由被更新以反映用户的输入,并且搜索结果组件重新呈现结果输出。
li(v-for='type in searchTypes') router-link(:to="'/' + type + '/' + query") {{ type }}
|
这段来自SearchBox.vue模板的代码段显示了如何使用vue-router提供的router-link组件更改路由。
searchByType () { this.$router.push('/' + this.activeType + '/' + encodeURI(this.query) || '') }
|
也可以编程方式更改路由。当将上面配置的路由器被添加到vue实例,Vue使得$router变得可用了。 调用push是将新路由放入浏览器的历史堆栈,并使路由发生变动。
现在,让我们来看看搜索结果组件,看看它对路由更改的反应。
<template lang="pug"> div.container ul.list-group li.list-group-item(v-for='result in searchResults' @click='goToDetail(result)') div.media div.media-left img.media-object(v-if='result.imageUrl', : height='64px' width='64px') div.media-body h4.media-heading {{ result.name }} p {{ resultInfo(result) }} button.btn.btn-default.center-block(v-if='hasMoreResults' @click='loadMoreResults()') span Show more results </template>
import {fetchByType} from '../api' const UP = 'UP'
// TODO: Add busy status and indicator. export default { name: 'search-results', created () { this.routeChanged() }, data () { return { hasMoreResults: false, searchResults: [], type: null, query: null, offset: 0 } }, methods: { routeChanged () { // reset query params this.type = this.$route.params.type this.query = this.$route.params.query this.offset = 0 if (!this.query) { this.searchResults = [] return } fetchByType(this.query, this.type) .then(res => { this.hasMoreResults = res.length >= 10 this.searchResults = res this.handleScroll(UP) }) }, loadMoreResults () { this.offset += 10 fetchByType(this.query, this.type, this.offset) .then(res => { this.hasMoreResults = res.length >= 10 this.searchResults = [ ...this.searchResults, ...res ] }) }, goToDetail (result) { switch (this.type) { case 'Artist': case 'Album': this.$router.push('/' + this.type + '/detail/' + result.id) break case 'Track': this.$router.push('/' + this.type + '/detail/' + result.album.id) break default: return } }, handleScroll (direction) { this.$nextTick(() => { switch (direction) { case UP: window.scrollTo(0, 0) break default: window.scrollTo(0, document.body.scrollHeight) } }) }, resultInfo (result) { switch (this.type) { case 'Artist': return result.genres ? result.genres.join(', ') : null case 'Album': return result.artists ? result.artists[0].name : null case 'Track': return result.album && result.artists ? result.album.name + ' by ' + result.artists[0].name : null default: null } } }, watch: { '$route': 'routeChanged' } }
<style scoped> li { cursor: pointer; } </style>
|
在上面created的生命周期钩子中我调用一个routeChanged函数。此函数从路由参数获取用户正在搜索的类型和查询,然后根据这些参数获取数据。这还不够,只有当搜索结果添加到DOM时才会调用created。 要使搜索结果注意到路由更改,这里使用watch对象来监视$route。现在当应用程序的路由更新时,将调用搜索结果routeChanged函数。
希望本文帮助您了解如何设置和使用vue-router在组件之间导航和保持它们同步。