先聲明,本篇不會講帶有年代性的前端發展史,不講故事,想了解的讀者能夠去查閱一些其餘的資料和文章,本篇僅僅從技術發展角度結合案例分析,說明前端技術的發展和開發模式的演進變化。本篇內容重點說明PC端技術,移動端、桌面端本篇不涉及,防止讀者看到後面有疑惑,這裏強調一下。javascript
這裏先講一個需求,有一個系統須要實現一個模塊,用戶管理,模塊的功能很簡單,就是查詢、刪除。基於這個需求,南風哥會使用幾代不一樣的前端技術分別予以實現,讓讀者感覺其中的變化和奧祕。界面大概是這個樣子。css
很是老土,很是簡單,這都不是重點,重點是能說明問題就行。html
第一代:單文件模式前端
何爲單文件模式,解釋一下就是一個模塊全部的代碼都集中在一個文件中,實現上面說的需求,目錄大概是這個樣子的。vue
而後看「user.html」具體裏面的內容。java
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用戶管理</title> <style> .bg{ background-image: url("../img/bg.jpg"); } .text-center{ text-align: center; } .condition-box{ margin-bottom: 10px; } table{ width:90%; } .table-header{ background-color: #dddddd; } .delete{ text-decoration: none; } </style> </head> <body class="bg"> <div class="text-center"> <h3>用戶添加</h3> </div> <div class="text-center"> <!-- 查詢條件 --> <div class="condition-box"> 用戶姓名:<input id="name" type="text"> <button id="search" onclick="search()">查詢</button> </div> <!-- 用戶列表 --> <table align="center" border="1" cellpadding="0" cellspacing="0"> <tr class="table-header"> <td>id</td> <td>姓名</td> <td>性別</td> <td>年齡</td> <td>聯繫電話</td> <td>建立時間</td> <td>操做</td> </tr> <tr> <td>1</td> <td>張三</td> <td>男</td> <td>22</td> <td>123456789</td> <td>2018-08-08</td> <td><a class="delete" href="javascript:void(0);" onclick="deleteUser(this,1)">刪除</a></td> </tr> <tr id="5"> <td>2</td> <td>李四</td> <td>女</td> <td>18</td> <td>987654321</td> <td>2018-07-18</td> <td><a class="delete" href="javascript:void(0);" onclick="deleteUser(this,2)">刪除</a></td> </tr> </table> </div> </body> <script src="../js/jquery-2.1.1.min.js"></script> <script type="text/javascript"> function deleteUser(e,id){ // 調用控制器刪除方法 // 後端邏輯是,控制器調用刪除業務方法後,返回到user.html頁面,即當前頁面 location.href = "/userDelete?id=" + id; } function search(){ var name = $("#name").val(); // 調用控制器搜索方法 // 後端邏輯是,控制器調用刪除業務方法後,返回到user.html頁面,即當前頁面 location.href = "/userList?name=" + name; } </script> </html>
咱們會發現,全部的樣式聲明,js代碼以及html代碼都會集中在一個文件中。這樣一個功能若是較爲複雜,頁面代碼看起來會很是複雜,長此以往就會變得不易維護。node
固然有一些優化的方式。就是目錄劃分更友好一些,剝離css和js腳本,採用外部引入的方式,這種方式也是後來採用的比較多的方式,像下面這樣,看起來就清爽多了。react
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用戶管理</title> <!-- 用戶樣式 --> <link type="text/css" rel="stylesheet" href="../css/user.css" /> </head> <body class="bg"> <div class="text-center"> <h3>用戶添加</h3> </div> <div class="text-center"> <!-- 查詢條件 --> <div class="condition-box"> 用戶姓名:<input id="name" type="text"> <button id="search" onclick="search()">查詢</button> </div> <!-- 用戶列表 --> <table align="center" border="1" cellpadding="0" cellspacing="0"> <tr class="table-header"> <td>id</td> <td>姓名</td> <td>性別</td> <td>年齡</td> <td>聯繫電話</td> <td>建立時間</td> <td>操做</td> </tr> <tr> <td>1</td> <td>張三</td> <td>男</td> <td>22</td> <td>123456789</td> <td>2018-08-08</td> <td><a class="delete" href="javascript:void(0);" onclick="deleteUser(this,1)">刪除</a></td> </tr> <tr id="5"> <td>2</td> <td>李四</td> <td>女</td> <td>18</td> <td>987654321</td> <td>2018-07-18</td> <td><a class="delete" href="javascript:void(0);" onclick="deleteUser(this,2)">刪除</a></td> </tr> </table> </div> </body> <script src="../js/jquery-2.1.1.min.js"></script> <script src="../js/user.js"></script> </html>
以上兩種形式,本質上沒有什麼區別,因此就放在一塊兒說明,以上方式的優勢就是簡單粗暴,代碼開發的速度較高。jquery
與後端的交互都是後端創建一個Controller或Servlet 這麼一個控制器,請求到達控制器,處理完邏輯,而後控制器中跳轉到一個頁面,頁面進行數據渲染展現。每次交互過程目標跳轉頁面的內容都須要所有刷新加載,即便屢次交互跳轉的是同一個頁面。web
這種有什麼問題嗎?咱們來看一下,在用戶管理中的用戶列表頁咱們有個查詢功能,咱們輸入一個姓名,點擊查詢,若是採用上面的方式,想一想看,點一下查詢,到控制器,後端執行完SQL拿到數據後,又會跳轉到用戶列表頁,我點5次查詢,整個列表頁就會加載5次,並且整個頁面只有用戶表格這個部分是變化的,其餘部分是沒有變化的,這樣資源(背景圖、jquery庫、css)每次就是重複加載,帶來的問題就是1、體驗很差,2、資源重複加載,給web服務器帶來必定的請求壓力。
這裏補充多說一點內容,就是jsp這個東西,雖然如今用的很少,但仍是說一下,以前南風哥面試問過不少面試者,前端都會什麼技術,面試者張口就來,jsp .... 什麼什麼,這裏南風哥想說,jsp不屬於前端技術,爲何 ?
由於jsp自己執行前是須要編譯的,編程成class文件,是在服務端執行的,而真正的前端技術必定是在瀏覽器或相關前端技術執行引擎上(類Chrome V8)解釋執行的。像jsp裏寫的css、html、js這些東西屬於前端技術的範疇,這些都是由瀏覽器解釋執行的,不少人被jsp這種表象迷惑了,看到jsp裏面寫的都是頁面展示相關的代碼,認爲jsp就是前端技術。
回到上面的問題,咱們再來看如何解決上面的問題,這就是第二代技術。
第一代表明技術:html、css、javascript、jquery
第二代:SPA
單頁Web應用(single page web application,SPA),簡稱SPA,因爲ajax技術的興起,使得局部加載變得流行,單頁應用頁獲得了廣大開發者和用戶的青睞。
簡單理解單頁應用,就是整個應用加載都在一個頁面當中,作的就是局部替換,好比點擊一個菜單,系統整個頭部,底部,菜單部分都不變化,只變化中間區域的模塊內容。
再好比上面說到的查詢需求,點擊查詢後,用戶列表總體內容不變,經過ajax請求,數據回來後,經過js僅僅修改表格部分的數據內容。
這樣就解決了上面說的1、體驗的問題,2、資源重複加載的問題。
大部分狀況下,咱們只須要在總體頁面中引入須要的全部資源,模塊中就不須要在引入資源,只處理模塊自身的內容和業務便可。
這種方式與後端的交互都是就是創建一個Controller或Servlet 這麼一個控制器,請求到達控制器,處理完邏輯,而後返回局部頁面片斷或者json數據,頁面進行渲染展現。每次交互過程只刷新局部,總體頁面不作刷新。
這裏的數據渲染,一種是經過jquery或js的方式,字符串拼接,而後設置到對應的dom中,像這樣。
-頁面Dom
<!-- 用戶列表 --> <table id="userList" align="center" border="1" cellpadding="0" cellspacing="0"></table>
-Ajax加載渲染
function search(){ var name = $("#name").val(); // 調用控制器搜索方法,返回json數據 $.get('/userList?name=' + name,{},function(data){ var userList = data.userList; var row = ''; row += '<tr class="table-header">'; row += ' <td>id</td>'; row += ' <td>姓名</td>'; row += ' <td>性別</td>'; row += ' <td>年齡</td>'; row += ' <td>聯繫電話</td>'; row += ' <td>建立時間</td>'; row += ' <td>操做</td> '; row += ' </tr>'; for(var i = 0 ; i < userList.length; i++){ var user = userList[i]; row += '<tr>'; row += ' <td>' + user.id + '</td>'; row += ' <td>' + user.name +'</td>'; row += ' <td>' + user.sex + '</td>'; row += ' <td>' + user.age +' '</td>'; row += ' <td>' + user.phone + '</td>'; row += ' <td>' + user.createDate + '</td>'; row += ' <td><a class="delete" href="javascript:void(0);" onclick="deleteUser(this,1)">刪除</a></td>'; row += '</tr>'; } $("#userList").html(row); },'json'); }
-另外一種方式模板引擎
<!-- 定義模板 --> <script id="userListTpl" type="text/html"> <tr class="table-header"> <td>id</td> <td>姓名</td> <td>性別</td> <td>年齡</td> <td>聯繫電話</td> <td>建立時間</td> <td>操做</td> </tr> {{each list as user i}} <tr> <td>{{user.id}}</td> <td>{{user.name}}</td> <td>{{user.sex}}</td> <td>{{user.age}}</td> <td>{{user.phone}}</td> <td>{{user.createDate}}</td> <td><a class="delete" href="javascript:void(0);" onclick="deleteUser(this,{{user.id}})">刪除</a></td> </tr> {{/each}} </script>
-使用
function search(){
var name = $("#name").val();
// 調用控制器搜索方法,返回json數據
$.get('/userList?name=' + name,{},function(data){
var userList = data.userList;
var html = template('userListTpl', userList);
document.getElementById('userList').innerHTML = html;
},'json');
}
複雜頁面建議使用模板引擎的方式,代碼結構更清晰,更容易維護。
這種方式的優勢,顯而易見能夠解決第一代技術的問題,那它有問題嗎 ? 顯然有,1、破壞瀏覽器的後退、前進功能(異步加載,地址欄不發生變化,因此是沒法後退前進),固然有一些解決辦法,下面再說 ,2、SEO不友好。
因此比較常見的作法是,後端管理系統通常會總體採用SPA方式,包括如今的不少系統也是,有強SEO需求的仍然會採用上面第一代的方式,而後結合一點點ajax的內容,這即是第二代前端的開發模式。
局部加載的實現方式:
(1)ajax 局部請求加載
(2)前端hash路由,也是如今的主流方式,這種方式能夠解決瀏覽器的前進、後退問題,經過地址欄hash值的變化,但歷史頁面狀態沒法保持,回退的頁面數據仍須要從新加載、初始化,但隨着前端數據持久化的逐步流行,回退歷史頁面狀態的保存也漸漸不是問題。
(3)iframe、frameset 也能夠實現,可是不建議。
第二代表明技術:ajax、artTemplate
第三代:模塊化
隨着系統功能愈來愈多,代碼文件也愈來愈多,相互之間的依賴調用關係變得異常複雜。這時候兩個問題變得十分突出,一是js中的命名衝突問題,二是資源的加載問題。
先看js的命名問題,看一個簡單的js文件
function deleteUser(e,id){
// 調用控制器刪除方法
// 後端邏輯是,控制器調用刪除業務方法後,返回到user.html頁面,即當前頁面
location.href = "/userDelete?id=" + id;
}
function search(){
var name = $("#name").val();
// 調用控制器搜索方法
// 後端邏輯是,控制器調用刪除業務方法後,返回到user.html頁面,即當前頁面
location.href = "/userList?name=" + name;
}
這是上面user模塊的js文件,user.js,有兩個方法,這個js會被在user.html中引入
可是一個稍微複雜點的系統或功能,不可能只引入這麼幾個外部js,經常有幾十個甚至上百個js須要引入,還有咱們本身封裝的組件,通用方法等。這樣的大量引入,若是都是user.js 這種全局命名形式,很容易就發生命名上的衝突,就是你定了一個search函數,另外一個開發者或者插件裏面也定義了一個search方法,這就會帶來莫名奇妙的BUG和問題。
在來看一下資源加載的問題,一樣是用戶模塊,引入了好比說50個js文件,可是有40個文件是點擊搜索時才須要使用,加載列表時不須要,若是每次加載列表都去加載所有的js文件,那將須要多大的帶寬資源和帶來多大的請求壓力(這裏先不考慮CDN等優化方式)。能不能讓資源在真正須要的時候再去加載 ?
那麼前端模塊化技術就是重點解決以上兩個問題。咱們的代碼結構就會變成這個樣子。
-定義模塊
define(function(require, exports, module) {
var $ = require('jquery');
var tpl = require('template');
var user = {}
// 初始化
user.init = function(){
user.deleteUser();
user.search();
}
user.deleteUser = function() {
$(".delete").click(function(){
$.get('/userDelete?id=' + id,{},function(data){
user.reloadData();
},'json');
});
};
user.search = function() {
$("#search").click(function () {
var name = $("#name").val();
// 調用控制器搜索方法,返回json數據
$.get('/userList?name=' + name,{},function(data){
var userList = data.userList;
var html = template('userListTpl', userList);
document.getElementById('userList').innerHTML = html;
},'json');
});
};
exports user;
});
-使用模塊
完整頁面代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用戶管理</title> <!-- 用戶樣式 --> <link type="text/css" rel="stylesheet" href="../css/user.css" /> </head> <body class="bg"> <div class="text-center"> <h3>用戶添加</h3> </div> <div class="text-center"> <!-- 查詢條件 --> <div class="condition-box"> 用戶姓名:<input id="name" type="text"> <button id="search">查詢</button> </div> <!-- 用戶列表 --> <table id="userList" align="center" border="1" cellpadding="0" cellspacing="0"> </table> </div> </body> <script src="../js/jquery-2.1.1.min.js"></script> <script src="../js/sea.js"></script> <script> seajs.use(['./js/user', 'jquery'], function(user, $) { user.init(); }); </script> <!-- 定義模板 --> <script id="userListTpl" type="text/html"> <tr class="table-header"> <td>id</td> <td>姓名</td> <td>性別</td> <td>年齡</td> <td>聯繫電話</td> <td>建立時間</td> <td>操做</td> </tr> {{each list as user i}} <tr> <td>{{user.id}}</td> <td>{{user.name}}</td> <td>{{user.sex}}</td> <td>{{user.age}}</td> <td>{{user.phone}}</td> <td>{{user.createDate}}</td> <td><a class="delete" href="javascript:void(0);" onclick="deleteUser(this,{{user.id}})">刪除</a></td> </tr> {{/each}} </script> </html>
第三代表明技術:seajs、requirejs、kissy
第四代:前端MVC
MVC開始是存在於桌面程序中的,做者意圖解決桌面端GUI的開發耦合問題,後來被結合後端技術,總體又組成了一個MVC模式以及純前端的MVC模式。但思想是一致的,就是包含 控制器、模型、視圖三個部分。
當一個系統的模塊愈來愈多以後,模塊內部的代碼維護即是個大問題,爲了讓模塊代碼變得清晰,更易維護,相互之間沒有緊密的耦合關係,技術先驅者們想了不少解決辦法,MVC即是其中一種,像還有MVP模式,以及後面將提到的MVVM。
第四代表明技術:Extjs、backbone.js
Extjs不是UI框架嗎?,實際上它不僅有UI控件,Extjs4開始有了MVC模式,南風哥以前所在公司使用的即是Extjs,充分使用了其中的MVC模式。雖然說如今收費了,用的人不怎麼多了,但在當年仍是有一席之地的。
如何將一個前端模塊使用MVC的方式就行構建,以Extjs的爲例構建用戶管理就是以下。
將各層分離,使代碼總體結構更清晰,耦合度更低,可維護性更強,感受很複雜?沒錯,這樣原本一個簡單的模塊開發起來就會變得看起來複雜,須要分離,須要遵循必定的規範,但長遠看這麼作是有好處的,代碼的可維護性和可讀性會有不小的提高。
第五代: MVVM
這裏就不在提MVP這種模式了,沒有太流行起來,直接看MVVM模式。 拆解一下,其實是 M-V-VM(即模型-視圖-視圖模型)三個部分 ,M和V仍是原來的M和V,惟一不一樣的是VM這個東西,經過VM(ViewModel)徹底將M和V分離,是M和V的鏈接紐帶。實現了雙向數據綁定,簡單理解一下就是,模型發生變化,視圖會自動更新,視圖數據發生變化,模型會自動感知也發生變化,固然這是直觀的使用感覺,底層是有比較複雜的實現邏輯支撐。
對開發者來講,就能夠從之前的dom操做中解放出來,想一想之前的操做模式,接收到後臺的數據,jquery開幹,選擇dom,拿到數據,拼接字符串,填充到dom中。視圖數據變化了,js 每次須要主動從新取值,有了MVVM這都不須要了,作好了雙向綁定。只要將數據綁定到M,V自動更新,V層表單或其餘視圖組件狀態發生變化,M自動更新,沒有了中間的DOM操做和控制。一切都變得簡單。相互之間也沒有耦合,本身幹本身的事情。
相比前端模板引擎,它又少了模板定義這個部分,也是簡單很多,省了很多事情。
使用MVVM後,代碼會變成這樣。
-定義
new Vue({
el: '#app',
data: {
name:'',
userList:[]
},
methods:{
deleteUser:function(){
},
search:function(){
var _this = this;
$.get('/userList?name=' + name,{},function(data){
var userList = data.userList;
// 賦值,視圖自動更新
_this.userList = userList;
},'json');
}
}
})
-使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用戶管理</title> <!-- 用戶樣式 --> <link type="text/css" rel="stylesheet" href="../css/user.css" /> </head> <body class="bg"> <div class="text-center"> <h3>用戶添加</h3> </div> <div class="text-center"> <!-- 查詢條件 --> <div class="condition-box"> 用戶姓名:<input v-model="name" type="text"> <button v-on:click="search()">查詢</button> </div> <!-- 用戶列表 --> <table id="userList" align="center" border="1" cellpadding="0" cellspacing="0"> <tr class="table-header"> <td>id</td> <td>姓名</td> <td>性別</td> <td>年齡</td> <td>聯繫電話</td> <td>建立時間</td> <td>操做</td> </tr> <tr v-for="user in userList"> <td>{{user.id}}</td> <td>{{user.name}}</td> <td>{{user.sex}}</td> <td>{{user.age}}</td> <td>{{user.phone}}</td> <td>{{user.createDate}}</td> <td><a class="delete" href="javascript:void(0);" onclick="deleteUser(this,{{user.id}})">刪除</a></td> </tr> </table> </div> </body> <script src="../js/jquery-2.1.1.min.js"></script> <script src="../js/vue.js"></script> <script src="../js/user.js"></script> </html>
第五代表明技術: Angularjs、Vue
第六代:nodejs 爲基礎的大前端
關於nodejs引用網上的一句解釋 「Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行環境。」,簡單的說 Node.js 就是運行在服務端的 JavaScript。What ?js 能運行在服務端了 ?對的。
先說下爲何出現了nodejs這個東西,nodejs做者的初衷是設計一個高性能的Web服務器,讓前端開發人員經過javascript也能夠進行服務端的開發,不能否認,事件驅動 + 非阻塞 + Chrome v8 引擎,nodejs的性能表現確實優異。
發展至今,純使用nodejs做爲後端開發的企業,顯然很少。可是卻在另外兩個方面大放異彩。
1、開發時,打破先後端協做的壁壘,即便在後端沒有按時提供接口的狀況下,前端依舊能夠按照本身的節奏完成開發任務。是大前端開發的基石。
2、生產環境中,藉助其高性能,更多做爲一層網關或代理,轉發請求到真正的後端服務上。
隨着nodejs的流行,前端變得更加獨立,產生了以vue和react爲表明的兩大陣營,結合其餘插件模塊,前端也有模塊依賴了,也能夠管理依賴了,前端也須要打包編譯了,對,沒錯,前端須要學的東西也愈來愈多了,體系也愈來愈龐大了,這是真正徹底的先後端分離,大前端來了。
第六代表明技術:nodejs、Vue、React、Webpack
經過以上幾代技術的演進,前端技術發展一直在朝着解耦、可維護、高性能的目標不懈的努力着,致力於良好的用戶體驗,將來還會出現哪些NB的技術,讓咱們拭目以待吧。
南風哥對以上幾種方式都經歷過,體驗過,如今每種方式確定都有企業在用或者組合使用,技術這個東西自己就是爲解決問題而存在的,不能爲了技術而技術,爲了追求潮流而無論不顧,立足企業的痛點和需求,結合企業的實際狀況,選擇合適的技術解決問題纔是王道。
文章篇幅較長,能堅持看完着實不易,給本身點個贊吧。
創做也不易,給做者點個贊。