京東商品詳情頁前端開發寶典

錢曙光發表於2016-08-05

宣告:本位來自京東張開濤的微信公眾號(kaitao-1234567),授權CSDN轉載,如需轉載請聯絡作者。
作者:周琪力,前端工程師,網路常用暱稱「keelii」。在過去的4年裡主要負責京東網站商品詳情頁的前端系統架構和開發,平時主要寫JavaScript偶爾寫一些NodeJS,Python。
責編:錢曙光,關注架構和演算法領域,尋求報導或者投稿請發郵件qianshg@csdn.net,另有「CSDN 高階架構師群」,內有諸多知名網際網路公司的大牛架構師,歡迎架構師加微信qshuguang2008申請入群,備註姓名+公司+職位。

簡介

詳情頁也叫做單品頁,域名以「item.jd.com/skuid.html」為格式的頁面。是負責展示京東商品SKU的落地頁面。主要任務是展示商品相關資訊,如價格、促銷、庫存、推薦,從而引導使用者進入購買流程。同時單品頁有很多版本。一般分為兩類。一類我們通常看到的「通用類目詳情頁」——所有類目都可以使用,一類是不經常看到的「垂直屬性詳情頁」——一些有特殊屬性的商品集合

圖片描述

首先,由於詳情頁量大(SKU數十億)、高併發(日PV數十億)等特性,在很長的一段時間裡,單品頁都是後端程式生成靜態頁面使用CDN來解決量大、高併發的問題。

其次。單品頁涉及的「三方」系統特別多,比如促銷、庫存、合約、秒殺、預售、推薦、IM、店鋪、評價社群等。而單品頁的主要任務就是展示這些系統的資訊,並且適當的處理他們之間的邏輯關係,而這些系統的介面一般都使用非同步Ajax來完成,因為其一CDN無法做到頁面的動態化,其二一些系統的資訊對實時性要求特別高(價格、秒殺),即使使用後端動態渲染也很難做到無快取零延遲。

基於上面兩個原因,註定了單品頁是一種重多系統業務邏輯展示型頁面,重前端頁面。我大概彙總了一下頁面上非同步介面,總共約有30個,頁首屏的介面特別重要,介面之間幾乎都有耦合關係:

圖片描述

前端的發展歷程

混沌時期

混沌時期的單品頁並沒有前端開發的概念。核心的功能指令碼只有三個:促銷價格(promotion.js)、庫存地區(iplocation.js)、其它邏輯(pshow.js)。這三個指令碼分別是三個不同團隊的同事負責維護,當時我剛進入京東的時候在UED部門,負責頁面指令碼整體的維護工作和pshow的開發。那時候我自己維護的pshow.js指令碼壓縮後只有80kb,所有的程式碼都是過程式的,沒有任何使用模式和程式碼技巧,JS最多也只被用來做個判斷渲染DOM。那時候的前端工作內容只在UI層面,寫樣式和一些互動指令碼。

這個階段給我最深刻的感覺是單品頁後端模板很少維護(後端架構是最老的aspx版本)。大多數的改動都要用JavaScript去動態渲染。因為後端頁面是一個生成器生成的。如果頁面後端模板有改動那麼就需要全量的生成一次,過程可能需要幾個小時。

初見端倪

當我接手這個專案時剛好有一次大改版,就在這時候老大說頁面上的指令碼都要放在我們手裡維護。然後就是一大波的重構、重寫。基本上pshow被重寫了大概80%其它的因為業務邏輯的問題並沒有完全重寫,只是做了些程式碼層面的優化。

有一個模板引擎叫trimPath,知道這個的估計都算老前端了。最早的客戶端JavaScript MVC模式代表作品,只到現在還在使用。這個階段像評價這種完全非同步載入的模組特別適合使用模板引擎來減少維護的工作量。這個時候雖然頁面上的程式碼並不都是我們寫的,但基本上前端對頁面的JavaScript有了控制權,接下來的事情就是尋找機會逐個優化。

