1. 程式人生 > 實用技巧 >前端js打包工具

前端js打包工具

1. 前言

1.1前端

前端三劍客:結構層html,表現層css,行為層JavaScript。

html好比是房子的地基,css和javascript是房子的建築材料,這三個部分一起組成個漂亮的房子。我們不能把他們分開說,某某部分是個房子,只有三個一起才能組成一個漂亮的房子 。

1.2 JavaScript 的簡介(參考阮一峰的ES6入門簡介)

這幾年,javascript 發展非常快速,特別是在2015年,更是有一個質的飛躍。
1.2.1 ECMA

說到 JavaScript,就要說下Web標準的組織協會,ECMA,它是“European Computer Manufactures Association”的縮寫,中文稱歐洲計算機制造聯合會

,1961年成立,旨在建立統一的電腦操作格式標準--包括程式語言和輸入輸出的組織。

1.2.2 JavaScript

2015年,JavaScript 引入許多新的語法糖,而且制定過程當中,還有很多組織和個人不斷提交新功能。事情很快就變得清楚了,不可能在一個版本里麵包括所有將要引入的功能。

常規的做法是先發布 6.0 版,過一段時間再發 6.1 版,然後是 6.2 版、6.3 版等等 ,這個2015年之前 JavaScript 現在習慣稱為ECMAScript5,而之後稱為ECMAScript6。

標準委員會商定後最終決定,標準在每年的 6 月份正式釋出一次,作為當年的正式版本。接下來的時間,就在這個版本的基礎上做改動,直到下一年的 6 月份,草案就自然變成了新一年的版本。這樣一來,就不需要以前的版本號了,只要用年份標記就可以了。

因此,ES6 既是一個歷史名詞,也是一個泛指,含義是 5.1 版以後的 JavaScript 的下一代標準,涵蓋了 ES2015、ES2016、ES2017 等等,而 ES2015 則是正式名稱,特指該年釋出的正式版本的語言標準。本書中提到 ES6 的地方,一般是指 ES2015 標準,但有時也是泛指“下一代 JavaScript 語言”。

  • 問題一:關於ECMAScript 和 JavaScript 是什麼關係 ?

    回答:從現在的角度來看,二者是可以互換的。即ECMAScript是JavaScript ,JavaScript 是ECMAScript。

  • 問題二:ECMAScript 6 和 ECMAScript 2015 是什麼關係 ?

    回答:ECMAScript 6泛指下一代 JavaScript 語言,ECMAScript 2015指的是 2015年的 JavaScript 標準;

總結

// es6 泛指下一代 JavaScript 語言,當時部分人也會認為特指ES2015
ECMAScript6.0  =  ECMAScript2015 = es2015  =  es6(部分人會這麼認為)
ECMAScript6.1  =  ECMAScript2016 = es2016  =  es7(部分人會這麼認為)
ECMAScript6.2  =  ECMAScript2017 = es2017  =  es8(部分人會這麼認為)
1.2.3瀏覽器的遭遇

很尷尬的是,JavaScript發展很快,但是瀏覽器跟不上指令碼更新的進度。一方面給出了標準,一方面卻不能直接在瀏覽器上使用。

這就出現了Babel,Babel自稱是 JavaScript 編譯器,它的作用就是將ES6新語法轉成ES5,即現在瀏覽器可識別的指令碼(基本現在是針對老版 IE 核心)。

但是使用 Babel 編譯也有缺陷,那就是每一次儲存,都需要手動的使用命令列編譯,而且編譯過程中還需要相關聯的包配合使用,很繁瑣。所以,打包工具就出現了,它可以幫助做這些繁瑣的工作。

2. 打包工具

2.1 介紹

僅介紹 4 款主流的打包工具:grunt,gulp,webpack,rollup,以釋出時間為順序。

  1. Grunt:最老牌的打包工具,它運用配置的思想來寫打包指令碼,一切皆配置,所以會出現比較多的配置項,諸如option,src,dest等等。而且不同的外掛可能會有自己擴充套件欄位,認知成本高,運用的時候需要明白各種外掛的配置規則。
  2. Gulp:用程式碼方式來寫打包指令碼,並且程式碼採用流式的寫法,只抽象出了gulp.src,gulp.pipe,gulp.dest,gulp.watch介面,運用相當簡單。更易於學習和使用,使用gulp的程式碼量能比grunt少一半左右。
  3. webpack: 是模組化管理工具和打包工具。通過 loader 的轉換,任何形式的資源都可以視作模組,比如 Commonjs模組、AMD 模組、ES6 模組、CSS、圖片等。它可以將許多鬆散的模組按照依賴和規則打包成符合生產環境部署的前端資源。還可以將按需載入的模組進行程式碼分隔,等到實際需要的時候再非同步載入。它定位是模組打包器,而 Gulp/Grunt 屬於構建工具。Webpack 可以代替 Gulp/Grunt 的一些功能,但不是一個職能的工具,可以配合使用。
  4. Rollup:下一代 ES6 模組化工具,最大的亮點是利用 ES6 模組設計,利用tree-shaking生成更簡潔、更簡單的程式碼。一般而言,對於應用使用 Webpack,對於類庫使用 Rollup;需要程式碼拆分(Code Splitting),或者很多靜態資源需要處理,再或者構建的專案需要引入很多 Commonjs模組的依賴時,使用 webpack。程式碼庫是基於 ES6 模組,而且希望程式碼能夠被其他人直接使用,使用 Rollup。

