前端工程化、爲何須要工程化

前端工程化

  前端工程化的概念在近些年來逐漸成爲主流構建大型web應用不可或缺的一部分,在此我經過如下這三方面總結一下本身的理解。javascript

  1. 爲何須要前端工程化。css

  2. 前端工程化的演化。前端

  3. 怎麼實現前端工程化。vue

爲何須要工程化

  隨着近些年來前端技術的不斷髮展,愈來愈多複雜的業務放在了前端,前端再也不是之前幾個HTML + CSS + javascript就能解決的了。業務複雜了,須要維護的代碼量就天然多了,如此一來,前端代碼的可靠性,可維護性,可拓展性,以及前端web應用的性能,開發效率等等各方面就成了不起不考慮的問題。java

  因而咱們就產生了前端工程化這個概念,來解決這些問題。現階段的前端工程化,須要考慮到各個方面,包括但不限於如下這幾點:node

提高開發效率

  • webpack-dev-server 熱加載
      之前,咱們的平常前端開發的流程是這樣的: 修改代碼 -> 切換IDE到瀏覽器 -> 刷新瀏覽器查看效果(有時候還須要清除緩存) -> 修改代碼 ....。

      這套流程,尤爲是刷新瀏覽器這個過程,無疑是至關低效繁瑣枯燥的。 而webpack-dev-server 替咱們解決了這個問題,它有兩種模式,兩種模式,一種是 watch 模式,功能是你修改代碼,自動幫你刷新頁面,無需手動刷新;另外一種更增強大,基於 websocket 全雙工通訊技術,直接無刷新幫你把修改的代碼替換掉。 從而極大程度上提升了開發效率。
     react

  • 數據mock
      在後端接口還沒提供的時候,先後端制定好共同的接口協議,開發時前端可使用mock模擬數據,與後端完全分離,並行開發。面向接口編程,儘量減小先後端溝通成本。jquery

優化性能

  • 代碼合併壓縮,混淆加密 
     android

  • 減小小圖片請求
      webpack中url-loader:loader: 'url-loader?limit=8192',使得小於8kb的圖片使用data:image base64 編碼內聯,減小圖片請求量
     webpack

  • 部署靜態文件緩存管理
      使用webpack的內置的chunkhash功能,能夠給生成的js文件添加hash後綴,標識文件版本。

提升代碼質量

  • 模塊化
      主要指 js 代碼的模塊化。之前的前端開發並無模塊化這個概念,這給維護大型項目帶來了極大的困難。發展到如今的前端有不少模塊化的方法可供選擇,如seajs ,requirejs, webpack 等。 模塊化能很大程度上提升了代碼的可維護性
     

  • CSS 預處理
      經過sass,less 等css 預處理器,能夠實現 css 文件的拆分,顆粒化,實現css可複用。並且經過autoprefixer或postcss 還可讓 css 樣式對老舊瀏覽器向下兼容。 

      此外,經過使用 css-modules 可以避免css全局污染的問題,極大提升css代碼的可控性,不須要設定一堆命名空間與命名規範來限制。
     

  • ES6 + babel 編譯
      javascript自己設計存在必定程度上的缺陷,例如「沒有模塊化」,「沒有塊級做用域」,「全局變量污染」,「回調地獄」等等之類的問題,爲了改善這些缺陷,計算機協會在2015年推出了ECMAScript 6 標準(今年已經ES8 已經發布了),使用ES6的語法除了能有效減小代碼量以外,還引入了塊級做用域,模塊化,類的語法糖,promise以及一些新的API,很大程度上填了之前javascript的遺留下的坑,以及提升了代碼質量。

      不過即使過了兩年,ES6也並無被市面的主流瀏覽器徹底支持,因此咱們還需用 babel 將ES6 編譯成ES5,再將一些不支持的API polyfill 處理。
     

  • eslint 代碼檢查
      一直一來,代碼風格都是一場無休止的爭論,每一個人都有本身的代碼風格習慣,而這些習慣無非就是tab仍是空格,換不換行,加不加空格等等之類的雜事,與其經過制定規範去強行限制開發者的編寫習慣,不如從工具層面完全解決代碼風格的問題。eslint能夠自動處理一些代碼風格的問題,直接將代碼經過指定的規則格式化,使代碼總體風格統一。 

      更進一步,eslint 還能夠禁止代碼的一些可能形成不良影響的行爲(例如eval,未定義變量),使其拋出錯誤。下降代碼產生bug的可能性。
     

  • 單元測試
      集成單元測試,提升代碼可靠性。前端較爲流行的單元測試 mocha,qunit 等
     

  • UI 自動化測試
      UI 自動化測試是 軟件經過模擬瀏覽器,對頁面進行UI操做,判斷是否產生預想的UI效果。目前較爲流行的UI自動化測試套件主要是 基於phantomjs的 nightmare
     

  • web組件化
      web組件化是經過自定義標籤,從UI層面對代碼的拆分,提升前端代碼的可複用性。儘管w3c已經初步對web組件化制定了規範, 但目前瀏覽器對web 組件化的支持慘不忍睹,沒法經過原生的方法來實現web組件,但目前流行的前端框架,如vue,angular,react都有提供本身的web組件化,從而提升代碼可複用性

