PS:转载请注明出处
作者: TigerChain
地址https://www.jianshu.com/p/9a7d79249741
本文出自 TigerChain 简书 手把手教 Vue 系列
大纲
教程简介
1、阅读对象
本篇教程适合新手阅读,老手直接略过2、教程难度
初级,本人水平有限,文章内容难免会出现问题,如果有问题欢迎指出,谢谢3、Demo 地址:https://github.com/githubchen001/vue-lesson 请看 06、Vue路由 这一节
4、演示地址
正文
一、什么是路由
以前在 React 的文章中说过路由这个东西,这里再说一下「再次加深一下记忆」。路由是什么我们可能不太理解,但是我说一个东西我们一定知道,就是"路由器",路由器的功能用一句话概括就是:数据从一个网络到另一个网络就是靠路由器来完成的[当然路由器的功能不仅仅于此]。
我们说的程序开发中的路由不是指路由器和网络协议中的路由,但是基本思想是一样的。而路由又分为前端路由和后端路由。
我们来看一个路由的简易图吧,有了这个图,大家对路由就有一个大致的了解了。
路由简易图
1、后端路由
举个栗子,分配一个站点,服务器地址是:http://192.168.1.200:8899
,在这个网站中提供了三个界面
http://192.168.1.200:8899/index.html 主页http://192.168.1.200:8899/about/aboutus.html 关于我们页面http://192.168.1.200:8899/feedback.html 反馈界面
当我们在浏览器输入 http://192.168.1.200:8899/index.html
来仿问界面的时候,web 服务器就会接收到这个请求,然后把 index.html 解析出来,并找到相应的 index.html 并展示出来,这就是路由的分发,路由的分发是通过路由功能来完成的
2、前端路由
虽然前端路由和后端路由的实现方式不一样,但是原理都有是相同的,在 H5 的 history Api 出来之前,前端路由的功能都是通过 hash 「散列值」 来实现的,hash 能兼容低版本的浏览器
PS:后端路由每次仿问一个页面都要向浏览器发送请求,然后服务端再响应解析,在这个过程中肯定会存在延迟,但是前端路由中仿问一个新的界面的时候只是浏览器的路径改变了,没有和服务端交互「所以不存在延迟」,这个对用户体验来说是大大的提高。如下所示
http://192.168.1.200:8080/#/index.htmlhttp://192.168.1.200:8080/#/about/aboutus.htmlhttp://192.168.1.200:8080/#/feedback.html
由于 web 服务器不会解析 # 后面的东西「所以通过 hash 能提高性能」,但是客户端的 js 可以拿到 # 后面的东西,有一个方法是 window.location.hash 来读取,使用这个方法来匹配到不同的方法上「配合前端的一些逻辑操作就完成路由功能,剩下只是关心接口调用」
3、举个栗子
假设有一个地址
http://www.xxx.com/path/a/b/c.html?key1=Tiger && key2=Chain && key3=fuck#/path/d/e.html
1、我们把这个地址分析一下「这个地址基本上包含了一个复杂地址的所有情况」
http:协议 www.xxx.com:域名 /path/a/b/c.html:路由,即服务器上的资源 ?key1=Tiger && key2=Chain && key3=fuck:这个很好理解 Get 请求的参数#/path/d/e.html:hash 也叫散列值,也叫锚点
上面的 hash 是和浏览器交互的,其它的都是和服务器进行交互
通过上述我们知道,前端路由的实现方式有两种:
(1)、一是改变 hash 值,监听 hashchange 事件,可以兼容低版本浏览器
(2)、二是通过 H5 的 history API 来监听 popState 事件,使用 pushState 和 replaceState 实现
2、hash 改变,不会导致浏览器刷新「请求服务器」,我们来写个 demo 验证一下
先看一下效果图
hash-router
从图中我们可以看到,使用 hash 并不会导致浏览器刷新,并且我们 js 拿到了 hash 值并且打印出来了
3、源码
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>hash 实现前端路由</title> <style> #nav { margin: 0; border:0; height: 40px; border-top: #060 2px solid; margin-top: 10px; border-bottom: #060 2px solid; background-color: red; } #nav ul { margin: 0; border: 0; list-style: none; line-height: 40px; } #nav li { display: block; float: left; } #nav a { display: block; color: #fff; text-decoration: none; padding: 0 20px; } #nav a:hover { background-color: orange; } </style></head><body> <h3>使用 hash 实现前端路由</h3> <hr/> <a href="#hash1">#hash1</a> <a href="#hash2">#hash2</a> <a href="#hash3">#hash3</a> <a href="#hash4">#hash4</a> <p/> <div id = "show-hash-result" style="color:blue"> 点击上面链接,并观察浏览器 </div> <h4>定义一个简单的 tab 路由页面</h4> <div id="nav"> <ul> <li><a href="#/index.html">首页</a></li> <li><a href="#/server">服务</a></li> <li><a href="#/mine">我的</a></li> </ul> </div> <div id="result"></div> <script type="text/javascript"> window.addEventListener("hashchange", function(){ //变化后输出当前地址栏中的值 document.getElementById("show-hash-result").innerHTML = "当前的 hash 值是: "+location.hash; //打印出当前 hash 值 console.log("当前的 hash 值是:"+window.location.hash) ; }); </script><!-- 定义 router 的 js 代码块 --> <script type="text/javascript"> //自定义一个路由规则 function CustomRouter(){ this.routes = {}; this.curUrl = ''; this.route = function(path, callback){ this.routes[path] = callback || function(){}; }; this.refresh = function(){ if(location.hash.length !=0){ // 如果 hash 存在 this.curUrl = location.hash.slice(1) || '/'; if(this.curUrl.indexOf('/')!=-1){ //这里粗略的把 hash 过滤掉 this.routes[this.curUrl](); } } }; this.init = function(){ window.addEventListener('load', this.refresh.bind(this), false); window.addEventListener('hashchange', this.refresh.bind(this), false); } } //使用路由规则 var R = new CustomRouter(); R.init(); var res = document.getElementById('result'); R.route('/hash1',function () { document.getElementById("show-hash-result").innerHTML = location.hash; }) R.route('/index.html', function() { res.style.height='150px'; res.style.width='300px'; res.style.background = 'green'; res.innerHTML = '<html>我是首页</html>'; }); R.route('/server', function() { res.style.height='150px'; res.style.width='300px'; res.style.background = 'orange'; res.innerHTML = '我是服务页面'; }); R.route('/mine', function() { res.style.background = 'red'; res.style.height='150px'; res.style.width='300px'; res.innerHTML = '我的界面'; }); </script></body></html>
以上代码只是为了演示前端路由的作用,一般情况下,这种路由我们是不需要自己写的,使用 react/vue 都会有相应的路由工具类,我们发现了 hash 只会改变浏览器地址,不会刷新浏览器
H5 的 history
window 的 history 提供了对浏览器历史记录的访问功能,并且它暴露了一些方法和属性,让你在历史记录中自由的前进和后退,并且在 H5 中还可以操作历史记录中的数据。
我们在 chrome 浏览器的调试窗口中在 Console 中输入 window.history,会得到 history 的一些方法和属性,如下图所示
history-api
总结一下 history 的 API 如下:
interface History { readonly attribute long length; readonly attribute any state; void go(optional long delta); void back(); void forward(); //h5 引进以下两个方法 void pushState(any data, DOMString title, optional DOMString? url = null); void replaceState(any data, DOMString title, optional DOMString? url = null); };
1、
back():
在历史记录中后退
history.back() ;
2、
forward:
在历史记录中前进
history.forward();
3、
go():
移动到指定的历史记录点
history.go(-1)
其中正数是前进「+1就是前进一个界面」,负责是后退的意思「-1就是后退一个界面」
4、
length:
hisgory 的属性,显示 history 的长度5、
pushState(data,title[,url]):
给历史记录堆栈顶部添加一条记录
history.pushState(data,title[,url])
如果想更进一步的了解 H5 的 history ,推荐看这里:https://developer.mozilla.org/en-US/docs/Web/API/History_API,非常值得一看
从上面我们了解到,使用 H5 的 history 的 pushState 可以代替 hash,并且更加优雅,废话不多说,我们直接上效果图
history-demo
从效果图中我们可以看到前端路由实现了,点击各个导航没有刷新浏览器,并且点击浏览器的回退按钮,会显示上一次记录,这都是使用 h5 history 的 pushState 和监听 onpopstate 实现的,这就是一个简单的 SPA ,基本上实现了和上面 hash 一样的功能
源码
我们只看核心代码
<h4>使用 h5 实现前端路由</h4> <ul> <li> <a ="home()">首页</a></li> <li> <a ="message()">消息</a></li> <li> <a ="mine()">我的</a></li> </ul> <div id="showContent" style="height:240px;width:200px;background-color:red"> home </div> <script type="text/javascript"> function home() { // 添加到历史记录栈中 history.pushState({name:'home',id:1},null,"?page=home#index") showCard('home') }; function message() { history.pushState({name:'message',id:2},null,"?page=message#haha") showCard('message') } function mine(){ history.pushState({ id:3, name:'mine' },null,"?name=tigerchain&&sex=man") showCard('mine') } // 监听浏览器回退 并且刷新到指定内容 window.addEventListener('popstate',function (event) { var content = ""; if(event.state) { content = event.state.name; } console.log(event.state) console.log("history 中的历史栈中的 name :"+content) showCard(content) }) // 此方法和上面的方法是一毛一样的,只是两种不同的写法而已 // window.onpopstate = function (event) { // var content = ""; // if(event.state) { // content = event.state.name; // } // showCard(content); // } function showCard(name) { console.log("当前的 hash 值是:"+location.hash) document.getElementById("showContent").innerHTML = name; } </script>
以上就是通过 H5 的 history 实现的一个前端路由
我们稍微总结一下:
http://192.168.1.200:8080/indexhttp://192.168.1.200:8080/about/aboutus.html/#/flag=1http://192.168.1.200:8080/feedback
后端路由:每次仿问都要向 server 发送一个请求,server 需要响应解析,会有延迟「网络不好更严重」
前端路由:只是改变浏览器的地址,不刷新浏览器,不和服务端交互,所以性能有大大的提高「用户体验提高」,并且前端路由有两种实现方式
(1)、实现 hash 并监听 hashchange 事件来实现
(2)、使用 H5 的 hisgory 的 pushState() 监听 popstate 方法来实现
到这里,我们大概对路由有一个整体的了解了,下面我们看看 veu 的路由
二、Vue 路由
Vue 中的路由,推荐使用官方支持的 vue-router 库,当然我们可以不使用 vue-router 可以使用三方的路由库,或者自己牛 b 完全可以自己写一个路由库「使用 hash 或 history」
本文中我们使用 vue-router 3.0.1来讲解,考虑到团队开发协作,我们先写一个使用 html 引入 vue.js 的方式来使用 vue-router,后面专注说使用模块化开发「使用 vue-cli 创建项目中使用 vue-router,这应该是团队开发的最佳方式」
html 中 引入 vue-router
1、废话不多了,直接写一个简单的 SPA 应用来感受一下
效果如下:
vue-in-html-router
从上图中我们可以看到,我们使用 vue-router 实现了一个简单的类 hash 的路由功能
2、源码
1、引入 vue.js 和 vue-router.js
import-aboutjs
2、定义 Main、Message、Mine 组件
custom-vue-component
3、声明路由规则
decale-router
声明路由规则,把路径所对应要跳转的组件先定义出来「相当于一个配置项」,到时候浏览器地址指定到对应的路由下会自动跳转到所对应的组件「这样就完成了路由功能」
4、创建 router 实例
create-router-instance
创建 router 实例,并把 routes 传递进去
5、注册 router「把 router 注入到指定的挂载点下」
register-router
经过以上几步,路由功能就完成了,我们现在定义了路径,定义了路由所对应的组件,那组件要显示在哪里呢?那就前面 1 中的图中所显示的 <router-view />
中
可是如何让我在点击不同的按钮进不同的路由「不是的组件呢?」,这里就要使用到 <router-link to="路由配置中的路径">比如首页<router-link />
,从名字就可以看出来就是路由链接到那个路径,路径会匹配出相应的组件显示在 router-view
中
这样我们就完成了 vue-router 的基本使用,使用声明式导航 <router-link :to="...">
「其实就是创建了一个 a 标签」来完成了导航的链接
模块化「组件式开发」中 vue-router 的使用
如果使用 vue 开发手机 webapp ,那么页面跳转就非常多,路由使用的非常非常多,这样就更能体现出路由的强大之处,在这里我们使用 vue-cli 来创建一个 webapp ,来模拟一个简单的手机 app ,体验一下路由
效果如下:
router-demo
1、初始化项目
vue-init-router-demo
使用 vue-cli 命令创建项目的时候,我们选择安装 vue-router,默认进去项目中就会有 vue-router 的配置,创建好的项目结构如下:
folder-struct
我们看到创建的目录结构多了一个 router 目录和 index.js 文件「vue-cli默认给添加的,如果你选了安装 vue-router 的话」
2、文件简单的分析
我们来看看 router 下的 index.js「路由配置文件」
router-indexjs
我们可以看到默认 vue-cli 创建的带路由的项目把 router 配置文件单独的写在了 router/index.js 文件中了,并且我们看到默认指定打开的是 HelloWorld 组件「想配置路由组件,引入组件配置即可」
再看 main.js
router-mainjs
一般来说 APP.vue 就是我们应用的首页,我们在 main.js 中注入路由,从而让整个应用都具有路由功能
3、再看 App.vue
核心就一个 <router-view/>
即可路由组件要显示的地方,默认路由路径是 / 对应的是 HelloWorld 组件,所以运行起应用就会显示 HelloWorld 组件,这里不显示运行后的结果了,我们使用 vue-cli 创建的 demo 样式看的太多了,自行运行查看即可
经过以上分析,我们基本上就把 vue-cli 带路由的 demo 说完了「核心就这几个东西,剩下的无非就是配置路由,然后组件组合,然后各种路由跳转」
4、修改 demo,一步步修改成效果图的样式
添加 First.vue 文件「核心代码
add-firstvue
样式和数据「mockList 就是一个数组」就不截图了,完整例子可以查看 源码:https://github.com/githubchen001/vue-lesson/tree/master/06、Vue路由/vue-cli-router-webapp
其中的条目点击方法 nav(index)
就是路由跳转功能
first-item-nav-method
以上点击方法我们使用编程式导航「跳转」,当然你也可以不传参数
this.$router.push({name:xxx,params:{xxxx}})
细心的朋友会发现,我们这个 name 叫 second 这是那里来的,它其实就是我们在 router/index.js 中配置的别名,再看 router/index.js 文件之前,我们先添加一个 Second.vue 组件
添加 Second.vue 组件「核心代码
second-vue
这个没有什么好说的,就是一个有导航条并且接收到 First.vue 路由跳转传递过来的参数
修改 router/index.js 文件
前面我们使用跳转 this.$router.push({name:'second',params:{itemData:this.mockList[index]}})
中传了一个 name 为 second ,我们说了这是在 router/indes.js 中配置的,下面我们来看此文件
modify-router-indexjs
我们看到 / 对应的是 First 组件,/second 对应的是 Second 组件,并且分别给了 name 属性「在路由 push 的时候就可以使用到」,没什么好说的,当然你也可以不写 name 属性,路由跳转有几种写法
// 命名的路由router.push({ path: '/second', params: { xxx:xxx }})// 对象router.push({ name: 'second', params: { xxx:xxx }})
等几种方式,如果有 name 属性直接使用即可,如果没有就使用 path「router/inde.js 中配置的路由 path」 即可,具体可以看官网:https://router.vuejs.org/zh-cn/essentials/navigation.html
如果想返回上一个界面,那么就使用 this.$router.go(-1)
和 h5 的 history 是一样的
修改 App.vue
我们来修改 App.vue 来让每个组件的内容都能全屏显示「我这里使用的 flexbox 布局,具体看源码」
modify-appvue
这没有什么好说的,都是一些 css 样式的设置
运行查看效果
vue-cli-router-one
从效果图中我们可以看到我们已经实现了两个界面之间的跳转「通过路由功能」只不过这个界面跳转有点生硬,显的很不友好,下面我们给界面跳转添加一个动画
添加界面跳转动画
说到 vue 的跳转动画,我们就要说说 transition 过度效果,简单的说 transition 就是控制组件或标签的进入和离开的过度「这里不过多的介绍,我们看如何修改代码
修改 App.vue
第一步:给 router-view 添加过虑效果
add-transition
第二步:给 transitionName 设置个属性值
transition-data
在这里我们给 transitionName 设置一个值「这个值可以随便起一个名字,这里我就叫做 slide-right,向右滑动」
第三步:给 transitionName 添加进入,退出效果
transition-setcss
这里我们给过度设置效果,一般使用 name-enter「过渡开始的状态」 name-enter-active「定义进入过渡的状态」、name-leave「离开过渡的开始状态」、name-leave-active「离开的过度状态」,其中 name 是可以的值是可以动态设置的,类如上面的 slide-right 和 slide-left,但是后面的部分是固定的,下面我们来使用 name
第四步:监听路由变化
我们先把路由定义一个统一的返回方法,打开 router/index.js,添加如下代码,使用 js 的 prototype「prototype 属性使您有能力向对象添加属性和方法」 属性即可
addGoBackToRouter
这样一来,router 就有了统一的返回方法,直接调用即可,我们修改返回按钮的返回方法如下:
this.$router.goBack()
第五步:监听路由变化
在这里我们使用到了 Vue 的 watch「这里不专门说,以后有需要会抽出来说」 方法,修改如下:
addWatchRouter
这样我们就可以监听是进入界面还是返回界面「这里 watch 和 data 方法是平级的,如果是返回的话,则从左边滑入到右边,打开新界面就是从右边滑到左边」
通过以上步骤,我们就给路由添加了一个过渡效果,我们来看看效果,就和刚开始的效果图是一样的
router-demo
这样我就完成了组件式开发中的路由的基本功能,结束了吗?这里本应该可以结束了,但是我们前面学过组件,并且 vue 的核心特点之一就是组件化开发,我们这里使用的头部功能「两个界面头部如下」
first-titlebar
second-titlebar.png
我们可以看到两个界面的头部是何其的相似,这里我们完全可以把这个头部封装成一个组件呀,下面我们继续改造我们的代码
封装头部组件
1、新建一个 Head.vue 文件「核心代码」
custom-head-template
custom-head-js
2、在 First.vue 和 Second.vue 中引入
分三步:
第一步:引入组件
import-head-component
第二步:注册组件
register-head-component
第三步:使用组件
use-head-component
想在那个 vue 组件中使用以上三步就可以搞定了
到这里我们就把 head 组件封装完成了,并且达到使用的上的,具体可以看源代码
到此为止,我们把 vue-router 就介绍完了,总结一下
三、总结
路由的分类:前端路由和后端路由「区别和联系」
使用 hash 和 history 分别实现前端路由
vue-router 的基本使用方法「使用 html 引入和模块化开发两种方式」
vue-router 的举例子「手机端开发 webapp」
作者:TigerChain
链接:https://www.jianshu.com/p/9a7d79249741
共同学习,写下你的评论
评论加载中...
作者其他优质文章