「本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!」css
衆所周知,在前端工程化日趨複雜的今天,模塊化打包工具在咱們的平常開發中起着愈來愈重要的做用,而其中, webpack
已然是前端打包構建的不二選擇。html
說到 webpack
,可能不少小夥伴會以爲既熟悉又陌生,熟悉是由於在咱們開發的每個項目中,都會使用到它。而陌生在於, webpack
有着複雜的配置和五花八門的功能而感到陌生。前端
所以,咱們有時候會被這複雜的配置先嚇到,從而被勸退學習。vue
然而,在技術更新迭代這麼快的一個大環境下, webpack
仍是很值得咱們去學習的。node
在下面的這篇文章中,將講解 webpack
的入門核心概念。一塊兒來學習吧~🎬webpack
當咱們要寫一個網頁時,首先咱們會先建立一個 index.html
文件,以後在可能還會有一些 js
文件,那麼咱們就會在 html
文件中,引入這些 js
文件。web
試想一下,若是 js
文件不少,而後咱們一個個引入,這樣若是遇到報錯了呢?會不會就很難定位到是哪一個文件錯了,這樣就會使得開發效率很是低下了。面試
所以,就有了 webpack
。 webpack
會將咱們所寫的代碼,進行一個翻譯,並將翻譯完的內容,進行一個模塊化的打包,使得項目變得工程化。正則表達式
接下來,咱們就來說解, webpack
到底是什麼以及怎麼安裝使用。express
webpack
有時候會被誤認爲是一個 js
的翻譯器,但其實 webpack
稱不上是一個 js
的翻譯器。
由於它只認識 import
這樣相似的語句,而不認識其餘的 js
高級語法。因此若是稱它是一個 js
的翻譯器,其實是咱們高看了它。
查看官方的定義咱們能夠發現, webpack
是一個模塊打包工具,同時 webpack
也能夠支持 commonjs
的模塊打包規範。
然而,隨着時間的推移和技術的不斷更新, webpack
再也不是隻會打包 js
的模塊打包工具, webpack
如今還支持 css文件
、 jpg
、 png
等各類文件的打包。
webpack
是基於 nodejs
開發的模塊打包工具,本質上是由 node
實現的。所以咱們要先安裝本機的 node
環境。這裏附上官方網站的連接,建議你們下載穩定版本
以後查詢本機的 node
和 npm
版本,查看是否安裝成功。
node -v
npm -v
複製代碼
先建立一個項目,假設命名爲 webpack-demo
。以後在該項目下經過如下命令建立一個文件夾:
mkdir webpack-demo
複製代碼
進入 webpack-demo
文件,初始化項目。命令行以下:
cd webpack-demo
npm init 或 npm init -y //加-y表示默認自動配置項
複製代碼
以後一路 Enter
回車便可。
你們能夠看到項目結構,以上操做就是在 webpack-demo
文件下建立了一個 package.json
文件,以後咱們把 package.json
文件的內進行改造。具體代碼以下:
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
//將這個項目設置爲私有
"private":true,
//"main": "index.js", //去掉入口文件
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
//寫上做者名
"author": "Monday",
//保持項目私有
"license": "ISC"
}
複製代碼
第一種方式:全局安裝
npm install webpack webpack-cli -g
複製代碼
第二種方式:當前項目下安裝
npm install webpack webpack-cli -D
複製代碼
在這裏的建議是選擇第二種方式進行安裝。那爲何呢?是由於全局安裝 webpack
有什麼問題嗎?
事實上,全局安裝只會安裝一個版本。
那麼假如咱們如今要跑兩個項目,而且這兩個項目出現原先安裝的 webpack
版本不同,安裝低版本 webpack
的項目,就有可能會致使在咱們的本機中運行不起來。因此建議是使用第二種方式進行安裝。
安裝完成以後,咱們還要來查詢當前使用的 webpack
版本號,以確保咱們是否已經安裝成功。具體命令行以下:
npx webpack -v
複製代碼
這個時候就有小夥伴會有疑問說,爲何前面還要加個 npx
才能查找版本號。
緣由在於,咱們只在當前項目中安裝,因此 webpack
這個命令在全局中並無找到。而 node
提供了 npx
,這個時候 npx
就能夠找到咱們所運行項目目錄下的 node-module
中的 webpack
安裝包,因此這種方式是把咱們的 webpack
安裝在咱們的項目內,而後經過 npx
去運行 webpack
就能夠了。
若是咱們想要安裝具體版本號的 webpack
,那麼咱們先查看 webpack
的版本號信息。命令以下:
npm info webpack
複製代碼
查到具體版本號之後,使用如下命令進行安裝:
npm install webpack@4.16.5 webpack-cli -D //4.16.5表示版本號
複製代碼
不少時候,咱們還沒寫配置文件,項目就成功跑起來了。這並非由於不用寫,而是 webpack
團隊提早幫咱們寫好了不少默認的配置文件,使得咱們在運行項目時不用進行過多的配置就能夠達到咱們的使用需求。那下面,就跟着你們一塊兒來寫一個配置文件。
咱們先來了解下項目結構,在咱們建立完項目時,咱們的源代碼通常放在 src
文件夾下,而且打包後的文件通常放在 dist
文件下,同時 webpack
的配置文件命名爲 webpack.config.js
,而且放在項目的根目錄下。
├── dist 放置打包後的文件
├── src 放置項目源代碼
├── webpack.config.js webpack配置文件
├── package-lock.json
├── package.json
複製代碼
看完項目結構,咱們來了解一下 webpack
的配置文件具體須要怎麼配置。具體代碼以下:
//node的核心模塊
const path = require('path');
module.exports = {
//設置爲development時,代碼不會進行壓縮;設置爲production時,代碼會進行壓縮。
mode:'production',
// 放置入口文件,明確怎麼打包,要打包哪個文件
entry: './src/index.js',
//entry: {
// main: './src/index.js'
//},
// 輸出,代表webpack應該怎麼輸出,輸出到哪一個地方
output: {
filename: 'bundle.js',
// 指打包後的文件要放在哪一個文件下
// __dirname表示該項目的根目錄
path: path.resolve(__dirname, 'dist')
}
}
複製代碼
瞭解完基本配置,此時可能有小夥伴內心有一個疑惑, webpack
配置文件的命名必定要命名爲 webpack.config.js
嗎,是否能夠命名爲其餘的呢?
答案是確定能夠的,不過咱們須要進行一個特殊處理。日常若是咱們命名爲 webpack.config.js
時,能夠直接使用 npm webpack
來跑咱們的項目。若是不用這個命名時,假設命名爲 webpackconfig.js
,那麼咱們能夠經過如下命令行,來打包咱們具體的項目。
npx webpack --config webpackconfig.js
複製代碼
以上命令行的意思爲:指讓 webpack
來幫咱們打包,具體打包哪個文件呢?以 webpackconfig.js
爲配置文件,來幫咱們打包。
看完上面的內容,相信小夥伴們對 webpack
有了一個基礎的認識。
那試想如下,常常要使用 npx webpack
來幫咱們打包文件,這樣是否是會有點略顯麻煩呢?
因此,咱們來再瞭解一個內容:使用 npm script
來簡化咱們的打包代碼。
在咱們的項目根目錄下,會有一個 package.json
文件,這個文件的代碼以下:
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"private": true,
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Monday",
"license": "ISC",
"devDependencies": {
"webpack": "^5.39.1",
"webpack-cli": "^4.7.2"
}
}
複製代碼
你們定位到scripts部分,接下來,咱們把script部分進行一個改造。具體代碼以下:
"scripts": {
/* webpack 會先到node_module下面進行打包*/
"bundle": "webpack"
},
複製代碼
改造完成之後,咱們就至關於在 package.json
裏面配置了一個 npm script
,上面的代碼意思爲這個 script
對應的名字叫作 bundle
,以後呢, bundle
的底層會幫助咱們執行 webpack
,並進行打包。
這麼編寫以後,咱們就再也不須要使用 npx webpack
來作運行 webpack
,而是使用 npm run bundle
來作命令進行打包。
講解完上面的內容,咱們來對 webpack
打包之後控制檯的一些輸出內容進行概括總結。
輸出內容 | 具體含義 |
---|---|
hash | 表明本次打包對應的惟一哈希值 |
version | 表明這次打包使用的webpack的版本 |
time | 當前項目總體打包的具體耗時 |
assets | 打包後的文件具體是哪個,好比:bundle.js |
size | 打包後文件的大小 |
chunks | 放置本身對應文件的id值以及對應文件所引入其餘文件的id值 |
chunk Names | 對應entry配置的main |
Webpack
默認是知道如何打包 js
模塊的,可是它不知道 jpg
這種文件該怎麼打包。
這個時候咱們須要在 webpack.config.js
文件下作配置,配置 file-loader
。咱們在配置中增長一個新的 module
。具體代碼以下:
module:{
rules:[{
test:/\.jpg$/,
use:{
loader:'file-loader'
}
}]
}
複製代碼
接下來咱們來正向分析下 webpack
是如何打包 jpg
這種靜態文件的。
首先 webpack
會進入src
目錄, new
一個 index.js
文件,那麼如今咱們要對這個文件進行打包。
因此,我在命令行裏,運行了 npm run bundle
。當你運行 npm run bundle
時,實際上在執行的是 package.json
裏面的 script
,這個 script
幫咱們運行 webpack
,而後呢, webpack
幫咱們作打包。這個時候 webpack
就會去找它相對應的配置,根據這個配置幫咱們作打包。
那麼咱們來看一下,若是咱們遇到的是 js
文件,那麼 webpack
默認會進行打包。可是呢?若是遇到的是一張 jpg
的圖片呢? webpack
這個時候就懵了, webpack
並不認識 jpg
這種格式的代碼。
所以,咱們就能夠引用一個模塊module,來幫咱們打包。這個模塊叫 file-loader
,file-loader
這個 loader
就能夠幫助咱們完成打包的過程。
那麼實際上, file-loader
的底層,幫咱們作了什麼事情呢?
當咱們打包 jpg
文件時, webpack
會把 jpg
文件移動到 dist
文件下,而且對 jpg
文件賦予一個新的名稱。而後呢,它會把這個名稱做爲一個返回值,返回給咱們引入模塊的變量之中。
這就是 file-loader
底層處理打包文件的一個流程。
固然。 file-loader
不只僅能夠處理 jpg
這樣的文件圖片。理論上,它還能夠處理不少種類型的靜態資源。
闡述了 file-loader
以後,相信你們對 loader
有了一個基礎認識。那麼咱們如今就來對loader的基礎定義進行梳理。具體以下:
自己 webpack
對於一些文件是不知道如何處理的,可是 loader
知道。因此呢,當遇到一些 非js
文件時,通常去求助於 loader
就能夠了。所以咱們就須要讓 webpack
去求助 loader
模塊,來識別出 非js
的文件。
引用 webpack
官方中文文檔的一句話,文檔中說到: webpack
只能理解 JavaScript
和 JSON
文件,這是 webpack
開箱可用的自帶能力。loader 讓 webpack
可以去處理其餘類型的文件,並將它們轉換爲有效模塊,以供應用程序使用,以及被添加到依賴圖中。本質上, loader
是導出爲函數的 JavaScript
模塊。
若是咱們如今要對打包後的圖片進行自定義命名,那該怎麼作呢?
咱們在 webpack.config.js
文件下的 module
再進行改進。具體代碼以下:
module:{
rules:[{
test:/\.jpg$/,
use:{
loader:'file-loader',
options: {
//placeholder 佔位符
name: '[name]_[hash].[ext]'
}
}
}]
}
複製代碼
經過配置 options
,就能夠達到圖片自定義命名的效果。
假設咱們如今不侷限於打包 jpg
文件,還想要打包其餘的圖片文件。那麼咱們能夠這樣配置:
module:{
rules:[{
test:/\.(jpg|png|gif)$/,
use:{
loader:'file-loader',
options: {
//placeholder 佔位符
name: '[name]_[hash].[ext]'
}
}
}]
}
複製代碼
經過正則表達式的方式,來增長新的圖片類型。
假設咱們如今想要把打包後的文件放到 image
文件下,那該怎麼作呢?具體代碼以下:
module:{
rules:[{
test:/\.(jpg|png|gif)$/,
use:{
loader:'file-loader',
options: {
//placeholder 佔位符
name: '[name]_[hash].[ext]',
//將jpg、png和gif圖片文件,指定到dist目錄下的images文件下
outputPath: 'images/'
}
}
}]
}
複製代碼
在使用了 file-loader
以後,咱們再來了解一個知識點: url-loader
。 url-loader
能夠達到幾近 file-loader
的效果。具體使用方法以下:
npm i url-loader -D
複製代碼
module:{
rules:[{
test:/\.(jpg|png|gif)$/,
use:{
loader:'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/'
}
}
}]
}
複製代碼
咱們只要安裝上 url-loader
,而且在配置中將 file-loader
替換爲 url-loader
便可。
但值得注意的是,使用 url-loader
進行打包,會有一些須要注意的事項。
當你去打包一個 jpg
文件的時候,與 file-loader
不同的是, url-loader
會將圖片轉換成一個 base64
的字符串,而後直接放到咱們 dist
目錄下的 bundle.js
裏面,而不是單獨生成一個圖片文件。
好處就是,直接訪問,而不用去文件夾下訪問,節省了一次 http
請求。
而帶來的壞處就是,若是這個 js
文件特別大,那麼打包生成的js文件也將會特別大,隨之加載這個 js
的時間就會很長。
因此, url-loader
的最佳使用方式是什麼呢?
若是說咱們引入的圖片很小,只有 1~2KB
等小的體積,那麼這個圖片以 base64
的形式打包到 js
文件裏,是一個很好的選擇,就沒有必要讓這麼小的圖片再去發一次 http
請求。
但假設這個圖片很大的話,那麼儘可能不要使用 url-loader
,而使用 file-loader
,把這個圖片打包到dist目錄下,不要打包到 bundle.js
裏面。否則會使得 bundle.js
文件變得很大,使得加載時間變得很長,不利於維護。
還有一種方法就是,咱們能夠直接在 url-loader
下再加一個配置: limit
。具體代碼以下:
module:{
rules:[{
test:/\.(jpg|png|gif)$/,
use:{
loader:'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
//20480=>20KB
limit: 20480
}
}
}]
}
複製代碼
經過以上代碼咱們能夠知道,當使用 url-loader
時,咱們能夠給其加一個 limit
屬性。那麼上面代碼所要表達的意思就是,當圖片文件大小大於 20KB
時,咱們使用 url-loader
打包。當大於 20KB
時,就將圖片文件放到 dist
的 images
文件下。
好比說,咱們如今想要讓一張圖片的大小爲 150*150
,那麼咱們就須要寫下樣式來修改這張圖片。那 webpack
如何打包 css
文件呢?
咱們可使用 css-loader
和 style-loader
來對文件進行打包。具體配置以下:
npm install sass-loader node-sass --save-dev
複製代碼
module:{
rules:[{
test:/\.(jpg|png|gif)$/,
use:{
loader:'file-loader',
options: {
//placeholder 佔位符
name: '[name]_[hash].[ext]',
outputPath: 'images/'
}
}
},{
test:/\.css$/,
use:['style-loader', 'css-loader']
}]
}
複製代碼
css-loader
會幫咱們分析出,幾個 css
文件之間的關係,最終把一個 css
文件,合併成一個 css
。那 style-loader
的做用又是是什麼呢?
在獲得了 css-loader
生成的 css
文件以後, style-loader
會把這段內容掛載到頁面的 head
部分,並將樣式掛載到 head
中的 <style></style>
裏面。
若是是要打包 sass
文件呢,則使用 sass-loader
、 style-loader
和 css-loader
這三個 loader
來對文件進行打包。具體配置以下:
npm install sass-loader node-sass --save-dev
複製代碼
module:{
rules:[{
test:/\.(jpg|png|gif)$/,
use:{
loader:'file-loader',
options: {
//placeholder 佔位符
name: '[name]_[hash].[ext]',
outputPath: 'images/'
}
}
},{
test:/\.scss$/,
use:[
'style-loader',
'css-loader',
'sass-loader'
]
}]
}
複製代碼
值得注意的是, loader
的執行順序是從下到上,從右到左。
因此當咱們去執行一個 sass
文件時,首先會執行 sass-loader
,以後執行 css-loader
,最後才執行 style-loader
。
有時候咱們若是想要兼容多個瀏覽器時,那麼咱們可能會在 css
文件裏面添加 -webkit-
等廠商的前綴。可是呢,要知道 webpack
是沒辦法識別這些前綴的。這個時候咱們瞭解一個新的 loader
,就是 postcss-loader
,這個loader能夠自動地幫咱們添加廠商前綴的信息。具體使用方式以下:
首先咱們先安裝 postcss-loader
這個庫,具體代碼以下:
npm i postcss-loader -D
複製代碼
安裝完成以後,咱們在項目根目錄下建立一個新的文件,名字叫 postcss.config.js
,以後對這個文件進行配置,代碼以下:
首先安裝 autoprefixer
:
npm install autoprefixer -D
複製代碼
安裝完成以後,如今咱們在 postcss.config.js
文件下來使用它,具體代碼以下:
module.exports = {
Plugin:[
require('autoprefixer')
]
}
複製代碼
接下來咱們在 webpack.config.js
文件下面,使用 postcss-loader
。具體代碼以下:
module:{
rules:[{
test:/\.(jpg|png|gif)$/,
use:{
loader:'file-loader',
options: {
//placeholder 佔位符
name: '[name]_[hash].[ext]',
outputPath: 'images/'
}
}
},{
test:/\.scss$/,
use:[
'style-loader',
'css-loader',
'sass-loader',
'postcss-loader'
]
}]
}
複製代碼
經過以上方式, webpack
就能夠在幫咱們打包靜態文件時,把對應的須要加入廠商前綴的樣式給添加上前綴。
假設咱們如今想要給某一個 loader
增長配置項,好比說咱們要給 css-loader
增長配置項,那麼咱們能夠對代碼進行以下處理。具體代碼以下:
module:{
rules:[{
test:/\.(jpg|png|gif)$/,
use:{
loader:'file-loader',
options: {
//placeholder 佔位符
name: '[name]_[hash].[ext]',
outputPath: 'images/'
}
}
},{
test:/\.scss$/,
use:[
'style-loader',
{
loader: 'css-loader',
options: {
//代表前面要先走sass-loader和postcss-loader
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}]
}
複製代碼
從以上代碼中咱們能夠看到,當咱們想要給 css-loader
增長配置項時,那麼再也不使用字符串的形式,咱們把字符串轉化成一個對象。在對象裏面,咱們填寫相應的 loader
和 options
配置,這樣就達到了咱們想要的需求。
有時候咱們在一個頁面上,若是全局引入 css
,可能會很容易致使樣式衝突問題。那這種狀況該怎麼處理呢?
爲此咱們能夠藉助 css-loader
中的 modules
來實現 css
的模塊化,旨在讓引入的 css
文件擁有它獨立的模塊。那具體該怎麼使用呢?
module:{
test:/\.scss$/,
use:[
'style-loader',
{
loader: 'css-loader',
options: {
//代表前面要先走sass-loader和postcss-loader
importLoaders: 2,
modules: true
}
},
'sass-loader',
'postcss-loader'
]
}]
}
複製代碼
從以上代碼中咱們能夠知道,經過 modules:true
語句,就能夠開啓 css
的模塊化打包。開啓以後,咱們就能夠在各類文件下引入。引入方式以下:
import style from './index.scss';
var img = new Image();
img.src = bug;
img.classList.add(style.bug);
複製代碼
從上述代碼中咱們能夠看到,開啓 module
後,就能夠爲所欲爲的引用該 css
中的內容啦!
有時候咱們在項目中有可能會遇到想要引入字體的問題,那 webpack
如何打包字體呢?具體代碼以下:
module:{
rules:[{
test: /\.(eot|ttf|svg)$/,
use: {
loader: 'file-loader',
}
}]
},
複製代碼
經過以上代碼咱們能夠知道,跟 webpack
相關的靜態文件格式爲 eot、ttf和svg
等格式,因此須要須要把這幾種類型引入。同時,跟其相關的 loader
爲 file-loader
。
學完關於如何打包靜態資源後,建議能夠再用官方文檔的相關部分來進行復習,具體連接戳~
在學習瞭如何使用 loader
來打包靜態文件以後,接下來咱們一塊兒來了解在 webpack
中,如何使用 plugins
讓打包更便捷。
咱們在打包項目時, webpack
老是會把打包後的內容放到 dist
目錄下。這個時候咱們可能還須要自行再去建立一個 index.html
來引入核心文件。那這樣會不會就顯得略有點麻煩了?
所以, webpack
給咱們提供了 plugin
插件來解決這個問題。
首先,咱們先來安裝 plugin
,具體命令行以下:
npm install html-webpack-plugin -D
複製代碼
接下來咱們將插件引入 webpack.config.js
當中,具體代碼以下:
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [new HtmlWebpackPlugin({
//代表要引用哪個模板
template: 'src/index.html'
})]
複製代碼
如今,咱們來梳理一下 htmlWebpackPlugin
如何幫咱們完成自動打包。
首先, htmlWebpackPlugin
會在打包結束後,自動生成一個 html
文件。以後呢,把打包生成的 js
文件自動引入到這個 html
文件中。
因此,從某種程度上來講就是, plugin
能夠在 webpack
運行到某個時刻的時候,自動地幫你作一些事情。即當咱們打包結束的這兒一時刻, plugin
會自動幫咱們建立 html
文件以供咱們直接使用。
有時候咱們有可能對 webpack.config.js
中的output所對應的filename進行修改,這就很容易致使在打包過程當中遇到多文件衝突。
那麼咱們想要實現的就是,在打包時,先清空原來的dist文件夾,而後再生成一個新的dist文件夾。如何處理呢?請看下方。
首先咱們先安裝依賴 clean-webpack-plugin
,具體命令行以下:
npm install clean-webpack-plugin -D
複製代碼
接下來咱們將插件引入 webpack.config.js
當中,具體代碼以下:
const CleanWebpackPlugin = require('clean-webpack-plugin');
plugins: [new HtmlWebpackPlugin({
//代表要引用哪個模板
template: 'src/index.html'
}),new CleanWebpackPlugin(['dist'])]
複製代碼
經過以上代碼,就能夠在咱們項目打包時,先刪除 dist
文件夾,以後再建立一個新的文件夾。
接下來咱們再來看 webpack
中的 entry
和 output
中幾個比較核心的配置。
module.exports = {
mode:'development',
// 放置入口文件,明確怎麼打包
entry:{
main: './src/index.js',
sub: './src/index.js'
},
plugins: [new HtmlWebpackPlugin({
//代表要引用哪個模板
template: 'src/index.html'
}),new CleanWebpackPlugin(['dist'])],
// 輸出,代表webpack應該怎麼輸出
output: {
//若是把資源放在cdn下,則引入cdn
publicPath: 'http://cdn.com.cn',
//當entry有多個入口文件時,用[]能夠輸出多個文件
filename: '[name].js',
// 指打包後的文件要放在哪一個文件下
path: path.resolve(__dirname, 'dist')
}
}
複製代碼
有時候,咱們在寫代碼時,總會莫名的出bug。看着控制檯那紅紅的報錯,內心總歸很不是滋味。同時,若是咱們沒有配置好 webpack
的話,那錯誤找起來簡直是很恐怖的。
好比,在開發模式下,咱們默認 webpack.config.js
像下面這樣配置,具體代碼以下:
module.exports = {
mode:'development',
devtool: 'none',
entry:{
//打包到dist目錄下的main.js
main: './src/index.js'
},
output: {
//用[]能夠生成多個文件
filename: '[name].js',
// 指打包後的文件要放在哪一個文件下
path: path.resolve(__dirname, 'dist')
}
}
複製代碼
而後呢,假設咱們如今代碼裏面錯把 console.log
寫成 consele.log
。那麼如今控制檯的打印效果以下:
你們能夠看到,此時的錯誤定位到打包後的 main.js
文件裏面的第96行。那試想一下,若是咱們的業務代碼特別多,報錯有可能就是在文件中的上前行了。
這樣的場景並非咱們想看到的。咱們想作的事情呢就是,但願 webpack
打包完成以後就把錯誤直接拋給咱們,並把其對應的具體文件地址顯示出來。也就是咱們出錯的那個代碼文件,而不是打包後的文件 main.js
。
所以, webpack
給咱們提供了 sourceMap
這個配置,來解決這個問題。
咱們如今把 devtool
這個配置,改爲 sourceMap
。具體代碼以下:
module.exports = {
mode:'development',
devtool: 'source-map',
entry:{
//打包到dist目錄下的main.js
main: './src/index.js'
},
output: {
//用[]能夠生成多個文件
filename: '[name].js',
// 指打包後的文件要放在哪一個文件下
path: path.resolve(__dirname, 'dist')
}
}
複製代碼
改完以後呢,咱們來看一下控制檯的打印結果:
如今你們能夠看到,改爲 source-map
的配置以後,報錯的定位直接到了咱們本身所編寫代碼的目錄下,即 index.js
。而再也不是大海撈針似的在 main.js
裏面找。
看完上面的例子以後,相信你們對 SourceMap
有了必定的瞭解。接下來咱們來看一下 sourceMap
的一些常見配置。具體看看下方:
SourceMap | 含義 |
---|---|
inline-source-map | 報錯時將行和列都顯示出來 |
cheap-inline-source-map | 報錯時只知道哪一行出錯了,不知道在哪一列 |
cheap-module-source-map | 生產環境最佳實踐,不只管本身的業務代碼錯誤,還要管其餘的其餘的錯誤,像loader、其餘第三方模塊的錯誤等等 |
eval | eval是打包速度最快的一種方式,但若是遇到業務代碼比較複雜的狀況下,用eval提示出來的效果可能不太全面 |
module-eval-source-map | 用module,代表不只要顯示業務錯誤,還要顯示loader、第三方錯誤等等 |
cheap-module-eval-source-map | 開發環境最佳實踐 |
事實上,若是咱們不採用 WebpackDevServer
的方式來開發的話,那麼咱們每一次想要查看編譯後的運行結果,都須要先命令行編譯 npm run bundle
命令,以後再打開 dist
目錄下的 index.html
文件才能從新查看。這樣一來二往的,不免效率低下。咱們期待的結果是什麼呢?
咱們把 package.json
文件裏的 script
進行一番改造,具體代碼以下:
"scripts": {
"watch": "webpack --watch",
"bundle": "webpack"
},
複製代碼
經過以上代碼你們能夠看到,將 webpack
後面加上 --watch
字段,而後運行 npm run watch
,就能夠每次修改完代碼後, webpack
實現自動監聽,而不用像以往那樣,每修改一次代碼都要對再從新運行命令來對 webpack
進行打包。
可是呢,這種方式可能還不夠友好,畢竟開發者老是懶惰的,能儘可能讓程序來幹活就不要用手工來幹活。
實際上咱們想要達到的效果是,當咱們運行完 npm run watch
這行命令的時候,不只能自動幫咱們實現打包,同時還能幫咱們打開控制檯,而且模擬一些服務器上的特性。那麼咱們就能夠經過 webpackDevServer
來實現咱們想要的效果。如何使用 webpckDevServer
呢?具體看下方。
咱們如今項目中安裝webpackDevServer,具體命令行以下:
npm install webpack-dev-server -D
複製代碼
接下來咱們來配置 package.json
文件的 script
。具體代碼以下:
"scripts": {
"watch": "webpack --watch",
"start": "webpack-dev-server"
},
複製代碼
接下面咱們來配置 webpack.config.js
文件,具體代碼以下:
module.exports = {
mode:'development',
devtool: 'source-map',
// 放置入口文件,明確怎麼打包
entry:{
main: './src/index.js'
},
devServer: {
contentBase: './dist',
// 當運行完npm run start時,會自動的幫咱們打開瀏覽器
open: true
},
output: {
//用[]能夠生成多個文件
filename: '[name].js',
// 指打包後的文件要放在哪一個文件下
path: path.resolve(__dirname, 'dist')
}
}
複製代碼
那麼如今,咱們來看下, webpackDevServer
如何作到自動打開瀏覽器。詳情見下圖:
你們能夠看到,經過 webpackDevServer
,它不但會監聽到咱們的文件發生了改變,從新幫咱們進行打包。同時它還會自動的幫咱們從新刷新瀏覽器,而且會自動地幫咱們打開瀏覽器。因此用它呢,能夠大大提高咱們的代碼開發效率。
webpackDevServer
默認咱們服務器的端口號是 8080
,若是咱們想要修改它爲其餘的端口號,該怎麼作呢?
咱們須要在來修改 webpack.config.js
文件下的 DevServer
,具體代碼以下:
module.exports = {
mode:'development',
devtool: 'source-map',
// 放置入口文件,明確怎麼打包
entry:{
main: './src/index.js'
},
devServer: {
contentBase: './dist',
// 當運行完npm run start時,會自動的幫咱們打開瀏覽器
open: true,
//修改端口號
port: 3000
},
output: {
//用[]能夠生成多個文件
filename: '[name].js',
// 指打包後的文件要放在哪一個文件下
path: path.resolve(__dirname, 'dist')
}
}
複製代碼
咱們只須要在 devServer
裏面加上一個 port
的配置,便可實現自定義端口號。
同時值得注意的是,當咱們在用 webpackDevServer
幫咱們項目作打包時,它不會自動生成 dist
目錄,那這是爲何呢?用 webpackDevServer
打包後的項目會放在咱們的電腦內存中,這在某種程度下能夠有效的提高項目的打包速度,讓打包變得更快。
假設咱們如今要實現一個新增元素的功能,這個功能所要達到的效果是每點擊一次按鈕,就新添加一次文本 item
。具體實現代碼以下:
index.js文件:
import './style.css';
var btn = document.createElement('button');
btn.innerHTML = '新增';
document.body.appendChild(btn);
btn.onclick = function(){
var div = document.createElement('div');
div.innerHTML = 'item';
document.body.appendChild(div);
}
複製代碼
style.css文件:
div:nth-of-type(odd){
background: yellow;
}
複製代碼
此時瀏覽器的顯示效果以下圖所示:
假設咱們如今來給css的背景改個顏色,好比說改爲紫色。具體代碼以下:
div:nth-of-type(odd){
background: purple;
}
複製代碼
此時咱們保存後瀏覽器會從新進行刷新,以後每個 item
又要從新 append
進來。以下圖:
那這種狀況下可能就不是咱們想要的結果了。咱們但願的是,全部的 item
不進行從新刷新,而且當 css
樣式改變的時候,對應的 item
顏色也能夠獲得改變。那麼這就要引出 webpackDevServer
中的一個內容:熱模塊更新 Hot Module Replacement
。接下來咱們來了解熱模塊更新相關的配置。
熱模塊更新,即Hot Module Replacement,簡稱爲 HMR
。
接下來咱們在 webpack.config.js
文件夾下進行配置。具體代碼以下:
//node的核心模塊
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
mode:'development',
devtool: 'source-map',
// 放置入口文件,明確怎麼打包
entry:{
main: './src/index.js'
},
devServer: {
contentBase: './dist',
// 當運行完npm run start時,會自動的幫咱們打開瀏覽器
open: true,
port: 8080,
// 讓咱們的webpackDevServer開啓hotModuleReplacement這樣子的功能
hot: true,
// 即使HMR沒有生效,也不讓瀏覽器自動刷新
hotOnly: true
},
module:{
rules:[{
test:/\.(jpg|png|gif)$/,
use:{
loader:'file-loader',
options: {
//placeholder 佔位符
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 10240
}
}
},{
test:/\.scss$/,
use:[
'style-loader',
{
loader: 'css-loader',
options: {
//代表前面要先走sass-loader和postcss-loader
importLoaders: 2,
modules: true
}
},
'sass-loader',
'postcss-loader'
]
},{
test:/\.css$/,
use:[
'style-loader',
'css-loader',
'postcss-loader'
]
}]
},
plugins: [new HtmlWebpackPlugin({
//代表要引用哪個模板
template: 'src/index.html'
}),new CleanWebpackPlugin(['dist']),
new webpack.HotModuleReplacementPlugin()
],
// 輸出,代表webpack應該怎麼輸出
output: {
// 下載middle:npm install express webpack-dev-middleware -D
publicPath: '/',
//用[]能夠生成多個文件
filename: '[name].js',
// 指打包後的文件要放在哪一個文件下
path: path.resolve(__dirname, 'dist')
}
}
複製代碼
經過以上代碼咱們能夠知道,配置 devServer
下的 hot
和 hotOnly
,以及 plugins
下的 new webpack.HotModuleReplacementPlugin()
,來達到熱模塊更新的效果。
接下來咱們來看一下,進行配置以後,瀏覽器的效果。詳情見下圖:
你們能夠看到,加上這幾個配置以後, item
不會再從新刷新了,而是在原來的基礎上進行樣式修改。
繼續,接下來咱們來了解,如何使用webpack和babel,來編寫ES6的語法。
你們都知道,ES6的語法規範是2015年才正式出版的。因此有時候,並非全部的瀏覽器都支持ES6語法。所以,咱們如今想要作的事情就是,在webpack打包時,可以將ES6的語法轉換爲ES5的語法。這樣,項目運行的時候,瀏覽器就不會報錯了。
那怎麼實現這樣子的打包呢?接下來咱們一塊兒來了解一下。
首先咱們打開babel的官方網站,按照步驟,咱們一步步在 webpack
中使用 babel
。
第一步: 安裝 babel-loader
和 @babel/core
這兩個庫。具體代碼以下:
npm install --save-dev babel-loader @babel/core
複製代碼
babel-loader
是幫助webpack進行打包使用的一個工具,而 @babel/core
則是 babel
的一個核心庫,它可以讓 babel
去識別 js
代碼裏的內容,而後呢把 js
代碼轉換成 AST
抽象語法樹,以後再把抽象語法樹編譯成一些新的語法。
第二步: 在 webpack.config.js
文件下的配置項裏增設規則。具體代碼以下:
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
複製代碼
第三步: 安裝 @babel/preset-env
。具體代碼以下:
npm install @babel/preset-env --save-dev
複製代碼
爲何要安裝這個模塊呢?實際上,當咱們使用 babel-loader
處理文件時,實際上 babel-loader
只是 webpack
和 babel
之間作通訊的一個橋樑,它只是幫咱們打開了一個通道,可是它並不會幫咱們把 ES6
的語法轉換爲 ES5
的語法。因此,咱們就還須要藉助一些其餘的模塊,來作這項工做。這個模塊就是前面咱們說的,preset-env 。
babel/preset-env
,包含了全部 ES6
轉換爲 ES5
的語法規則,當使用此模塊打包時,就能夠把咱們全部 js
中 ES6
的代碼轉換爲 ES5
了。具體配置方式見以上第二步。
經過以上的方式,咱們能夠達到將ES6語法轉換爲ES5語法的效果。可是呢,咱們還要考慮到的一個問題就是,若是遇到像promise這一類新的語法變量,或者時像數組裏面map這一類的函數,低版本的瀏覽器裏面,實際上仍是不存在的。雖然咱們作了語法解釋和語法翻譯,但也只是翻譯了一部分。還有一些對象和函數,在低版本的瀏覽器仍是沒有的。
因此呢,這個時候咱們不只要使用 babel/preset-env
作語法轉換,還要把這些缺失的變量和函數補充到低版本的瀏覽器裏面。
那怎麼補充呢,這個時候咱們就須要藉助 babel-polyfill
這個工具來進行補充。接下來說解這個模塊的使用操做。
第一步: 定位到官方文檔,安裝 babel-polyfill
。具體代碼以下:
npm install --save @babel/polyfill
複製代碼
第二步: 引入該模塊。具體代碼以下:
import "@babel/polyfill";
複製代碼
一般狀況下,這段代碼放到項目的 js
入口文件下。
第三步: 改造 webpack.cofig.js
文件下的 module
,減小打包大小。具體代碼以下:
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [['@babel/preset-env'],{
useBuiltIns: 'usage'
}]
}
}
}
]
}
複製代碼
這段代碼的意思就是,當用 babel-polyfill
填充低版本瀏覽器特性的時候,不是把是多有的特性都加進來,而是根據咱們的業務代碼來決定到底到加什麼。
同時, babel-preset
也有不少其餘值得學習的配置屬性,這裏再也不進行講解。你們能夠自行到官方文檔上進行查看~
寫完這篇文章的時候,忽然想起上次面試時的面試官。在最後的反問環節問他關於 webpack
的問題,他說 webpack
通常會讓對公司業務很熟悉的員工來處理,畢竟前端工程化不是兒戲。
當時我尚未很大的感觸,但如今學到這裏忽然就想到了那個場景。確實是這樣,我這才學了不到它的冰山一角,就已經感到 webpack
的龐大工程了。若是在打包時候,但凡是有一個小地方的配置出現問題,就有可能引起整個項目的不可收拾局面。(固然通常狀況下不會出現這樣的狀況,言重了……)
在學習 webpack
的過程當中,要明確好本身所使用的webpack版本。好比周一剛開始迷迷糊糊的,感受版本4和版本5都差很少。但對於 webpack
來講,這徹底就是在跟它開玩笑。每個依賴都有4和5對應的版本,而不是說想用哪一個就哪一個。若是胡亂使用的話,無形之中可能會報錯到懷疑人生……
所以,肯定好此時用 webpack
打包時所使用的版本,並在使用 npm
依賴時也一樣要找到對應的版原本進行使用,下降錯誤的發生。
到這裏,關於webpack的超入門知識就講到這裏啦!但願對你們有幫助~
本系列文章代碼已上傳至公衆號,後臺回覆關鍵詞 webpack
便可獲取~
- 關注公衆號星期一研究室,第一時間關注學習乾貨,更多精選專欄待你解鎖~
- 若是這篇文章對你有用,記得留個腳印jio再走哦~
- 以上就是本文的所有內容!咱們下期見!👋👋👋