前端工程化的發展

<script> 直接引入加載

  在沒有引入模塊化的概念以前,前端每每須要手動處理js文件的依賴關係,例如;bootstartp 依賴 jquery,就須要在引入bootstrap以前引入jquery

<script src="src/jquery.min.js" ></script>
<script src="src/bootstrap.min.js" ></script>

  若是引入js文件順序錯了則會報錯。 乍一看彷佛沒什麼難度呀,是人都能分清是吧。那麼請看下面這種狀況:
  有 a.js, b.js, c.js, d.js, e.js 五個文件,其中

  • a 依賴 b和e,

  • b 依賴 d和e,

  • c 依賴 a和d,

  • d 依賴 e,

  • e 無依賴。

  那麼根據以上關係,請按正確順序引入js文件(黑人問號???)。固然,事實上也並不難區分其優先級,逐級遞推就很快能夠推斷出引入順序爲 e,d,b,a,c

  毫無疑問,對於稍微複雜點的web工程,存在複雜依賴狀況是極有可能發生的,而且把時間耗費在管理依賴關係上也不值當。

  因此就誕生了前端模塊化

模塊化標準(AMD,CMD,ES6 Module)

  經歷了混亂加載的黑歷史,咱們終於迎來了js的模塊化,忽如一晚上春風來,一晚上之間冒出一堆模塊化標準。

  其中具備表明性的模塊加載器分別是是遵循AMD(Asynchronous Module Definition)規範的RequireJS ,還有淘寶玉伯開源的 遵循CMD(Common Module Definition)規範的 SeaJS。 二者除了遵循規範不同以外,封裝模塊有差異以外,都各有所長,並且對舊版本瀏覽器的支持都至關完美。

  固然除了這兩個,還有各種其餘開發者開發的模塊加載器,當真是一番羣魔亂舞百家爭鳴的盛世呀。在此就不一一細述了。

  下面有請咱們的主角出廠: ES6 Module。 

  ES6 Module 是新一代javascript標準 ECMAScript 6 的新增特性,其語法和Python類似,比較簡潔易用。另外,相比於其餘模塊加載器,ES6 Module 是語法級別的實現,其靜態代碼分析相比於其餘框架會更快更高效,方便作代碼檢測。

// import 基本語法
import React from 'react';    //等價於 var React = require("react");
import { stat, exists, readFile } from 'fs';
// 等價於 
// var fs = require('fs');
// var stat = fs.stat, exists = fs.exists, readFile = fs.readFile;

  
  並且,且不論其API優劣,其語法與前面說的模塊化有什麼區別的,ES6 Module最大優勢是顯而易見的: 它是官方標準,而不是其餘妖豔賤貨第三方開發的框架/庫。跟着有名分的原配混,毫無疑問是有前途更穩定的吧。 

  固然,缺點也是很明顯的,不一樣於RequireJS,SeaJS 向下兼容到極致(ie6+),ES6 Module 的兼容性還未覆蓋絕大部分瀏覽器,支持ES6 Module的瀏覽器寥寥無幾,雖然能夠經過babel進行語法轉譯,不過兼容性畢竟是硬傷,惟有時間能治癒。