這段時間是最痛苦的時候,維護的工作統一到前端。然後後端幾乎沒有變化,只是在一段時間將後臺的架構從aspx過渡到了java。本質上並沒有什麼改變。前端卻做了比以前更多的事情,也是在這個時候我接手了大量的維護工作(包含全站公共庫的維護)使得我意識到了一些自動化、工程化方面的重要性,後文會主要講解,順便說下,那時候前端自動化工具Grunt剛面世,但是我自己卻用的是apache ant,不過不久就切換到了Grunt來構建專案。

撥雲見日

單品頁不僅重系統邏輯,也重維護。在這段時間裡一方面有正常的維護類需求要做,一方面自己也不斷的學習新知識為以後的改版做鋪墊。不過就在這時單品頁有歷史意義的一次技改出現了——單品頁動態化技改。關於後端部分的改造細節可以去億級商品詳情頁架構演進技術解密瞭解。

總的來說這次的改版後很多資料直接從後端讀取,不再從前端非同步獲取而且我們也做過一些非同步載入的優化,多介面combo從統一服務吐出給前端使用。這時前端就不用再為非同步介面的載入時苦腦了,只需要專注系統介面的邏輯。

隨著這次技改,前端的程式碼也迎來了模組化的時代。我們把所有的前端程式碼都進行了模組化然後基於SeaJS重寫,配合Nginx concat功能實現了本地模組化開發,線上服務端合併。

單品頁前端模組的結構與劃分

概覽

圖片描述

上圖可以看出,基本上最核心的模組都在首屏。每個模組都有單獨的一/多個指令碼。程式碼行數(LOC)由230+~1200+不等。通常來說程式碼行數越多程式碼複雜性就越高,邏輯越複雜。很難想象「購買方式」這種只有一行屬性選擇功能的程式碼行數卻高達1200多行。其主要原因就在於購買方式所在的系統和其它首屏核心系統(庫存、促銷、地址選擇、白條)都有邏輯上的耦合。

看著不錯,然而在一個前端工程師眼裡至少應該是這樣的(我只取了一些典型的模組,並不是全部):

圖片描述

這就可以解釋為什麼有的時候只是加一個很小的東西我們都為考慮再三然後通過AB測試提取相關資料,最後後再進行決策。單品頁的首屏可以說是寸土寸金。

按什麼維度劃分模組

起初我按模組的屬性劃分,比如核心、公共指令碼、模組指令碼。但用了一段時候以後發現這樣劃分在單品頁這種大型系統中並不科學,因為這樣劃分出來的程式碼只有劃分的人知道是什麼規則,其它人接手程式碼很難快速掌握程式碼架構,而且尤其在模組比較多的時候不方便維護。

後來我嘗試完全以功能模組在頁面上出現的位置維度劃分。這樣以來維護起來方便多了,需要修改某個模組程式碼只需要對照著圖裡面標識的模組資訊就能輕易找到程式碼。

整體核心模組

我們按頁面上的模組結構首屏劃分出來這幾個核心模組:

  • curmb - 麵包屑
  • concat - 聯絡諮詢相關店鋪資訊
  • prom - 價格促銷資訊
  • address - 地區庫存選擇,配送服務
  • color - 顏色尺碼
  • buytype - 合約機購買方式
  • suits - 套裝購買
  • jdservice - 增值服務
  • baitiao - 白條支付
  • buybtn - 購買按鈕
  • info - 地區提示資訊

專案的整體樹形結構是這樣的:

圖片描述

模組內部結構

比如下面這個大圖預覽的功能,我全部放在一個資料夾裡面維護,但是邏輯上的JavaScript模組是分離的,只是說資料夾(preview)就代表頁面上的某一部分功能集合:

圖片描述

注意資料夾的命名有一定的規則:

  • 模組指令碼與樣式名必須一樣;
  • 需要製作sprite的圖片統一放在module/i目錄下面,生成的sprite圖片也在其中;
  • 生成的mixin在模組根目錄下,便於其它樣式檔案呼叫;

我們再來看下自動生成生成的__sprite.scss是什麼內容:

/*__sprite.scss 自動生成 */

@mixinsprite-arrow-next{

    width:22px;

    height:32px;

    background-image:url(i/__sprite.png);

    background-position:-0px-30px;

}



