1、路由原理
1、需要路由的原因:
传统开发方式url改变后,立刻发生请求,响应整个页面,有可能资源过多,传统开发会让页面出现白屏的现象,或者加载慢!
2、路由原理:
SPA(Single Page Application)单页面应用
锚点值改变,不立刻发送请求,而是在合适的时机发Ajax请求,局部改变页面数据(改变页面的局部组件)
3、优点:页面不立刻跳转用户体验好
4、模拟路由的实现
1.定义一个a标签,
2.用window.hashchange获取a标签的href属性值
3.根据属性内容值,将不同数据插入到一个入口中
2、vue-router的使用
1、引包
环境准备:引资源包,
方式一:通过npm 下载到本地,
npm install vue-router --save
方式二:bootcdn引入
1、先引vue.js
2、再引入vue-router
2、让vue使用vueRouter对象
Vue.use(vueRouter);
注意:
如果Vue是局部作用域的对象,那么必须上一步,否则可以省略
3、创建路由对象
这里创建的路由对象
var router = new VueRouter({
//4、配路由对象
routes:[
//路由规则
{
path:"/login",
components:Login
},
{
path:"/register",
name:"register",
components:Register
}
]
})
重定向
{
path:'/',
redirect:'/home'
}
router抛出两个全局组件:
<router-link>
默认渲染a标签,to对应路由规则中的path
写法1:<router-link to="/login">
写法2:
在路由规则中,也可以给路由信息加上name属性,通过命名路由方式跳转,to要用绑定属性的方式:to<router-link :to="{name:'register'}">
<router-view>
匹配的路由的出口。(通过路由的方式显示就不要在去挂载相应的组件了)
4、配置路由对象
在组件的template中通过使用router的两个全局组件来配置
var App = {
template: `<div>
<router-link :to="{name:'login'}">登录</router-link>
<router-link to='/register'>注册</router-link>
<router-view></router-view>
</div>
`
}
5、关联路由对象到Vue实例化对象中
new Vue({
el: "#app",
template: `
<App/>
`,
router,
components: {
App
}
})
若没挂载到Vue实例化对象中,会报错
报错内容:
vue.js:1897 TypeError: Cannot read property 'matched' of undefined
at render (vue-router.min.js:6)
at createFunctionalComponent (vue.js:3065)
at createComponent (vue.js:3238)
at _createElement (vue.js:3428)
at createElement (vue.js:3360)
at Proxy.vm._c (vue.js:3497)
at Proxy.eval (eval at createFunction (vue.js:11649), <anonymous>:3:310)
at VueComponent.Vue._render (vue.js:3551)
at VueComponent.updateComponent (vue.js:4067)
at Watcher.get (vue.js:4478)
路由抛出两个全局对象:
路由对象:$router
路由信息:$route
3、路由参数
1、动态路由
路由范式:
1、Xxx.html/user/1
2、Xxx.html/user?userid=1
动态路由参数:
routes:[
{
path:user/:id,
name:"userP"
component:UserP
},{
path:user/,
name:"userQ"
component:userQ
},
]
注意params里的值要和path的冒号后的值对应
:to={'name':"userP",params:{id:1}}
:to={'name':"userQ",query:{userid:2}}
当组件内容不同,整体样式一样的时候可以使用动态路由,结合导航守卫,根据动态传进来的参数修改页面数据:
例子详见嵌套路由中的动态模块
2、嵌套路由
在子组件中使用<route-link>和<route-view>
路由匹配规则中通过children匹配
动态路由通过冒号匹配
routes:[{
path:"/home",
component:Home,
children:[{
path:"song",
component:Song,
},{
path:"/song/:comDec",
component:comDec,
}
]
}
]
具体详见官网
模仿掘金做一个动态路由和嵌套路由
第一步先引vue包,和VueRouter包
第二步加到body中
<div id='app'>
</div>
<script>
var Comdec = {
data(){
return{
msg:"后端"
}
},
template: `
<div>{{msg}}</div>
`,
watch:{
// 导航守卫
$route(to,from){
this.msg = to.params.id
}
}
}
var Home = {
template: `
<div>
<router-link :to = "{name:'im',params:{id:'backend'}}" >后端</router-link>
<router-link :to = "{name:'im',params:{id:'frontend'}}" >前端</router-link>
<router-link :to = "{name:'im',params:{id:'android'}}" >安卓</router-link>
<router-view></router-view>
</div>
`
}
var Pins = {
template: `
<div>我是沸点</div>
`
}
var router = new VueRouter({
routes: [
{
path:'/',
redirect:Home
},
{
path: "/home",
name: "home",
component: Home,
children:[
{
path:'im/:id',
name:'im',
component:Comdec
}
]
},
{
path: "/pins",
name: "pins",
component: Pins
},
{
path: '/',
redirect: '/userq'
}
]
});
var App = {
template: `<div>
<router-link :to="{name:'home'}">首页</router-link>
<router-link :to = "{name:'pins'}">沸点</router-link>
<router-view></router-view>
</div>
`,
}
new Vue({
el:"#app",
data(){
return{
msg:"这是测试"
}
},
template:`
<App/>
`,
router,
components:{
App
}
})
动态路由的目的:
组件相同,内容不同,则可以通过动态路由的方式修改组件数据,不用频繁创建和销毁组件,性能高
动态路由和嵌套路由的应用
动态路由应用:通常用在公共组件中
嵌套路由应用:组件结构不一样
4、导航
1、导航守卫
监听动态路由变化
watch:{
'$route' (to,from){
console.log(to)
}
}
2、全局路由守卫
meta和全局路由的渲染前配置
给未来路由做权限控制,全局路由守卫来做参照条件
1、路由配置信息中给需要权限控制的对象加meta
meta:{
auth:true
}
2、在进入路由前加判断
router.beforeEach((to,from,next)=>{
if(to.meta.auth){
//处理
}else{
//直接放行
next()
}
})
关于next的概念:
不传参数代表直接跳转到当前路由,next()
传参数跳转到对应的路由next({path:'/login'})
例子:
<div id='app'>
</div>
<script>
var Comdec = {
data() {
return {
msg: "后端"
}
},
template: `
<div>{{msg}}</div>
`,
watch: {
'$route'(to, from) {
this.msg = to.params.id
}
}
}
var Blog = {
template: `
<div>
我是博客
</div>
`
}
var Pins = {
template: `
<div>点我发布</div>
`
}
var Login = {
data() {
return {
name: "zhangsan",
pwd: 124
}
},
template: `
<div>
<input type = text v-model = "name"/>
<input type = text v-model = "pwd"/>
<input type = button value = "登录" @click = 'login' />
</div>
`
,
methods: {
login() {
window.localStorage.setItem("item", { name: this.name, pwd: this.pwd });
let log = window.localStorage.getItem("item")
console.log(log);
this.$router.push('/pins');
}
}
}
Vue.use(VueRouter)
var router = new VueRouter({
routes: [
{
path: "/blog",
name: "blog",
component: Blog,
},
{
path: "/pins",
name: "pins",
meta: {
auth: true
},
component: Pins
},
{
path: "/login",
name: "login",
component: Login
}, {
path:'/',
redirect:Blog
},
]
});
router.beforeEach((to, from, next) => {
if (to.meta.auth) {
if(window.localStorage.getItem("item")){
console.log(this)
next()
}else{
next({ path: '/login' })
}
// this.$router.push('/login');
// router.push('/login');
} else {
next()
}
})
var App = {
template: `<div>
<router-link :to="{name:'blog'}">博客</router-link>
<router-link :to = "{name:'pins'}">发布</router-link>
<router-link :to = "{name:'login'}" >登录</router-link>
<a href="javascript:void(0)" @click = "out">退出</a>
<router-view></router-view>
</div>
`,
methods: {
out() {
window.localStorage.removeItem("item");
if(this.$router.currentRoute.name!=="login"){
this.$router.push('/login');
}
}
}
}
new Vue({
el: "#app",
template: `
<App/>
`,
router,
components: {
App
}
})
</script>
3、路由的两种导航
1、声明式导航
<router-link>
2、编程式导航
参数可以是name属性,也可以path属性
this.$router.push({name:'blog'});
this.$router.push('/login');
4、 路由跳转的bug
当login跳转到login的时候路由相同会报错,在做路由跳转的时候可以先判断下当前路由是否等于要跳转的路由
if(this.$router.currentRoute.name!=="login"){
this.$router.push('/login');
}
错误内容
vue-router.min.js:6 Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: "/login".
at xt (http://127.0.0.1:5500/code/node_modules/vue-router/dist/vue-router.min.js:6:15948)
at e.St.confirmTransition (http://127.0.0.1:5500/code/node_modules/vue-router/dist/vue-router.min.js:6:18842)
at e.St.transitionTo (http://127.0.0.1:5500/code/node_modules/vue-router/dist/vue-router.min.js:6:18210)
at e.push (http://127.0.0.1:5500/code/node_modules/vue-router/dist/vue-router.min.js:6:22535)
at http://127.0.0.1:5500/code/node_modules/vue-router/dist/vue-router.min.js:6:25920
at new Promise (<anonymous>)
at Ft.push (http://127.0.0.1:5500/code/node_modules/vue-router/dist/vue-router.min.js:6:25884)
at VueComponent.out (http://127.0.0.1:5500/code/DAY3-%E6%8F%92%E4%BB%B6vueRouter/5%E3%80%81%E5%85%A8%E5%B1%80%E8%B7%AF%E7%94%B1%E5%AE%88%E5%8D%AB.html:144:38)
at invokeWithErrorHandling (http://127.0.0.1:5500/code/node_modules/vue/dist/vue.js:1863:28)
at HTMLAnchorElement.invoker (http://127.0.0.1:5500/code/node_modules/vue/dist/vue.js:2188:16)
5、组件中的导航守卫
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
注意1:beforeRouteEnter
不!能!获取组件实例 this
,可以通过next函数的回调函数调用:
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
注意2:动态路由。组件相同数据不同,这个函数只会进来一次,切换路由的时候,到beforeRouteUpdate中
注意3:如果写了 beforeRouteUpdate
和 beforeRouteLeave
函数,需要调用一下next()方法,否则路由进不去
注意4:路由离开前。要离开这个组件才会进入这个函数
学习小demo。实现组件内动态路由的跳转,和离开,实现清空定时器和保存数据后离开的小功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.min.js"></script>
<script src="../DAY4-axios/node_modules/axios/dist/axios.js"></script>
</head>
<body>
<div id="app"></div>
<script>
// https://www.easy-mock.com/mock/5d65ef060d2acc6a1fb9a126/example/restful/:id
Vue.prototype.$axios = axios;
var User = {
template:`
<div>
<h2>{{userName}}</h2>
<input type="text" v-model = "name" placeholder="请输入名字"></input>
<button @click = "saveValue">保存</button>
<h3>{{saveName}}</h3>
</div>`,
data(){
return{
userName:"用户",
timer:null,
num:null,
name:"",
saveName:''
}
},
methods:{
changName(val){
this.userName = val.data.user;
},
saveValue(){
this.saveName = this.name
}
},
beforeRouteEnter(to,from,next){
//注意1:动态路由。组件相同数据不同,这个函数只会进来一次,切换路由的时候,到beforeRouteUpdate中
// 注意2:这个函数内路由还灭挂载通过this去取不到组件对象,需要通过next函数取
axios.defaults.baseURL = "https://www.easy-mock.com/mock/5d65ef060d2acc6a1fb9a126/example"
console.log(to.params.id);
axios.get(`/vuelearn/${ to.params.id }`).then(res=>{
console.log(res);
next((vm=>{
vm.changName(res);
}))
}).catch(err=>{
console.log(err)
})
},
beforeRouteUpdate(to,from,next){
// 注意3:一定要调用next函数,否则组件不会进来
axios.get(`/vuelearn/${ to.params.id }`).then(res=>{
this.changName(res)
}).catch(err=>{
console.log(err)
});
next();
if(to.params.id==2){
// 添加定时器
var time = new Date();
this.num =time.getSeconds()
this.timer = setInterval(()=>{
this.num++;
console.log(this.num);
},1000);
}
},
beforeRouteLeave(to,from,next){
// 路由离开前。要离开这个组件才会进入这个函数
console.log("离开了");
// 常用场景1:清空定时器
clearInterval(this.timer);
// 判断是否保存,保存了才能离开
if(this.saveName===this.name){
next()
}else{
confirm("请保存后在离开")
}
}
}
var Test = {
template:`
<div>我是测试</div>`,
}
var router = new VueRouter({
routes:[
{
path:"/test",
component:Test,
name:"test"
},
{
path:"/user/:id",
component:User,
name:"user"
}
]
})
// <router-link :to = "{name:'user',params:{id:2}}">用户2</router-link>
var App = {
template:`
<div>
<router-link :to = "{name:'user',params:{id:1}}">用户1</router-link>
<router-link :to = "{name:'user',params:{id:2}}">用户2</router-link>
<router-link to="/test">测试</router-link>
<router-view></router-view>
</div>
`,
components:{
User,
Test
}
}
new Vue({
el:'#app',
template:`
<App/>
`,
router,
components:{
App
}
})
</script>
</body>
</html>
6、keep alive在路由中的使用
缓存机制
<keep-alive>
<router-view><router-view>
</keep-alive>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。