一、 什么是單頁面富應用?
單頁面應用:Single Page Application
- 概念:Web應用即使不刷新也在不同的頁面間切換,解決瀏覽器前進、后退等機制被破壞等問題。並且頁面訪問會被瀏覽器保存。
- 實現方法:
- Node+Html5實現
- React/Vue等MVVM框架
二、單頁面應用的實現
1. Node+Html5
H5實現單頁面應用為什么需要Node?
雖然使用的是H5的新特性:History API,但是單頁面應用實際上是利用路由變化從而判斷是否改變內容。這里僅用node開啟服務,url地址的變化采用的是H5的History API。
步驟
- 使用express自動化構建項目
express myAppName
- 去掉多余路由文件(user.js),修改App.js渲染文件的類型(默認jade => html)
app.engine('html',require('ejs').renderFile);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');
- 編寫index.html
項目效果:點擊相應button,url路由和底部內容都會相應變化

//html
<div class="appWried">
<div class="appBtn">
<button>PAGE1</button>
<button>PAGE2</button>
<button>PAGE3</button>
</div>
<div class="appContent">
暫無內容
</div>
</div>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
$(function(){
var button = $('.appBtn button');
button.click(function(){
let route = $(this).text(); //獲取按鈕的文本
//把按鈕內容當做url路由導航
pageChange(route);
//history.pushState 添加瀏覽器歷史
history.pushState('','',route) //state回調函數傳入對象 title新頁面標題 url新頁面路徑,地址欄會顯示新路徑
})
//根據點擊或者路由改變相應的頁面內容
function pageChange(route){
console.log(route);
button.removeClass('active');
//filter() 方法將匹配元素集合縮減為匹配指定選擇器的元素
button.filter(function(){
return $(this).text() == route;
}).addClass('active');
//改變內容
$('.appContent').text(`我是${route}`);
}
})
</script>
這里為了方便,我采用了JQuery的官方CDN。
pushState是html5的History新增的。
window.history.pushState(json,title,url)
// 狀態對象:記錄歷史記錄點的額外對象,可以為空
// 頁面標題:目前所有瀏覽器都不支持, 可以為空
// 可選的url:瀏覽器不會檢查url是否存在,只改變url,url必須同域,不能跨域
此外,pushState經常搭配監聽歷史記錄點事件window.onpopstate來監聽url的變化。並且可以獲取存儲在該歷史記錄點的狀態對象,也就是pushState存儲的json對象。例如:
window.addEbentListener('popState',function(){
console.log('url改變')
})
2. React
現在很多前端框架都追求組件化開發、組件化復用。組件化和單頁面應用非常配。所以React、Vue等也常常用於SPA的開發。
使用React開發SPA至少需要用到:React、React-router(-dom)
項目使用的是React-router-dom。
react-router 和 react-router-dom 的不同之處就是后者比前者多出了這樣的 DOM 類組件。並且react-router-dom是其升級版,可以更快更新,react-router即將廢棄.
項目效果:

//views/index.js
import React from 'react';
import { Link,withRouter } from 'react-router-dom'
import './style.css'
class AppPage extends React.Component{
constructor(arg){
super(arg)
this.state={}
}
render(){
let appContent = ''
if(this.props.history.location.pathname){
appContent = this.props.history.location.pathname
}else{
appContent = '暫無內容'
}
return(
<div className="appWried">
<div className="appBtn">
<Link to="/PAGE1">
<button className={ this.props.history.location.pathname === '/PAGE1' ? 'active' : '' }>PAGE1</button>
</Link>
<Link to="/PAGE2">
<button className={ this.props.history.location.pathname === '/PAGE2' ? 'active' : '' }>PAGE2</button>
</Link>
<Link to="/PAGE3">
<button className={ this.props.history.location.pathname === '/PAGE3' ? 'active' : '' }>PAGE3</button>
</Link>
</div>
<div className="appContent">
{appContent}
</div>
</div>
)
}
}
AppPage = withRouter(AppPage); //通過withRouter給AppPage組件注入路由信息
export default AppPage;
//App.js
import AppPage from './views/index'
<Router>
...
<AppPage />
...
</Router>
注意:使用了route、withRouter需要在app.js最外層嵌套Router組件
至此,利用React實現簡單的SPA就完成了!
三、總結
單頁面應用開發在前端已經是不可或缺了。單頁面應用既有它的優點,也有它的缺點。
優點:
- 良好的交互體驗
用戶不需要重新刷新頁面,獲取數據也是通過Ajax異步獲取,頁面顯示流暢。
- 良好的前后端工作分離模式
單頁Web應用可以和RESTful規約一起使用,通過REST API提供接口數據,並使用Ajax異步獲取,這樣有助於分離客戶端和服務器端工作。更進一步,可以在客戶端也可以分解為靜態頁面和頁面交互兩個部分。
- 減輕服務器壓力
服務器只用出數據就可以,不用管展示邏輯和頁面合成,吞吐能力會提高幾倍;
- 共用一套后端程序代碼
不用修改后端程序代碼就可以同時用於Web界面、手機、平板等多種客戶端;
缺點:
- SEO難度較高
由於所有的內容都在一個頁面中動態替換顯示,所以在SEO上其有着天然的弱勢,所以如果你的站點對SEO很看重,且要用單頁應用,那么就做些靜態頁面給搜索引擎用吧。
- 前進、后退管理
由於單頁Web應用在一個頁面中顯示所有的內容,所以不能使用瀏覽器的前進后退功能,所有的頁面切換需要自己建立堆棧管理,當然此問題也有解決方案,比如利用URI中的散列+iframe實現。
- 初次加載耗時多
為實現單頁Web應用功能及顯示效果,需要在加載頁面的時候將JavaScript、CSS統一加載,部分頁面可以在需要的時候加載。所以必須對JavaScript及CSS代碼進行合並壓縮處理,如果使用第三方庫,建議使用一些大公司的CDN,因此帶寬的消耗是必然的。
解決SPA的SEO(搜索引擎優化):SSR(服務器渲染)
SSR:服務器將每個要展示的頁面都運行完成后,將整個相應流傳送給瀏覽器,所有的運算在服務器端都已經完成,瀏覽器只需要解析 HTML 就行。
具體SSR作用下次介紹~
附上上面代碼的github:SPA的實現