/*preview.scss 手動新增 */

@import"./__sprite";

.sprite-arrow-next{

    @includesprite-arrow-next;

}

注意引用的mixin名稱和我們需要手動新增的樣式類名一致。當然也可以直接生成一個類名對應的樣式,但是靈活性不好。比如hover的時候是另外一張圖片就沒法自動生成了。

前端技能樹

一、HTML

DOM節點數

與重業務邏輯的頁面不同,重展示的頁面一般具有很高的DOM節點數。比如京東首頁,正常情況載入完頁面一共有3500多個DOM節點,基本上全部用於展示商品資訊、廣告圖和內容佈局,頁面上的三方非同步服務也比較少。尤其像頻道頁基本上沒有什麼業務上的邏輯,全部是靜態頁面。這種頁面的特點是更新換代頻率高,一年兩三次改版很正常,CMS做模組化後兩天換個皮膚都是沒問題的。但是這種思路並不適合單品頁。單品頁更重業務邏輯,同時展示層UI邏輯也有很多關係。

我自己的經驗是:頁面上的DOM節點數絕對不能超過5000個,否則頁面滾動的時候就會出現卡頓的情況,尤其是移動端。

同步渲染還是非同步載入

理論情況下最好的做法是後端同步動態渲染頁面,但是由於Web應用中很多功能都是使用者行為驅動的。同步載入不可避免的消耗了後端服務資源。比如非首屏模組(公共頭尾、評價)、點選事件觸發的DOM內容(非同步tab)。

所以我的經驗是:能放到後端做判斷渲染的DOM就儘量放在後端(尤其是首屏)。這樣做的好處有四點好處:

  1. 後端渲染頁面相對穩定,不像前端JavaScript動態渲染DOM,可能因為指令碼報錯或者不可用造成模組都無法展示;
  2. 可訪問性、SEO及使用者體驗也比較好。不會產生指令碼的渲染抖動問題;
  3. 一定程度上減少了前端渲染頁面的複雜性,減少前端程式碼複雜度;
  4. 邏輯統一到一個地方維護起來也方便,而且後端應該為業務邏輯負責,前端應該為展示UI互動負責。

對於非同步渲染的模組來說,後端通常需要判斷「頁面有什麼元素」,以及元素之間的依賴對應關係;而前端需要專注於「元素應該怎麼展示」,UI層面的互動以及模組與模組之前的邏輯關係。

其實更多的時候非同步是一種沒有辦法的辦法,也就是說非同步是其它方案都解決不了的情況下才考慮的。

外鏈靜態資源

儘量使用外鏈CSS和JavaScript資源,一方面便於快取,減少服務同步輸出的資源浪費。IE 6裡面會有一些可怪的bug,比如有內聯樣式style標籤的頁面A如果在另外一個頁面B中的link標籤中引用,那麼這段style會在B頁面也起作用。

使用雙協議的URL

使用//來代替http:和https:瀏覽器會自動適應兩種協議的資源訪問,相容性較好。注意IE 8下使用指令碼更新src為雙協議時會出現bug,建議使用location.protocol來判斷然後做相容處理。

刪除元素預設屬性

比如script標籤預設的type就是text/javascript,如果script裡面的內容是JavaScript時可以不用寫type。另外如果要在頁面裡面插入一段不需要瀏覽器解析的HTML片段時可以將type寫成text/x-template(任意不存在的type)用於放置模板檔案,通常用來在指令碼中獲取其innerHTML而無任何負作用。

給指令碼控制元素加上類鉤子

在指令碼中取頁面元素使用J-字首類名,與普通樣式類分離。這樣做會生成很多冗餘的類名,但卻很好的降低了樣式和指令碼的耦合,並且在重構和指令碼職位分開團隊裡會是一條最佳實踐。

二、CSS

樣式分類

所有頁面只共享一個sass Mixin,裡面包含了基礎的sass語法糖、常用類(清浮動、頁面整體顏色字型等)。

