在前端單頁面應用裏面,路由是比較重要的部分,現有的路由系統從簡易的director.js到backbone,react等內置路由,功能一步步加強。那麼這些系統原理是什麼呢,本文將分析並實現一份簡易的路由,以闡述其工做原理。css
以hash作示範,其運行機制以下:html
儲存hash與對應的回調函數,以key,value的形式存入對象cache中前端
設置監聽函數onhashchange監聽url的hash變化react
一旦hash變化,則遍歷cache對象,將屬性key作正則處理,生成對應的正則,再將其拿去和hash作正則匹配,匹配到後執行相應的value/回調函數git
回調函數中執行渲染ui的代碼,進而更新頁面github
router.js瀏覽器
function Router() { this.cache = {}; //將url/callback 以key/value形式儲存在cache內 this.on = function (key, value) { var cache = this.cache; cache[key] = value; }; //匹配hash對應的回調函數,並觸發 this.trigger = function (hash) { var cache = this.cache; for (var r in cache) { var reg = this.initRegexps(r); if (reg.test(hash)) { var callback = cache[r] || function () { }; var params = this.getParams(reg, hash); callback.apply(this, params); } } }; //初始化 添加監聽瀏覽器hashchange 以及dom loaded函數 this.init = function () { window.addEventListener('hashchange', function () { var hash = location.hash.slice(1); router.trigger(hash); }); window.addEventListener('load', function () { var hash = location.hash.slice(1) || 'default'; router.trigger(hash); }) }; /** *將cache內的key 作正則處理,並返回 * 第一個正則 匹配諸如/,.+-?$#{}[]] 關鍵字 並在關鍵字前面加轉譯字符\ * 第二個正則 匹配() 標示()內部內容無關緊要 * 第三個正則 匹配: 在/後面能夠由接受任意字符,直到遇到下一個/ * 第四個正則 匹配* 在*後面能夠由接受任意字符 */ this.initRegexps = function (route) { route = route.replace(/[/,.+\-?$#{}\[\]]/g, '\\$&') .replace(/\((.*?)\)/g, '(?:$1)?') .replace(/(\/\w?:\w+)+/g, '\/([^/]+)') .replace(/\*\w*/g, '([^?]*?)'); return new RegExp('^' + route + '$'); }; //將匹配的正則返回,爲回調函數提供參數 this.getParams = function (reg, hash) { return reg.exec(hash).slice(1); } }
index.htmlapp
<style> .test { width: 200px; height: 200px; color:white; } </style> <div> <a href="#/aaaa/bcd">hash=aaaa/bcd 匹配/aaaa/:id</a> </div> <div> <a href="#/bbbb">hash=bbbb 匹配/bbbb(/:name)</a> </div> <div> <a href="#/bbbb/ddd">hash=bbbb/ddd 匹配/bbbb(/:name)</a> </div> <div> <a href="#/cccc/s/d">hash=cccc/s/d 匹配cccc/*</a> </div> <div class="test"> </div> <script> var router = new Router(); var test = $('.test'); router.on('/', function () { test.css('background-color', 'green').css('color','white').html('我是綠色'); }) router.on('/aaaa/:id', function (id) { console.log(id); test.css('background-color', 'red').css('color','white').html('我是紅色'); }) router.on('/bbbb(/:name)', function (name) { console.log(name); test.css('background-color', 'yellow').css('color','red').html('我是黃色'); }) router.on('/cccc/*', function (x) { console.log(x); test.css('background-color', 'black').css('color','white').html('我是黑色'); }) router.init(); </script>
雖然本文實現比較簡單,但不少框架的內部路由也是基於這種機制,只不過有基於對自身的特性作了一些優化。dom
本文有什麼不完善的地方,或者流程圖有待改進的地方,敬請斧正。