webpack

目錄

1.webpack是什麼?

2.webpack安裝

3.webpack使用

4.webpack打包模塊

4.1入口文件

4.2打包命令

 5.打包配置webpack.config.js

6.核心配置

6.1mode——打包模式

6.2entry——入口文件形式

 6.3output——打包後文件存放位置

7.深入——執行簡要流程

8.Loaders

8.1raw-loader

8.2file-loader

8.3url-loader

 8.4css-loader

8.5style-loader

8.6sass-loader

9.Plugins

9.1HtmlWebpackPlugin

9.2clean-webpack-plugin

 9.3mini-css-extract-plugin

9.4sourceMap

10.WebpackDevServer

11.Proxy

12.Hot Module Replacement


1.webpack是什麼?

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

2.webpack安裝

webpack 是一個使用 Node.js 實現的一個模塊化代碼打包工具。所以,我們需要先安裝 webpack,安裝之前需要搭建好 Node.js 環境

npm install -D webpack webpack-cli

注:不推薦全局安裝

  • webpack-cli : 提供 webpack 命令、工具,類似 create-react-app
  • webpack : webpack 代碼,類似 react

3.webpack使用

注意:在window下查看命令需要使用反斜槓,否則會報錯,不是內部或外部命令。

.\node_modules\.bin\webpack

// 查看版本
.\node_modules\.bin\webpack -v

也可以編輯 package.jsonscripts 來簡化輸入 :

scripts 中可以自動定位到 ./node_modules/.bin/ 目錄下;且只能匹配teststartrestartstop,不能自定義其他命令

// package.json
{
	...,
	"scripts": {
		"start": "webpack"	// scripts 中可以定位到 ./node_modules/.bin/ 目錄下
        // "start": "webpack -v"
	}
}

 scripts 中使用 teststartrestartstop 命名的時候,可以在調用的時候省略 run,即直接 npm start

當然,還可以使用更加方便的方式:

npx webpack
npx webpack -v

通過 npx 也可以幫助我們直接定位命令到 ./node_modules/.bin/ 目錄下 :

注:npm5.2+ 增加,如果沒有,可以使用 npm i -g npx 來安裝。 

4.webpack打包模塊

打包之前,我們需要了解一個概念,入口文件 。

4.1入口文件

入口文件就是我們項目中加載的第一個文件,比如上面的 main.js 文件,其它文件都是通過 import 等方式引入的,webpack 會從我們指定的入口文件開始分析所有需要依賴的文件,然後把打包成一個完整文件。

4.2打包命令

webpack ./js/index.js

 上面命令會使用 webpack 默認的一些配置對模塊文件進行打包,並把打包後的文件輸出到默認創建的 ./dist 目錄下,打包後的文件名稱默認爲 main.js

模塊文件打包以後,就可以在不支持 es6 模塊語法的瀏覽器環境下引入使用了。

打包文件分析

  • 把分散的模塊文件打包到一個文件中,不需要外部引入了

  • 內置了一個小型模塊加載器(類似 requireJS),實現了打包後的代碼隔離與引用

以上就是 webpack 最基礎的使用於基本原理,當然強大的 webpack 遠遠不止這些功能。

示例:

  • 直接在package.json文件中設置入口文件;
  • 或者直接使用命令.\node_modules\.bin\webpack入口文件;

使用webpack對模塊進行打包,通常會對源代碼的目錄進行分類,如src。

項目路徑:

index.html:

<body>
    <script type="module" src="js/main.js"></script>
</body>

 m1.js:

let a = 100;
export default a;

mian.js:

import a from './m1.js';
console.log(a);

 執行命令:npm start ./js/main.js後報錯

報錯原因:沒有指定打包的模式

在package.json文件中修改入口文件及模式:

"start": "webpack src/js/main.js --mode=development"

再次執行命令後:在項目路徑下自動生成了dist文件夾,且生成了打包後的main.js文件

並且將index.html中js文件,更改爲打包生成後的main.js文件:在瀏覽器中打開頁面打印正確值a爲100。

<body>
    <script type="module" src="../dist/main.js"></script>
