文章目录
VueRouter路由
Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。
安装vue-router插件
vue-router 是一个插件包,所以我们还是需要用 npm/cnpm 来进行安装的。打开命令行工具,进入你的项目目录,输入下面命令。
npm install vue-router --save --registry=https://registry.npm.taobao.org
VueRouter+ElementUI基本使用
ElementUI,下面是根据vuecli2安装的ElementUI.vuecli3可使用插件安装
创建项目,安装依赖
# 使用 webpack 打包工具初始化一个名为 hello-vue-element 的工程
vue init webpack hello-vue-element
# 进入工程目录
cd hello-vue-element
# 安装 vue-router
npm install vue-router --save --registry=https://registry.npm.taobao.org
# 安装 element-ui
npm i element-ui -S --registry=https://registry.npm.taobao.org
# 安装 SASS 加载器,运行时如果报如下错误: Module build failed: TypeError: this.getResolve is not a function 原因是当前sass的版本太高
npm install sass-loader@7.3.1 node-sass --save-dev --registry=https://registry.npm.taobao.org
# 安装依赖初始化
npm install --registry=https://registry.npm.taobao.org
#启动
npm run dev
修改目录结构
在源码目录中创建如下结构:
- 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
}
]
});
导入路由和ElementUI
修改 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中路由匹配到的组件,在标签所在位置渲染
路由匹配规则
path: '/' //根或缺省
path: '' //根或缺省
path: '/params/:foo // :动态路由
path: '/optional-params/:foo?' // ?表示可选参数
path: '/params-with-regex/:id(\\d+)' // 参数后紧跟的括号里可以使用正则
path: '/asterisk/*' // *通配
path: '/optional-group/(foo/)?bar' // ()? 表示路径中的一部分可选
路由跳转
标签
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)。
VueRouter 动态路由
使用动态路径参数(以冒号开头)把某种模式匹配到的所有路由,全都映射到同个组件。
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
}
}
})
r o u t e 路 由 记 录 和 route路由记录和 route路由记录和router路由实例
- $route: 路由记录,即路由配置中routes数组中的一个对象,可用来查看路径参数(params),请求参数(query),路由路径(path),hash等
- $router: 路由实例,new Router创建的路由,用来进行路由跳转
VueRouter 参数传递
使用params的方式
修改路由配置,主要是在 path属性中增加了 :id 这样的占位符
{path: '/user/profile/:id', name:'UserProfile', component: UserProfile}
传递参数
- router-link to方式
<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}});
接收参数
- 在目标组件中接收路径参数:
{{ $route.params.id }}
使用query方式
路由配置使用最普通的方式
{path: '/user/profile', name:'UserProfile', component: UserProfile}
传递参数
<router-link :to="{name: 'UserProfile', query: { plan: 'private'}}">query</router-link>
接收参数
- 在目标组件中接收路径参数:
{{ $route.query.plan }}
使用 props 的方式
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。使用 props 将组件和路由解耦.
修改路由配置,主要增加了 props: true 属性
{path: '/user/profile/:id', name:'UserProfile', component: UserProfile, props: true}
传递参数
- router-link to方式传递
<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}});
接收参数
- 为目标组件增加 props 属性,自动映射
export default {
//接收占位符id传递的值
props: ['id'],
name: "UserProfile"
}
//模板中直接使用
{{ id }}
嵌套路由
嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。
创建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中渲染
VueRouter重定向
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
VueRouter 路由模式与 404
路由模式
路由模式有两种
- hash:路径带 # 符号(默认),如 http://localhost/#/login
- html5的history:路径不带 # 符号,如 http://localhost/login
修改路由配置(/router/index.js文件),代码如下:
export default new Router({
mode: 'history',
routes: [
]
});
使用history模式用户直接访问或者刷新非index.html时,浏览器会绕过VueRouter直接请求后端服务,导致404.后端应进行相应设置
处理 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
字段。
路由钩子(导航守卫)
“导航”表示路由正在发生改变。导航守卫主要用来通过跳转或取消的方式守卫导航。导航守卫就是路由的生命周期钩子函数.
全局钩子
- beforeEach :全局前置钩子
- afterEach: 全局后置钩子,不接受 next 函数
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
meta: {
title: '标题1'
}
}
]
})
// 每个路由跳转前,将要跳转到的路由记录设置的元信息的title值,设置为网页的title
router.beforeEach((to,from,next)=>{
document.title = to.mathed[0].meta.title
next()
})
路由独享的守卫
- beforeEnter: 进入路由之前
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
组件内钩子
- beforeRouteEnter:在进入路由前执行
- beforeRouteLeave:在离开路由前执行
export default {
props: ['id'],
name: "UserProfile",
beforeRouteEnter: (to, from, next) => {
console.log("准备进入个人信息页");
next();
},
beforeRouteLeave: (to, from, next) => {
console.log("准备离开个人信息页");
// 当前页面路由的路径
console.log(this.$route.path);
next();
}
}
路由钩子中如果不使用next(),路由会一种被钩子劫持不能进行跳转.
参数说明:
- to:路由将要跳转的路由记录,类型为route
- from:路由跳转前的路由记录,类型为route
- next:路由的控制参数
- next() 路由继续执行,跳入下一个页面
- next(’/path’) 改变路由的跳转方向,使其跳到另一个路由
- next(false) 返回原来的页面
- next((vm)=>{}) 仅在 beforeRouteEnter 中可用,vm 是组件实例
Axios异步请求
安装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结合router-view
keep-alive可以使被包含的组件保留状态,或避免重新渲染
<keep-alive>
<router-view/>
</keep-alive>
命名视图(一个路由同时渲染多个组件)
<router-view></router-view>
<router-view name="a"></router-view>
<router-view name="b"></router-view>
如果 没有设置名字,那么默认为 default。
const router = new VueRouter({
routes: [
{
path: '/',
// 注意多了个 s
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})