模組級的樣式分為兩類:

  1. 與指令碼無關的公共樣式,單獨在模組資料夾中組織。比如:按鈕、標籤頁。全部放在common模組中維護;
  2. 與指令碼相關的模組級樣式,與對應模組指令碼放在一起,可以引用common中的公共樣式,但不可以被其它模組引用。

雪碧圖

關於雪碧圖我經驗是:永遠不要想把所有的圖示拼合在一起。按模組而不是按頁面去拼sprite更合理,更方便維護,然後配合構建工具自動接合生成樣式檔案才是最好的解決方案。當然如果你的頁面比較簡單,那這條規則並不適用。說到這個問題我就得把珍藏多年的圖片拿出來show一把,用事實來說明為什麼把所有圖片都拼在一張圖上就一定是對的。早期由於年輕篤信將所有的icon拼在一張圖上才是完美的(圖 1)

圖片描述

後來維護起來實在不方便,就把按鈕全部單獨接合起來。注意,當時的按鈕都是圖片,設計方面要求的很嚴格。加入購物車按鈕做的也非常漂亮(圖 2)

圖片描述

然後這些都不是最典型的,下面這個promise icon才是(圖 3)

圖片描述

從圖裡面可以看到,這個功能在第一個版本的時候只有7個icon,後來不斷增加,最多的時候達到77個。以至於當時每週都會新增兩個的頻率。

同時這個icon當時接合的時候技術上也有問題:不應該把文字也切到圖片裡面,主要原因是早期icon比較少加上外邊框樣式對齊的問題綜合選擇了直接使用圖片。

後來我就覺得這樣是不對的。然後通過和產品的溝通,說明我的考慮以及新的解決方案後得到了認同。結果就是對圖片不進行拼合,後臺上傳經過稽核的不帶文字icon,文字由介面輸出,然後在產品上做了約定:icon最多不能超過4個,程式碼裡也做了相應限制。這樣就能保證頁面上的請求數不會太多同時方便系統維護,問題得到了解決。

適當使用DataURI

這個在一些小圖片場景方面特別適合,比如1*1的佔點陣圖、loading圖等,不過IE 6並不支援這種寫法,需要的時候可以加上一些相容寫法:

