2016年3月,互聯網技術聯盟(ITA1024)推出前端技術專題月,由聯盟成員企業推薦國內一流技術專家聯手打造。經過每週的線上萬人課堂和每個月的線下ITA1024互聯網技術大會,針對前端開發,前端框架,性能調優,複合型前端技術等熱門話題展開深刻的分享和交流。javascript
本期分享嘉賓:劉威(京東資深前端架構開發工程師)php
本期特邀主持:趙曉強(百度高級前端工程師)css
以下是3.28劉威在ITA1024前端精英羣分享實錄。html
面臨挑戰前端
前端頁面靜態化java
前端頁面總體架構node
前端頁面加載策略python
前端基礎架構linux
前端工具和系統nginx
前端災備策略
前端性能優化
命令行工具
前端模塊
前端組件
前端開發流程
前端文檔
實際應用
頁面DOM元素劇增:單個樓層Tab標籤由5個到9個
頁面總體高度翻倍:算上頭尾,共計14個樓層,高度也由4820px到9862px
頁面圖片量增長:80%的位置變爲圖片展現
首屏加載時間要有保證:加載時間相比原來不能增長
首頁獨特的影響力:頁面不能空白,不能有報錯
大流量高併發,對穩定性要求極高
對接業務方不少,臨時需求、緊急需求較多
衆所周知,通常網站首頁欄目會有不少,不一樣欄目的數據庫查詢方式也不一樣,而首頁流量巨大,若是按照通常的動態網站每次用戶來時查詢後臺數據庫取數據的話,開銷巨大,從而致使首頁訪問速度下降,因而要考慮作靜態化,咱們具體的架構以下:
接入層:CDN—>HAProxy—>nginx
應用層:PHP Redis
存儲層:Mysql
首先,用戶訪問首頁實際是請求CDN上拼接好的靜態頁面,往下會到達Nginx動態緩存,而後到達Nginx,Nginx再把請求轉發給PHP進行處理,並經過HTTP頭來精確控制緩存;接着到了PHP應用層,使用了Redis做爲緩存,並用MySQL存儲數據;定時2分鐘循環任務生成靜態文件,從源服務器同布分發至各地CDN結點。這樣用戶訪問時,會訪問到離當前用戶最近CDN結點機器上拼裝好的靜態文件。
JS部分
首頁頁面依賴的JS庫爲jQuery V1.6.4版本,前期調研評估後採用這個比較低的版本,是由於首頁改版上線後,頭尾組件以及一大部分公共組件要推至全站使用,即還要平滑升級上千個一二級頁面站點的公共頭尾,而舊頁面引用了很是舊的jQery版本,要想平滑升級綜合評估後,1.6.4這個版本最合適;另外jQuery咱們根據總體業務進行特殊修正,就好比jsonp回調函數名稱後端規範對長度有要求,咱們就對長度進行修正等等。
第二層是JDF公共組件,包括UI組件、Unit業務組件、Widget模塊,這些組件包括event,localStorage,焦點圖,動畫,地區選擇,對話框,下拉菜單,懶惰加載,suggestion,login,search,category,cookie等常見交互組件和業務組件;同時也有當前業務級須要的公共組件,好比樓層懶加載組件,樓層切換電梯組件等。
第三層是頁面腳本,好比今日推薦樓層,猜你喜歡樓層,廣告樓層,每天低價樓層等等。
CSS部分
改版前的公共樣式base.css耦合很多公共組件樣式,如今採用很輕的樣式重置ui-base.css,公共組件單獨按需調用自身樣式,完全解耦。另外首頁全部樓層依賴的樣式文件是放在首屏加載,而不是請求當前樓層時異步加載,主要爲了保證樓層的高可用性和預加載,另外單一個樓層文件很是小,每次到達時候異步加載並不如放在首屏,全部樓層樣式文件combo成一個文件請求加載合算。
常見的前端頁面加載策略通常爲:後端從數據庫讀取出數據,拼裝好必要的頁面元素,圖片作lazyload(懶加載),用戶經過瀏覽器加載頁面上的全部HTML元素。
基於首屏加載時間最短的原則,咱們的頁面加載採用:樓層異步加載和本地緩存方式,具體以下:
<divclass="w floor lazy-fn" data-title="服飾"id="lazy-clothes" data-path="floor1-floor_index.js"data-time="01d15d664a61ff8f11cf6321f5b7a503"></div> 其中floor1-floor_index.js是樓層數據文件 ,包含樓層數據和信賴的腳本文件,其樣式已放在header裏預加載。
給每一個樓層設置默認高度,到達這個區域時請求當前樓層數據文件,同時對樓層數據文件進行md5(即data-time),並把樓層請求後的數據文件和其data-time localStorage至本地,若是頁面上的樓層data-time和本地localStorage中的data-time值同樣,數據直接取localStorage,若是不一致,從新ajax請求數據,請求後同時會localStorage數據至本地;
第一次ajax請求數據時,會延時5秒,查看請求是否成功,若是成功則退出,若是失敗:首先會取上次localStorage數據作墊底數據,但瀏覽器若是不支持localStorage, 基於高可用性原則考慮會進行第二次ajax請求數據,若是第二請求成功則正常渲染和localStorage樓層,若是第二次請求失敗,此時極大可能說明網絡存在異常,這時爲了保持良好的用戶體驗,不能留有空白,直接隱藏當前樓層。
其中用戶到達當前樓層時,請求樓層數據(包括樓層的html結構和當前樓層的腳本),渲染數據到樓層元素中,同時初始化樓層腳本。另外樓層有提早預加載邏輯,即用戶在第一二樓層時,提早加載好第三四五樓層的數據,到達樓層即顯示當前樓層數據。
樓層異步加載和本地緩存方式的好處:頁面大約共有3288個元素,首屏大約僅900左右,樓層數據lazyload,只渲染首屏元素,大大下降首屏頁面內容大小,極大的縮短頁面首屏加載完成時間,當時測算首屏約1.6s即加載完,時間比舊版和競品縮短不少。
以下圖:
主要是以JDF命令行工具爲核心進行開發,工具和系統的具體使用步驟以下:
在代碼共享平臺新建項目工程
使用JDFinit進行項目構建,生成標準化的文件目錄和工程文件
使用JDFbuild進行模塊編譯
使用JDFoutput進行輸出
聯調時使用JDF upload把編譯好的靜態文件上傳至測試服務器
在頁面元素上增長前端統計埋點,檢測元素的點擊量
聯繫後端把項目中頁面頭部和尾部代碼放在頭尾系統裏,以備後續頭尾業務變動
上線。上線前經過git提交代碼至代碼庫,上線時在Jone統一工做平臺使用線上JDF進行打包;經過Deploy系統把靜態文件發佈至CDN; 上線後清除靜態文件CDN緩存,同時檢查線上是否正常;上線後在聽雲上監控頁面性能,按期生成性能報表郵件。
災備是爲了保持線上業務在極端不好網絡環境下的高可用性,具體作法以下:
本地緩存:異步接口數據優先使用本地localStorage中的緩存數據
二次請求:接口數據本地無localStorage緩存數據,從新再次發出ajax請求
雙層接口:部分接口和域名會上線至CDN,業務調用時優先使用CDN接口,若是必定時間內未請求到數據,會用源站接口再次請求
墊底數據:源站接口萬一沒法訪問,使用預設好的墊底備份數據
接口下線:必要時候,下線非核心業務接口和非核心功能
好的頁面性能能夠提升頁面加載速度,從而增長用戶體驗,主要以下:
儘量減小首屏元素數量:用異步加載和本地緩存加載數據
CDN加載慢時候,減小頁面空白機率:頁面CSS樣式文件內聯在頁面上
減小頁面請求數量:CSS/JS combo,CSS sprite
減小靜態文件體積:CSS/JS/Images壓縮
DNS預解析:頭部增長好比<link rel="dns-prefetch" href="//d.jd.com"/>
減少Cookie體積等等
JDF京東前端開發集成解決方案,核心就是解決前端工程化的問題,包括命令行工具、前端模塊、前端開發流程、前端組件、前端文檔。
Github地址:https://github.com/putaoshu/jdf/
JDF命令工具基於nodejs,介紹以下:
跨平臺
完美支持windows、mac、linux三大系統
項目構建
生成標準化的項目文件夾
支持本地,聯調,線上三種開發流程
每一個項目都擁有一個單獨的配置文件,按選項統一編譯
模塊開發
可快速方便的對模塊進行建立,引用,預覽,安裝和發佈
經過積累,可造成徹底符合本身業務的模塊雲服務
模塊編譯
支持模塊編譯,內置模塊編譯引摯
支持將vm和smarty模版編譯爲html
支持將sass和less編譯爲css
支持ES6
項目優化
自動將頁面中的js、css引用轉換成combo請求格式
自動壓縮優化js、css、png文件
項目輸出
默認給全部靜態資源添加CDN域名前綴或後綴戳
支持cmd規範,自動提取文件id和dependencies,壓縮時保留require關鍵字
支持png圖片壓縮插件,將png24壓縮爲png8
自動生成css雪碧圖,並更新background-position屬性值
可將小圖片一鍵生成base64編碼
文件編碼統一化,即不管當前文件格式是gbk,gb2312,utf8,utf8-bom,統一輸出utf8
項目聯調
一鍵上傳文件到測試服務器,方便其餘同窗開發預覽
本地服務
支持開啓本地服務器,方便調試
支持本地靜態文件預覽,內置本地開發調試服務器,以及當前目錄瀏覽
支持實時監聽文件,文件被修改時會自動編譯成css,並刷新瀏覽器
實時在控制檯輸出錯誤信息,方便定位代碼錯誤
輔助工具
支持html/js/css文件格式化
支持html/js/css代碼壓縮
支持html/js/css文件lint,代碼質量檢查
支持chrome瀏覽器的LiveReload插件
前端模塊即widget:包括配置文件、數據源文件、模版文件、樣式文件、JS文件、圖片文件,以下ui-product-list爲一個widget:
ui-product-list[文件夾]
component.json[配置文件]
ui-product-list.json[數據源文件]
ui-product-list.vm[模板文件或者.smarty文件]
ui-product-list.scss[scss文件]
ui-product-list.js[js文件]
images[圖片文件]
頁面中引用widget用以下代碼片段
{%widgetname="ui-product-list"%}
很明顯html不支持{%%}語法,此時會用到jdf的編譯命令"jdf build",核心是把ui-product-list.json中的json數據打到ui-product-list.vm模板上,最後輸出靜態html片段;同時.scss編譯成.css,而且在header頭增長樣式引用:
<linktype="text/css" rel="stylesheet" href="/widget/ui-product-list/ui-product-list.css"source="widget"/>
頁面尾部增長js引用:
<scripttype="text/javascript" src="/widget/ui-product-list/ui-product-list.js"source="widget"></script>
輸出的時候使用jdf output會把多個wiget中js/css引用變成combo的形式如:
??/widget/ui-product-list/ui-product-list.css,/widget/header/header.css/widget/footer/footer.css
這樣就算頁面引用N多個模塊,也只會發出一個請求
模塊安裝:(jdf widget -install xxx ) 解決可複用的問題,不用每次都新建,能夠先在模塊雲上查找合適的模塊,而後下載至當前項目
模塊新建:(jdf widget -create xxx ) 在本地新建一個模塊
發佈模塊:(jdf widget -publish xxx ) 解決了共享和沉澱的問題,項目結項,能夠把可複用或者修改後的模塊提交至模塊雲,審覈經過就會發布至模塊雲上
模塊預覽:(jdf widget -preview xxx ) 用於調試單個模塊,同時解決了團隊協做問題,項目分紅多個獨立模塊,各負其責
模塊列表:(jdf widget -list) 展現模塊雲上全部模塊的列表
搭建成本很是低,只需配置好一臺FTP服務器,經過FTP服務器儲存,下載,以及分配相關用戶權限,版本管理依賴於widget中的componet.json中的version,使用方便,可使用jdf命令行工具進行發佈,下載
前端組件主要由UI交互組件和Unit業務組件構成,通過積累,已有如下部分公共組件可供全站使用,以下:
前端開發流程
使用JDFinit進行項目構建,生成標準化的文件目錄和工程
引用JDFwidget -install模塊雲中能夠直接使用的已有模塊,同時對頁面按業務進行拆分,一個同窗負責一個或者多個模塊,獨立開發和調試
使用JDFbuild進行模塊編譯
使用JDFoutput進行輸出
聯調時使用JDF upload把編譯好的靜態文件上傳至測試服務器
上線前經過git提交代碼至代碼庫,上線時使用線上JDF進行打包
前端文檔-規範
文檔請參考: https://github.com/putaoshu/jdf/tree/master/doc
前端文檔-組件API
生成工具請參考: https://github.com/putaoshu/jdd
前端文檔-命令行工具
文檔請參考: https://github.com/putaoshu/jdf/tree/master/doc
本地新建項目,調用線上模塊雲相關widget,本地使用JDF工具進行編譯,聯調,輸出,上線時使用線上JDF進行打包,發佈至CDN,最後清靜態文件緩存。
————————————Q&A————————————
問:公共模塊雲是本身搭建的,仍是基於私有的npm倉庫?
劉威:本身搭建的,只需在linux上配置好一臺FTP服務器,經過FTP服務器儲存,下載,以及分配相關用戶權限;有好的模塊能夠發佈,開發項目時候看到已有模塊能夠下載到當前項目中;版本管理依賴於widget中的componet.json中的version;使用方便,可使用jdf命令行工具進行發佈,下載
問:不少這種樣式class=「xxx|yyy|zzz」 ,是什麼意思啊?
劉威:你說的應該是clstag,這是前端埋點統計用的,就是記錄某個連接或者某個區域點擊的次數,xxx是頁面,yyy爲樓層,zzz爲某個元素,是當前站惟一的標識
問:jdf用什麼語言寫的?go?python?
劉威:Nodejs
問:剛纔提到的「上線後清理本地緩存」是什麼意思,是指服務器的cache麼?那麼用戶本地的資源文件緩存是如何處理的?
劉威:是指清CDN上靜態文件的緩存,咱們某個項目中靜態資源會有統一前綴,好比product/home/1.0.0/a.js,若是項目有更新會發product/home/1.0.1,大型項目會變動大的版本號product/home/2.0.0這樣,這樣到用戶那裏都是全新的文件。
問:關於cdn緩存,假若有文件更新大家是怎麼處理?時間截、md5?
劉威:通常是給靜態資源前面加統一的版本號來實現,若是特別小的變更,不想變動文件url,能夠在文件上線後,經過CDN後臺,手動清當前文件的CDN緩存。
問:大家的圖片壓縮是如何實現的,應用那些組件?
劉威:咱們本身根據不一樣操做系統進行了封裝,能夠下載https://www.npmjs.com/package/jdf-png-native查看
問:模塊的依賴解析也是在jdf構建時處理的麼?好比a依賴b,b依賴c,在使用a時,是否是會把a b c三個js都加到尾部?
劉威:模塊中的js依賴咱們基於CMD規範,jdf工具會自動提取文件id和dependencies,壓縮時保留require關鍵字,另外某個項目通常建議有一個統一的入口文件,好比init.js,若是init.js有依賴的經過seajs的combo插件完成全並依賴加載。
問:剛纔資源文件是用版本號來區分,那麼若是多出引用更新後是否須要多處修改版本號?
劉威:咱們項目通常會把頁面的公共頭尾接入公共頭尾系統,就是頭部的靜態html片段以及js/css引用,而項目會對應頭尾部系統中某一個編號的頭尾,前端靜態資源上線後,只須要修改一處,而後系統就會推送到業務線中。
問:DNS預解析在不一樣瀏覽器的兼容性如何?會不會影響當前頁面的性能? 確實咱們也有一些頁面作了上十個DNS預解析,甚至一些暫時用不到的。
劉威:DNS預解析在不一樣瀏覽器的兼容性:Chrome: 所有 IE: 9+ Firefox: 3+ Safari 5+ ,能夠說大部分高級瀏覽器都支持的;若是業務依賴的域名不少,建議增長上DNS預解析。
問:先後分離的狀況下,SEO方面,怎麼處理會比較好?
劉威:先後端分離對SEO沒有什麼大的關係吧,個人建議是最好前端把後端模板層接過來,好比php的smarty,java的vm,由前端來寫模板,另外而jdf工具支持.smarty,.vm模板渲染,能夠看一下;SPA頁面的SEO我還沒好的建議
問:jdf應該與glup,grunt,fis等相似的構建工具吧,jdf有什麼特色?
劉威:jdf比較輕便,另外jdf依賴的npm插件通常通過會咱們精心篩選,也有部分第二次封裝,不像grunt的插件質量良莠不齊;fis相對jdf比較重,fis基於java,php,node都有一套獨立的解決方案,而jdf只須要nodejs下便可支持.smarty,.vm模板渲染和解析
問:關於頁面靜態化,有幾個問題
1.「並經過HTTP頭來精確控制緩存」,這個精確控制指的是什麼?
2.我是否是能夠這麼理解,用戶請求先訪問CDN,若是CDN有數據直接返回,沒有數據回源到後端服務器,此時的邏輯是分級緩存策略,減小對服務端上游接口的壓力,「定時2分鐘循環任務生成靜態文件」 這個任務是單獨部署一臺服務器,只從上游接口拼裝數據生成靜態html,而後推送同步到CDN節點?
3.靜態html從源服務器同步到不一樣的CDN節點這個是怎麼實現的?
4.若是個性化的數據好比根據用戶維度下發不一樣的定向數據,或者是 有秒殺樓層等時效性很強的樓層,這個靜態化就沒有意義了吧?
劉威:1.經過Cache-Control控制,每兩分鐘更新一次 2.對的 3.生成好靜態文件碎片,經過nginx定時任務,從源服務器同步至CDN 4.對的,個性化推薦是經過ajax異步加載數據,每次都不同。
問:若是是統一頭尾更新版本號那麼是針對各個資源更新仍是全部資源?若是是各個資源豈不是要維護不少版本?如何自動化?若是是全部資源用戶的緩存豈不是浪費了?
劉威: 恩,jdf工具能夠支持全部靜態資源加統一前綴版本號,另外咱們有線上jdf環境,只須要在本地版本庫裏更新一下版本號,上線會系統自動處理,再而後這些靜態資源上線後經過頭尾系統手動更新版本號;固然最理想的方式是前端所有接入模板層,這樣頭部文件的版本號能夠用所有jdf工具來自動更新了,但電商網站的後端系統複雜性暫時選擇了半手動的方式,只有部分業務線進行了實現.
問:京東是如何處理線下測試smarty模版的?是前端直接在php環境下開發嗎 ?
劉威:jdf工具支持.smarty解析,只要nodejs就ok,前端在本地修改測試後,能夠一鍵發送至聯調機器