Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。
vue-router 是一个插件包,所以我们还是需要用 npm/cnpm 来进行安装的。打开命令行工具,进入你的项目目录,输入下面命令。
npm install vue-router --save --registry=https://registry.npm.taobao.orgElementUI,下面是根据vuecli2安装的ElementUI.vuecli3可使用插件安装
在源码目录中创建如下结构:
assets:用于存放资源文件components:用于存放 Vue 功能组件views:用于存放 Vue 视图组件router:用于存放 vue-router 配置在 views 目录下创建一个名为 Main.vue 的视图组件;该组件在当前章节无任何作用,主要用于登录后展示登录成功的跳转效果;
<template> <div> 首页 </div> </template> <script> export default { name: "Main" } </script> <style scoped> </style>一个组件页面里只能有一个根元素
在 views 目录下创建一个名为 Login.vue 的视图组件,其中 el-* 的元素为 ElementUI 组件;
<template> <div> <el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box"> <h3 class="login-title">欢迎登录</h3> <el-form-item label="账号" prop="username"> <el-input type="text" placeholder="请输入账号" v-model="form.username"/> </el-form-item> <el-form-item label="密码" prop="password"> <el-input type="password" placeholder="请输入密码" v-model="form.password"/> </el-form-item> <el-form-item> <!--提交的loginForm为表单的 ref--> <el-button type="primary" v-on:click="onSubmit('loginForm')">登录</el-button> </el-form-item> </el-form> <el-dialog title="温馨提示" :visible.sync="dialogVisible" width="30%" :before-close="handleClose"> <span>请输入账号和密码</span> <span slot="footer" class="dialog-footer"> <el-button type="primary" @click="dialogVisible = false">确 定</el-button> </span> </el-dialog> </div> </template> <script> export default { name: "Login", data() { return { form: { username: '', password: '' }, // 表单验证,需要在 el-form-item 元素中增加 prop 属性 rules: { username: [ {required: true, message: '账号不可为空', trigger: 'blur'} ], password: [ {required: true, message: '密码不可为空', trigger: 'blur'} ] }, // 对话框显示和隐藏 dialogVisible: false } }, methods: { onSubmit(formName) { // 为表单绑定验证功能 this.$refs[formName].validate((valid) => { if (valid) { // 使用 vue-router 路由到指定页面,该方式称之为编程式导航 this.$router.push("/main"); } else { this.dialogVisible = true; return false; } }); } } } </script> <style lang="scss" scoped> .login-box { border: 1px solid #DCDFE6; width: 350px; margin: 180px auto; padding: 35px 35px 15px 35px; //圆角 border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; //阴影 box-shadow: 0 0 25px #909399; } .login-title { text-align: center; margin: 0 auto 40px auto; color: #303133; } </style>在 router 目录下创建一个名为 index.js 的 vue-router 路由配置文件
import Vue from 'vue' import Router from 'vue-router' import Login from "@/views/Login" import Main from '@/views/Main' // 通过Vue.use(插件),安装插件,默认执行插件的install方法 Vue.use(Router); export default new Router({ // 注意拼写,不要写成routers // 匹配的优先级就按照路由的定义顺序 routes: [ { // 登录页 path: '/login', // 命名路由,非必选项,跳转时与跳转方法的name属性匹配 name: 'Login', component: Login }, { // 首页 path: '/main', name: 'Main', component: Main } ] });修改 main.js 入口代码
import Vue from 'vue' import App from './App' //导入自定义路由配置, 如果/index.js前面是目录可省略不写 import router from './router' // 导入 ElementUI import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.config.productionTip = false // 安装 ElementUI Vue.use(ElementUI); new Vue({ el: '#app', components: { App }, template: '<App/>', // 启用路由 router, // 启用 ElementUI render: h => h(App) });修改 App.vue 组件代码,渲染组件
<template> <div id="app"> <router-link to="/main">跳转首页</router-link> <router-view/> </div> </template> <script> export default { name: 'App', } </script>VueRouter定义的两个全局标签:
router-link: 默认会被渲染成一个 a 标签,to 属性为指定链接router-view: 会将url中路由匹配到的组件,在标签所在位置渲染router-link标签的属性:
to: 指定跳转路径 to使用v-bind绑定后可以传对象,具体用法同router.push(). tag: 指定router-link会被渲染成什么标签active-class: 当router-link对应的路由匹配成功时,会自动给当前元素添加一个名为router-link-active的class. 使用active-class修改添加的class的名字.(可以在路由配置中使用linkActiveClass属性统一修改)replace: 表示跳转时使用replace函数,无法前进后退.(不添加时默认使用push函数)当router-link对应的路由匹配成功时,会自动给当前元素添加一个名为router-link-active的class,可根据这个class修改标签路由匹配后的样式
<router-link to="/main" tag='button' active-class='active' replace >跳转首页</router-link> // 统一修改active-class export default new Router({ routes: [ { // 登录页 path: '/login', // 命名路由,非必选项 name: 'Login', component: Login } ], linkActiveClass: 'active' });不要使用history绕过VueRouter修改路径
在 Vue 实例内部,你可以通过 this.$router 访问路由实例。进行路由跳转
router.push(location, onComplete?, onAbort?) // 字符串 router.push('home') // 字符串格式时传入data中的属性值 router.push('/home/'+ this.id) // 对象 router.push({ path: 'home' }) // 命名的路由 , 变成 /user/123 router.push({ name: 'user', params: { userId: '123' }}) // 带查询参数,变成 /register?plan=private router.push({ path: 'register', query: { plan: 'private' }}) const userId = '123' router.push({ name: 'user', params: { userId }}) // -> /user/123 router.push({ path: `/user/${userId}` }) // -> /user/123 // 这里的 params 不生效 router.push({ path: '/user', params: { userId }}) // -> /user注意:如果使用了 path,params 会被忽略,同样的规则也适用于 router-link 组件的 to 属性。
router.replace(location, onComplete?, onAbort?): 跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录.router.go(n): 类似js的 history.go(n)。使用动态路径参数(以冒号开头)把某种模式匹配到的所有路由,全都映射到同个组件。
const router = new VueRouter({ routes: [ // 动态路径参数 以冒号开头 { path: '/user/:id', component: User } ] }) /user/foo 和 /user/bar 都将映射到User组件 但 /user 不会映射到User组件跳转时传递参数
// 绑定data中的userId属性,单引号括起来的部分不会被Vue当做变量解析 <router-link :to="'/user/'+userId">个人信息</router-link>在路由到的组件中接收传递的参数
<div>{{ $route.params.id }}</div> 或 new Vue({ computed: { currentTime(){ this.$route.params.id } } })修改路由配置,主要是在 path属性中增加了 :id 这样的占位符
{path: '/user/profile/:id', name:'UserProfile', component: UserProfile}
<router-link to="/user/profile/1">个人信息</router-link>
router-link :to方式<router-link :to="{name: 'UserProfile', params: {id: 1}}">个人信息</router-link>
注意: 此时我们将 to 改为了 :to,是为了传对象参数,不然{}不会被解析. 注意 router-link 中的 name 属性名称 一定要和 路由中的 name 属性名称 匹配,因为这样 Vue 才能找到对应的路由路径;
代码方式传递this.$router.push({ name: 'UserProfile', params: {id: 1}});
路由配置使用最普通的方式
{path: '/user/profile', name:'UserProfile', component: UserProfile}
<router-link :to="{name: 'UserProfile', query: { plan: 'private'}}">query</router-link>
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。使用 props 将组件和路由解耦.
修改路由配置,主要增加了 props: true 属性
{path: '/user/profile/:id', name:'UserProfile', component: UserProfile, props: true}
<router-link to="/user/profile/2">个人信息</router-link>
router-link :to方式传递<router-link :to="{name: 'UserProfile', params: {id: 1}}">个人信息</router-link>
代码方式传递this.$router.push({ name: 'UserProfile', params: {id: 1}});
嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。
创建Profile.vue和List.vue 的视图组件,内容添加个根元素其他默认即可
修改 router 目录下的 index.js 路由配置文件,代码如下:
// 用于嵌套的路由组件 import UserProfile from '../views/user/Profile' import UserList from '../views/user/List' .... { path: '/main', name: 'Main', component: Main, // 配置嵌套路由 children: [ { // 默认重定向 /main/posts path: '', redirect: 'posts' }, { // 当 /user/profile匹配成功 // UserProfile会被渲染在 Main 组件的<router-view> 中 path: '/user/profile', component: UserProfile }, { // 当 /main/posts 匹配成功 // UserPosts 会被渲染在 Main 的 <router-view> 中 path: 'posts', component: UserPosts } ] } 以 / 开头的嵌套路径会被当作根路径。可以达到嵌套组件而无须设置嵌套的路径。非 / 开头的嵌套路径会被当做子路径,拼接在父路径之后进行匹配Main.vue添加的内容
<template> <div id="app"> <router-link to="/user/profile">跳转profile</router-link> <router-link to="/main/posts">跳转posts</router-link> <router-view/> </div> </template>Main.vue在App.vue渲染, profile和posts在Main.vue中渲染
Vue 中的重定向是作用在路径不同但组件相同的情况下,或用于缺省时重定向到首页
修改路由配置(router/index.js)
{ // 缺省时重定向到/main path: '', // 与 path: '/' 等效 redirect: '/main' }, { path: '/main', name: 'Main', component: Main }, { //将/goHome重定向到/main path: '/goHome', redirect: '/main' }组件中使用 <router-link to="/goHome">回到首页</router-link>即可重定向到/main
路由模式有两种
hash:路径带 # 符号(默认),如 http://localhost/#/loginhtml5的history:路径不带 # 符号,如 http://localhost/login修改路由配置(/router/index.js文件),代码如下:
export default new Router({ mode: 'history', routes: [ ] });使用history模式用户直接访问或者刷新非index.html时,浏览器会绕过VueRouter直接请求后端服务,导致404.后端应进行相应设置
创建一个视图组件,在路由配置文件(/router/index.js)导入并配置
//该路由放到最后 { path: '*', component: NotFound }定义路由的时候可以配置 meta 字段:
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, meta: { title: '标题1' }, children: [ { path: 'bar', component: Bar, // a meta field meta: { requiresAuth: true } } ] } ] })一个路由匹配到的所有路由记录都会保存在$route.matched 数组.我们可以遍历 $route.matched 来检查路由记录中的 meta 字段。
“导航”表示路由正在发生改变。导航守卫主要用来通过跳转或取消的方式守卫导航。导航守卫就是路由的生命周期钩子函数.
路由钩子中如果不使用next(),路由会一种被钩子劫持不能进行跳转.
参数说明:
to:路由将要跳转的路由记录,类型为routefrom:路由跳转前的路由记录,类型为routenext:路由的控制参数 next() 路由继续执行,跳入下一个页面next(’/path’) 改变路由的跳转方向,使其跳到另一个路由next(false) 返回原来的页面next((vm)=>{}) 仅在 beforeRouteEnter 中可用,vm 是组件实例安装Axios
npm install axios -s --registry=https://registry.npm.taobao.org
main.js中引用Axios
import axios from 'axios' /*原型链*/ Vue.prototype.axios = axios;进行异步请求,要在进入路由前发送请求
export default { props: ['id'], name: "UserProfile", beforeRouteEnter: (to, from, next) => { console.log("准备进入个人信息页"); // 注意,一定要在 next 中请求,因为该方法调用时 Vue 实例还没有创建,此时无法获取到 this 对象, //在这里使用官方提供的回调函数拿到当前实例,再调用方法 next(vm => { vm.getData(); }); }, methods: { getData: function () { this.axios({ method: 'get', url: 'http://localhost:8080/static/data.json' }).then(function (repos) { console.log('返回结果'+repos); }).catch(function (error) { console.log(error); }); } } } .then(function (repos) { console.log('返回结果'+repos); }) //可使用lambad .then(repos => { console.log('返回结果'+repos); })当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
const Foo = () => import('./Foo.vue') const router = new VueRouter({ routes: [ { path: '/foo', component: Foo } ] })打包时每个懒加载的组件会生成一个js文件,用到时按需加载.
webpackChunkName相同的会打包在一个文件
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')keep-alive可以使被包含的组件保留状态,或避免重新渲染
<keep-alive> <router-view/> </keep-alive>如果 没有设置名字,那么默认为 default。
const router = new VueRouter({ routes: [ { path: '/', // 注意多了个 s components: { default: Foo, a: Bar, b: Baz } } ] })