.ELazy-loading{

    background:url(data:image/gif;base64,R0lGODlhKwAeAJEAAP///93d3Xq9VAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFFAAAACwDAA0AJQADAAACEpSPAhDtHxacqcr5Lm416f1hBQAh+QQJFAAAACwDAA0AJQADAAACFIyPAcLtDKKcMtn1Mt3RJpw53FYAACH5BAkUAAAALAMADQAlAAMAAAIUjI8BkL0CoxQtrYrenPjcrgDbVAAAOw==)centercenterno-repeat;

    *background-image:url(//misc.360buyimg.com/lib/skin/e/i/loading-jd.gif);

}

關於相容性

相容性可以說是前端工程師在平常開發中花費很大量無意義工作的地方。關於相容性我想說的是如果你不願意去說服周圍的人放棄或者讓他們意識到相容性是個不可能完全解決的問題,那麼你就得為那些低階瀏覽器給你帶來的痛苦埋單。

其實更好的辦法是你和設計、產品溝通然後給出一種分級支援的方案。把每種瀏覽器定義一個級別。然後在開發功能的時候以「漸進增強」的方式。通常來講我們的解決方案是在低階瀏覽器裡面保證流程正常進行、模組可以使用,但忽略一些無關緊要的錯位、不透明等問題,在高階瀏覽器裡面需要對設計稿進行精確還原,適當的加上一些井上添花在細節。比如微小的動畫、邏輯細節上的處理等。

舉個例子吧,下面這個進度條表示預約的人數,它是介面非同步載入完才展示的。如果載入完就立即設定進度條寬度會顯得生硬無趣,但是如果加上一點動畫效果的話就好多了。然而問題又來了,如果加上動畫那麼邏輯上這個進度條應該是一點點的增加,對應的人數也應該是逐個增加。於是我就做了個優化,讓人數在這段時間內均勻的增加。這個細節並不是很容易被人發現,但是這種設計會讓使用者感覺很用心而且有意思:

圖片描述

三、JavaScript

圖片描述

單品頁的指令碼載入/執行順序:

  1. 等待頁面準備就緒(DOM Ready);
  2. 準備就緒後載入入口指令碼(main.js),指令碼負責其它功能模組的排程,動態接合模組通過seajs的require.async方法非同步呼叫;
  3. 公共模組(common.js)負責初始化全域性變數並掛載到pageConfig名稱空間;
  4. 動態模組陣列,這個是後端通過程式判斷處理生成的一個模組名列表。一般只包含首屏需要載入的模組;
  5. 後載入模組(lazyinit.js)初始化,這個指令碼只做一些頁面滾動才載入的模組事件繫結。當模組出現在視口內再使用require.async非同步載入模組的資源及初始化。

入口指令碼

大致程式碼如下:

/**

* 模組入口(1. 公共指令碼 2. 首屏模組資源 3. 非首屏「後載入模組」)

*/

var entries=[];

// 頁面公共指令碼樣式

entries.push('common');

// 頁面使用到的首屏模組(後端開發根據頁面不同配置需要呼叫的模組)

entries=entries.concat(config.modules);

// 非首屏「後載入模組」

entries.push('lazyinit');



for(var i=0;i<entries.length;i++){

    entries[i]='MOD_ROOT/'+entries[i]+'/'+entries[i];

}



if(/debug=show_modules/.test(location.href))console.log(entries);



require.async(entries,function(){

    var modules=Array.prototype.slice.call(arguments);

    var len=modules.length;



    for(var i=0;i<len;i++){

        var module=modules[i];



        if(module&&typeof module.init==='function'){

            module.init(config);

        }else{

            console.warn('Module[%s]must be exports a init function.',entries[i]);

        }

    }

});

注意模組路徑中的MOD_ROOT是提前在頁面定義好的一個seajs path。目的是為了把前端版本號更新的控制權釋放給後端,從而解決了前後端依賴上線不同步造成的快取延遲問題,配置指令碼中只有幾個定義好的路徑:

seajs.config({

    paths:{

        'MISC':'//misc.360buyimg.com',

        'MOD_ROOT':'//static.360buyimg.com/item/default/1.0.12/components',

        'PLG_ROOT':'//static.360buyimg.com/item/default/1.0.12/components/common/plugins',

        'JDF_UI'   :'//misc.360buyimg.com/jdf/1.0.0/ui',

        'JDF_UNIT':'//misc.360buyimg.com/jdf/1.0.0/unit'

    }

});

還有一點,在測試環境的頁面中版本號(上面程式碼中的1.0.12是一個全量的版本號)是後端從URL上動態讀取的(使用引數訪問就可以命中對應版本item.jd.com/sku.html?version=1.0.12)。這樣以來測試環境上就可以並行測試不同版本的需求,而且互不影響。當然如果不同版本的後端程式碼也有修改的話這樣是不行的,因為後端程式碼也需要有個對應的版本號。

不過我們已經解決了這個問題。後端會在測試環境裡動態載入後端模板並且可以做到版本號與前端一致。這樣以來配合git方便的分支策略就可以同時並行開發測試多個需求,不用單獨配多個測試環境。什麼?你還在使用SVN!哦。那當我沒說過。

事件處理模型

客戶端的JavaScript程式碼基本上都是事件驅動的,程式碼的載入解析依賴於瀏覽器提供的DOM事件。比如onload、mouseover、scroll等。

事件驅動的的模型特別適用於非同步程式設計,而JavaScript天生就是非同步,所有的非同步操作行為都最終會在一個回撥函式(callback)中觸發。

比如單品頁中價格介面,載入完成後需要更新DOM元素來展示實時價格;地區選擇介面載入完成後會更新配送資訊、庫存/商品狀態等,虛擬碼如下:

/*onPriceReady 和onAreaChange 可以認為都是一個 Ajax 非同步函式呼叫

 * code 1 和 code 2 執行到的時間是不確定先後順序的

 */

/* prom.js*/

onPriceReady(function(price){

    // code 1

    $('#price').html(price);

});



/*address.js */

onAreaChange(function(area){

    // code 2

    $('#stock').html(area.stockInfo);

});

上面的兩段程式碼分別在兩個指令碼中維護,因為他們的邏輯相對獨立。早期並沒有關聯關係。後來需求有變,他們之間需要共享一些對方的資料(切換地區後需要重新獲取價格資料並展示)。但是物理上又不能放在一起通過使用全域性變數的方式共享,而且它們都是非同步載入介面後才取到資料的,並不好確定誰先誰後(非要做到那就只能用全域性變數雙向判斷)。所以這樣並不能很好的解決問題,而且程式碼的耦合度會成倍增加。

這時候我們引入了一種設計模式來解決這種問題——釋出者/訂閱者,我們把這種模式抽象成了自定義事件程式碼來解決這一問題。這段程式碼是由YUI核心開發者NicholasC. Zakas實現的。程式碼很簡單,事件物件主要有兩個方法addListener(type,listener)和fire(event)。於是我們重構了上面的虛擬碼:

/* prom.js*/

// 在程式碼中註冊一個地區變化事件,獲取變化後的地區 id

// 然後重新請求價格介面並展示

Event.addListener('onAreaChange',function(data){

    getAreaPrice(data.areaIds)

});



onPriceReady(function(price){

    $('#price').html(price);



    Event.fire({

        type:'onPriceReady',

        data:'Any datayou want'

    })

});



/*address.js */

onAreaChange(function(area){

    $('#stock').html(area.stockInfo);



    // 在地區變化後除了做自己該做的事情以外

    // 觸發一個名為onAreaChange 的事件,用來

    // 通知其它訂閱者事件完成,並傳遞地區相關引數

    // 這個時候在onAreaChange Ajax 回撥函式

    // 中就只需要關心自己的邏輯,其它模組的耦合關係

    // 交給它們自己通過訂閱事件來處理

    Event.fire({

        type:'onAreaChange',

        data:area.ids

    })

});

需要注意的一點是,必須確保事件先註冊後觸發執行,也就是說先addListener,再fire。

一些典型的效能優化點

基本上客戶端的JavaScript效能問題都來自於DOM查詢和遍歷,在使用的時候一定要小心,可能不經意的一個操作就會損失很多效能,尤其在低端瀏覽器中。順便多說一點,現代的JavaScript直譯器本身是很快的,語言層面的效能問題很少遇到。DOM查詢慢是因為瀏覽器給JavaScript訪問頁面提供的一套DOM API本身慢:

  1. 快取DOM查詢,同時DOM查詢不要超過2000個,低階瀏覽器會卡頓;
  2. 不要使用鏈式呼叫find,如:find(‘li’).find(‘a’)而是find(‘li a’);
  3. 在切換元素顯示狀態的時候,如果元素很多。優先使用show()/hide()方法,而不是css(‘display’, ‘block/none’)前者有快取,後者會強制觸發reflow;
  4. 給節點新增data-xx屬性在存放一些資料,通過使用jQuery的data(‘xx’)方法取更高效,減少DOM屬性訪問;
  5. 高密度事件(scroll,mousemove)觸發場景請使用節流方法;
  6. 使用事件代理,而不是直接繫結。如果不確定程式碼被呼叫次數,可以先解除繫結再繫結具有名稱空間的事件處理函式;
  7. 儘量少用DOM動畫,使用CSS 3動畫代替;

前端工程化

原由

前端工程化其實並不是最近兩年才有的概念。大約在2013年的時候Grunt問世的時候就已經有所涉及。這類打包工具主要的目的是自動化一些開發流程,我最早使用Grunt來構建程式碼的時候只解決了三個問題:

  1. 合併壓縮優化樣式指令碼;
  2. 上線完自動備份;
  3. 單個檔案打包到多目錄(歷史原因一個檔案線上的路徑有兩種,需要傳兩個目錄);

當時我還在組內做過一個分享,有興趣的可以去圍觀一下Best WorkflowWith Grunt

其實這些工具出現的原因是:當時前端領域的各種基礎設施很缺乏,而前端的工作內容又相對零散。工作時需要開很多的軟體。再加上JavaScript語言本身也很弱,就連包管理這種基礎的東西也沒有內建,以至於模組化要通過一些第三方類庫來實現,比如:RequireJS、SesJS。

工具的重要性可以在我之前的一個分享中找到前端開發工具系列

現狀

如今前端工程的生態環境由於NodeJS的出現已經變得很好了。你可以根據自己的需求選一個合適的直接用到專案裡面。像Grunt、Gulp、browserify、webpack等。不過要明白這些工具的出現從另一方面證明了前端開發天生存在很多的問題:

  • HTML從誕生到HTML 5之前幾乎沒有任何變化,DOM效能天生缺失。所以才有了Virtual DOM這種東西;
  • CSS只是一門描述型的語言,沒有變數、邏輯控制、語句。所以才出現了Sass、Less這種預編譯工具;
  • JavaScript號稱「高階的(high-level)、動態的(dynamic)、弱型別的(untyped)、解釋型(interpreted)程式語言,適合物件導向(oop)和函式式的(functional)程式設計風格」的程式語言,但是語言本身有很多問題(ES 6之前)。不適合大型專案的開發、沒有一些高階特性的支援、同時被其它語言詬病的callback風格、單執行緒執行等。所以才出現了像TypeScript、Babel這種編譯成JavaScript程式碼的語言;

這些問題幾乎都是歷史性的原因和相容性因素造成的。作為一名好的前端工程師要看清楚現狀,然後按自己專案的需求去定製一些前端工程化的方案,而不是隨波逐流。

選擇

其實現在自己開發一套前端工程化/自動化流程的成本已經很低了,你只需要學習一些NodeJS的知識,配合NPM包管理機制,隨手就搞出一個構建工具出來。因為並不需要你去實現什麼東西,所有的東西都有現成的包。指令碼壓縮有UglifyJS、CSS優化有CSS-min、圖片壓縮優化有PNG-quant等。你只需要想清楚自己要達到什麼目的,解決什麼問題就可以抄傢伙自己寫一套工作流出來。

我自己的經歷也從Grunt、GulpJS到現在自造輪子。自己根據需求開發出來一套整合的打包工具,有興趣的可以去圍觀一下Wooo

當然你也可以不用任何打包工具,自己寫一些NPM Script來完全定製化專案開發/測試/打包流程。我猜這也是為什麼現在類似Grunt不再那麼火,Gulp遲遲沒有釋出4.0版本的原因。寫一個構建工具的成本太低了,而且這種整合的工具很難滿足差異的開發需求。君不知已有人意識到了這一點麼why-i-left-gulp-and-grunt-for-npm-scripts

程式、設計、產品

我始終認為程式、設計是為了產品服務的。好的產品是要重視設計的,好的(前端)工程師是要有一些審美素養。

其實很多時候技術解決方案都是要根據產品的定位來設計的,瞭解產品需求以後才能定製出真正合適的高效的解決方案。好比前面講到的那個sprite案例,如果一開始就和產品討論好方案後來也不可能有那種失控的情況發生。在產品形成/上線前期能發現問題比上線後發現問題更容易解決。

這部分內容和程式碼無關,就不多說了。然而早年我還有一次分享關於前端、改變

總結

關於單品頁的前端開發本篇文章只是冰山一角,還有很多沒有提及,每個小東西都可以單獨寫一篇文章來分享。隨後希望可以有更多的總結和分享。

相關閱讀:

2016年8月12日-13日,由CSDN重磅打造的網際網路應用架構實戰峰會運維技術與實戰峰會將在成都舉行,目前18位講師和議題已全部確認。兩場峰會大牛講師來自阿里、騰訊、百度、京東、小米、樂視、聚美優品、YY互娛、華為、360等知名網際網路公司,一線深度的實踐,共同探討高可用/高併發/高效能系統架構設計、電商架構、分散式架構、運維工具研發與實踐、運維自動化系統的構建、DevOps、雲上的運維案例分析、虛擬化技術、應用效能檢測與管理、遊戲行業的運維實踐等,將和與會嘉賓共同探討「構建更安全、更高效能、更穩定的架構和運維體系」等領域的話題與技術。【八折優惠中,點選這裡搶票,欲購從速。】

相關文章