自動化構建工具(gulp,grunt)

  從描述可知,前端工程化須要作的事情,單憑人力一個一個去處理基本沒有可能完成,那麼,咱們就須要學會使用工具,畢竟程序猿和猿之間最大的區別就是會不會使用工具。 

  grunt 和 gulp 就是自動化構建工具。咱們經過安裝對應的node_module,根據gulp/grunt 的API編寫相對應的任務(如:css預處理,代碼合併壓縮,代碼校驗檢查等任務,js代碼轉譯),那麼就能夠生成咱們想要的結果,完成前端工做流管理,極大程度地提升效率。其做用其實就至關於makefile 的make 操做,將手工操做自動化,其任務編寫格式以下。

// gulp scss預處理任務
gulp.task('styles', function() {
  return gulp.src('src/styles/main.scss')
    .pipe(sass({ style: 'expanded' }))
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .pipe(gulp.dest('dist/assets/css'))
    .pipe(rename({suffix: '.min'}))
    .pipe(minifycss())
    .pipe(gulp.dest('dist/assets/css'))
    .pipe(notify({ message: 'Styles task complete' }));
});

模塊化打包器(webpack)

  前面說了那麼多SeaJS,RequireJS的模塊化 ,又有gulp ,grunt的自動化處理,想必都有點以爲這前端工程化的技術棧也太繁瑣了吧。

  那麼如今,你能夠通通不用管啦,讓咱們推出終極解決方案:Webpack。  

  相比於seajs / requirejs 須要在瀏覽器引入 sea.js 、require.js 的模塊解析器文件,瀏覽器才能識別其定義的模塊。 webpack不須要在瀏覽器中加載解釋器,而是直接在本地將模塊化文件(不管是AMD,CMD規範仍是ES6 Module)編譯成瀏覽器可識別的js文件。 

  另外,相對於gulp/grunt 的批處理工做流功能,webpack 也能夠經過 loader、plugin的形式對全部文件進行處理,來實現相似的功能。 

  其主要工做方式是: 整個項目存在一個或多個入口js文件,經過這個入口找到項目的全部依賴文件,經過loader,plugin進行處理後,打包生成對應的文件,輸出到指定的output目錄中。能夠說是集模塊化與工做流於一身的工具

clipboard.png


  固然,webpack也並不是銀彈。工具沒有好壞,只有適合與否。即使是webpack也並不是適用於全部場合。 

  webpack 的最大特色是一切皆爲模塊,一切全包,最適和應用在SPA一站式應用場景。只有簡單幾個頁面的狀況下使用 webpack 反而可能會增長沒必要要的配置成本,反而直接用gulp或者其餘工具處理代碼壓縮,css 預處理之類的工做會更加快捷易用。 

  另外,除了最主流的 webpack 以外,同性質的模塊化打包器還有 browserIfy,以及百度的 fis ,因爲對這二者瞭解很少,就不一一比較了。

使用 webpack 實現工程化

  廢話少說,talk is easy , show me the code,咱們來看看webpack是怎麼工做的。如下是一個配置了webpack-dev-server的本地開發webpack配置文件。 具體可訪問 github 地址 查看完整信息

// webpack.dev.config.js
let path = require('path'),
      webpack = require('webpack');

let resolve = path.resolve;
let webRootDir = resolve(__dirname, '../');


module.exports = {
    entry: {                 // 入口文件,打包經過入口,找到全部依賴的模塊,打包輸出
        main: resolve(webRootDir, './src/main.js'), 
    },
    output: {
        path: resolve(webRootDir, './build'),  // 輸出路徑
        publicPath: '/build/',     // 公共資源路徑
        filename: '[name].js'      // 輸出文件名字,此處輸出main.js, babel-polyfill.js ,  視狀況能夠配置[name].[chunkhash].js添加文件hash, 管理緩存
    },
    module: {
        rules: [   //模塊化的loader,有對應的loader,該文件才能做爲模塊被webpack識別
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/
相關文章
相關標籤/搜索