摘要:單頁面應用的出現依然存在著爭議性,我們該如何看待他的兩面性呢接下來小生給大家總結一下他的優缺點。單頁面應用的優勢無刷新體驗沒有了令人詬病的頁面頻繁刷新,同時節約瀏覽器資源,路由響應比較及時,提升了用戶的體驗。
認識SPA前端猿一天不學習就沒飯吃了,后端猿三天不學習仍舊有白米飯擺于桌前。IT行業的快速發展一直在推動著前端技術棧在不斷地更新換代,前端的發展成了互聯網時代的一個縮影。而單頁面應用的發展給前端猿分了一杯羹。
最早單頁面的應用無從知曉,在2004年,google的Gmail就使用了單頁面。到了2010年,隨著Backbone的問世之后,此概念才慢慢熱了起來。隨著后來React、Angular、Vue的興起,單頁面應用才成了前端圈里人人皆知的架構模式。接下來小生將通過對比傳統頁面應用和單頁面應用來說明SPA具體是什么。
傳統的頁面應用早期web應用的前后端交互模式是這樣的,每個html作為一個功能元件,通過刷新、超鏈接、表單提交等方式,將頁面組織起來后給用戶提供交互。
后期很多流行的框架都是基于此模式進行設計的,比如 Ruby on Rails,Spring MVC,Express 等等
傳統的web應用中,瀏覽器只是作為展示層,路由、服務調用、頁面跳轉都是服務端來處理的。也就是MVC的架構都是放在后端的,只有V這一層,將頁面通過網絡發送到瀏覽器端,渲染給用戶。
傳統的模式具有以下特點:
重服務端:瀏覽器只作為展示層,將MVC全置于后端,加重了服務端的體量,開發中主要以后端為主。
頻繁刷新:頁面展示依賴于不同的功能元件,所以必須依靠刷新頁面,或者跳轉路由來實現功能塊的切換,這種方式嚴重耗費資源,同時用戶體驗很差。
單頁面應用和傳統應用相比較,單頁面應用就是將MVC個架構搬到了前端來實現
控制器:將處理路由的功能放在前端,當瀏覽器的路由發生變化時,由控制器來響應其變化,指向其對應的處理邏輯(組件),最終將頁面展現給用戶。
視圖:這一層就是功能元件,也就是單個的組件,當路由發生變化的時候由組件來處理,只處理變化的那部分,最后組織成頁面。
數據層:單頁面應用有自己的數據層定義,簡化了后端服務的復雜度,后端只要提供公共的數據接口即可,而數據層會對數據服務API進行進一步的封裝,然后提供數據給視圖層。
如此看來單頁面應用很像移動客戶端,后端的精力就是提供高質量的、可復用的Rest API服務。
世間萬物皆有裂痕,哪又怎樣?裂痕,那是光照進來的地方。
單頁面應用的出現依然存在著爭議性,我們該如何看待他的兩面性呢?接下來小生給大家總結一下他的優缺點。
單頁面應用的優勢:
無刷新體驗:沒有了令人詬病的頁面頻繁刷新,同時節約瀏覽器資源,路由響應比較及時,提升了用戶的體驗。
共享組件:前端組件化是將獨立完整的功能模塊封裝到一個組件中,代碼結構更加規范,便于代碼維護,同時模塊化后的組件可以在不同的場景中進行復用,極大地加快了迭代開發的速度。這也是為什么主流的前端框架都提倡組件化編程的原因。
共享API:給后端減負,前端加碼的好處就是,前端能有一點口糧吃了(開玩笑,前端那么牛怎么能沒飯吃呢?),前端擔起家務的事,后端就可以安心地處理業務邏輯了,于是才能寫出高質量并可共享的API,供自己或者其他的合作伙伴使用。一個優秀的產品背后,一定有一群出色的前端(小生臉皮太厚)。
單頁面應用的劣勢:
抬高了前端門檻:SPA模式的流行,引領了前端技術的飛速發展,與此同時對前端人員在學習和使用上的能力就有了更高的要求,同時工作量也增加了,前端想活的更好就要付出的更多,所以不要再以為前端就是切切圖,畫畫頁面這么簡單。too young, too naive。
首次加載大量資源:既然只有一個頁面顯示,那許多功能元件(組件)所依賴的靜態資源就需要在初次時進行加載,加載時間相對比較長。
不利于SEO:單頁面應用,數據都是在前端進行渲染的,所以就影響了SEO。
徒手實現SPA隨著SPA的流行,目前主流的框架都實現了SPA模式,包括我們夏洛克產品里面用到的Angular和Vue。但是作為一家愛折騰公司里面愛折騰的前端團隊里面愛折騰的人,我們總想跟自己較勁來試試自己去實現簡單的模式,這次小生也簡單地實現了一把,于是將其分享于諸位,目前只是簡單的模型,不能用于生產(主流框架都有,干嘛用我的?學習一下思想即可),除非你愿意折騰。在此之前需要介紹幾個核心點:
路由:小生使用H5中的History API來管理路由的更新(地址欄URL更新、前進、后退)。
視圖:小生還是使用原生的Document來操作,目前渲染的內容比較簡單。
數據層:小生使用XMLHttpRequest寫了一個Ajax服務,幫助請求后端數據(此服務較簡單,不適用生產環境)。
H5 History API關于H5 History API在此需要介紹一下,他是HTML5引入的操作瀏覽器路由歷史堆棧的內容,其中兩個主要的方法為history.pushState(stateObj, title, URL) 和 history.replaceState(stateObj, title, URL) 方法,它們分別可以添加和修改歷史記錄條目。這些方法通常與window.onpopstate 配合使用。三個參數分別為:
狀態對象 — 狀態對象是一個JavaScript對象,通過pushState () 創建新的歷史記錄條目。無論什么時候用戶導航到新的狀態,popstate事件就會被觸發,且該事件的state屬性包含該歷史記錄條目狀態對象的副本。
title — Firefox 目前忽略這個參數,但未來可能會用到。
URL — 該參數定義了新的歷史URL記錄。注意,調用 pushState() 后瀏覽器并不會立即加載這個URL,但可能會在稍后某些情況下加載這個URL,比如在用戶重新打開瀏覽器時。新URL不必須為絕對路徑。如果新URL是相對路徑,那么它將被作為相對于當前URL處理。新URL必須與當前URL同源,否則 pushState() 會拋出一個異常。該參數是可選的,缺省為當前URL。
小生結合window.onpopstate事件來監聽瀏覽器前進和后退的動作來重新請求數據服務,更新視圖。
每當處于激活狀態的歷史記錄條目發生變化時, popstate事件就會在對應window對象上觸發。 如果當前處于激活狀態的歷史記錄條目是由history.pushState()方法創建, 或者由history.replaceState()方法修改過的, 則popstate事件對象的state屬性包含了這個歷史記錄條目的state對象的一個拷貝。
調用history.pushState()或者history.replaceState()不會觸發popstate事件. popstate事件只會在瀏覽器某些行為下觸發, 比如點擊后退、前進按鈕(或者在JavaScript中調用history.back()、history.forward()、history.go()方法)。
目錄結構-- data -- auto.json -- contact.json -- home.json -- platform.json -- sharplook.json -- ajax.js -- index.js -- index.html -- index.css源碼分享
data文件下是模擬的后端數據,數據的結構都與下面一樣,比如home.json
{ "content": "上海擎創信息技術有限公司是專業服務于企業級客戶的ITOA智能運營大數據分析解決方案提供商,專注于將人工智能技術賦予IT運維管理,創造具備分析和思考能力的IT管理軟件,讓每家企業都擁有自己的IT運維專家。" }
ajax.js的代碼如下:
function ajax() { const ajaxData = { type: arguments[0].type || "GET", url: arguments[0].url || "", async: arguments[0].async || "true", data: arguments[0].data || null, dataType: arguments[0].dataType || "text", contentType: arguments[0].contentType || "application/x-www-form-urlencoded", beforeSend: arguments[0].beforeSend || function () {}, success: arguments[0].success || function () {}, error: arguments[0].error || function () {} } ajaxData.beforeSend() const xhr = _createxmlHttpRequest(); xhr.responseType = ajaxData.dataType; xhr.open(ajaxData.type, ajaxData.url, ajaxData.async); xhr.setRequestHeader("Content-Type", ajaxData.contentType); xhr.send(_convertData(ajaxData.data)); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { ajaxData.success(xhr.response); } else { ajaxData.error(); } } } } function _createxmlHttpRequest() { if (window.ActiveXObject) { return new ActiveXObject("Microsoft.XMLHTTP"); } else if (window.XMLHttpRequest) { return new XMLHttpRequest(); } } function _convertData(data) { if (typeof data === "object") { let convertResult = ""; for (let c in data) { convertResult += `${c}=${data[c]}&`; } convertResult = convertResult.substring(0, convertResult.length - 1); return convertResult; } else { return data; } }
index.html的代碼如下:
SPA SHARPLOOK 大數據運維監控平臺
index.js的代碼如下:
class SPA { constructor () { this.elment = void 0; this.menu = Array.from(document.getElementsByTagName("a")); } getCurrentHash() { return window.history.state ? window.history.state.hash : "/home"; } isSupportH5History() { return !!(window.history && window.history.pushState); } setElement(hash) { if (!hash) { // 默認為根路由 ‘/’ this.elment = this.menu[0]; } else { this.menu.forEach(item => { if(item.getAttribute("href") === hash) { this.elment = item; } }); } } renderData() { const contentElement = document.getElementById("p"); this.loadData(contentElement, this.elment.getAttribute("href").split("/")[1]); } addHistory(hash, isReplace) { const stateObj = { hash }; if(isReplace) { window.history.replaceState(stateObj, null, hash); } else { window.history.pushState(stateObj, null, hash); } } loadData(contentElement, type) { ajax({ type: "get", url: `/data/${type}.json`, dataType: "json", success: function(msg) { console.log(msg); contentElement.innerText = msg.content; }, error: function() { console.log("error") } }) }; popStateHandler(linkHash, isPopState = false) { if(!linkHash) {// 刷新界面時候,默認獲取刷新之前的路由信息 this.addHistory(this.getCurrentHash(), true); } else { if(!isPopState) this.addHistory(linkHash, false); } this.setElement(this.getCurrentHash()); this.renderData(); this.addActiveClass(); } bindLiClick() { const list = document.getElementsByTagName("li"); Array.from(list).forEach(item => { item.onclick = (event) => { const linkHash = item.childNodes[0].getAttribute("href"); this.popStateHandler(linkHash); } }); } addActiveClass() { this.menu.forEach(item => { item.parentNode.classList.remove("active"); }) this.elment.parentNode.classList.add("active"); } init() { if(!this.isSupportH5History()) throw new Error("對不起!不支持 H5 History API!"); this.bindLiClick(); window.onpopstate = (event) => { this.popStateHandler(event.state.hash, true); } // 首次默認首次進入頁面 this.popStateHandler(); } }
index.css的代碼如下:
* { margin: 0; padding: 0; } html, body { height: 100%; } .container { height: 100%; display: flex; flex-direction: column; } .bar { background-color: #213442; color: white; height: 60px; display: flex; align-items: center; font-size: 20px; } .body { display: flex; height: calc(100% - 60px); border-top: 1px solid #ccc; } #content { border: 1px solid #ccc; border-radius: 3px; padding: 10px; width: 600px; box-shadow: 5px 5px 15px 0 #bbb; } #nav { background-color: #213442; width: 120px; text-align: center; } a { color: white; text-decoration: none; pointer-events: none; } #content-main { flex: 1; display: flex; justify-content: center; align-items: center; } .title { padding-left: 20px; } li { margin: 10px 0; line-height: 30px; cursor: pointer; } li:hover { background-color: #00A5D5; } .active { background-color: #00A5D5; }
代碼的具體邏輯不做過多介紹,特別需要注意的是請將代碼部署到web服務器上查看效果,因為history api需要在同域里面才能使用,否則報錯,愛學習的小伙伴請自行學習。 完整代碼
效果圖 小結小生給大家介紹了目前web開發的SPA模式,希望諸君在使用主流框架時能進一步了解其原理,你我共勉。小生基于H5的History實現了一個簡單的SPA模式,僅供學習之用,最后小生想說,身為后端轉為前端的前端猿,感覺前端的技術棧應是最有活力的,因為一旦你不想動了,就如溫水里的青蛙,距離另一個世界也就近了,祝君能像前端的發展勢頭一樣,活力四射,不斷進步。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/116618.html
摘要:單頁面應用的出現依然存在著爭議性,我們該如何看待他的兩面性呢接下來小生給大家總結一下他的優缺點。單頁面應用的優勢無刷新體驗沒有了令人詬病的頁面頻繁刷新,同時節約瀏覽器資源,路由響應比較及時,提升了用戶的體驗。 前端猿一天不學習就沒飯吃了,后端猿三天不學習仍舊有白米飯擺于桌前。IT行業的快速發展一直在推動著前端技術棧在不斷地更新換代,前端的發展成了互聯網時代的一個縮影。而單頁面應用的發展...
摘要:解決方法如果使用頁面數據不超過一屏禁止滾動,那么即使變成了頁面也不會有什么變化。 作者:@micky思 @wupq @yewq 在H5的開發中,個人的制作頁面布局習性不同,多多少少會產生在真機上input的光標和鍵盤的彈出會出現的各種BUG,文中整理了部分遇到的問題,歡迎新增 ios移動端輸入框上浮導致輸入位置偏移 問題原因:遮罩層定位為fixed,當鍵盤彈起時,ios11以及以下...
摘要:解決方法如果使用頁面數據不超過一屏禁止滾動,那么即使變成了頁面也不會有什么變化。 作者:@micky思 @wupq @yewq 在H5的開發中,個人的制作頁面布局習性不同,多多少少會產生在真機上input的光標和鍵盤的彈出會出現的各種BUG,文中整理了部分遇到的問題,歡迎新增 ios移動端輸入框上浮導致輸入位置偏移 問題原因:遮罩層定位為fixed,當鍵盤彈起時,ios11以及以下...
閱讀 3543·2021-11-22 15:22
閱讀 3332·2019-08-30 15:54
閱讀 2729·2019-08-30 15:53
閱讀 816·2019-08-29 11:22
閱讀 3537·2019-08-29 11:14
閱讀 2077·2019-08-26 13:46
閱讀 2217·2019-08-26 13:24
閱讀 2280·2019-08-26 12:22