- 注册路由器插件
在 ./router/index.js 中,引入 VueRouter 插件并用 Vue.use() 注册。 - 创建路由对象
定义路由规则数组,并传入创建的 VueRouter 实例对象,导出对象。
import Vue from 'vue'import VueRouter from 'vue-router'import Index from '../views/Index.vue'// 1. 注册路由插件// Vue.use 是用来注册插件,他会调用传入对象的 install 方法Vue.use(VueRouter)// 路由规则const routes = [{path: '/',name: 'Index',component: Index},{path: '/blog',name: 'Blog',// route level code-splitting// this generates a separate chunk (about.[hash].js) for this route// which is lazy-loaded when the route is visited.component: () => import(/* webpackChunkName: "about" */ '../views/Blog.vue')},{path: '/photo',name: 'Photo',// route level code-splitting// this generates a separate chunk (about.[hash].js) for this route// which is lazy-loaded when the route is visited.component: () => import(/* webpackChunkName: "photo" */ '../views/Photo.vue')}]// 2.创建路由对象const router = new VueRouter({routes})export default router
- 注册 router 对象
在 main.js 中创建 vue 实例并把 router对象传进去。
import Vue from 'vue'import App from './App.vue'import router from './router'Vue.config.productionTip = falsenew Vue({router,render: h => h(App)}).$mount('#app')
- 创建路由组件的占位
App.vue 中引入<router-view/>标签。页面嵌套就由这个组件实现。 - 创建链接
App.vue 中引入<router-link>标签。这个并不是必要的,只是按需引入一些组件。
<template><div id="app"><div><img src="@/assets/logo.png"></div><div id="nav"><!-- 5. 创建链接 --><router-link to="/">Index</router-link> |<router-link to="/blog">Blog</router-link> |<router-link to="/photo">Photo</router-link>|</div><!-- 4. 创建路由组件的展占位 --><router-view/></div></template><style>#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;}#nav {padding: 30px;}#nav a {font-weight: bold;color: #2c3e50;}#nav a.router-link-exact-active {color: #42b983;}</style>
动态路由
例如 Detail 组件,对于不同 ID 的详情,都要使用这个组件来渲染。在路由路径中使用“动态路径参数”就能达到这种效果。
const routes = [{path: '/',name: 'Index',component: Index},{// 动态路由path: '/detail/:id',name: 'Detail',// 开启 props,会把 URL 中的参数传递给组件// 在组件中通过 props 来接收 URL 参数props: true,// route level code-splitting// this generates a separate chunk (about.[hash].js) for this route// which is lazy-loaded when the route is visited.component: () => import(/* webpackChunkName: "detail" */ '../views/Detail.vue')}]
组件接收参数时有两种方式:
- 通过当前路由规则获取,但是这种方式强依赖于路由。
通过在路由规则数组里开启 Detail 的 props 属性来获取。 ```vue
通过当前路由规则获取:{{ $route.params.id }}
通过开启 props 获取:{{ id }}
```javascriptconst routes = [{name: 'login',path: '/login',component: Login},// 嵌套路由{path: '/',component: Layout,children: [{name: 'index',path: '',component: Index},{name: 'detail',path: 'detail/:id',props: true,component: () => import('@/views/Detail.vue')}]}]
编程式导航
除了使用 <router-link>创建 a 标签来定义导航链接以外,还可以借助 router 的实例方法来实现组件加载。
常用的方法有 $router.push(),$router.replace(),$router.go()等,事实上当点击<router-link>时,内部调用的就是 $router.push()方法。另外,<router-link to= "..." replace>相当于 $router.replace()。
<template><div class="home"><div id="nav"><router-link to="/">Index</router-link></div><button @click="replace"> replace </button><button @click="goDetail"> Detail </button></div></template><script>export default {name: 'Index',methods: {replace () {this.$router.replace('/login')},goDetail () {this.$router.push({ name: 'Detail', params: { id: 1 } })}}}</script>
$router.push和$router.replace都可以传入字符串或对象。如果提供了path,那么 params不会生效。name和path都能使用query传参,但是会变成 url 参数,使用动态路由的话,不能用query。所以动态路由可以使用name+params或者使用path+ 字符串拼接或者直接使用字符串拼接。
const userId = '123'// 动态路由可用的模式router.push(`/user/${userId}`) // 直接传字符串 -> /user/123router.push({ name: 'user', params: { userId }}) // -> /user/123router.push({ path: `/user/${userId}` }) // -> /user/123// 这里的 params 不生效router.push({ path: '/user', params: { userId }}) // -> /user// 带查询参数,变成 /register?plan=privaterouter.push({ path: 'register', query: { plan: 'private' }})
Hash 模式和 History 模式
- URL 的区别:Hash 模式的 URL 带了一个 # 号,可以携带问号和 url 参数,History 模式不带 # 号。
const hashStyle = 'https://music.163.com/#/playlist?id=310321893'const historyStyle = 'https://music.163.com/playlist/310321893'
- Hash 模式是基于锚点以及 onhashchange 实现的,以锚点的值作为路由地址,当锚点变化,触发 onhashchage 事件,根据当前的路由地址找到对应组件重新渲染,如果改变 # 号后面的内容,浏览器不会向服务器发出请求,但是会把它加入访问历史;History 模式基于 Html5 的 API实现,
history.pushState()和history.replaceState()。调用history.pushState()时,浏览器不会向服务器发出请求,只会替换地址栏的 url,并且添加历史记录。通过监听 popstate 事件,可以获取到浏览器历史操作的变化,在 popstate 事件的处理函数中,可以获取改变后的地址,根据当前路由地址找到对应组件重新渲染。需要注意的是 popstate 只有在点击浏览器的前进和后退,以及调用history.back()和histoty.forward()时才会出发。
History 模式
History 模式需要服务器支持。因为 Vue 应用是单页应用,如果服务器没有配置,访问一个不存在的地址或点击浏览器的刷新按钮会返回 404 或“找不到该页面”,比如要访问 http://yoursite.com/user/id 输入错误变成 http://yoursite.com/userid,或者在http://yoursite.com/user/id刷新页面。因此,服务器端应该配置匹配不到任何静态资源就返回index.html页面。
Vue 项目默认开启 Hash 模式,在实例化 router 对象的时候,传入 mode 参数,可以控制要使用的模式。
const router = new VueRouter({mode: 'history',routes: [...]})
启用了 History 模式后,服务器就不再返回 404 页面,不能匹配的请求都会返回 index.html。为了避免这种情况,应该在路由规则里添加一条覆盖所有路由情况的规则,返回自己实现的 404 页面。
const routes = [{path: '*',name: '404',component: () => import(/* webpackChunkName: "404" */ '../views/404.vue')}]const router = new VueRouter({mode: 'history',routes})
Node.JS 环境
模拟在 Node.JS 环境下,运行打包后生成的 web应用。connect-history-api-fallback就是用来处理 history 模式的模块。
const path = require('path')// 导入处理 history 模式的模块const history = require('connect-history-api-fallback')// 导入 expressconst express = require('express')const app = express()// 注册处理 history 模式的中间件app.use(history())// 处理静态资源的中间件,网站根目录 ../webapp.use(express.static(path.join(__dirname, '../web')))// 开启服务器,端口是 3000app.listen(3000, () => {console.log('服务器开启,端口:3000')})
Nginx 服务器
Nginx 默认没有支持 History 模式,所以刷新页面或者访问不存在的页面会返回 404,需要修改 nginx 配置。
需要修改的文件是/Nginx根目录/conf/nginx.conf,找到项目对应的 server,配置try_files查找文件的顺序。首先查找是否有这个文件,没有的话查找是否有这个文件夹,再没有直接返回 index,.html。
server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {root html;index index.html index.htm;try_files $uri $uri/ /index.html;}}
