知乎上有人問「比一我的吃火鍋更寂寞的是什麼?」我想回答「一我的寫先後端,卻要裝成一個團隊。」html
咱們在前期開發的過程當中,更可能是一我的單打獨鬥,由於是本身一我的,能夠把代碼寫的很隨意,也不用注意什麼工程化的東西;但做爲一個有追求的程序員,不能爲將來的本身挖坑,堅定走前端工程化的路線。前端
雖然我是一我的,但我是一個團隊。java
因此這是我本身在開發過程當中總結的一些知識,慢慢會寫成一個系列吧。「單頁應用」做爲系列的第一篇。jquery
對於單頁應用個人定義是:在瀏覽器地址欄輸入地址以後,服務器獲取到HTML文檔,以後全部頁面的呈現都在這份HTML文檔之上進行。webpack
由於這是流行啊。即便我司對性能啥的徹底沒有任何要求,我仍是強行用了單頁應用,本身不努力,沒人幫我學。具體有什麼好處仍是壞處,若是本身沒寫過單頁應用,即便別人說一大堆好處壞處你也仍是不懂。(這是吐槽,不用理會)git
在瀏覽器地址欄輸入地址以後,會從服務器端下載HTML文檔並開始渲染。(這個誰能看懂,誰看吧,反正我看不懂)在渲染的過程當中,解析 script 標籤,外部引入的話,就發起請求獲取 JavaScript 文件。
頁面渲染完成,以後全部網站的內容都會在這個頁面上呈現,全部的操做也只會在這個頁面進行。程序員
假設咱們在瀏覽器輸入 http://xxxx.com 的時候,服務端會返回 index.html 文檔以下:github
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>demo</title> </head> <body> <div id="app"> <nav id="nav"></nav> <div id="container"></div> </div> <script src="/jquery.js"></script> <script src="/app.js"></script> </body> </html>
這裏插一句爲何把 script 標籤放到最後?瀏覽器在渲染的時候會被 script 標籤阻塞,影響頁面的首次渲染。可能會由於 JavaScript 文件過大,等待加載時間過長(若是是外部引入),頁面一直是空白。web
咱們在構建單頁面應用時,大部分的內容經過 JavaScript 生成。在 app.js 中,咱們會生成一個導航:
var $nav = $('#nav') var sLi = ['home','about','article','history'].reduce(function(pre, n) { return pre + '<li><a href="/' + n + '">' + n + '</a></li>'; }, ''); var sUl = '<ul>' + sLi + '</ul>'; $nav.html(sUl);
生成以下圖的導航條
以後咱們想根據導航生成不一樣的頁面內容:
var $container = $('#container') $('#nav ul li').on('click', function() { $container.html('這是' + $(this).find('a').text() + '區域哦') return false; })
然而現實中,咱們的頁面多數是根據後端返回的數據來渲染,例如咱們的後端提供了一個接口 /api/home
, 經過這個接口能夠得到數據 ["今", "天", "天", "氣", "不", "錯", "啊"]
,當咱們進入 home 頁面的時候頁面上會結合經過 api 接口獲取來的數據展現新的頁面:
$('#nav ul li').on('click', function() { var route = $(this).find('a').text() if('home' === route) { $.get('/api/home', function(data) { $container.html(data.join('') + '!') }) } else { $container.html('這是' + route + '區域哦') } return false; })
這是我剛接觸單頁應用時候比較頭疼的地方。
沒有路由,我就不知道你在哪兒。 --- by 我要某上頭條君
經過 Ajax 能夠獲取服務器的數據而後再渲染到頁面上,這個方法雖然交互很友好,不須要從新刷新頁面就能夠看到新的內容;可是有一點很差,那就是當點擊導航後,瀏覽器地址欄的連接不會有變化,用戶徹底不知道如今在哪一個頁面。
不過這裏有個知識點是須要你們理解的,當咱們在瀏覽器地址欄輸入地址或者經過其餘頁面的外鏈跳轉到這個頁面時,整個頁面都會刷新一遍,從服務器獲取 HTML 文檔渲染。在 JavaScript 中,能夠經過 location.href 修改地址,可是這個方法和瀏覽器輸入地址的效果是相同的,那有沒有辦法只修改瀏覽器的地址欄,而不刷新整個頁面呢?
還好咱們有 HTML5 的 History Api 來解決這個問題,固然低版本的IE瀏覽器也能夠經過 location.hash 的方法實現,hash 來解決的方法不在這裏深究了。畢竟低版本瀏覽器已經不必支持了。
$('#nav ul li').on('click', function() { var route = $(this).find('a').text() history.pushState({ title: route }, route, route) show() return false; }) function show() { var route = window.location.pathname if('/home' === route) { $.get('/api/home', function(data) { $container.html(data.join('') + '!') }) } else { $container.html('這是' + route + '區域哦') } }
在點擊導航以後,會將當前的路由體如今瀏覽器的地址中。不過,瀏覽器地址變化以後,點擊前進後退,頁面並不會有什麼變化。咱們能夠經過對 window 對象綁定 popstate 方法來解決
window.addEventListener("popstate", show);
傳統的網站,用戶一般輸入具體 url 來進入相關頁面,例如:咱們進入 GitHub 本身的主頁是輸入的是 github.com/user
;可是單頁面應用,咱們的頁面都是在一個空的 html文檔中經過 JavaScript 生成的頁面,因此服務器不能經過url來返回具體的頁面(實際上是能夠的,同構
,不在這裏展開),那麼應該怎麼作呢?
對於服務器來講,不用管那麼多,當用戶輸入 url 以後,只要 url 符合必定的規則(例如:請求頭的 accept 是 'text/html' 或者全部非 /api/ 開頭的 url 等),都返回默認的 index.html。JavaScript 能夠經過當前的路由來渲染:
function show() { var route = window.location.pathname if('/home' === route) { $.get('/api/home', function(data) { $container.html(data.join('') + '!') }) } else if('/' !== route) { $container.html('這是' + route + '區域哦') } } // 初始化的時候就調用 show()
到此爲止,單頁面的應用就算介紹完了。理解了單頁面應用的原理,對於最近流行的 Angular
, React
, Vue
之類庫或框架的纔算剛剛開始。
若是以爲本文對你有幫助的話,就點個推薦唄。
下一篇我會介紹 一我的的團隊(二)--- 神兵利器之 webpack 和 webpack-dev-server