webpack (1)——核心概念的理解

AdityaSui發表於2018-05-17

引子

為什麼要寫這篇文章?因為今天掘金的早報有一篇關於 Webpack 的入門的文章,讀完之後發現自己確實該學習一下 Webpack 了,所以有了這篇文章。
我對於 webpack 的瞭解僅限於在 vue-cli 中接觸過,當然都是現成的,自己也從沒操作過。
但是隨著自己的成長,這些必須要了解一下,接下來簡單都構建一個屬於自己的 webpack 打包工具...

  1. webpack (1)——核心概念的理解
  2. webpack(2)——配置項詳解
  3. ...

初試 webpack

我在初次學習新東西的時候,都會先先弄懂三個問題:

  1. 它是什麼東西;
  2. 它能幹什麼;
  3. 它每一步大體是什麼意思。

概念

webpack 文件中是這樣定義的:

本質上,webpack 是一個現代 JavaScript 應用程式的靜態模組打包器(module bundler)。當 webpack 處理應用程式時,它會遞迴地構建一個依賴關係圖(dependency graph),其中包含應用程式需要的每個模組,然後將所有這些模組打包成一個或多個 bundle。

image

配合其官網的首頁圖不難理解其含義:一切檔案如:JavaScriptCSSSASSIMG/PNG等,在 Webpack 眼中都是一個個模組,通過對 Webpack 進行配置,對模組進行組合和打包。經過 Webpack 的處理,最終會輸出瀏覽器能使用的靜態資源。

核心概念

基本理解了什麼是 Webpack 、能幹什麼之後,接下來就開始理一下如何使用它。 順著 Webpack 的文件學習,在正式使用之前,它需要我們理解幾個核心概念:

  • Entry 入口
  • Output 輸出結果
  • Loader 模組轉換器
  • Module 模組
  • Chunk 程式碼塊
  • Plugin 外掛

Entry

entry 是配置模組的入口,它指示 Webpack 執行構建的第一步。
可以在配置檔案中配置 entry 屬性,來指定一個或多個入口點

entry 型別有三種:字串、陣列、物件。

  • String"./src/entry" 入口模組的檔案路徑,可以是相對路徑
  • array : ["./src/entry1", "./src/entry2"] 入口模組的檔案路徑,可以是相對路徑。與字串型別不同的是陣列可將多個檔案打包為一個檔案
  • object{ a: './src/entry-a', b: ['./src/entry-b1', './app/entry-b2']} 配置多個入口,每個入口有一個 Chunk

Output

output 配置如何輸出最終想要的程式碼。output 是一個 object,裡面包含一系列配置項。

  • filename 用於輸出檔案的檔名。
  • path 目標輸出目錄的絕對路徑,必須是絕對路徑。

module、Loader

module 配置如何處理模組

配置Loader

Loader 可以看作具有檔案轉換功能的翻譯員,配置裡的 module.rules 陣列配置了一組規則,告訴 Webpack 在遇到哪些檔案時使用哪些 Loader 去載入和轉換。

  • use 屬性的值需要是一個由 Loader 名稱組成的陣列,Loader 的執行順序是由後到前的;
  • 每一個 Loader 都可以通過 URL querystring 的方式傳入引數,例如 css-loader?minimize 中的 minimize 告訴 css-loader 要開啟 CSS 壓縮。

Plugins

Plugin 用於擴充套件 Webpack 功能,各種各樣的 Plugin 幾乎讓 Webpack 可以做任何構建相關的事情。

配置 Plugin

Plugin 的配置很簡單,plugins 配置項接受一個陣列,陣列裡每一項都是一個要使用的 Plugin 的例項,Plugin 需要的引數通過建構函式傳入。

當然使用 Plugin 的難點在於掌握 Plugin 本身提供的配置項,而不是如何在 Webpack 中接入 Plugin

想實現某項 Webpack 沒有的功能去 gayHub 碰碰運氣吧!哦不,是 github 碰碰運氣吧!

Chunk

Chunk,程式碼塊,即打包後輸出的檔案。

Webpack 會為每個 Chunk 取一個名稱,可以根據 Chunk 的名稱來區分輸出的檔名。

filename: '[name].js'
複製程式碼

一個入口檔案,預設 chunknamemain。 除了內建變數 name,與 chunk 相關的變數還有:

  • id Chunk 的唯一標識,從0開始
  • name Chunk 的名稱
  • hash Chunk 的唯一標識的 Hash
  • chunkhash Chunk 內容的 Hash

簡單配置一個 webpack

瞭解了基本概念就直接開車吧,咳咳咳,開始實戰。

// ./webpack.config.js

module.exports = {
    mode: 'development',
    entry:{
        app1: './src/index.js',
        app2: './src/main.js'
    },
    output: {
        path: __dirname + '/dist',
        filename: '[hash][name].js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },
            {
                test: /\.js$/,
                use: 'babel-loader',
                exclude: /node_modules/
            }
        ]
    }
};
// ./src/index.css
html {
    background: #333;
    color: #fff;
}
// ./src/index.js
import './index.css'
const sleep = async time => await new Promise( resolve => {
    setTimeout( () => {
       console.log(`等待${time}s`)
        return resolve
    }, time*1000)
} )
sleep(5)
console.log(`Success!`)

// ./src/main.js
const sleep = async time => await new Promise( resolve => {
    setTimeout( () => {
        console.log(`等待${time}s`)
        return resolve
    }, time*1000)
} )
sleep(2)


// .babelrc
{
  "presets": [
    "es2015",
    "stage-0"
  ],
  "plugins": [
    ["transform-runtime", {
      "polyfill": false,
      "regenerator": true
    }]
  ]
}

