前端工程化javascript
web 應用複雜度的增長,特別是單頁面應用的風靡。組件化,工程化,自動化成了前端發展的趨勢。或者說一線的互聯網公司就是這麼作的。css
每一個前端團隊都在打造本身的前端開發體系,這一般是一個東拼西湊,逐漸磨合的過程,在技術發展突飛猛進的今天,這樣的過程真的是不可抽象和複製的麼?經過拆解前端開發體系,對前端工程化有所理解。html
前端是一種技術問題較少、工程問題較多的軟件開發領域。前端
<!-- markdown-to-slides index.md -o index.html -s slide.css -->vue
前端工程本質上是軟件工程的一種。軟件工程化關注的是性能、穩定性、可用性、可維護性等方面,注重基本的開發效率、運行效率的同時,思考維護效率。一切以這些爲目標的工做都是"前端工程化"。
大致量:多功能、多頁面、多狀態、多系統;
大規模:多人甚至多團隊合做開發;java
這兩個問題的解決方案有兩點:jquery
從部署角度,要解決的問題主要是資源管理,包括:webpack
CDN 部署、緩存控制、文件指紋、緩存複用、請求合併、按需加載、同步/異步加載、移動端首屏 CSS 內嵌、HTTP 2.0 服務端資源推送。git
規範化實際上是工程化中很重要的一個部分,項目初期規範制定的好壞會直接影響到後期的開發質量。github
組件(component)和模塊(module)應該是兩個不一樣的概念。二者的區別主要在顆粒度方面
用通俗的話講,模塊能夠理解爲零件,好比輪胎上的螺絲釘;而組件則是輪胎,是具有某項完整功能的一個總體。具體到前端領域,一個 button 是一個模塊,一個包括多個 button 的 nav 是一個組件。
模塊/組件化開發的必要性
隨着 web 應用規模愈來愈大,模塊/組件化開發的需求就顯得愈來愈迫切。模塊/組件化開發的核心思想是分治,主要針對的是開發和維護階段。
雲物理機部門前端組件工具庫:@jd/cloudid_frontend
http://npm.m.jd.com/package/@...
爲何搭建私有組件庫
添加新組件原則
由「增量」原則引伸出的前端優化技巧幾乎成爲了性能優化的核心。
有加載相關的按需加載、延遲加載、預加載、請求合併等策略;
有緩存相關的瀏覽器緩存利用,緩存更新、緩存共享、非覆蓋式發佈等方案
優化方向 | 優化手段 |
---|---|
請求數量 | 合併腳本和樣式表,CSS Sprites,拆分初始化負載,劃分主域 |
請求帶寬 | 開啓 GZip,精簡 JavaScript,移除重複腳本,圖像優化 |
緩存利用 | 使用 CDN,使用外部 JavaScript 和 CSS,添加 Expires 頭, 減小 DNS 查找,配置 ETag,使 AjaX 可緩存 |
頁面結構 | 將樣式表放在頂部,將腳本放在底部,儘早刷新文檔的輸出 |
瀏覽器緩存是 Web 性能優化的重要方式。那麼瀏覽器緩存的過程到底是怎麼樣的呢?
瀏覽器緩存主要分爲強強緩存(也稱本地緩存)和協商緩存(也稱弱緩存)。
Etag 和 If-None-Match
Etag/If-None-Match 返回的是一個校驗碼。ETag 能夠保證每個資源是惟一的,資源變化都會致使 ETag 變化。服務器根據瀏覽器上送的 If-None-Match 值來判斷是否命中緩存
與 Last-Modified 不同的是,當服務器返回 304 Not Modified 的響應時,因爲 ETag 從新生成過,response header 中還會把這個 ETag 返回,即便這個 ETag 跟以前的沒有變化。
Last-Modify/If-Modify-Since
瀏覽器第一次請求一個資源的時候,服務器返回的 header 中會加上 Last-Modify,Last-modify 是一個時間標識該資源的最後修改時間,例如 Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。
當瀏覽器再次請求該資源時,request 的請求頭中會包含 If-Modify-Since,該值爲緩存以前返回的 Last-Modify。服務器收到 If-Modify-Since 後,根據資源的最後修改時間判斷是否命中緩存。
若是命中緩存,則返回 304,而且不會返回資源內容,而且不會返回 Last-Modify。
Last-Modified 與 ETag 是能夠一塊兒使用的,服務器會優先驗證 ETag
a.css 的請求,若是每次用戶訪問頁面都要加載,很影響性能,很浪費帶寬
利用 304,讓瀏覽器使用本地緩存。304 叫協商緩存,這玩意仍是要和服務器通訊一次,咱們的優化級別是變態級,因此必須完全滅掉這個請求,變成這樣:
強制瀏覽器使用本地緩存(cache-control/expires),不要和服務器通訊。好了,請求方面的優化已經達到變態級別,那問題來了:你都不讓瀏覽器發資源請求了,這緩存咋更新?
很好,相信有人想到了辦法:經過更新頁面中引用的資源路徑,讓瀏覽器主動放棄緩存,加載新資源。好像這樣:
頁面引用了 3 個 css,而某次上線只改了其中的 a.css,若是全部連接都更新版本,就會致使 b.css,c.css 的緩存也失效,那豈不是又有浪費了?!
從新開啓變態模式,咱們不難發現,要解決這種問題,必須讓 url 的修改與文件內容關聯,也就是說,只有文件內容變化,纔會致使相應 url 的變動,從而實現文件級別的精確緩存控制。
什麼東西與文件內容相關呢?咱們會很天然的聯想到利用 數據摘要算法 對文件求摘要信息,摘要信息與文件內容一一對應,就有了一種能夠精確到單個文件粒度的緩存控制依據了。好了,咱們把 url 改爲帶摘要信息的:
現代互聯網企業,爲了進一步提高網站性能,會把靜態資源和動態網頁分集羣部署,靜態資源會被部署到 CDN 節點上,網頁中引用的資源也會變成對應的部署路徑:
此次發佈,同時改了頁面結構和樣式,也更新了靜態資源對應的 url 地址,如今要發佈代碼上線,親愛的前端研發同窗,你來告訴我,我們是先上線頁面,仍是先上線靜態資源?
好的,上面一坨分析想說的就是:先部署誰都不成!都會致使部署過程當中發生頁面錯亂的問題。因此,訪問量不大的項目,可讓研發同窗苦逼一把,等到半夜偷偷上線,先上靜態資源,再部署頁面,看起來問題少一些。
有些公司超變態,沒有這樣的「絕對低峯期」,只有「相對低峯期」。So,爲了穩定的服務,還得繼續追求極致啊!
這個奇葩問題,起源於資源的 覆蓋式發佈,用 待發布資源 覆蓋 已發佈資源,就有這種問題。解決它也好辦,就是實現 非覆蓋式發佈。
看上圖,用文件的摘要信息來對資源文件進行重命名,把摘要信息放到資源文件發佈路徑中,這樣,內容有修改的資源就變成了一個新的文件發佈到線上,不會覆蓋已有的資源文件。上線過程當中,先全量部署靜態資源,再灰度部署頁面,整個問題就比較完美的解決了。
因此,靜態資源優化方案,基本上要實現這麼幾個東西:
總之,前端性能優化絕逼是一個工程問題!
webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個 bundle。
const ExtractTextPlugin = require('extract-text-webpack-plugin') // extract css into its own file new ExtractTextPlugin({ filename: utils.assetsPath('css/[name].[contenthash].css'), allChunks: true })
webpack-dev-server 是一個小型的 Node.js Express 服務器,它使用 webpack-dev-middleware 來服務於 webpack 的包,除此自外,它還有一個經過 Sock.js 來鏈接到服務器的微型運行時.
配置 服務器的 host 端口號 proxy 代理等
CDN[Content Delivery Network] 內容分發網絡 主要功能是在不一樣的地點緩存內容,經過負載均衡技術,將用戶的請求定向到最合適的緩存服務器上去獲取內容
總結一下 CDN 的工做原理:經過權威 DNS 服務器來實現最優節點的選擇,經過緩存來減小源站的壓力。
CDN 應用場景:
除卻 CDN 自身的優點,在前端工程中,將靜態文件放到 CDN 上,能夠直觀地減少資源包大小,同時加快首屏加載。
防止將某些 import 的包(package)打包到 bundle 中,而是在運行時(runtime)再去從外部獲取這些擴展依賴(external dependencies)。
externals: { "jquery": 'jQuery', "vue-router":'VueRouter', "vue":'Vue' }
樣就剝離了那些不須要改動的依賴模塊
BootCDN穩定、快速、免費的前端開源項目 CDN 加速服務
html-webpack-plugin 是 webpack 的一個插件,能夠動態的建立和編輯 html 內容,在 html 中使用 EJS 語法能夠讀取到配置中的參數,簡化了 html 文件的構建。
EJS 高效的 JavaScript 模板引擎。
https://ejs.bootcss.com/
<% if (user) { %> <h2><%= user.name %></h2> <% } %>
plugins: [ // html模板、以及相關配置 new HtmlWebpackPlugin({ title: 'Lesson-06', template: resolve('../public/index.html'), // cdn(自定義屬性)加載的資源,不須要手動添加至index.html中, // 順序按數組索引加載 cdn: { css: ['https://cdn.bootcss.com/element-ui/2.8.2/theme-chalk/index.css'], js: [ 'https://cdn.bootcss.com/vue/2.6.10/vue.min.js', 'https://cdn.bootcss.com/element-ui/2.8.2/index.js' ] } }) ]
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title><%= htmlWebpackPlugin.options.title %></title> <!-- import cdn css --> <% if(htmlWebpackPlugin.options.cdn) {%> <% for(var css of htmlWebpackPlugin.options.cdn.css) { %> <link rel="stylesheet" href="<%=css%>" /> <% } %> <% } %> </head> <body> <div id="box"></div> <!-- import cdn js --> <% if(htmlWebpackPlugin.options.cdn) {%> <% for(var js of htmlWebpackPlugin.options.cdn.js) { %> <script src="<%=js%>"></script> <% } %> <% } %> </body> </html>
commit message 做用
目前,社區有多種 Commit message 的寫法規範,咱們介紹的工具是 commitizen,它使用的是 Angular 規範AngularJS Git Commit Message Conventions,這是目前使用最廣的寫法,而且有對應的工具去生成 change log。
標準說明
每次提交, Commit message 都包括 Header, Body 和 Footer 三個部分。
<type>(<scope>): <subject> // 空行 <body> // 空行 <footer>
Header 部分只有一行,包括三個字段:type, scope 和 subject 。
type 用於說明提交的類別,只運行使用下面幾種,
若是是 feat 和 fix ,則這個 commit 將確定出如今 change log 中,其它狀況可自行決定是否放入。
scope 用於說明 commit 影響的範圍 好比數據層、控制層、視圖層等等,視項目不一樣而不一樣。
subject 是 commit 目的的簡短描述,不超過 50 個字符。
Body 部分是對本次 commit 的詳細描述,可分紅多行。可是通常我都不寫。
Footer 部分只用於兩種狀況。
不兼容變更
若是當前代碼與上一個版本不兼容,則 Footer 部分以 BREAKING CHANGE 開頭,後面是對變更的描述、以及變更理由和遷移方法。
BREAKING CHANGE: isolate scope bindings definition has changed. To migrate the code follow the example below: Before: scope: { myAttr: 'attribute', } After: scope: { myAttr: '@', } The removed `inject` wasn't generaly useful for directives so there should be no code using it.
關閉 Issue
若是當前 commit 針對某個 issue ,那麼能夠在 Footer 部分關閉這個 issue 。
Closes #1234
Closes #1234, $1235, #1236
Revert
若是當前 commit 用於撤銷之前的 commit,則必須以 revert:開頭,後面跟着被撤銷 Commit 的 Header
revert: feat(pencil): add 'graphiteWidth' option <br /> This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
Commitizen 是一個撰寫合格 Commit message 的工具。。
npm install -g commitizen # 在項目目錄裏,運行下面的命令,使其支持 Angular 的 Commit message 格式。 commitizen init cz-conventional-changelog --save --save-exact
用 git cz -m 代替 git commit -m 就能夠輕鬆的寫出 Angular 規範的 commit message 了。
用於檢查 Node 項目的 Commit message 是否符合格式。
生成 Change log 的工具,運行下面的命令便可。
npm install -g conventional-changelog-cli cd my-project conventional-changelog -p angular -i CHANGELOG.md -s
若是你的全部 Commit 都符合 Angular 格式,那麼發佈新版本時, Change log 就能夠用腳本自動生成
生成的文檔包括如下三個部分。
每一個部分都會羅列相關的 commit ,而且有指向這些 commit 的連接。固然,生成的文檔容許手動修改,因此發佈前,你還能夠添加其餘內容。
爲了方便使用,能夠將其寫入 package.json 的 scripts 字段。
{ "scripts": { "changelog": "conventional-changelog -p angular -i CHANGELOG.md -w -r 0" } }
npm run changelog
獲取用戶基本信息、行爲以及跟蹤產品在用戶端的使用狀況,並以監控數據爲基礎,指明產品優化的方向。
前端監控能夠分爲三類:數據監控、性能監控和異常監控。
數據監控就是監聽用戶信息和行爲,常見的監控項有:
統計這些數據是有意義的,好比咱們知道了用戶來源的渠道,能夠促進產品的推廣,知道用戶在每個頁面停留的時間,能夠針對停留較長的頁面,增長廣告推送等等。
性能監控指的是監聽前端的性能,主要包括監聽網頁或者說產品在用戶端的體驗。常見的性能監控項包括:
這些性能監控的結果,能夠展現前端性能的好壞,根據性能監測的結果能夠進一步的去優化前端性能,好比兼容低版本瀏覽器的動畫效果,加快首屏加載等等。
異常監控因爲產品的前端代碼在執行過程當中也會發生異常,所以須要引入異常監控。及時的上報異常狀況,能夠避免線上故障的發上。雖然大部分異常能夠經過 try catch 的方式捕獲,可是好比內存泄漏以及其餘偶現的異常難以捕獲。常見的須要監控的異常包括:
咱們說完了前端監控的三個分類,如今就來聊聊怎麼實現前端監控。實現前端監控,第一步確定是將咱們要監控的事項(數據)給收集起來,再提交給後臺,最後進行數據分析。數據收集的豐富性和準確性會直接影響到咱們作前端監控的質量,由於咱們會以此爲基礎,爲產品的將來發展指引方向。
收集監控數據咱們是經過前端埋點來實現的,目前常見的前端埋點方法有三種:手動埋點、可視化埋點和無埋點。
收集監控數據咱們是經過前端埋點來實現的,目前常見的前端埋點方法有三種:手動埋點、可視化埋點和無埋點。
手動埋點,也叫代碼埋點,即純手動寫代碼,調用埋點 SDK 的函數,在須要埋點的業務邏輯功能位置調用接口,上報埋點數據,像友盟、百度統計等第三方數據統計服務商大都採用這種方案。
優點:
缺陷:
$(document).ready(() => { // ... 這裏是你的業務邏輯代碼 sendData(params) //這裏是發送你的埋點數據,params是你封裝的埋點數據 })
前端框架式手動埋點
若是使用 Vue 或者 React 等前端框架,這些框架都有本身的各類生命週期,爲了減小重複性的手動埋點次數,能夠在各個生命週期位置,根據你的需求封裝你所需的埋點。好比你是 SPA 單頁應用,你但願在每個頁面的 componentDidMount 埋點,並由此肯定用戶已經打開了頁面。
css 埋點:
.link:active::after { content: url('http://www.example.com?action=yourdata'); }
<a class="link">點擊我,會發埋點數據</a>
可視化埋點解決了純手動埋點的開發成本和更新成本,經過可視化工具快速配置採集節點(圈點),在前端自動解析配置,並根據配置上傳埋點數據,比起手動埋點看起來更無痕,
好比國外比較早作可視化的是 Mixpanel,國內較早支持可視化埋點的有 TalkingData、諸葛 IO,2017 年騰訊的 MTA 也宣佈支持可視化埋點;
無埋點則是前端自動採集所有事件,上報埋點數據,由後端來過濾和計算出有用的數據。
優勢:
前端只要一次加載埋點腳本
缺點:
服務器性能壓力山大
採用無埋點技術的有主流的 GrowingIO、神策。
在業界內有這麼一句話:任何簡單機械的重複勞動都應該讓機器去完成。現代前端技術再也不是之前刀耕火種的年代了。因此前端工程化的不少髒活累活都應該交給自動化工具來完成。
如何選型技術、如何定製規範、如何分治系統、如何優化性能、如何加載資源,當你從切圖開始轉變爲思考這些問題的時候,我想說:
你好,工程師!
<center>-- End --</center>