人們常說的前端工程化究竟是什麼?

前端工程本質上是軟件工程的一種。軟件工程化關注的是性能、穩定性、可用性、可維護性等方面,注重基本的開發效率、運行效率的同時,思考維護效率。一切以這些爲目標的工做都是"前端工程化"。工程化是一種思想而不是某種技術。舉例說明:php

要蓋一棟大樓,假如咱們不進行工程化的考量那就是一上來掂起瓦刀、磚塊就開幹,直到把大樓壘起來,這樣作每每意味着中間會出現錯誤,要推倒重來或是蓋好之後結構有問題但又不知道出如今哪誰的責任甚至會在某一天轟然倒塌,那咱們若是用工程化的思想去作,就會先畫圖紙、肯定結構、肯定用料和預算以及工期,另外須要用到什麼工種多少人等等,咱們會先打地基再建框架再填充牆體這樣最後創建起來的高樓纔是穩固的合規的,什麼地方出了問題咱們也能找到源頭和負責人。css

前端,是一種GUI軟件

咱們知道前端技術的主要應用場景並不是只是高大上的基礎庫/框架,拽炫酷的宣傳頁面,或者屌炸天的小遊戲等這些一兩個文件的小項目,更具商業價值的則是複雜的Web應用,它們功能完善,界面繁多,爲用戶提供了完整的產品體驗。(多是新聞聚合網站,多是在線購物平臺,多是社交網絡,多是金融信貸應用,多是音樂互動社區,也多是視頻上傳與分享平臺……)前端

從本質上講,全部Web應用,都是一種運行在網頁瀏覽器中的軟件,這些軟件的圖形用戶界面(Graphical User Interface,簡稱GUI)即爲前端。jquery

如此複雜的Web應用,動輒幾十上百人共同開發維護,其前端界面一般也頗具規模,工程量不亞於通常的傳統GUI軟件:webpack

GUI軟件

前端工程化須要考慮哪些因素?

1. 模塊化

簡單來講,模塊化就是將一個大文件拆分紅相互依賴的小文件,再進行統一的拼裝和加載。(方便了多人協做)。git

分而治之是軟件工程中的重要思想,是複雜系統開發和維護的基石,這點放在前端開發中一樣適用。模塊化是目前前端最流行的分治手段。es6

模塊化開發的最大價值應該是分治!

無論你未來是否要複用某段代碼,你都有充分的理由將其分治爲一個模塊。github

  • JS模塊化方案

AMD/CommonJS/UMD/ES6 Module等等。web

CommonJS的核心思想是把一個文件當作一個模塊,要在哪裏使用這個模塊,就在哪裏require這個模塊,而後require方法開始加載這個模塊而且執行其中的代碼,最後會返回你指定的export對象。bootstrap

module.export = function() {
    hello: function() {
        alert("你好");
    }
}

var a = require('./xxx/a.js');
a.hello(); // ==> 彈窗「你好」

CommonJS 加載模塊是同步的,因此只有加載完成才能執行後面的操做,不能非阻塞的並行加載多個模塊。

AMD(異步模塊定義,Asynchronous Module Definition),特色是能夠實現異步加載模塊,等全部模塊都加載而且解釋執行完成後,纔會執行接下來的代碼。

// 經過AMD載入模塊
// define(
//     module_id /*可選*/, 
//     [dependencies] 可選, 
//     definition function /*回調 用來初始化模塊或對象的函數*/
// );
define(['myModule', 'myOtherModule'], function(myModule, myOtherModule) {
    console.log(myModule.hello());
    //會先並行加載全部的模塊a b 並執行其中模塊的代碼後,在執行逐步執行下面的 console
    require("a");
    console.log("a required");

    require("b");
    console.log("b required");

    console.log("all modules have been required");
});

在一些同時須要AMD和CommonJS功能的項目中,你須要使用另外一種規範:Universal Module Definition(通用模塊定義規範)。UMD創造了一種同時使用兩種規範的方法,而且也支持全局變量定義。因此UMD的模塊能夠同時在客戶端和服務端使用。

幸運的是在JS的最新規範ECMAScript 6 (ES6)中,引入了模塊功能。
ES6 的模塊功能汲取了CommonJS 和 AMD 的優勢,擁有簡潔的語法並支持異步加載,而且還有其餘諸多更好的支持(例如導入是實時只讀的。(CommonJS 只是至關於把導出的代碼複製過來))。

// CommonJS代碼
// lib/counter.js
var counter = 1;
function increment() {
  counter++;
}
function decrement() {
  counter--;
}
module.exports = {
  counter: counter,
  increment: increment,
  decrement: decrement
};
// src/main.js
var counter = require('../../lib/counter');
counter.increment();
console.log(counter.counter); // 1
// 使用 es6 modules 經過 import 語句導入
// lib/counter.js
export let counter = 1;
export function increment() {
  counter++;
}
export function decrement() {
  counter--;
}
// src/main.js
import * as counter from '../../counter';
console.log(counter.counter); // 1
counter.increment();
console.log(counter.counter); // 2
  • CSS模塊化方案

在less、sass、stylus等預處理器的import/mixin特性支持下實現、css modules。

雖然SASS、LESS、Stylus等預處理器實現了CSS的文件拆分,但沒有解決CSS模塊化的一個重要問題:選擇器的全局污染問題;

CSS in JS是完全拋棄CSS,使用JS或JSON來寫樣式。這種方法很激進,不能利用現有的CSS技術,並且處理僞類等問題比較困難;

