认识路由
路由介绍
路由(routing) 就是通过互联的网络信息从源地址传输到目的地址的活动
路由提供两种机制:**路由**和**转发**
1.路由是决定数据包从来源到目的地的路径
2.转发将输入端的数据转移到合适的输出端
路由有个非常重要的概念是路由表
路由表本质上就是一个映射表,决定了数据包的指向
后端路由
早期网站开发由服务器渲染HTML页面也称后端渲染
那么什么是后端渲染
一个页面有自己对应的网址,也就是URL
URL会发送至服务器,通过正则匹配对该URL进行匹配,并且最后交给一个Controller进行处理
Controller进行各种处理,最终生成HTML或数据,返回前端
以上就完成了一个IO操作
当我们页面中需要请求不同的路径内容时,交给服务器来进行处理,服务器渲染好整个页面,并且将页面返回给客户顿,这种情况下渲染好的页面,不需要单独加载任何的 js 和 css ,可以直接交给浏览器展示这样也有利于 SEO 的优化。
缺点:
一种情况是整个页面的模块由后端人员来编写和维护的.
另一种情况是前端开发人员如果要开发页面,需要通过 PHP 和 java 等语言来编写页面代码,
而且通常情况下 HTML 代码和数据以及对应的逻辑会混在一起编写和维护都是非常糟糕的事情
前端路由
前后端分离阶段
随着Ajax的出现有了前后端分离的开发模式
后端只提供API来返回数据,前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面中, 这样做最大的优点就是前后端责任清晰,
后端专注于数据上前端专注于交互和可视化上 并且当移动端(iOS/Android)出现后,后端不需要进行任何处理依然使用之前的一套API即可
目前很多的网站依然采用这种模式开发
单页面富应用阶段
其实SPA最主要的特点就是在前后端分离的基础上加了一层前端路由口 也就是前端来维护一套路由规则.
Vue-router基本使用
URL的hash
URL的hash也就是锚点(#),本质上是改变window.location的href属性
我们可以通过直接赋值location.hash来改变href 但是页面不发生刷新
1 2 3 4 5 6
| location.href 显示当前路由 "http://localhost:8080/#/"
location.hash='/' "/"
|
HTML5的history模式:pushState
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| history.pushState({data},'title','url') 例: history.pushState({},'','html') undefined http:
history.pushState({},'','about') undefined
http:
history.pushState({},'','me') undefined http:
history.back() #通过back消除原先的进栈 http: 以此类推
history.go(-1) http: 以此类推
history.go(1) http: 以此类推
|
补充说明
上面只演示了三个方法
因为 history.back() 等价于 history.go(-1) ,history.forward() 则等价于 history.go(1)。
这三个接口等同于浏览器界面的前进后退
认识Vue-router
目前前端流行的三大框架,都有自己的路由实现:
- Angular的ngRouter
- React的ReactRouter
- Vue的vue-router
vue-router是Vuejs官方的路由插件,它和vuejs是深度集成的,适合用于构建单页面应用。
路由用于设定访问路径,将路径和组件映射起来
vue-router的单页面应用中,页面路径的改变就是组件间的切换
安装vue-router
- 安装vue-router:npm install vue-router –save
- 模块化工程使用(ps:因为是一个插件,所以可以通过Vue.use()来安装路由功能)
- 导入路由对象,并调用Vue.use(VueRouter)
- 创建路由实例,并传入路由映射配置
- 在Vue实例中挂载创建的路由实例
使用vue-router
- 创建路由组件
- 配置路由映射: 组件和路径映射关系
- 使用路由: 通过和
例(以我自己的编写的为例):
router文件下的index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
import Vue from 'vue' import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
]
const router = new VueRouter({ routes, })
export default router
|
src文件下的main.js
1 2 3 4 5 6 7 8 9 10 11 12
| import Vue from 'vue' import App from './App' import router from './router' 导入index.js的router
Vue.config.productionTip = false
new Vue({ el: '#app', router, 挂载导入router实例 render: h => h(App) })
|
路由映射配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
import Vue from 'vue'; import VueRouter from 'vue-router'; import Home from "../components/Home" import About from "../components/About"
Vue.use(VueRouter);
const routes = [{ path: '/home', component: Home }, { path: '/about', component: About } ];
const router = new VueRouter({ routes, });
export default router;
|
Home组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <template> <div> <h2>我是首页</h2> <p>我是首页,hhh</p> </div> </template>
<script> export default { name: 'Home',
data() { return {
}; },
mounted() {
},
methods: {
}, }; </script>
<style>
</style>
|
About组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <template> <div id="Home"> <h2>我是关于</h2> <p>我是关于,aaa</p> </div> </template>
<script> export default { name: 'About',
data() { return {
}; },
mounted() {
},
methods: {
}, }; </script>
<style scoped>
</style>
|
App组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <template> <div id="app"> <router-link to="/home">首页</router-link> <router-link to="/about">关于</router-link> </div> </template>
<script> export default { name: 'App' } </script>
<style> </style>
|
进入网站首页需要默认进入显示,那么就要使用redirect重定向
1 2 3 4 5
| 在routes下再配置一个映射 { path:'', redirect:'/home' },
|
将原本URL的hash模式路径更改为HTML5的history模式
默认路径显示的是URL的hash模式这样子做的话就看起来有点不美观多个/#/这个就感觉会怪怪的
所以在router下设置mode为history模式
1 2 3 4 5 6 7
| const router = new VueRouter({ routes, mode:'history' }); 原先显示路径为:http: 显示路径为:http:
|
router-link补充
router-link:
to将映射的路由显示在改内容上,通过router-view显示组件内容,是vue自带的一个标签默认形式为a标签可以通过tag更改标签属性,同时replace可以禁止用户通过点击前进后退网页键,router-link-active默认为route-link的class名,通过active-class可以更改class名,也可以通过在router实例属性下设置linkActiveClass进行修改
通过代码进行路由跳转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <button @click="homeclick">首页</button> <button @click="aboutclick">关于</button>
methods:{ homeclick(){ this.$router.replace('/home'); }, aboutclick(){ this.$router.replace('/about'); }
}
|
动态路由
在不确定的情况下,页面的路径是不确定的
希望路径为/user/aaaa或/user/bbbb
除了有前面的/user之外,后面还跟上了用户的ID
这种path和Component的匹配关系,我们称之为动态路由(也是路由传递数据的一种方式)
1 2 3 4
| { path:'/user/:userId', component:User }
|
app组件截取片段
1 2 3 4 5 6 7
| <router-link v-bind:to="'/user/'+userid">用户</router-link> data(){ return{ userid:'list' } },
|
user组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <div> <h2>我是用户界面</h2> <p>我是用户的相关信息,lll</p> <h2>{{ userId }}</h2> </div> </template>
<script> export default { name: "User", computed:{ userId(){ return this.$route.params.userId } } } </script>
<style scoped>
</style>
|
认识路由懒加载
官方解释:
当打包构建应用时,Javascript 包会变得非常大,影响页面加载。
如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
打包之后会一个dist文件夹其中包含了static文件夹和index.html文件,其中static文件夹下又包含有css和js文件夹其中js文件夹(以我自己打包好的js里的文件名为例)
app.211f9a59264ede307579 => 存放业务代码(开发者所有代码)
manifest.2ae2e69a05c33dfc65f8 => 为打包代码做底层支撑
vendor.eebede3e224c6b5c62ca => 存放提供商也就是第三方插件
路由懒加载使用
首先,我们知道路由中通常会定义很多不同的页面
这个页面最后被打包在哪里呢?一般情况下是放在一个js文件中
但是,页面这么多放在一个js文件中,必然会造成这个页面非常的大
如果我们一次性从服务器请求下来这个页面,可能需要花费一定的时间,甚至用户的电脑上还出现了短暂空白的情况
如何避免这种情况呢?
使用路由懒加载就可以
路由懒加载做了什么?
路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块
只有在这个路由被访问到的时候,才加载对应的组件
懒加载方式
1.结合Vue的异步组件和Webpack的代码分析.
1
| const Home = resolve =>{ require.ensure(['../components/Home.vue'],()=> resolve(require('../components/Home.vue'))])};
|
2.AMD写法
1
| const About = resolve => require(['../components/About.vue'],resolve);
|
3.在ES6中,我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割 (最常用)
1
| const Home =()=>import('../components/Home.vue")
|
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const Home =() => import('../components/Home') const About =() => import('../components/About') const User =() => import('../components/User')
{ path: '/home', component:Home }, { path: '/about', component: About }, { path:'/user/:userId', component:User }
|
Vue-router嵌套路由
嵌套路由是一个很常见的功能
比如在home页面中,我们希望通过/home/news和/home/message访问一些内容
一个路径映射一个组件,访问这两个路径也会分别渲染两个组件
实现嵌套路由分两步
- 创建对应子组件,并且在路由映射中配置对应的子路由
- 在组件内部使用router-view标签
index.js片段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const Home =() => import('../components/Home') const HomeNews =() => import('../components/HomeNews') const HomeMessage =() => import('../components/HomeMessage')
{ path: '/home', component:Home, children:[ { path:'news', component:HomeNews }, { path:'message', component:HomeMessage } ] },
|
Home组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <template> <div id="Home"> <h2>我是首页</h2> <p>我是首页,hhh</p> <router-link to="/home/news">新闻</router-link> <router-link to="/home/message">消息</router-link> <router-view ></router-view> </div> </template>
<script> export default { name: 'Home',
data() { return {
}; },
mounted() {
},
methods: {
}, }; </script>
<style>
</style>
|
HomeNews组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <div> <ul> <li>新闻1</li> <li>新闻2</li> <li>新闻3</li> <li>新闻4</li> <li>新闻5</li> </ul> </div> </template>
<script> export default { name: "HomeNews" } </script>
<style scoped>
</style>
|
HomeMessage组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <div> <ul> <li>消息1</li> <li>消息2</li> <li>消息3</li> <li>消息4</li> <li>消息5</li> </ul> </div> </template>
<script> export default { name: "HomeMessage" } </script>
<style scoped>
</style>
|
Vue-router参数传递
演示参数传递
为了演示传递参数我们这里再创建一个组件,并且将其配置好
- 创建新的组件Profile.vue
- 配置路由映射
- 添加跳转的
route参数传递
params的类型
配置路由格式:**/router/:id**
传递的方式:在path后面跟上对应值
传递后形成的路径:/route/123,/router/abc
1 2 3 4 5 6 7 8 9 10 11 12
| { path:'/user/:id', component:User } data(){ return{ userid:'list' } } <router-link v-bind:to="'/user/'+userid">用户</router-link>
|
query的类型
配置路由格式:**/router**,就是普通配置
传递方式:对象中使用query的key作为传递方式
传递后形成的路径:/route?id=123,/router?id=abc
1 2 3 4 5
| const Profile =() => import('../components/Profile') { path: '/profile', component:Profile }
|
使用
app组件截取片段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <router-link :to="{path:'/profile',query:{name:'why',age:18,height:1.88}}">档案</router-link>
<button @click="userClick">用户</button> <button @click="profileClick">档案</button> data(){ return{ userid:'list' } } userClick(){ this.$router.push('/user/'+this.userid) }, profileClick(){ this.$router.push({ path:'/profile', query:{ name:'zhangsan', age:17, height:1.75 } }) }
|
profile组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div> <h2>我是Profile</h2> <h2>{{ $route.query.name }}</h2> <h2>{{ $route.query.age }}</h2> <h2>{{ $route.query.height }}</h2> </div> </template>
<script> export default { name: "Profile" } </script>
<style scoped>
</style>
|
理解router与route
$route和$router是有区别的
$router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
$route为当前router跳转对象里面可以获取name、path、query、params等
Vue-router导航守卫
为什么使用导航守卫
我们来考虑一个需求:在一个SPA应用中,如何改变网页的标题呢?
网页标题是通过title来显示的,但是SPA只有一个固定的HTML切换不同的页面时,标题并不会改变但是我们可以通过JavaScript来修改title的内容window.document.title =’新的标题’
那么在Vue项目中,在哪里修改?什么时候修改比较合适呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| const routes = [ { path:'', redirect:'/home' }, { path: '/home', component:Home, meta:{ title:'首页' }, children:[ { path:'news', component:HomeNews },{ path:'message', component:HomeMessage } ] }, { path: '/about', component: About, meta:{ title:'关于' } }, { path:'/user/:id', component:User, meta:{ title:'用户' } },{ path: '/profile', component:Profile, meta:{ title:'档案' } } ];
router.beforeEach((to, from, next)=>{ document.title=to.matched[0].meta.title; next(); console.log('++++++') })
router.afterEach((to, from)=>{ console.log('-------') })
|
meta:元数据(描述数据的数据)
to:即将要进入的目标的路由对象
from:当前导航即将要离开的路由对象
next: 调用该方法后,才能进入下一个钩子
补充
- 如果是后置钩子,也就是afterEach,不需要主动调用next()函数.
- 上面我们使用的导航守卫,被称之为全局守卫
路由独享的守卫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const router = new VueRouter({ routes:[ { path:'/foo', component:Foo, befoteEnter:(to,from,next)=>{ } } ] }) 例: { path: '/about', component: About, meta:{ title:'关于' }, befoteEnter:(to,from,next)=>{ console.log('about befoteEnter') next(); }
}
|
组件内的守卫
1 2
| 可以通过访问官方网站深入学习相关知识 Vue-router官方网站: https:
|
keep-alive
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:
keep-alive有两个非常重要的属性:
include-字符串或正则表达,只有匹配的组件会被缓存
exclude-字符串或正则表达式,任何匹配的组件都不会被缓存
保持原有操作状态
比如:首页点击消息后,跳转其他页面再跳转回来,页面显示为最原先操作的页面内容即保持不变
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #app组件片段截取 <button @click="userClick">用户</button> <button @click="profileClick">档案</button> <keep-alive> <router-view/> </keep-alive>
#Home组件内容 <template> <div id="Home"> <h2>我是首页</h2> <p>我是首页,hhh</p> <router-link to="/home/news">新闻</router-link> <router-link to="/home/message">消息</router-link> <router-view></router-view> </div> </template>
<script> export default { name: 'Home', data() { return { message:'aaa', path:'/home/news' }; }, created() { console.log('home created') }, destroyed() { console.log('home destroyed') }, activated() { console.log('activated'); this.$router.push(this.path); }, beforeRouteLeave(to,from,next){ this.path = this.$route.path next() }, methods: {
}, }; </script>
<style>
</style>
|
注意:
其中activated与deactivated这两个函数需要该组件保持了状态使用keep-alive时,才会有效