在單頁應用上,前端路由並不陌生。單頁應用是指在瀏覽器中運行的應用,在使用期間頁面不會從新加載。
基本原理:以 hash 形式(也可使用 History API 來處理)爲例,當 url 的 hash 發生改變時,觸發 hashchange 註冊的回調,回調中去進行不一樣的操做,進行不一樣的內容的展現。
基於hash的前端路由優勢是:能兼容低版本的瀏覽器。
history 是 HTML5 纔有的新 API,能夠用來操做瀏覽器的 session history (會話歷史)。
從性能和用戶體驗的層面來比較的話,後端路由每次訪問一個新頁面的時候都要向服務器發送請求,而後服務器再響應請求,這個過程確定會有延遲。而前端路由在訪問一個新頁面的時候僅僅是變換了一下路徑而已,沒有了網絡延遲,對於用戶體驗來講會有至關大的提高。 javascript
SPA的核心便是前端路由。何爲路由呢?說的通俗點就是網址,好比https://www.talkingcoder.com/article/6333760306895988699;專業點就是每次GET或者POST等請求,在服務端有一個專門的正則配置列表,而後匹配到具體的一條路徑後,分發到不一樣的Controller,而後進行各類操做後,最終將html或數據返回給前端,這就完成了一次IO。固然,目前絕大多數的網站都是這種後端路由,也就是多頁面的,這樣的好處有不少,好比頁面能夠在服務端渲染好直接返回給瀏覽器,不用等待前端加載任何js和css就能夠直接顯示網頁內容,再好比對SEO的友好等。那SPA的缺點也是很明顯的,就是模板是由後端來維護或改寫。前端開發者須要安裝整套的後端服務,必要還得學習像PHP或Java這些非前端語言來改寫html結構,因此html和數據、邏輯混爲一談,維護起來即臃腫也麻煩。而後就有了先後端分離的開發模式,後端只提供API來返回數據,前端經過Ajax獲取到數據後,再用必定的方式渲染到頁面裏,這麼作的優勢就是先後端作的事情分的很清楚,後端專一在數據上,前端專一在交互和可視化上,今後先後搭配,幹活不累,若是從此再開發移動App,那就正好能使用一套API了,固然缺點也很明顯,就是首屏渲染須要時間來加載css和js。這種開發模式被不少公司認同,也出現了不少前端技術棧,好比以jQuery+artTemplate+Seajs(requirejs)+gulp爲主的開發模式所謂是萬金油了。在Node.js出現後,這種現象有了改善,就是所謂的大前端,得益於Node.js和JavaScript的語言特性,html模板能夠徹底由前端來控制,同步或異步渲染徹底由前端自由決定,而且由前端維護一套模板,這就是爲何在服務端使用artTemplate、React以及即將推出的Vue2.0緣由了。那說了這麼多,到底怎樣算是SPA呢,其實就是在先後端分離的基礎上,加一層前端路由。css
前端路由,即由前端來維護一個路由規則。實現有兩種,一種是利用url的hash,就是常說的錨點(#),JS經過hashChange事件來監聽url的改變,IE7及如下須要用輪詢;另外一種就是HTML5的History模式,它使url看起來像普通網站那樣,以"/"分割,沒有#,但頁面並無跳轉,不過使用這種模式須要服務端支持,服務端在接收到全部的請求後,都指向同一個html文件,否則會出現404。因此,SPA只有一個html,整個網站全部的內容都在這一個html裏,經過js來處理。html
前端路由的優勢有不少,好比頁面持久性,像大部分音樂網站,你均可以在播放歌曲的同時,跳轉到別的頁面而音樂沒有中斷,再好比先後端完全分離。前端路由的框架,通用的有Director,更多仍是結合具體框架來用,好比Angular的ngRouter,React的ReactRouter,以及咱們後面用到的Vue的vue-router。這也帶來了新的開發模式:MVC和MVVM。現在前端也能夠MVC了,這也是爲何那麼多搞Java的鐘愛於Angular。前端
開發一個前端路由,主要考慮到頁面的可插拔、頁面的生命週期、內存管理等。vue
(function() { window.Router = function() { var self = this; self.hashList = {}; /* 路由表 */ self.index = null; self.key = '!'; window.onhashchange = function() { self.reload(); }; }; /** * 添加路由,若是路由已經存在則會覆蓋 * @param addr: 地址 * @param callback: 回調函數,調用回調函數的時候同時也會傳入相應參數 */ Router.prototype.add = function(addr, callback) { var self = this; self.hashList[addr] = callback; }; /** * 刪除路由 * @param addr: 地址 */ Router.prototype.remove = function(addr) { var self = this; delete self.hashList[addr]; }; /** * 設置主頁地址 * @param index: 主頁地址 */ Router.prototype.setIndex = function(index) { var self = this; self.index = index; }; /** * 跳轉到指定地址 * @param addr: 地址值 */ Router.prototype.go = function(addr) { var self = this; window.location.hash = '#' + self.key + addr; }; /** * 重載頁面 */ Router.prototype.reload = function() { var self = this; var hash = window.location.hash.replace('#' + self.key, ''); var addr = hash.split('/')[0]; var cb = getCb(addr, self.hashList); if(cb != false) { var arr = hash.split('/'); arr.shift(); cb.apply(self, arr); } else { self.index && self.go(self.index); } }; /** * 開始路由,實際上只是爲了當直接訪問路由路由地址的時候可以及時調用回調 */ Router.prototype.start = function() { var self = this; self.reload(); } /** * 獲取callback * @return false or callback */ function getCb(addr, hashList) { for(var key in hashList) { if(key == addr) { return hashList[key] } } return false; } })();
每次hash的變化咱們還能夠經過onhashchange事件【核心+精髓】來監聽,而後作出相應的處理。。java
<!DOCTYPE html> <html> <head> <meta charset="utf8" /> <script type="text/javascript" src="./router.js"></script> <style type="text/css"> </style> </head> <body> <a href="#!index">go to index</a><br /> <a href="#!search/SB/shi/ni">go to search</a> </body> <script type="text/javascript"> window.onload = function() { var router = new Router(); router.add('index', function() { alert('current page: index'); }); router.add('search', function(wd, sortType, sortBy) { alert('current page: search' + '\nwd: ' + wd + '\nsortType: ' + sortType + '\nsortBy: ' + sortBy); }); router.setIndex('index'); router.start(); }; </script> </html>