前端為什麼要工程化?

逐夢song 發表於 2021-01-16
什麼是前端工程化?

雖然前端工程化的概念興起還沒幾年的時間,但是對於“工程化”這個詞並不是一個新鮮詞了,在其他軟體開發的領域很早就已經有了高度的工程化,例如Web服務端開發。只不過那個時候,前端工程師並沒有工程化的意識,也沒有必要對前端進行工程化的操作,畢竟在那個時期,前端的開發工作只能算是整個專案開發過程中的“附屬品”。那為什麼這幾年,前端工程化的概念又突然成為了熱點話題了呢?要想講好前端工程化其實並不容易,我就從下面幾個方面入手,對前端工程化的概念做個簡單的講解,僅僅是個人理解,希望大家多多交流討論。

1、前端為什麼要工程化?

在回答前端工程化這個問題之前,我們應該先考慮另外一個問題:前端開發中會不會涉及到業務?再詳細一點,就是前端工程師到底需不需要了解服務端的業務邏輯,再或者說,要不要將一部分服務端的業務邏輯放到前端來實現。這個問題沒有標準的答案,實際上應該屬於工程協作問題,就是誰該幹什麼的問題。

最早的前端開發就是實現頁面,頂多再寫寫JS讓頁面可以有互動的特效。但是隨著需求的增加,我們不僅要做Web應用,還要做App、小程式以及各種端。在這種需求日增的情況下,必須得考慮一種新的方式,優化前端的開發工作,例如,解決程式碼冗餘,專案可維護性,提升版本迭代速度等等一系列的問題。前端工程化的概念也就是在這中情況下被提出了。

2、實現前端工程化的基礎——前後端分離

實際上,現在的前端工程化應該還處於早期階段,畢竟前端工程師這個崗位也才誕生沒幾年。在網際網路發展的早期,Web應用很多情況下都是由服務端工程師一肩挑,前端開發頂多是寫寫HTML程式碼,實現頁面的佈局,然後再把寫好的HTML靜態檔案交給後端工程師套模板,因為當時大部分的Web應用都是使用的服務端渲染技術,例如Java的JSP。

這種傳統模式下的協作開發效率非常低,如果在頁面測試中發現了一個bug,這個bug是由於一個ClassName的值少寫了一個字母,你說這種情況到底是前端工程師的錯,還是後端工程師套模板時的粗心大意呢?畢竟一個網站上有成千上萬個DOM節點,誰也不能保證一行程式碼都不會寫錯。再假如,專案上線後發現網頁的實際畫素和設計稿上存在1畫素的偏差,這個時候就需要由前端工程師重新設計一次HTML靜態頁面,然後再交給後端工程師繼續套模板,等整個流程走完之後,你會發現一個更加嚴重的問題,整個專案中僅僅是1畫素的偏差,就有可能要調動整個開發團隊來處理這1畫素的問題,極大的浪費了團隊資源。

上面的問題也僅僅是傳統開發問題中的冰山一角,面對這麼多的問題,前後端分離開發也就應運而生了。前後端分離開發,為前端工程化的發展提供了生存的土壤。隨著市場需求的不斷變化,前端開發從傳統的WebPage模式,轉變到WebApp模式,Web產品形態的變化也不斷推動著前端工程師的工作內容發生變化。在應對各種“變化”,前端工程師們也要設計出自己的前端開發“方法論”。

前端工程化的主要目標就是解放生產力、提高生產效率。通過制定一系列的規範,藉助工具和框架解決前端開發以及前後端協作過程中的痛點和難度問題。

3、如何實施前端工程化?

明確前後端開發的分工,是實現前後端分離的第一步,也是後面實現前端各種優化方案的基礎。前端工程師主要負責的內容包括:

  • 靜態資源和動態資源的處理;
  • JavaScript實現前端業務邏輯;
  • HTML模板檔案的產出;
  • 中間層Web服務,一般由Node.js實現;
  • 前端單元測試;
  • 前端專案部署;

其中,靜態資源包括.js檔案、.css檔案以及各種格式的圖片、媒體檔案等,這些檔案不依賴於伺服器,只需要在瀏覽器裡面解析就可以了;動態資源是指HTML的模板,如果專案不是由伺服器完成渲染的SPA(單頁面)應用,那我們就要考慮如何實現HTML模板的渲染了。前端專案部署是指靜態資原始檔在測試伺服器上的部署,以及HTML模板檔案在Node.js中間層伺服器上的部署。

從專案開發的整體環節來說,實現前端工程化還需要熟練掌握下面幾個方面:

(1)使用Webpack實現專案構建

構建,簡單來說就是編譯,前端開發的所有檔案最終歸屬是要交給瀏覽器去解析、渲染,並將頁面呈現給使用者,構建就是將前端開發中的所有原始碼轉化為宿主瀏覽器可以執行的程式碼。前端構建產出的資原始檔只有三種,HTML、CSS、JS檔案。需要完成編譯的內容有:

