什么是路由
路由(route)是什么?我们抛开生活中的路由不谈,在 web 开发中,路由的主要工作是根据 url 分配对应的处理程序。
假如有一个服务,域名为 a.com
,它提供了一个可供访问的页面 http://a.com/about
,当用户输入 http://a.com/about
访问时,Web 服务会根据用户输入的这个 URL 找到对应的处理程序,这个过程就是路由分发。
前端路由
在以前,我们一般是在后端做路由(想想熟悉的 Spring MVC 的控制器),分析处理 URL。但是随着前端的发展,单页面应用的流行,页面交互越来越复杂,一些路由的处理已经交给了前端。如今很多流行的 js 类库框架:AngularJS、Backbone、Vue、React 等等都支持前端路由,那么前端路由的原理是什么呢?
在 HTML5 出现以前,通常使用 hash 来实现路由功能。那么 hash 是什么呢?比如 www.nekolr.com/#git
,这个 # 号及后面的字符被称为 hash。当 hash 值改变时,浏览器会产生历史记录,但不会向服务器发送请求,此时后退地址栏会发生变化但页面不会刷新。在了解了 hash 之后,还需要了解 hashchange 事件,该事件在 hash 值改变时触发,这里有个小栗子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>hashchange</title> </head> <body> <ul> <li><a href="#first">first</a></li> <li><a href="#second">second</a></li> </ul> </body> <script type="text/javascript"> window.addEventListener("hashchange", function (e) { console.log(window.location.hash) console.log("这是之前的:" + e.oldURL) console.log("这是新的:" + e.newURL) }) </script> </html>
|
手写一个前端路由
下面结合这两个知识点,我们来做一个简单的前端路由:
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>blog</title> <style> nav{line-height:2em;} nav a{display:inline-block;vertical-align:top;padding:0 .5em;} nav a.active{background:#369;color:#FFF;} </style> </head> <body> <nav> <a href="#!home" class="active"> 首页 </a> <a href="#!about"> 关于 </a> <a href="#!friend"> 友情链接 </a> </nav> <div id="content"></div> </body> <script type="text/javascript">
function Router() { this.key = "!"; this.currentUrl = ""; this.routes = {}; }
Router.prototype.reg = function (address, callback) { this.routes[this.key+address] = callback; };
Router.prototype.go = function () { this.currentUrl = window.location.hash.slice(1) || "!home"; this.routes[this.currentUrl](); };
Router.prototype.init = function (callback) { var self = this;
window.addEventListener("hashchange", function () { self.go.bind(self)() if (typeof callback === "function") callback.bind(self)(self.currentUrl) }, false); window.addEventListener("load", this.go.bind(this), false); }
var router = new Router();
router.init(function (currentUrl) { var a = document.querySelector('nav a.active') var i = document.querySelector('nav a[href="#'+currentUrl+'"]') if(a) a.className = ''; if(i) i.className = 'active'; });
router.reg("home", function () { document.querySelector("#content").innerHTML = "这是首页"; })
router.reg("about", function () { document.querySelector("#content").innerHTML = "这是关于页"; })
router.reg("friend", function () { document.querySelector("#content").innerHTML = "这是友链页"; })
</script> </html>
|
基本的思路就是:将关键字和对应的处理函数提前注册到路由表中,在页面 hash 改变,触发 hashchange 事件时调用对应的处理函数。
前端路由一般在单页面应用(SPA)使用较多,优点主要是用户体验好,页面内容不需要每次都从服务器全部获取,能够快速展现给用户。缺点包括使用浏览器的前进、后退键会重新发送请求,没有合理地利用缓存,无法记住之前滚动的位置等。
参考
Web 开发中的”路由”是什么意思?