前端工程化的概念在近些年來逐漸成爲主流構建大型web應用不可或缺的一部分,在此我經過如下這三方面總結一下本身的理解。javascript
爲何須要前端工程化。css
前端工程化的演化。前端
怎麼實現前端工程化。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工程,存在複雜依賴狀況是極有可能發生的,而且把時間耗費在管理依賴關係上也不值當。
因此就誕生了前端模塊化
經歷了混亂加載的黑歷史,咱們終於迎來了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進行語法轉譯,不過兼容性畢竟是硬傷,惟有時間能治癒。
從描述可知,前端工程化須要作的事情,單憑人力一個一個去處理基本沒有可能完成,那麼,咱們就須要學會使用工具,畢竟程序猿和猿之間最大的區別就是會不會使用工具。
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' })); });
前面說了那麼多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目錄中。能夠說是集模塊化與工做流於一身的工具。
固然,webpack也並不是銀彈。工具沒有好壞,只有適合與否。即使是webpack也並不是適用於全部場合。
webpack 的最大特色是一切皆爲模塊,一切全包,最適和應用在SPA一站式應用場景。只有簡單幾個頁面的狀況下使用 webpack 反而可能會增長沒必要要的配置成本,反而直接用gulp或者其餘工具處理代碼壓縮,css 預處理之類的工做會更加快捷易用。
另外,除了最主流的 webpack 以外,同性質的模塊化打包器還有 browserIfy,以及百度的 fis ,因爲對這二者瞭解很少,就不一一比較了。
廢話少說,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/