2.2 使用總結

Grunt:MPA,老牌打包工具,基於檔案為媒介(執行慢,零散的指令碼檔案一當多起來就受到影響

Gulp:MPA,易學,基於 nodejs 的 steam 流打包
Webpack:SPA,目前最強大的打包工具,但是過於臃腫,如何單純打包js不推薦

Roleup:MPA,tree-shaking特性(針對es6,按需打包,多餘的不要,目前(2018,vuex,react主流使用)

2.3 如何選擇

如果你一個都不熟悉的話,那麼我直接推薦webpack,官方文件非常詳細,更新頻率很高。而且在其他的打包工具在處理非網頁檔案(比如svg, png,vue等)基本還是需要藉助它來實現。最關鍵現在的腳手架主流依舊是它。

如果在處理檔案需要關注前端三劍客的話,那麼grunt和gulp會更好點,這兩者我直接推薦gulp,除非你已經很熟悉grunt了。

如果你更加在意指令碼程式碼的簡潔精煉,那麼可以使用rollup

如果你還要更加精煉一點,這裡新出來一個新的打包工具,免外掛式parcel

廣州VI設計公司https://www.houdianzi.com

3. 個人打包配置

在打包上,我個人注重的是配置從簡單到複雜,所以我分開使用。

css打包選擇了gulp,2個任務,3個外掛,有一個外掛是為了編譯scss,如果直接使用css,那麼這個外掛也可以去除。

// 任務一:編譯
gulp.task('compile', function () {
  return gulp.src('src/scss/*.scss')
    .pipe(sass({outputStyle: 'expanded'})) // 外掛一:編譯scss
    .on('error', showError)
    .pipe(autoprefixer({ // 外掛二:自動新增瀏覽器字首
      browsers: ['> 1%', 'last 4 versions'],
      cascade: false,
      remove: true
    }))
    .pipe(cleanCss({ // 外掛三:壓縮樣式
      compatibility: 'ie8',
      format: 'keep-breaks'
    }))
    .pipe(gulp.dest('../dist/css'));
})
// 任務二:觀察
gulp.task('watch', function(){
  gulp.watch('src/scss/*.scss', ['compile'])
})

ECMAScript個人現在基本使用es6,所以在打包指令碼上我選擇了rollup,只提取有用的程式碼,配置上參考react官方配置文件

import resolve from 'rollup-plugin-node-resolve';
import babel from 'rollup-plugin-babel';
import commonjs from 'rollup-plugin-commonjs';
import { eslint } from 'rollup-plugin-eslint';
import { uglify } from 'rollup-plugin-uglify';

const env = process.env.NODE_ENV;
console.log('當前環境:%s', env);

const configs = [
  {
    input: 'src/js/index.js',
    output: {
      file: 'dist/js/index.js',
      format: 'umd',
      name: 'atom',
      banner,
      sourcemap: true
    }
  }
]

const plugins = [
  eslint({ // 檢測js程式碼語法格式
    formatter: 'codeframe',
    include: [
      'src/js/**/*.js'
    ]
  }),
  resolve({ // 提取所依賴的程式碼
    jsnext: true,
    main: true,
    browser: true,
    module: true
  }),
  babel({ // 編譯es6 -> es5
    exclude: 'node_modules/**' // 只編譯我們的原始碼
  }),
  commonjs() // 將commonjs 轉成 es6 
]

export default configs.map(v => {
  v.plugins = plugins

  if (env === 'development') {
    v.watch = { // 監聽指令碼的變化
      include: 'src/js/**',
      exclude: ['node_modules/**']
    }
  }

  if (env === 'production') {
    v.plugins.push(
      uglify({ // 壓縮指令碼
        compress: {
          pure_getters: true,
          unsafe: true,
          unsafe_comps: true,
          warnings: false
        }
      })
    )
  }

  return v
});

html個人不做任何處理,可以在上線壓縮減少檔案的體積,壓縮直接使用gulp

// 任務一
gulp.task('testHtmlmin', function () {
    var options = {
        removeComments: true,//清除HTML註釋
        collapseWhitespace: true,//壓縮HTML
        collapseBooleanAttributes: true,//省略布林屬性的值 <input checked="true"/> ==> <input />
        removeEmptyAttributes: true,//刪除所有空格作屬性值 <input id="" /> ==> <input />
        removeScriptTypeAttributes: true,//刪除<script>的type="text/javascript"
        removeStyleLinkTypeAttributes: true,//刪除<style>和<link>的type="text/css"
        minifyJS: true,//壓縮頁面JS
        minifyCSS: true//壓縮頁面CSS
    };
    gulp.src('src/html/*.html')
        .pipe(htmlmin(options)) // 使用gulp-htmlmin外掛
        .pipe(gulp.dest('dist/html'));
});