CSS Modules 原理:使用JS 來管理樣式模塊,它可以最大化地結合CSS生態和JS模塊化能力,經過在每一個 class 名後帶一個獨一無二 hash 值,這樣就不有存在全局命名衝突的問題了。

webpack 自帶的 css-loader 組件,自帶了 CSS Modules,經過簡單的配置便可使用。

{
    test: /\.css$/,
    loader: "css?modules&localIdentName=[name]__[local]--[hash:base64:5]"
}

2. 組件化

前端做爲一種GUI軟件,光有JS/CSS的模塊化還不夠,對於UI組件的分治也有着一樣迫切的需求。分治的確是很是重要的工程優化手段。

前端組件化開發

頁面上的每一個 獨立的 可視/可交互區域視爲一個組件;
==每一個組件對應一個工程目錄==,組件所需的各類資源都在這個目錄下就近維護;
因爲組件具備獨立性,所以組件與組件之間能夠 自由組合;
頁面只不過是組件的容器,負責組合組件造成功能完整的界面;
當不須要某個組件,或者想要替換組件時,能夠整個目錄刪除/替換。

因爲系統功能被分治到獨立的模塊或組件中,粒度比較精細,組織形式鬆散,開發者之間不會產生開發時序的依賴,大幅提高並行的開發效率,理論上容許隨時加入新成員認領組件開發或維護工做,也更容易支持多個團隊共同維護一個大型站點的開發。

3. 「智能」加載靜態資源(性能優化)

模塊化/組件化開發以後,咱們最終要解決的,就是模塊/組件加載的技術問題。然而前端與客戶端GUI軟件有一個很大的不一樣:前端是一種遠程部署,運行時增量下載的GUI軟件。

若是用戶第一次訪問頁面就強制其加載全站靜態資源再展現,相信會有不少用戶由於失去耐心而流失。根據「增量」的原則,咱們應該精心規劃每一個頁面的資源加載策略,使得用戶不管訪問哪一個頁面都能按需加載頁面所需資源,沒訪問過的無需加載,訪問過的能夠緩存複用,最終帶來流暢的應用體驗。

這正是Web應用「免安裝」的魅力所在。

由「增量」原則引伸出的前端優化技巧幾乎成爲了性能優化的核心。

有加載相關的按需加載、延遲加載、預加載、請求合併等策略;
有緩存相關的瀏覽器緩存利用,緩存更新、緩存共享、非覆蓋式發佈等方案;

還有複雜的BigRender、BigPipe、Quickling、PageCache等技術。
這些優化方案無不圍繞着如何將增量原則作到極致而展開。

靜態資源管理系統 = 資源表 + 資源加載框架

資源表是一份數據文件(好比JSON),是項目中全部靜態資源(主要是JS和CSS)的構建信息記錄,經過構建工具掃描項目源碼生成,是一種k-v結構的數據,以每一個資源的id爲key,記錄了資源的類別、部署路徑、依賴關係、打包合併等內容。

{
    "res" : {
        "widget/a/a.css" : "/widget/a/a_1688c82.css",
        "widget/a/a.js"  : "/widget/a/a_ac3123s.js",
        "widget/b/b.css" : "/widget/b/b_52923ed.css",
        "widget/b/b.js"  : "/widget/b/b_a5cd123.js",
        "widget/c/c.css" : "/widget/c/c_03cab13.css",
        "widget/c/c.js"  : "/widget/c/c_bf0ae3f.js",
        "jquery.js"      : "/jquery_9151577.js",
        "bootstrap.css"  : "/bootstrap_f5ba12d.css",
        "bootstrap.js"   : "/bootstrap_a0b3ef9.js"
    },
    "pkg" : {
        "p0" : {
            "url" : "/pkg/lib_cef213d.js",
            "has" : [ "jquery.js", "bootstrap.js" ]
        },
        "p1" : {
            "url" : "/pkg/lib_afec33f.css",
            "has" : [ "bootstrap.css" ]
        },
        "p2" : {
            "url" : "/pkg/widgets_22feac1.js",
            "has" : [
                "widget/a/a.js",
                "widget/b/b.js",
                "widget/c/c.js"
            ]
        },
        "p3" : {
            "url" : "/pkg/widgets_af23ce5.css",
            "has" : [
                "widget/a/a.css",
                "widget/b/b.css",
                "widget/c/c.css"
            ]
        }
    }
}

在查表的時候,若是一個靜態資源有pkg字段(用來記錄web應用中一個頁面加載過的靜態資源,當下個頁面用到這個資源就無需加載了,有效利用緩存),那麼就去加載pkg字段所指向的打包文件,不然加載資源自己。

4. 規範化

規範化實際上是工程化中很重要的一個部分,項目初期規範制定的好壞會直接影響到後期的開發質量。

  • 目錄結構的制定
  • 編碼規範
  • 先後端接口規範
  • 文檔規範
  • 組件管理
  • Git分支管理
  • Commit描述規範
  • 按期CodeReview
  • 視覺圖標規範

...

5. 自動化

任何簡單機械的重複勞動都應該讓機器去完成。

  • 圖標合併
  • 持續集成
  • 自動化構建
  • 自動化部署
  • 自動化測試

References

使用 AMD、CommonJS 及 ES Harmony 編寫模塊化的 JavaScript
前端工程與性能優化
前端工程——基礎篇

相關文章
相關標籤/搜索