</body>

 5.打包配置webpack.config.js

雖然,我們可以直接通過命令的來打包,但是推薦創建一個 webpack.config.js 的配置文件來實現更方便和強大的功能。

webpack 命令在運行的時候,默認會讀取運行命令所在的目錄下的 webpack.config.js 文件,通常我們會在項目的根目錄下運行命令和創建配置文件。

我們也可以通過 —config 選項來指定配置文件路徑:

webpack --config ./configs/my_webpack.config.js

通常情況下,我們的項目目錄大致如下:

/
-- /dist - 項目打包後存放目錄
-- /node_modules - 第三方模塊
-- /src
------ css/
------ images/
------ js/
------ index.js
-- webpack.config.js
-- package.json

 配置文件webpack.config.js

module.exports = {
  ...	//配置項
}

6.核心配置

6.1mode——打包模式

模式 : "production" | "development" | "none" 。分別表示用於生產環境(打包文件會進行壓縮),開發環境,不指定。

module.exports = {
  mode: 'production'
}

6.2entry——入口文件形式

指定打包的入口文件,有三種不同的形式:string | object | array

  • 一對一:一個入口文件、一個打包文件 
module.exports = {
  entry: './src/index.js'
}
  • 多對一(數組方式):多個入口文件、一個打包文件
module.exports = {
  entry: [
    './src/index1.js',
    './src/index2.js',
  ]
}
  • 多對多(對象方式):多個入口、多打包文件  
module.exports = {
  entry: {
    'index1': "./src/index1.js",
    'index2': "./src/index2.js"
  }
}

 6.3output——打包後文件存放位置

打包後的文件位置 :

module.exports = {
  ...,
  output: {
  	path: path.resolve(__dirname, "dist"),
        filename: "bundle.js",
        //多入口多出口(`entry` 爲對象)
	// filename: "[name].js"
  }
}
  •  可以指定一個固定的文件名稱,如果是多入口多出口(entry 爲對象),則不能使用單文件出口,需要使用下面的方式
  • 通過 webpack 內置的變量佔位符:[name]

7.深入——執行簡要流程

webpack 中,有一個很重要的特性:模塊不僅僅只是 js 的文件,webpack 可以把任意文件數據作爲模塊進行處理,包括:非 js 文本、css、圖片等等 。

import txt from './a.txt';
console.log(txt);

但是 webpack 默認情況下只能處理 js 模塊,如果需要處理其它類型的模塊,則需要使用它提供的一些其它功能 。

執行簡要流程:

  • loaderswebpack 中非常核心的內容之一,前面我們說的非 js 類型的模塊處理就靠它了,不同類型的模塊的解析就是依賴不同的 loader 來實現的
  • pluginswebpack 中另外一個核心的內容,它主要是擴展 webpack 本身的一些功能,它們會運行在各種模塊解析完成以後的打包編譯階段,比如對解析後的模塊文件進行壓縮等。

8.Loaders

https://webpack.js.org/loaders/

module.exports = {
  ...,
  module: {
  	rules:[
  		{
  		    test:/\.xxx$/,
       	            use:{
        	           loader: 'xxx-load'
      	            }
		}
  	]
  }
}

 當 webpack 碰到不識別的模塊的時候,webpack 會在配置的 module 中進行該文件解析規則的查找:

  • rules 就是我們爲不同類型的文件定義的解析規則對應的 loader,它是一個數組
  • 每一種類型規則通過 test 選項來定義,通過正則進行匹配,通常我們會通過正則的方式來匹配文件後綴類型。如/\.css$/。
  • use 針對匹配到文件類型,調用對應的 loader 進行處理

從一個簡單的案例來了解 loader :

src/datas/data.txt :

我是 txt 的內容

src/datas/data.md :

# 我是 md 的內容

 src/raw-loader.js:

import txtData from './datas/data.txt';
import mdData from './datas/data.md';

console.log('txtData: ', txtData);
console.log('mdData: ', mdData);

