前端的打包工具


本文是當初學習打包工具記錄下,受限於當時技術有限,很多描述囫圇吞棗。
於 2020/07/14 重新整理。

很重要

打包工具只是一向可以很好的管理htmlcssjavascript,使用可以錦上添花,不使用也沒關系。
如果你是一位偏向於后端程序員,了解熟悉即可,對於前端程序員來說,現在打包工具基本是個基本技能,你需要多多擅用。

1. 為什么我們需要打包工具

1.1 前端

一般我們說前端,通常指結構層html,表現層css,行為層javascripthtml好比是房子的地基,cssjavascript是房子的建築材料,這三個部分一起組成個漂亮的房子。

所以我們不能將他們分開說,某某部分是個房子,只有三個一起才能組成一個漂亮的房子。

1.2 JavaScript 的簡介

這幾年,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的下一代標准,涵蓋了ES2015ES2016ES2017等等,而ES2015則是正式名稱,特指該年發布的正式版本的語言標准。本書中提到ES6的地方,一般是指ES2015標准,但有時也是泛指“下一代 JavaScript 語言”。

  • 問題一:關於ECMAScriptJavaScript是什么關系 ?

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

  • 問題二:ECMAScript 6ECMAScript 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發展很快,但是瀏覽器跟不上腳本更新的進度。一方面給出了標准,一方面卻不能直接在瀏覽器上使用。

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

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

2. 打包工具

2.1 介紹

僅介紹 4 款主流的打包工具:grunt gulpwebpackrollup,以發布時間為順序。

  1. Grunt:最老牌的打包工具,它運用配置的思想來寫打包腳本,一切皆配置,所以會出現比較多的配置項,諸如option,src,dest等等。而且不同的插件可能會有自己擴展字段,認知成本高,運用的時候需要明白各種插件的配置規則。

  2. Gulp:用代碼方式來寫打包腳本,並且代碼采用流式的寫法,只抽象出了gulp.src, gulp.pipe, gulp.dest, gulp.watch 接口,運用相當簡單。更易於學習和使用,使用gulp的代碼量能比grunt少一半左右。(PS:此介紹的是gulp3,在gulp4不可用

  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等)基本還是需要借助它來實現。最關鍵現在的腳手架主流依舊是它。

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

如果你更加在意腳本代碼的簡潔精煉,那么可以使用rollup。(PS:我目前主要使用的打包工具,搭建可以使用的 es6 環境可見我這篇文章

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

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 個人不做任何處理,可以在上線壓縮減少文件的體積,壓縮直接使用gulp3

// 任務一
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'));
});


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM