學習webpack:從最簡單的webpack說起

看了很多webpack的教程,大多是上來就講一堆配置,一堆插件的使用。這種文章看起來有點類似於官方文檔,或者新華字典。我想回歸初心,換一種方式,基於實際使用出發,一步一步介紹webpack。

從一個最簡單的例子開始,這個例子只爲了描述最簡單的webpack功能,實現一個最簡單的需求:在瀏覽器上顯示一段文字。

傳統方式

首先我們有一個index.html,這個html中只是引入了index.jsa.jsb.js

<!Doctype html>
<html>
  <head>
      <meta name="charset" content="utf-8">
      <title>webpack</title>
  </head>
  <body>
    <script type="text/javascript" src="./a.js"></script>
    <script type="text/javascript" src="./b.js"></script>
    <script type="text/javascript" src="./index.js"></script>
  </body>
</html>

a.js中聲明瞭變量var a = 1;,在b.js中聲明瞭變量var b = 2;。由於這種方式下,作用域是共享的,a和b相當於都是掛載在window上,所以在index.js中可以直接訪問到a和b的值。

index.js中,我們創建一個DOM並掛載到HTML上:

var dom = document.createElement('p');
dom.innerHTML = `a=${a}; b=${b}`;
document.body.appendChild(dom);

最後,在瀏覽器上就會顯示a=1; b=2

這種使用方式雖然簡單,但是會有潛在的問題:

  1. 瀏覽器在加載時,會先加載html文件,然後根據HTML裏面的script標籤去依次加載每個js文件。這樣對於每個js文件,瀏覽器都會向服務器發送一次請求。如果引入的文件數很多,那麼發送的請求次數就會過多,對服務器造成一定的壓力。而且,單個文件可能並不大,相對於瀏覽器對每次請求都需要建立鏈接、斷開鏈接的成本來說很不划算。要解決這個問題,就需要打包,將多個js文件打包成單個。
  2. 不能嚴格保證js文件的加載順序,比如index.js加載完了,但它所依賴的a.js還沒加載。當然,這個問題可以用require.js解決。
  3. 不同script標籤引入的js代碼,會污染全局作用域,比如a.js中聲明的a就直接掛載到了window上,其他文件中如果再聲明a變量,就會有衝突。這個問題可以用立即執行函數的方式解決。

雖然有辦法解決,但總感覺不是那麼完美,治標而不治本。迴歸到js的運行環境上,這都是因爲js代碼需要在瀏覽器中運行。如果是在node環境中運行,那可以直接使用CommonJS規範,每個文件就是一個模塊,各個模塊之間的作用域是獨立的,通過require可以解決模塊依賴和加載問題。甚至還可以在node中利用ES6的模塊機制,也同樣可以解決這個問題。顯然,這種寫代碼的方式要簡單很多,但只能在node環境下。而webpack的一個重要作用,就是可以讓你這種方式寫出來的代碼能在瀏覽器中運行。

webpack方式

a.jsb.js分別作爲2個模塊,通過ES6的export導出變量a和b,在index.js中通過import引入:

//a.js
export var a = 1;

//b.js
export var b = 2;

//index.js
import {a} from './a.js';
import {b} from './b.js';
var dom = document.createElement('p');
dom.innerHTML = `a=${a}; b=${b}`;
document.body.appendChild(dom);

最後,我們希望用webpack,將其打包成一個單獨的文件,直接掛載到index.html中。從零開始,安裝webpack。

  1. 新建一個文件夾,在這個文件夾中npm init,初始化。
  2. 安裝webpackwebpack-cli,運行npm install webpack webpack-cli -Dwebpack-cli爲webpack提供了命令行工具,讓我們可以直接在命令行中使用webpack
  3. 建立src文件夾,將a.js,b.jsindex.js存放在src文件夾下。這個文件夾存放的是原始文件
  4. 建立dist文件夾,用來存放編譯後的文件,也就是打包後的單個文件
  5. index.html放到src文件夾下,這時引用的不是index.js,而是打包後的位於dist目錄下的bundle.js文件

    <script type="text/javascript" src="../dist/bundle.js"></script>
  6. 配置webpack。webpack的配置就是在根目錄下直接新建一個webpack.config.js,配置如下:

    const path = require('path');
    
    module.exports = {
      entry: './src/index.js',
      output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js'
      }
    }
    • entry是打包的入口文件,也就是告訴webpack打包哪個文件,這裏指定的是index.js。由於在index.jsimporta.jsb.js,所以webpack在打包時會同時將a.jsb.js引入。從這裏可以看到,只用告訴webpack入口文件即可,所有的依賴文件webpack會自己尋找和解決。
    • output是告訴webpack,打包後的文件放哪裏。path指定了打包後的文件路徑,filename指定了打包後的文件名。綜合起來,打包後的文件就是dist目錄下的bundle.js
  7. package.json中的script下做個配置:

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack" 
      },
  8. 直接運行npm run build,node就會自動執行webpack,這時在dist目錄下就可以看到生成的js文件(只有一個),將index.html放到瀏覽器中,就會看到最後顯示的效果。

不止於此

從上面的例子看到,使用了webpack之後,我們解決了傳統方式裏面遇到的各種問題。當然,webpack能做的,遠不止這些。比如在寫代碼時,可能還會有這些需求:

  • 代碼轉換:將 TypeScript 編譯成JavaScript、將 SCSS 編譯成 CSS等。
  • 文件優化:壓縮JavaScript、CSS、HTML 代碼,壓縮合並圖片等。
  • 代碼分割:提取多個頁面的公共代碼,提取首屏不需要執行部分代碼讓其異步記在。
  • 模塊合併:在採用模塊化的項目裏會有很多個模塊和文件,需要通過構建功能將模塊分類合併成一個文件。
  • 自動刷新:監聽本地源代碼變化,自動重新構建、刷新瀏覽器。
  • 代碼校驗:在代碼被提交到倉庫前需要校驗代碼是否符合規範,以及單元測試是否通過。
  • 自動發佈:更新代碼後,自動構建出線上發佈代碼並傳輸給發佈系統。

我們需要一個工具來幫我們解決這些問題,完成整個構建流程。使用構建工具的目的,是爲了讓我們寫代碼更加方便,可以用更新的特性而不用過多關心瀏覽器的兼容問題;讓我們可以省去很多機械重複性的工作,比如修改代碼後瀏覽器會自動刷新,提高我們的開發效率。

當然,在webpack之前,已經有很多優秀的構建工具了,比如grunt、gulp等。稱webpack是當下最流行的構建工具毫不爲過。webpack強大不僅在其本身,還在於很多基於webpack的插件,提供了一個強大的生態系統。webpack能做的事情還有很多,將在後面一步步繼續學習。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章