個人前端工具集(三)頁面路由工具javascript
liuyuhang原創,未經容許禁止轉載css
目錄html
個人前端工具集前端
一、什麼是路由java
狹義上的路由就是咱們經常使用的路由器了,不論是何種方式鏈接,都是轉發的功能,jquery
實現了信道的擴展和加密功能。程序員
廣義上的路由,實際上就是一個指揮員,根據目標的性質,進行映射的工具。es6
能夠是交通訊號燈,能夠是路牌,能夠是網絡路由器,能夠是人生導師!spring
本文中所說的路由,是指根據配置文件的設置,在點擊指定內容的時候,對指定的div加載指定html的程序。編程
我叫他頁面路由,這種路由在不少前端框架中已經有實現了,功能也很多,學習的人不多去糾其做用機理。
本文以本身的視角去實現一個路由,並嘗試知足其各類功能。
二、爲何要本身寫前端路由
爲何要本身寫前端路由?我也不知道,老是以爲學一套語法,還不如本身實現一套語法,雖然是本身造輪子吧。
使用別人的輪子老是有各類限制的,也沒法根據須要進行功能的加減。
況且,最主要的目的,仍是學習。理論更重要!
三、路由在架構中的重要性
路由是及其重要的,可能這個詞並非不少見,可是相似的功能的東西比較多了,舉幾個例子:
Struts是一種優秀的前端路由映射工具。
SpringMVC是一種優秀的前端路由映射工具。
經常使用的了,這兩種對於java程序員來講太經常使用了。
基本的原理,都是根據url請求,解析映射,而後根據配置文件的配置,訪問到固定的java類,同時將封裝
的servlet的request和response做爲參數傳遞進去。
同時,一個好的路由架構,可以成功拆分文件,將任務分解(項目經理頭疼的問題)
雖然當前使用不少框架或者es6已經實現了模塊化編程和聚合,會這些的人要的薪水也較高!
發揮每一個人的優點纔是重要的。
有一說叫讓外部極簡(客戶方便),讓內部複雜(編碼結構複雜)。
在這個思想的指導下,讓大部分的開發者極簡,讓少部分的開發者複雜,也是一種架構思路了!
四、我想認爲路由應該有的功能與做用
4.1.無刷加載頁面功能:
前端路由的核心功能了,要加載的頁面直接加載,進行局部刷新,中間沒有白屏。
4.2.前置事件功能:
執行某種效果以前執行的函數,這裏的加載事件,是指一種AOP思想。
主要是在路由跳轉以前執行必定的函數,該函數應該會返回一個boolean,若是爲true,則執行跳轉。
4.3.後置事件功能:
好像你們都叫它鉤子函數吧,即執行了某個功能之後,當即執行的函數。
4.4.加載頁面須要的js
4.5.加載頁面須要的css
5.路由以何種形態出現?
根據4中描述的功能,前端路由應該設置的內容以下:
①state:跳轉名稱,應該是惟一的,做爲一個map的key的存在,應該是一個字符串。
②target:要改變的div的target,應該不是惟一的,多個target名字相同,應該對多個div進行一樣的改變,應該是一個字符串。
③template:html模板,該路由起做用的時候,應該在該div內加載該html模板。應該是一個字符串。
④url:加載div並非跳轉,也不是重定向,url是沒有變化的,此參數只是爲了改變地址(決定廢棄,感受沒啥用,之前作過)
⑤jsArr:多個js文件的名稱,執行該路由的時候,同時加載多個js文件。
⑥cssArr:多個css文件的名稱,執行該路由的時候,同時加載多個css文件。
⑦before:前置事件函數名,字符串格式,返回boolean,若爲true則執行跳轉,若爲false則執行某提示函數或donothing。
⑧after:後置事件函數名,字符串格式,即執行路由後,執行該函數(該函數不該該爲加載的js的函數,由於可能未加載成功緻使after沒法執行)
六、細節部分的實現
6.1.state
其中state應爲被點擊的html元素的一個屬性,頁面加載的時候掃描路由配置,給state屬性賦值,爲state的名稱。如:
<div state="myStateName"></div>
該功能應該配合一個listener來實現,假定配置文件中有key=「state」,value=「myStateName」,則listener應爲:
if(key=="state"){ var temp = $("[state="+value+"]"); if(temp.length>0){ temp..click(function(){ //執行路由功能 }) } }
6.2.before
執行路由功能首先要先執行before的字符串,假定配置文件中有key=「before」,value=「test001()」,
而且有
function test001(str){ console.log("test001"); console.log(str); } //則執行before的應爲: var flag = false; if(key=="before"){ flag = eval("test001('hello test')") }
6.3.template
執行template加載以前,要先使用flag進行判斷,假定配置文件中有key=「template」,value=「firstTemplate.html」,
//這裏下文中的loacl是得到http頭,host,port等內容,暫且貼在這,本身調整 var local = window.location.protocol + "//" + window.location.host + "/" + window.location.pathname.split("/")[1] + "/" if(key=="template"&&flag==true){ $("div [target="+'myStateName'+"]").load(loacl+"firstTemplate.html"); }
6.4.jsArr
加載js,前提是jsArr有內容。
if (key=="jsArr"&&jsArr.length>0) { for (var i = 0; i <jsList.length; i++) { var script = document.createElement("script"); script.setAttribute("type", "text/javascript"); script.src = local +jsArr[i]; document.getElementsByTagName("head")[0].appendChild(script); } }
6.5.cssArr
加載css,前提是cssArr有內容
if (key=="cssArr"&&cssArr.length > 0) { for (var i = 0; i <cssArr.length; i++) { var link = document.createElement("link"); link.setAttribute("rel", "stylesheet"); link.href = local+cssArr[i]; document.getElementsByTagName("head")[0].appendChild(link); } }
6.6.after和before是相同的,不寫了。
6.7.以上的代碼並不是是實際代碼,能夠理解爲僞代碼吧!!
7.代碼實現
說明,上述舉例是舉例,實際代碼會有更多的判斷內容和不一樣的格式,如下文爲準,自行修改!
7.1.準備配置數據,本身注意格式
//測試時用的是springboot,非生產環境,無pathName var local = window.location.protocol + "//" + window.location.host + "/"; /** * 準備配置文件,可單寫爲js文件 * 數組格式,每一個元素是一個json,表明一個路由事件配置 */ var routeConfig = [ { state : "firstState", //state屬性值,被點擊的標籤,執行名爲state的路由 target : "firstTarget", //執行路由將頁面注入的目標,是一個div的target屬性 template : local + "view/firstPage.html", //執行路由注入的頁面,一個html文件路徑,不帶local before : "firstFunBefore('my First Fun Before Here')", //路由執行以前的函數,決定是否執行 after : "firstFunAfter('my First Fun After Here')", //路由執行以後的函數 jsArr : [ local + "js/first01.js", local + "js/first02.js" ], //注入路由時注入的js文件列表,不帶local cssArr : [ local + "css/first01.css", local + "css/first02.css" ], //注入路由時注入的css文件列表,不帶local }, { state : "secondState", target : "secondTarget", template : local + "view/secondPage.html", before : "secondFunBefore('my Second Fun Before Here')", after : "secondFunAfter('my Second Fun After Here')", jsArr : [ local + "js/second01.js", local + "js/second02.js" ], cssArr : [ local + "css/second01.css", local + "css/second02.css" ], } ]
7.2.路由框架代碼,只是路由跳轉用的代碼而已
/** * 路由監聽器初始化的函數,初始加載執行,每次有路由跳轉後也要執行 */ function stateListener(routeConfig) { console.log(routeConfig); var setting = routeConfig; $("*[state]").unbind(); //移除state的監聽器 $("*[state]").click(function() { //注入監聽器 var state = $(this).attr("state"); StateTo(state); //一旦點擊即執行路由跳轉函數 }) } /** * 路由跳轉執行的函數 * 傳入參數爲路由中的state的value */ function StateTo(stateName) { console.log(stateName); //初始化變量 var index, state, target, template, before, after, jsArr, cssArr, flag = false; //從配置數組中獲取該元素的index for (var i = 0; i < routeConfig.length; i++) { if (stateName == routeConfig[i].state) { index = i; break; } } if (null != i) { state = routeConfig[index].state; target = routeConfig[index].target; template = routeConfig[index].template; before = routeConfig[index].before; after = routeConfig[index].after; jsArr = routeConfig[index].jsArr; cssArr = routeConfig[index].cssArr; } //檢測state,target,template正確性 if (checkEmpty(state) && checkEmpty(target) && checkEmpty(template) && $("[target = " + target + "]").length > 0) { //不爲空且頁面存在target flag = true; } //執行before並得到返回值 flag = eval(before); //注入template,jsArr,cssArr if (flag == true) { //template注入 $("div[target=" + target + "]").load(template); console.log("inject template : " + template); //js注入 var jsArrLength = jsArr.length; if (jsArrLength > 0) { for (var i = 0; i < jsArrLength; i++) { var script = document.createElement("script"); script.setAttribute("type", "text/javascript"); script.src = jsArr[i]; document.getElementsByTagName("head")[0].appendChild(script); console.log("inject js : " + jsArr[i]); } } //css注入 var cssArrLength = cssArr.length if (cssArrLength > 0) { for (var i = 0; i < cssArrLength; i++) { var link = document.createElement("link"); link.setAttribute("rel", "stylesheet"); link.href = cssArr[i]; document.getElementsByTagName("head")[0].appendChild(link); console.log("inject js : " + cssArr[i]); } } //若是有遮罩,所有刪除,防止跳轉後頁面家假死,bootstrap中的遮罩... $(".modal-backdrop").remove(); } //執行after if (flag == true) { flag = eval(after); stateListener(routeConfig);//重置路由監聽 } //========== //檢驗字符串是否爲空的內部工具 function checkEmpty(str) { return ('' != str && null != str && 'undefinded' != typeof str); } }
7.3.準備測試的before和after函數代碼
//準備測試用的四個before和after函數 function firstFunBefore(str) { console.log(str); return true; //容許路由跳轉 } function firstFunAfter(str) { console.log(str); } function secondFunBefore(str) { console.log(str); console.log("返回值爲false,不執行路由!"); return false; //不容許路由跳轉 } function secondFunAfter(str) { console.log(str); }
7.4.初始化與測試按鈕函數代碼
//init $(function() { stateListener(routeConfig) }) function test(){ StateTo("firstState"); }
7.5.html代碼,須要引入jquery和bootstrap,版本沒啥要求
<div class="row"> <div class="col-lg-12"> <!-- bar --> <div class="col-lg-2"> <div state="firstState"> <span><h3>First</h3></span> </div> <div state="secondState"> <span><h3>Second</h3></span> </div> <div> <button class="btn btn-default" onclick="test()">測試StateTo函數的按鈕</button> </div> </div> <!-- something --> <div class="col-lg-3"> <div target="firstTarget"> <h2>First Page</h2> </div> <div target="secondTarget"> <h2>Second Page</h2> </div> </div> </div> </div>
7.6.測試用的html,做爲template
firstPage.html
<div> <h2>The First Page Injected</h2> </div>
8.運行測試
8.1.頁面截圖
8.2.點擊First後的截圖
8.3.點擊測試按鈕後的截圖
8.4.兩次操做的時候,控制檯的打印結果,有些內容本身去寫吧!
9.總結
效果已經達到,js和css文件沒有寫,注入是可以成功的,自行去寫。
對於統一性的功能(如遮罩,如進度條,如加載錯誤信息,能夠統一編寫,直接寫在這個框架內,也能夠另增長init模塊,
使得這個模塊在before或者fater或者其餘地方選擇執行,相似於AOP思想,統一編寫,減小些業務的程序員的工做量。)
以上!