// package.json
{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "main": "webpack.config.js",
  "dependencies": {
    "babel-core": "^6.26.3",
    "babel-polyfill": "^6.26.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1",
    "css-loader": "^0.28.11",
    "style-loader": "^0.21.0",
    "webpack": "^4.8.3",
    "webpack-cli": "^2.1.3"
  },
  "devDependencies": {
    "babel-loader": "^7.1.4",
    "babel-plugin-transform-runtime": "^6.23.0"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Aditya",
  "license": "ISC"
}

複製程式碼

上述程式碼是我寫的第一個 webpack demo,入門是真的簡單,所以很快的根據文件配置了一個能夠打包 css、es6、async/awaitWebpack

其中一些依賴項的安裝如下

// npm i -D 是 npm install --save-dev 的簡寫
npm i -D webpack webapck-cli

npm i -D css-loader style-loader

npm i -D babel-loader babel-core babel-polyfill babel-preset-es2015 babel-preset-stage-0 babel-plugin-transform-runtime
複製程式碼

css-loader和style-loader

在配置過程中遇到的問題:css程式碼被打包了,卻沒有生效。

一開始在配置 Loader 中,我只寫了 css-loader,因為今天看的博文中的栗子就只寫 css-loader , 殊不知人家根本就沒寫任何 CSS 樣式=。=

所以善用了一下手中的搜尋引擎,從而得知這個過程應該是這樣的:

Webpack 在遇到以 .css 結尾的檔案時先使用 css-loader 讀取 CSS 檔案,再交給 style-loaderCSS 內容注入到 JavaScript 裡。

style-loader 的工作原理大概是這樣的:把 CSS 內容用 JavaScript 裡的字串儲存起來, 在網頁執行 JavaScript 時通過 DOM 操作動態地往 head 標籤裡插入 style 標籤。

再來說說 babel

瞭解 ES 6 的同學都應該知道,目前部分瀏覽器和 Node.js 已經支援 ES6,但由於它們對 ES6 所有的標準支援不全,這導致在開發中不敢全面地使用 ES6。 通常我們需要把採用 ES6 編寫的程式碼轉換成目前已經支援良好的 ES5 程式碼,這包含兩件事:

  1. 把新的 ES6 語法用 ES5 實現,例如 ES6 的 class 語法用 ES5 的 prototype 實現。
  2. 給新的 API 注入 polyfill ,例如使用新的 fetch API 時注入對應的 polyfill 後才能讓低端瀏覽器正常執行。

Babel 可以方便的完成以上2件事。 Babel 是一個 JavaScript 編譯器,能將 ES6 程式碼轉為 ES5 程式碼,讓你使用最新的語言特性而不用擔心相容性問題,並且可以通過外掛機制根據需求靈活的擴充套件。

Babel 執行編譯的過程中,會從專案根目錄下的 .babelrc 檔案讀取配置。.babelrc 是一個 JSON 格式的檔案,內容大致如下:

{
  "presets": [
    "es2015",
    "stage-0"
  ],
  "plugins": [
    ["transform-runtime", {
      "polyfill": false,
      "regenerator": true
    }]
  ]
}
複製程式碼

babel-loader

babel-loader 是必須的,在 Webpack 通過 Loader 接入 Babel,完成轉碼

babel-preset-es2015和babel-preset-stage-0

presets 屬性告訴 Babel 要轉換的原始碼使用了哪些新的語法特性,一個 Presets 對一組新語法特性提供支援,多個 Presets 可以疊加。 Presets 其實是一組 Plugins 的集合,每一個 Plugin 完成一個新語法的轉換工作。

  1. 已經被寫入 ECMAScript 標準裡的特性,由於之前每年都有新特性被加入到標準裡,所以又可細分為:
  • es2015 包含在2015里加入的新特性;
  • es2016 包含在2016里加入的新特性;
  • es2017 包含在2017里加入的新特性;
  • es2017 包含在2017里加入的新特性;
  • env 包含當前所有 ECMAScript 標準裡的最新特性。
  1. 被社群提出來的但還未被寫入 ECMAScript 標準裡特性,這其中又分為以下四種:
  • stage0 只是一個美好激進的想法,有 Babel 外掛實現了對這些特性的支援,但是不確定是否會被定為標準;
  • stage1 值得被納入標準的特性;
  • stage2 該特性規範已經被起草,將會被納入標準裡;
  • stage3 該特性規範已經定稿,各大瀏覽器廠商和 Node.js 社群開始著手實現;
  • stage4 在接下來的一年將會加入到標準裡去。

3.為了支援一些特定應用場景下的語法,和 ECMAScript 標準沒有關係,例如 babel-preset-react 是為了支援 React 開發中的 JSX 語法。

babel-plugin-transform-runtime

Plugin 屬性告訴 Bable 要使用那些外掛,外掛可以控制如何轉換程式碼。

官方給出的定義是: babel-plugin-transform-runtimeBabel 官方提供的一個外掛,作用是減少冗餘程式碼。 但是我們為什麼要使用它?主要是因為使用 ES 7async 函式。

因為 runtime 編譯器外掛做了以下三件事:

  • 當你使用 generators/async 函式時,自動引入 babel-runtime/regenerator
  • 自動引入 babel-runtime/core-js 並對映 ES 6 靜態方法和內建外掛。
  • 移除內聯的 Babel helper 並使用模組 babel-runtime/helpers 代替。

該外掛的具體資訊:transform-runtime

至於 babel 的其他的具體用法,可以查閱 Babel官方文件

寫在最後

對於 Webpack 第一步學習就到這裡結束了,翻閱的資料有點多,需要學習的東西太多了,一時半會也記不住,算是鋪個路吧,希望今後的學習能夠順暢一些。
歡迎大佬指點不足之處。

相關文章