默認情況下,webpack 會報錯,因爲 webpack 處理不了 txt 和 md 這樣的非 js 的模塊,但是我們可以通過不同的loader專門來處理純文本內容(不同的 loader 有不同的作用) 。 

8.1raw-loader

在 webpack 中通過 import 方式導入文件內容,loader 並不是 webpack 內置的,所以首先要安裝 :

npm install --save-dev raw-loader

然後在 webpack.config.js 中進行配置 :

module.exports = {
  ...,
  module: {
      rules: [
      {
        test: /\.(txt|md)$/,
        use: 'raw-loader'
    	}
    ]
	}
}

8.2file-loader

把識別出的資源模塊,移動到指定的輸出目錄,並且返回這個資源在輸出目錄的地址(字符串) 。

npm install --save-dev file-loader
rules: [
  ...,
	{
		test: /\.(png|jpe?g|gif)$/,
    use: {
      loader: "file-loader",
      options: {
        // placeholder 佔位符 [name] 源資源模塊的名稱
        // [ext] 源資源模塊的後綴
        name: "[name]_[hash].[ext]",
        //打包後的存放位置
        outputPath: "./images"
        // 打包後文件的 url
        publicPath: './images',
      }
    }
	}
]

 佔位符:https://webpack.js.org/loaders/file-loader#placeholders

8.3url-loader

可以處理理 file-loader 所有的事情,但是遇到圖片格式的模塊,可以選擇性的把圖片轉成 base64 格式的字符串,並打包到 js 中,對小體積的圖片比較合適,大圖不合適。

npm install --save-dev url-loader
rules: [
  ...,
	{
		test: /\.(png|jpe?g|gif)$/,
    use: {
      loader: "url-loader",
      options: {
        // placeholder 佔位符 [name] 源資源模塊的名稱
        // [ext] 源資源模塊的後綴
        name: "[name]_[hash].[ext]",
        //打包後的存放位置
        outputPath: "./images"
        // 打包後文件的 url
        publicPath: './images',
        // 小於 100 字節轉成 base64 格式
        limit: 100
      }
    }
	}
]

 8.4css-loader

分析 css 模塊之間的關係,併合成一個 css 。

npm install --save-dev css-loader
rules: [
  ...,
	{
		test: /\.css$/,
    use: {
      loader: "css-loader",
      options: {
  			// 啓用/禁用 url() 處理
  			url: true,
  			// 啓用/禁用 @import 處理
  			import: true,
        // 啓用/禁用 Sourcemap
        sourceMap: false
      }
    }
	}
]

8.5style-loader

css-loader 生成的內容,用 style 標籤掛載到 head

npm install --save-dev style-loader
rules: [
  ...,
	{
		test: /\.css$/,
    use: ["style-loader", "css-loader"]
	}
]

 同一個任務的 loader 可以同時掛載多個,處理順序爲:從右到左,也就是先通過 css-loader 處理,然後把處理後的 css 字符串交給 style-loader 進行處理。

rules: [
  ...,
	{
		test: /\.css$/,
    use: [
  		{
  			loader: 'style-loader',
  			options: {}
  		},
      'css-loader'
		]
	}
]

8.6sass-loader

sass 語法轉換成 css ,依賴 node-sass 模塊 。

npm install --save-dev sass-loader node-sass

9.Plugins

擴展 webpack 本身的一些功能,它們會運行在各種模塊解析完成以後的打包編譯階段,比如對解析後的模塊文件進行壓縮等 。

9.1HtmlWebpackPlugin

在打包結束後,自動生成一個 html 文件,並把打包生成的 js 模塊引入到該 html 中 。

npm install --save-dev html-webpack-plugin
// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
	...
  plugins: [
     new HtmlWebpackPlugin({
       title: "My App",
       filename: "app.html",
       template: "./src/html/index.html"
     }) 
  ]
};

html 模板中,可以通過 <%=htmlWebpackPlugin.options.XXX%> 的方式獲取配置的值 : 

<!--./src/html/index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%=htmlWebpackPlugin.options.title%></title>
</head>
<body>
    <h1>html-webpack-plugin</h1>
