前端技術及開發模式的演進,帶你瞭解前端技術的前世此生

先聲明,本篇不會講帶有年代性的前端發展史,不講故事,想了解的讀者能夠去查閱一些其餘的資料和文章,本篇僅僅從技術發展角度結合案例分析,說明前端技術的發展和開發模式的演進變化。本篇內容重點說明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>
View Code

咱們會發現,全部的樣式聲明,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>
View Code

以上兩種形式,本質上沒有什麼區別,因此就放在一塊兒說明,以上方式的優勢就是簡單粗暴,代碼開發的速度較高。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');

}
View Code

-另外一種方式模板引擎

<!-- 定義模板 -->
<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>
View Code

-使用

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');
}
View Code

複雜頁面建議使用模板引擎的方式,代碼結構更清晰,更容易維護。

這種方式的優勢,顯而易見能夠解決第一代技術的問題,那它有問題嗎 ? 顯然有,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;
}  
View Code

這是上面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;
}); 
View Code

-使用模塊

 

完整頁面代碼

<!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>
View Code

第三代表明技術:seajs、requirejs、kissy

 

第四代:前端MVC

MVC開始是存在於桌面程序中的,做者意圖解決桌面端GUI的開發耦合問題,後來被結合後端技術,總體又組成了一個MVC模式以及純前端的MVC模式。但思想是一致的,就是包含 控制器、模型、視圖三個部分。

當一個系統的模塊愈來愈多以後,模塊內部的代碼維護即是個大問題,爲了讓模塊代碼變得清晰,更易維護,相互之間沒有緊密的耦合關係,技術先驅者們想了不少解決辦法,MVC即是其中一種,像還有MVP模式,以及後面將提到的MVVM。

第四代表明技術:Extjs、backbone.js

Extjs不是UI框架嗎?,實際上它不僅有UI控件,Extjs4開始有了MVC模式,南風哥以前所在公司使用的即是Extjs,充分使用了其中的MVC模式。雖然說如今收費了,用的人不怎麼多了,但在當年仍是有一席之地的。

如何將一個前端模塊使用MVC的方式就行構建,以Extjs的爲例構建用戶管理就是以下。

  • UserController  註冊用戶管理的交互事件和方法定義
  • UserModel  數據模型,配置Store使用
  • UserStore  異步請求,負責交互後臺數據
  • UserGrid   表格視圖,負責展現Store請求回來的數據,綁定渲染

將各層分離,使代碼總體結構更清晰,耦合度更低,可維護性更強,感受很複雜?沒錯,這樣原本一個簡單的模塊開發起來就會變得看起來複雜,須要分離,須要遵循必定的規範,但長遠看這麼作是有好處的,代碼的可維護性和可讀性會有不小的提高。

 

第五代: 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');
        }
    }
})
View Code

-使用

<!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>
View Code

第五代表明技術: 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的技術,讓咱們拭目以待吧。

南風哥對以上幾種方式都經歷過,體驗過,如今每種方式確定都有企業在用或者組合使用,技術這個東西自己就是爲解決問題而存在的,不能爲了技術而技術,爲了追求潮流而無論不顧,立足企業的痛點和需求,結合企業的實際狀況,選擇合適的技術解決問題纔是王道。

文章篇幅較長,能堅持看完着實不易,給本身點個贊吧。  

創做也不易,給做者點個贊。

相關文章
相關標籤/搜索