本身動手寫一個前端路由插件

      在單頁應用上,前端路由並不陌生。單頁應用是指在瀏覽器中運行的應用,在使用期間頁面不會從新加載。 
      基本原理:以 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>
相關文章
相關標籤/搜索