一直想總結下自己摸打滾爬的前端經歷,3年,從一個極討厭前端的人,變成一個吃前端飯碗的人。沒有人帶過我,跌跌撞撞的緩慢前進,但我很喜歡分享,喜歡一起進步,這會是我以後一直樂意做的事情。
綜述
娃娃學步
w3cschool上的 html / css / javascript / 以及jquery教程,萬事開頭的第一步
Say hello to the world!
小跑上路
jquery 輕鬆玩耍DOM和event
jquery-ui 增強的UI元件
Bootstrap 從此傻瓜式排版就能搞定
Bootstrap 主題模板 and 管理系統模板 快速搭建專案,簡單,好看
遇上平衡木
在小跑上路的過程中,做出來的東西看起來很專業,注意是“看起來”。如果不理解真正的前端技能,那隻能是看起來專業,內部結構還是亂糟糟的,或者是遇到bug不知道怎麼調,東一句西一句,拆了東牆補西牆。這過程就像走在平衡木上,稍不留神就會摔下去。
首先要了解css的佈局原理
js最重要的三點: 閉包、原型、作用域
關於js的原型和作用域,我沒專門寫過文章,建議去部落格園搜下。如果想要完整的理解js語言機制,可以去看《Javascript模式》這本書,或者看我整理的讀書筆記
華麗跳躍謝幕
只有把平衡木上的技能磨熟練後,我們才能真正的遊刃有餘,保證不從平衡木上摔下來的前提下,再有餘力去設計如何華麗的跳躍和謝幕。
元件封裝
還沒了解js對類(或模組)的封裝前,我們的程式碼可能是這樣的
var getData = function(){
// ......
};
function editFunc(){
// ......
};
$('.refresh-btn').on('click', function(){
var data = getData();
var $target = $($(this).attr('data-target'));
$target.empty();
for(var i=0; i<data.length; i++){
var $child = $('<tr></tr>');
$child.append('<td>' + data[i]['name'] + '</td>');
// ......
$child.append('<td><a class="edit-link">編輯</a></td>');
$target.append($child);
}
$target.find('.edit-link').on('click', editFunc);
});
一個點選就獲取資料,然後重新整理表格的功能。如果一個頁面中有多個類似的非同步重新整理的表格,且每個表格的欄位又各不相同,那麼最偷懶的做法就是拷貝大段程式碼,然後再調整<td>
的欄位。這樣的程式碼簡直了,太難維護了!
var table1 = new AjaxTable({
el: '#dataTable',
dataUrl: '/path/to/action/'
});
table1.refresh();
如果程式碼變成這樣,那就爽多了,獲取資料和重新整理表格的過程都封在了AjaxTable
中,各個使用之處只需要傳個引數呼叫下refresh()
即可,減少了大量重複(相似的)程式碼。這就是對UI元件/功能元件的封裝。
以前為了準備面試時的“手撕程式碼”,寫過一個簡單的輪播元件,不用jquery(面試經常不允許使用任何庫)
還寫過下面一些文章
為Highcharts做包裝 (有點類似上面示例中的
AjaxTable
)一步步做元件-學校選擇器(系列) (系列長文,如何把一段生硬實現的程式碼一步一步封裝和擴充套件成為一個可配置的UI元件)
模組化開發
如果要在頁面上引入外部的js庫,最初學習的時候是這樣引入的
<script type="text/javascript" src="jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="jquery-ui/jquery-ui-1.8.24.min.js"></script>
<script type="text/javascript" src="jquery-ui/jquery-ui-datepicker-zh-CN.js"></script>
<script type="text/javascript" src="bootstrap-2.3.2.min.js"></script>
由於瀏覽器中js的執行(非載入)過程是在單執行緒中的,而各js檔案又會存在依賴關係,比如 jquery-ui 依賴 jquery,bootstrap 也依賴 jquery,所以<script>
標籤的引入得滿足依賴順序。當一個專案越做頁面越多時,這麼多頁面中會存在一堆<script>
標籤,如果要將某個js檔案升級版本,或者修改script的依賴關係時,這就會成為一個很繁瑣的工作,特別是<script>
分散在專案的各個檔案中時。
RequireJS就是出來解決這個問題的(簡單來說就是用js去管理js),還有SeaJS,它們分別代表著AMD和CMD兩種風格,關於模組化和兩者的區別可以看這篇文章。
實戰案例
頁面繼承
頁面繼承 這塊跟上面的各種具體的技術沒太大關係,頁面繼承主要是用來組織專案檔案結構(或頁面結構)的一些經驗規則。假設在一個系統裡,每個頁面都有相同的頭和尾,還有nav,那根據上面封裝和分離的思想,我們可能會這樣寫
<html>
<body>
%{ include header.html }%
<div class="container">
<div class="left">
%{ include nav.html }%
</div>
<div class="main">
<!-- 具體業務... -->
</div>
</div>
%{ include footer.html }%
<script src="require.min.js"></script>
<script type="text/javascript">
requirejs.config({
// 全域性配置...
});
</script>
<script type="text/javascript">
require(['jquery'], function($){
// 具體業務...
});
</script>
</body>
</html>
我們可以把這一段作為一個base的父頁面,命名為base.html
,每個“具體業務”的頁面都繼承自它。
%{ extends 'base.html' }%
%{ block styles }%
<style type="text/css">
</style>
%{ endblock }%
%{ block content }%
<div>具體業務...</div>
%{ endblock }%
%{ block scripts }%
<script type="text/javascript"
require(['jquery'], function($){
// 具體業務...
});
</script>
%{ endblock }%
把這個頁面叫做func1.html
,具體業務的頁面中只會包含自身業務功能需要關心(用到)的東西,不去多管base頁面的閒事。可以看到子頁面中有很多block
之類的錨點,會將與endblock
之間的內容插入到父頁面中的相應位置,所以要先在base.html
中“挖好坑”。
%{ block styles }% %{ endblock }%
%{ block content }% %{ endblock }%
%{ block scripts }% %{ endblock }%
具體做法可以去看常見的模板系統,本例中參考的是Django中的模板定義。
頁面元件化
頁面元件化 也是和具體技術沒有關係,它是順著 頁面繼承 的思路,把頁面或檔案結構做更小粒度的拆分,頁面由一個個頁面元件構成。
%{ include sectionA.css }%
%{ include sectionB.css }%
<div class="row">
%{ include sectionA.tpl }%
</div>
<div class="row">
%{ include sectionB.tpl }%
</div>
<script type="text/javascript">
require(['sectionA', 'sectionB'], function(A, B){
var App = Base.extend({
_init: function(){
var that = this;
var mods = [A, B];
this.modules = [];
mods.forEach(function(Module){
that.modules.push(new Module(App));
});
}
});
});
</script>
上面相當於一個業務頁面,它由sectionA
和sectionB
兩個頁面元件組成,sectionA.tpl
和sectionB.tpl
是html模板。在應用層(即業務)頁面中初始化兩個js模組A
和B
,並且把自身的App
變數傳遞給模組(new Module(App)
),可以實現子模組與應用層頁面的通訊,甚至是模組之間的通訊。
這樣把頁面拆成粒度更細的結構,好處是頁面模組可以複用,也便於管理,改動頁面中的一小塊時只需在所處的模組中,縮小改動的影響範圍。
還看過一種思想是,把css檔案也當做資源由requireJS動態載入,這樣上面示例中的include xxx.css
都不需要了,頁面模組的css資源作為該模組的依賴,寫在js模組的define
的依賴中。
define(['jquery', 'sectionA.css'], function($){
// 業務模組...
});
這樣把css和js都抽象成“資源”,相當於
元件 = 模板 + 資源
一個頁面整體的模板,相當於多個頁面元件的拼裝而成。更進一步,如果能讓頁面元件做到非同步渲染的話(即可以由js去解析模板語法和變數,而不是交給web框架),才能真正做到頁面渲染的本質:
呈現給使用者的頁面 = 頁面模板 (包括元件的模板) + 資料
“渲染”就是將帶資料變數的頁面模板輸出成標準的html,同步渲染是指在服務端解析模板並輸出完整html到瀏覽器中,而非同步渲染指直接在瀏覽器中通過javascript 根據傳入的資料將模板輸出成標準html。
同一模板如果既能在服務端同步渲染,又能在瀏覽器端非同步渲染的話,我們就不需要關心“資料”是後端框架直接輸出到頁面的,還是ajax動態取來的。對模板來說,資料就是個“介面”,而 模板 + 資料 = 渲染
。這樣我們作為前端,才能把更多精力放在模板和互動上,不用管資料的傳遞方式。
平凡之路
前端發展了十幾年,現在幾乎到達頂峰的速度了,近兩年推出的框架層出不窮,jquery早已不是一統江湖了。每個人的精力都有限,不可能一個個都學過來,但是必須承認,前端是一個完整的體系(我之前整理的知識體系),有它獨特和魅力之處。不僅是框架,還有更多的工程化問題,框架都是為了解決某類相通的問題而生。模板和資料分離也好,“狀態”和“表現”分離也好,我越來越體會到
“分”是為了“合”
這條平凡之路,還會“頻繁”的發展和融合下去。
本文最早發表在我的個人部落格上,轉載請保留出處 http://jsorz.cn/blog/2015/12/twisted-way-to-awesome-fe.html