無法被瀏覽器直接識別的JS程式碼,包括ES6/7/8/9/10等符合ECMAScript規範的JS程式碼; 無法被瀏覽器直接識別的CSS程式碼,包括SASS/LESS等預編譯的CSS語法; 無法被瀏覽器識別的HTML模板程式碼,包括jade、ejs、artTemplate、mustache等Node.js模板引擎; 專案構建其實就是為了彌補瀏覽器自身的缺陷和不足,是一種面向語言的編譯過程。那麼,除了針對語言本身之外,前端的構建還應該考慮到Web應用的效能優化。這些優化主要是為了減少HTTP請求,提升使用者體驗,包括:

依賴打包,將同步依賴的檔案打包在一起,減少HTTP請求數量; 資源嵌入,例如將小於10kb的圖片編譯為base64格式嵌入文件,減少HTTP請求; 檔案壓縮,減少檔案體積,縮短請求時間; 為檔案加入hash指紋,以應對瀏覽器快取策略; 將開發環境下的域名與靜態資原始檔路徑修改為生產環境下的域名和路徑; 檔名稱的改變; 這裡需要解釋一下,前端的構建工具除了Webpack之外,還有其他的工具,例如Gulp、Grunt等,為什麼這裡只提到了Webpack?其實,Gulp、Grunt只能算是工作流管理工具,它們本身是不提供具體的功能,所有的構建、部署等功能都要由對應的外掛來完成,使用Gulp和Grunt只是便於專案各個環節工作流程的控制。而且,這兩款工具的話題熱度和使用率遠不及Webpack,Webpack雖然是近兩年才崛起的構建工具,但目前依然成為了最流行的構建工具之一,Vue和React兩大前端框架的Webpack Loader也是有官方或作者本人編寫的。所以,我們在講前端工程化構建時,推薦使用Webpack這款工具。

(2)使用Babel完成JavaScript編譯

JavaScript可以說是前端最為核心的一門程式語言了,也就是我們常說的“JS”,JS本身是可以直接在瀏覽器中執行的,那麼為什麼還要使用Babel再編譯一次呢?其實,這裡要解釋一下,在ECMAScript2015(簡稱ES6)正式釋出以後,前端工程師關注的重心就由“JS”轉向了“ES”,作為專業的前端工程師們應該都瞭解,JavaScript ≠ ECMAScript

ECMAScript是一個標準,JavaScript是對ECMAScript標準實現的一個子集。宿主瀏覽器的API(BOM和DOM)再加上JavaScript,就組成了我們傳統認知中的JavaScript。但是隨著ECMAScript的版本不斷迭代更新,帶來的並不是開發的便利,由於瀏覽器對ECMAScript新規範的支援實現比較滯後,即使是目前最新版的Chrome瀏覽器也沒有完全支援ECMAScript2015(ES6)的所有規範,而且ECMAScript2017都已經發布了,為了更好的讓新的ES規範能夠無縫銜接瀏覽器,Babel編譯JavaScript語法的作用就突顯出來了。

Babel的作用簡單來說,就是將瀏覽器未實現的ECMAScript規範語法轉化為可執行的低版本語法,例如將ES6的class轉化為ES5的prototype實現。

(3)CSS預編譯

CSS作為瀏覽器可以直接識別的樣式語言,彌補了HTML原生樣式的不足,對於早期網際網路開發中,樣式的要求並不複雜,僅需要少量的CSS程式碼即可。但是在如今追求使用者極致體驗的潮流下,CSS的開發要求不斷提高,複雜的CSS開發變成一件非常繁瑣和痛苦的事情。最主要的原因還是受限於瀏覽器的實現和CSS自身的弱程式設計能力。

CSS預編譯器的工作原理是提供便捷的語法和特性供開發者編寫原始碼,隨後經過專門的編譯工具將原始碼轉化為CSS語法。

(4)模組化開發

模組化開發和元件化開發是兩個完全不同的概念,模組化屬於架構層面的概念,前端工程化與模組化的關係就類似於組裝車間與零件的關係。使用模組化開發,可以解決下面幾個問題:

  • 避免命名衝突;
  • 便於依賴管理;
  • 利於效能優化;
  • 提高可維護性;
  • 提高程式碼可複用性; 在ES6規範釋出之前,前端模組化開發主要有三種規範,分別是:CommonJS、AMD、CMD

CommonJS是一種只適用於JavaScript的靜態模組化規範,適合Node.js開發,但並不適合瀏覽器環境;而AMD/CMD規範並不是完全一致的,但核心功能是統一的,兩個規範都重點解決了瀏覽器對前端模組化的需求。

ES6 Module規範推出之後,前三者的模組化規範也逐漸退出前端的歷史舞臺。ES6 Module是語言層面的規範,與應用場景無關,所以一個不涉及執行環境API呼叫的模組可以在任何場景下執行。但是目前瀏覽器還沒有完全支援這種規範,所以,要實現ES6 Module規範的話,還需要使用構建工具進行編譯。

(5)元件化開發