</body>
</html>

 更多的配置

  • title: 用來生成裏面的 title 元素
  • filename: 輸出的 HTML 文件名,默認是 index.html, 也可以直接配置子目錄
  • template: 模板文件路徑,支持加載器(loader),例如 html!./index.html
  • inject: true | 'head' | 'body' | false,注入所有的資源到特定的 template 或者 templateContent 中,如果設置爲 true 或者 body,所有的 javascript 資源將被放置到 body 元素的底部,'head' 將放置到 head 元素中
  • favicon: 添加特定的 favicon 路徑到輸出的 HTML 文件中
  • minify: {} | false, 傳遞 html-minifier 選項給 minify 輸出
  • hash: true | false,如果爲 true,將添加 webpack 編譯生成的 hash 到所有包含的腳本和 CSS 文件,對於解除 cache 很有用
  • cache: true | false,如果爲 true,這是默認值,僅在文件修改之後纔會發佈文件
  • showErrors: true | false,如果爲 true,這是默認值,錯誤信息會寫入到 HTML 裏面
  • chunks: 允許只添加某些塊 (例如,僅 unit test 塊)
  • chunksSortMode: 允許控制塊在添加到裏面之前的排序方式,支持的值:'none' | 'default' |{function}-default:'auto'
  • excludeChunks: 允許跳過某些塊,(例如,跳過單元測試的塊)

9.2clean-webpack-plugin

刪除(清理)構建目錄

npm install --save-dev clean-webpack-plugin
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
	...
  plugins: [
    ...,
    new CleanWebpackPlugin(),
    ...
  ]
}

 9.3mini-css-extract-plugin

提取 CSS 到一個單獨的文件中

npm install --save-dev mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
	...,
  module: {
  	rules: [
  		{
  			test: /\.s[ac]ss$/,
  			use: [
  				{
  					loader: MiniCssExtractPlugin.loader
					},
          'style-loader',
          'css-loader',
          'sass-loader'
        ]
			}
  	]
	},
  plugins: [
    ...,
    new MiniCssExtractPlugin({
    	filename: '[name].css'
    }),
    ...
  ]
}

 

9.4sourceMap

我們實際運行在瀏覽器的代碼是通過 webpack 打包合併甚至是壓縮混淆過的代碼,所生成的代碼並不利於我們的調試和錯誤定位,我們可以通過 sourceMap 來解決這個問題,sourceMap 本質是一個記錄了編譯後代碼與源代碼的映射關係的文件,我們可以通過 webpackdevtool 選項來開啓 sourceMap

module.exports = {
  devtool: 'source-map',
  ...
}

首先,編譯後會爲每一個編譯文件生成一個對應的 .map 文件,同時在編譯文件中添加一段對應的 map 文件引入代碼 。 

...
//# sourceMappingURL=xx.js.map
...
/*# sourceMappingURL=xx.css.map*/

 同時,現代瀏覽器都能夠識別 sourceMap 文件,如 chrome,會在 Sources 面板中顯示根據編譯文件與對應的 map 文件定位到源文件中,有利於我們的調試和錯誤定位。

10.WebpackDevServer

每次的代碼修改都需要重新編譯打包,刷新瀏覽器,特別麻煩,我們可以通過安裝 webpackDevServer 來改善這方面的體驗 。

npm install --save-dev webpack-dev-server

啓動命令:

npx webpack-dev-server

 或者,package.json 中添加 scripts

...,
"scripts": {
  "server": "webpack-dev-server"
}

修改 webpack.config.js

module.exports = {
  ...,
  devServer: {
  	// 生成的虛擬目錄路徑
  	contentBase: "./dist",
  	// 自動開啓瀏覽器
  	open: true,
  	// 端口
  	port: 8081
	}
}

 啓動服務以後,webpack 不在會把打包後的文件生成到硬盤真實目錄中了,而是直接存在了內存中(同時虛擬了一個存放目錄路徑),後期更新編譯打包和訪問速度大大提升。

11.Proxy

