webpack 對我來說曾經是一個怪物般存在一樣,因為它有太多太多的配置項,相反,使用像create-react-App腳手架可以很輕松創建項目,所以有一段時間內,我會盡量避免使用 webpack,因為它看起來既復雜又望而卻步
如果你們不習慣從頭開始設置 webpack 來使用Babel、TypeScript、Sass、React或Vue,或者不知道為什么要使用 webpack,那么這篇文章是你的最佳選擇。就像所有的事情一樣,一旦你深入學習,你會發現它并不是那么可怕,只有幾個主要的概念需要學習掌握。
如果你是從 webpack 4 升級到 webpack 5,這里有一些注意事項:
- webpack-dev-server命令現在換成webpack-serve
- file-loader、raw-loader和url-loader不是必需的,可以使用內置的Asset Modules
- 節點 polyfill 不再可用,例如,如果遇到stream錯誤,則可以將stream-browserify包作為依賴項添加,并將{stream:'stream-browserify'}添加到webpack配置中的alias屬性。
什么是 webpack?
現在,大多數網站不再只是單單的由原生JS+純html編寫的,還涉及一些瀏覽器無法理解的語言,如果項目大,文件多,對應的體積就大。所以要壓縮文件和翻譯成所有瀏覽器都能理解的東西,這就是webpack的用武之地。
webpack 可以看做是模塊打包器:它做的事情是,分析你的項目結構,找到JAVAScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言(Scss,TypeScript等),并將其打包為合適的格式以供瀏覽器使用。
對于開發,webpack 還提供了一個開發服務器,它可以在我們保存時動態地更新模塊和樣式。vue create和create-response-app本質上都依賴于 webpack。
webpac k可以做很多事情,本文只是幫助大家熟悉一些主要概念并進行一些手動的配置。
安裝
首先,創建一個目錄webpack-tutorial,相關命令如下:
mkdir webpack-tutorial
cd webpack-tutorial
npm init -y // 創建默認的 package.json
安裝webpack和webpack-cli:
npm i -D webpack webpack-cli
接著,創建目錄 src,并在其里面創建 index.js,內容如下:
console.log('Interesting!')
基本配置
在項目的根目錄中創建一個webpack.config.js。
Entry
entry是配置模塊的入口,可抽象成輸入,Webpack 執行構建的第一步將從入口開始搜尋及遞歸解析出所有入口依賴的模塊。
entry 配置是必填的,若不填則將導致 Webpack 報錯退出。這里,我們將src/index.js做為入口點。
const path = require('path')
module.exports = {
entry: {
main: path.resolve(__dirname, './src/index.js'),
},
}
Output
配置 output 選項可以控制 webpack 如何向硬盤寫入編譯文件。注意,即使可以存在多個入口起點,但只指定一個輸出配置。這里指定輸出的路徑為 'dist':
module.exports = {
/* ... */
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].bundle.js',
},
}
現在,我們具有構建捆綁包所需的最低配置。在package.json中,我們可以創建一個運行webpack命令的構建腳本。
"scripts": {
"build": "webpack"
}
現在可以運行它了:
npm run build
現在在 dist 目錄會生成一個 main.bundle.js 文件
插件
webpack有一個插件接口,這使得它更加靈活。內部webpack代碼和第三方擴展使用插件,有一些主要的方法幾乎每個webpack項目都會用到。
HTML 模板文件
目前,我們有一個隨機的bundle文件,但它對我們還不是很有用。如果需要使用main.bundle.js,就要借助 HTML頁面來加載這個 JS 包作為腳本。我們希望HTML文件自動引入這個生成 js 文件,所以我們將使用html-webpack-plugin創建一個HTML模板。
安裝一下:
npm i -D html-webpack-plugin
在src文件夾中創建一個template.html文件,這里,我們自定義一個title,內容如下:
src/template.html
<!DOCTYPE html>
<html lang="en">
<head>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
創建配置的plugins屬性,然后將插件,文件名添加到輸出(index.html),并鏈接到將基于該模板的模板文件。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
/* ... */
plugins: [
new HtmlWebpackPlugin({
title: 'webpack Boilerplate',
template: path.resolve(__dirname, './src/template.html'), // template file
filename: 'index.html', // output file
}),
],
}
現在再次運行構建,會看到dist文件夾現在包含一個index.html,里面也自動引入了我們打包好的 js 文件。用瀏覽器打開 index.html,會在控制臺看到 Interesting!。
接著,在index.js中我們動態插入一些 dom 元素到頁面中,內容如下:
// Create heading node
const heading = document.createElement('h1')
heading.textContent = 'Interesting!'
// Append heading node to the DOM
const app = document.querySelector('#root')
app.append(heading)
重新構建,在進入 dist 目錄下面,用 http-server 運行 html 文件。
http-server
可以在頁面上看到,我們注入的 "Interesting!",還會注意到捆綁文件已縮小。
注意:在安裝HtmlWebpackPlugin后,你會看到一個DeprecationWarning,因為插件在升級到webpack 5后還沒有完全擺脫deprecation警告。
Clean
我們還需要設置clean-webpack-plugin,在每次構建后清除dist文件夾中的所有內容。這對于確保不遺留任何舊數據很重要。
clean-webpack-plugin-刪除/清理構建文件夾
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
/* ... */
plugins: [
/* ... */
new CleanWebpackPlugin(),
],
}
Modules and Loaders
webpack 使用 loader 預處理一些加載的文件,如 js 文件、靜態資源(如圖像和CSS樣式)和編譯器(如TypeScript和Babel)。webpack 5也有一些內置的資產加載器。
在我們的項目中,有一個HTML文件,該文件可以加載并引入一些 JS ,但實際上并沒有執行任何操作。那么這個webpack配置要做的主要事情是什么?
- 將 JS 編譯為瀏覽器可以理解的版本
- 導入樣式并將 SCSS 編譯為 CSS
- 導入圖像和字體
- (可選)設置React或Vue
Babel (JavaScript)
Babel是一個工具,可讓使用最新的 JS 語法。
建立一個規則來檢查項目中(node_modules之外)的任何.js文件,并使用babel-loader進行轉換。Babel 還有一些其他的依賴項:
- babel-loader-使用 Babel 和 webpack 傳輸文件。
- @babel/core-將ES2015+ 轉換為向后兼容的 JavaScript
- @babel/preset-env-Babel 的智能默認設置
- @babel/plugin-proposal-class-properties-自定義 Babel 配置的示例(直接在類上使用屬性)
npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-env @babel/plugin-proposal-class-properties
webpack.config.js
module.exports = {
/* ... */
module: {
rules: [
// JavaScript
{
test: /.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
],
},
}
如果是 TypeScript 項目,使用的是typescript-loader而不是babel-loader。
現在Babel已經設置好了,但是我們的Babel插件還沒有。可以在index.js中添加一些新的語法來證明它還不能正常工作。
// 創建沒有構造函數的類屬性
class Game {
name = 'Violin Charades'
}
const myGame = new Game()
// 創建 p 節點
const p = document.createElement('p')
p.textContent = `I like ${myGame.name}.`
const heading = document.createElement('h1')
heading.textContent = 'Interesting!'
const app = document.querySelector('#root')
app.append(heading, p)
要解決這個問題,只需在項目的根目錄中創建一個.babelrc文件。可以使用preset-env和plugin-proposal-class-properties添加更多默認值。
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
現在運行npm run build 一切準備就緒。
Images
假設我們需要引用一張圖片并直接導入到 JS 文件中,這樣是無法正常工作的。為了演示,創建 src/ images 并向其中添加圖像,然后嘗試將其導入到index.js文件中。
src/index.js
import example from './images/example.png'
/* ... */
運行構建時,再次看到錯誤:
webpack有一些內置的asset modules ,可用于靜態資源。對于圖像類型,我們將使用asset/resource,注意,這里是一個type,而不是loader。
webpack.config.js
module.exports = {
/* ... */
module: {
rules: [
// Images
{
test: /.(?:ico|gif|png|jpg|jpeg)$/i,
type: 'asset/resource',
},
],
},
}
構建后,可以在dist文件夾查看。
字體和內聯
webpack 還有一個asset module ,可以使用asset/inline內聯某些數據,例如svgs和字體。
src/index.js
import example from './images/example.svg'
/* ... */
webpack.config.js
module.exports = {
/* ... */
module: {
rules: [
// Fonts and SVGs
{
test: /.(woff(2)?|eot|ttf|otf|svg|)$/,
type: 'asset/inline',
},
],
},
}
Styles
同樣的需要使用 style loader才能在腳本中執行類似import 'file.css'的操作。
現在很多人都在使用CSS-in-JS、styled-components和其他工具來將樣式引入到他們的 JS 應用程序中。
當網站只有一個 CSS 文件,僅能夠加載一個CSS文件就足夠了。但如果想使用PostCSS,為了能在任何瀏覽器中使用所有最新的CSS特性。或者想使用Sass, CSS預處理器,那就需要使用其它的 loader 處理。
我想使用這三種方法——在Sass中編寫,在PostCSS中處理,以及編譯到CSS。這需要引入一些加載器和依賴項。
- sass-loader — 加載 SCSS 并編譯為CSS
- node-sass — Node Sass
- postcss-loader — 使用 PostCSS 處理 CSS
- css-loader — 解析 css import
- style-loader —— 將CSS注入到DOM中
npm i -D sass-loader postcss-loader css-loader style-loader postcss-preset-env node-sass
就像Babel一樣,PostCSS 也需要一個配置文件 postcss.config.js,在根目錄中創建它,并輸入以下內容:
postcss.config.js
module.exports = {
plugins: {
'postcss-preset-env': {
browsers: 'last 2 versions',
},
},
}
為了測試 Sass 和 PostCSS 是否正常工作,創建src/styles/main.scss,并輸入以下內容:
src/styles/main.scss
$font-size: 1rem;
$font-color: lch(53 105 40);
html {
font-size: $font-size;
color: $font-color;
}
現在,將文件導入index.js并添加四個 loader 。它們從最后編譯到第一個,因此列表中最后一個是sass-loader,因為需要編譯,然后是PostCSS,然后是CSS,最后是style-loader,它將CSS注入到DOM 中。
src/index.js
import './styles/main.scss'
/* ... */
webpack.config.js
module.exports = {
/* ... */
module: {
rules: [
// CSS, PostCSS, and Sass
{
test: /.(scss|css)$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
},
],
},
}
現在,重新構建時,項目中已經應用了Sass和PostCSS。
開發
每次進行更新時都要運行npm run build,站點越大,構建所需的時間就越長,這樣就十分的煩瑣。為此可以為 webpack 設置兩種配置:
- 生產配置,用于最小化,優化和刪除所有源映射
- 開發配置,該配置在服務器中運行webpack,每次更改都會更新,并具有源映射
開發模式下是在內存中運行所有內容,而不是構建一個dist文件,需要安裝 webpack-dev-server
npm i -D webpack-dev-server
出于演示目的,我們可以僅將開發配置添加到正在構建的當前webpack.config.js文件中并對其進行測試。但是,我們開發一般要創建兩個配置文件:一個生產環境用的 mode: production,一個開發環境用的mode: development。
const webpack = require('webpack')
module.exports = {
/* ... */
mode: 'development',
devServer: {
historyApiFallback: true,
contentBase: path.resolve(__dirname, './dist'),
open: true,
compress: true,
hot: true,
port: 8080,
},
plugins: [
/* ... */
// Only update what has changed on hot reload
new webpack.HotModuleReplacementPlugin(),
],
})
我們添加mode: development,并創建devServer屬性,其中,默認端口將為8080,自動打開瀏覽器窗口,并使用hot-module-placement,這需要webpack.HotModuleReplacementPlugin插件。這樣模塊執行更新而無需完全重新加載頁面-因此,如果你更新某些樣式,則這些樣式將發生變化,并且不用重新加載整個 JS ,大大加快了開發速度。
現在,可以使用webpack serve命令來啟動項目。
package.json
"scripts": {
"start": "webpack serve"
}
npm start
運行此命令時,將在瀏覽器中自動彈出一個指向localhost:8080的鏈接。現在,您可以更新Sass和JavaScript,并觀看其動態更新。
總結
我用 Babel,Sass,PostCSS,生產優化和開發服務器創建了可用于生產的webpack 5樣板,其中包含本文的所有內容,但會涉及更多細節。從這里,可以輕松設置React,Vue,Typescript或其他任何您想要的東西。
項目地址:webpack 5 boilerplate
看看它,擺弄它,享受它!
作者:Tania Rascia 譯者:前端小智 來源:taniarascia
原文:https://www.taniarascia.com/how-to-use-webpack/