前面提到了,元件化和模組化是兩個完全不同的概念,模組化是檔案層面上對程式碼和資源的拆分,元件化是設計層面上對UI的拆分。從UI中拆分出來的一個結構單元,成為UI元件,一個UI元件單元包含了HTML模板、CSS樣式、JS邏輯。在頁面的設計過程中,頁面上的每一個元素都是元件,頁面也是一個元件,只不過頁面是一個大型元件,然後這個大型元件又由多箇中小型元件拼裝而成。中型元件還可以再拆分成小型元件,小型元件再拆分成DOM元素,DOM元素也屬於瀏覽器自身的元件,是元件的基本單元。這種元件化開發就是前端開發的“分治思想”。

(6)開發環境的本地伺服器與Mock服務

在前端工程化開發中,通過構建工具可以將程式碼進行編譯,然後在瀏覽器中進行除錯,但是在開發過程中原始碼的每次修改都需要執行一次構建,構建完成後才能在瀏覽器裡執行,這對前端工程師來說無疑就是一種災難。要完美的解決這個問題,可以使用本地伺服器與構建工具結合,對原始碼進行監聽並在修改之後觸發動態構建,使用自動化構建的方式代替人工。這種動態構建是使用本地伺服器解決開發層面上的問題。

Mock服務解決的是前後端協作開發的問題,前後端開發人員提前約定好規範,前端工程師通過本地伺服器提供的Mock資料介面輔助前端邏輯的編寫和功能模組的開發。如果專案中需要伺服器端渲染(SSR),本地伺服器還需要具備解析HTML模板的能力,同時Mock服務提供SSR所需的初始化資料。

前端工程師可以使用本地伺服器提供的Mock資料介面,在後端人員開發的同時,進行前端邏輯的並行開發,等到後端真實介面開發完成後,將前端請求的地址從Mock服務遷移到伺服器的生產環境即可。

(7)規範化約束

無論是伺服器端開發還是前端工程化開發,規範化的約束都至關重要。開發人員在設計專案的整體架構時,為了考慮到專案的可擴充套件性、可維護性、高內聚性等因素,會對程式碼進行封裝,使用配置化操作,為專案開發帶來便利,這必然要求業務程式碼程式設計正規化遵循既定的約束。這種約束雖然帶來了開發上的便捷,但是在一定程度上制約了程式碼的可移植性。例如,在專案中使用了某個構建工具來解決專案需求,但是如果有一天專案需要更換另一個構建工具時,程式碼中原有的構建工具的配置會成為冗餘程式碼,而且不能保證這類配置不會對新構建工具產生衝突。即便是沒有產生衝突,對程式碼的效能優化也是會帶來一定負面影響的。工程化方案作為一種服務,應該儘量降低對專案產生的負面影響。這是制定程式設計正規化約束規範時最重要的考慮因素。

(8)專案部署流程化

站在前端開發的範疇來說,專案部署是指前端開發人員將構建產出的程式碼包部署到測試伺服器的過程,而並非是將測試完成的程式碼釋出到生產環境的過程。在部署過程中,要考慮目標伺服器、路徑資訊是否與專案一一對應,並且可供負責部署到生產環境的開發人員進行配置,部署的操作流程應儘量簡單。

在部署流程中,使用命令列取代工具執行(例如FTP)本地部署,能夠極大的提高部署的速度和效率,但是這只是初級階段的部署流程。考慮團隊協作和安全方面的因素,最佳的方式應該是搭建一個可供嚴格審查、佇列控制、操作簡化的部署平臺,並且有專人負責掌握流程進度。雖然這種搭建部署平臺的方式在一定程度上減緩了整體的部署速度,但是加強了團隊協作和安全保障。

4、前端工程化未來的發展如何?

目前,Web應用開發的分工模式還處於探索期,而且從最近流行的“大前端”的趨勢來看,前端工程師的發展或許會突破Web領域,向多端領域發展,例如React Native、Weex、Electron、小程式等。從誕生之初“切圖仔”到大前端概念,前端工程師的定位和負責的技術範疇一直在變化,但是前端工程師產出的服務物件永遠是使用者。在Node.js之前,瀏覽器是前端工程師生存的“一畝三分地”,Node.js的出現打破了這個局面,以致出現了“大前端”的發展趨勢。

Node.js中間層+瀏覽器是目前實現“大前端”的基本模式,前端工程師掌握著與使用者相關的所有資源,能夠全面地掌握開發進度以及實現更合理的前後端分離方案。這種模式讓前端工程師突破瀏覽器的瓶頸,邁向Web應用層面,這也是未來前端發展的主流趨勢。

無論是專注於瀏覽器,還是兼顧了Node.js中間層,前端工程師的利劍始終指向的是面向瀏覽器的Web領域,前端工程化所提供的是一種服務,服務物件是不斷參與到專案迭代過程中的前端開發人員,服務的內容涉及到開發、構建、部署等各個環節。

前端工程師在未來的定位必然會發生變化,但是前端工程化唯一不變的原則是始終以前端開發為中心,前端工程化沒有統一的行業標準,也沒有固定的形態,更沒有最合理的方案,只要前端工程師的定位還在不斷的變化,前端工程化的程式將會一直持續下去。