當下前端的開發都是前後端分離開發的,前端開發過程中代碼會運行在一個服務器環境下(如當前的 WebpackDevServer),那麼在處理一些後端請求的時候通常會出現跨域的問題。WebpackDevServer 內置了一個代理服務,通過內置代理就可以把我們的跨域請求轉發目標服務器上(WebpackDevServer 內置的代理髮送的請求屬於後端 - node,不受同源策略限制),具體如下:

後端代碼,以 node 爲例 :

const Koa = require('koa');
const KoaRouter = require('koa-router');

const app = new Koa();
const router = new KoaRouter();

router.get('/api/info', async ctx => {
    ctx.body = {
        username: 'zMouse',
        gender: 'male'
    }
})

app.use( router.routes() );
app.listen(8787);

 前端代碼:

axios({
  url: 'http://localhost:8787/api/info'
}).then(res => {
  console.log('res',res.data);
})

默認情況下,該代碼運行以後會出現跨域請求錯誤,修改 webpack 配置 :

module.exports = {
  ...,
  devServer: {
  	// 生成的虛擬目錄路徑
  	contentBase: "./dist",
  	// 自動開啓瀏覽器
  	open: true,
  	// 端口
  	port: 8081,
  	proxy: {
      '/api': {
      	target: 'http://localhost:8787'
    	}
    }
	}
}

 通過 proxy 設置,當我們在當前 WebpackDevServer 環境下發送以 /api 開頭的請求都會被轉發到 http://localhost:8787 目標服務器下。

修改前端代碼 :

axios({
  //url: 'http://locahost:8081/api/info',
  url: '/api/info'
}).then(res => {
  console.log('res',res.data);
})

注意 url 地址要填寫 WebpackDevServer 域,比如當前 WebpackDevServer 開啓的是 http://localhost:8081,也就是我們當前前端代碼運行的環境,那麼請求的 url 也必須發送到這裏,當我們的請求滿足了 proxy 中設置的 /api 開頭,那麼就會把請求轉發到 target ,所以最後的實際請求是:http://lcoahost:8787/api/info 。 

12.Hot Module Replacement

在之前當代碼有變化,我們使用的 live reload,也就是刷新整個頁面,雖然這樣爲我們省掉了很多手動刷新頁面的麻煩,但是這樣即使只是修改了很小的內容,也會刷新整個頁面,無法保持頁面操作狀態。HMR 隨之就出現了,它的核心的局部(模塊)更新,也就是不刷新頁面,只更新變化的部分 。

module.exports = {
  ...,
  devServer: {
  	// 生成的虛擬目錄路徑
  	contentBase: "./dist",
  	// 自動開啓瀏覽器
  	open: true,
  	// 端口
  	port: 8081,
  	// 開啓熱更新
  	hot:true,
  	// 即使 HMR 不生效,也不去刷新整個頁面(選擇開啓)
    hotOnly:true,
  	proxy: {
      '/api': {
      	target: 'http://localhost:8787'
    	}
    }
	}
}

開啓 HMR 以後,當代碼發生變化,webpack 即會進行編譯,並通過 websocket 通知客戶端(瀏覽器),我們需要監聽處理來自 webpack 的通知,然後通過 HMR 提供的 API 來完成我們的局部更新邏輯 。

./fn1.js :

export default function() {
    console.log('start1!');
}

index.js :

import fn1 from './fn1.js';
box1.onclick = fn1;

if (module.hot) {//如果開啓 HMR
    module.hot.accept('./fn1.js', function() {
      // 更新邏輯
      box1.onclick = fn1;
    })
}

上面代碼就是 當 ./fn1.js 模塊代碼發生變化的時候,把最新的 fn1 函數綁定到 box1.onclick 上

從上面就可以看到,HMR 其實就是以模塊爲單位,當模塊代碼發生修改的時候,通知客戶端進行對應的更新,而客戶端則根據具體的模塊來更新我們的頁面邏輯(這些邏輯需要自己去實現),好在當前一些常用的更新邏輯都有了現成的插件。

css熱更新

樣式熱更新比較簡單,style-loader 中就已經集成實現了,我們只需要在 use 中使用就可以了

react 熱更新

vue 熱更新

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