1. Entry
入口(Entry)指示webpack 以哪個檔案為入口起點開始打包,分析建構內部相依圖。
2. Output
輸出(Output)指示webpack 打包後的資源bundles 輸出到哪裡去,以及如何命名。
3. Loader
webpack 只能理解JavaScript 和JSON 文件,這是webpack 開箱可用的自帶能力。 loader 讓webpack 能夠去處理其他類型的文件,並將它們轉換為有效模組,以供應用程式使用,以及被添加到依賴圖中。
4. Plugins
插件(Plugins)可以用來執行範圍更廣的任務。插件的範圍包括,從打包最佳化和壓縮,一直到重新定義環境中的變數等。
5. Mode
模式(Mode)指示webpack 使用對應模式的配置。
以下就來跟大家詳細介紹一下webpack這五個核心概念。
entry 物件是用於webpack 查找啟動並建構bundle。 entry 是應用程式的起點入口,從這個起點開始,應用程式啟動執行。如果傳遞一個數組,那麼數組的每一項都會執行。入口起點(entry point) 指示webpack 應該使用哪個模組,來作為建立其內部依賴圖(dependency graph) 的開始。進入入口起點後,webpack 會找出有哪些模組和函式庫是入口起點(直接和間接)依賴的。
簡單規則:每個HTML 頁面都有一個入口起點。單頁應用(SPA):一個入口起點,多頁應用(MPA):多個入口起點。
預設值是./src/index.js
,但你可以透過在webpack configuration 中配置entry
屬性,來指定一個(或多個)不同的入口起點。例如:
//單入口--字串module.exports = { entry: './path/to/my/entry/file.js', }; //多入口--數組module.exports = { entry: ['./src/index.js', './src/add.js'] }; //多入口--物件module.exports = { entry: { home: './home.js', about: './about.js', contact: './contact.js' } };
entry的值類型:
字串:單一入口,打包形成一個chunk,最終只會輸出一個bundle文件,chunk 的名稱預設是main
陣列:多入口,所有的入口文件最終也只會形成一個chunk,最終輸出一個bundle 文件,chunk 的名稱預設為main。一般只用在HMR 功能中讓html熱更新生效
物件:多入口,有多少個key 就會形成多少個chunk,也就輸出多少個bundle 文件,每個鍵(key)會是chunk 的名稱。在物件類型中,每個key的值還可以是一個數組,而不僅僅是一個字串
output
指示webpack 如何去輸出、以及在哪裡輸出你的bundle、asset 和其他你所打包或使用webpack 載入的任何內容。輸出的bundle 的預設值是./dist/main.js
,其他產生檔案預設放置在./dist
資料夾中。
你可以透過在設定中指定一個output
字段,來設定這些處理過程:
//webpack.config.js const path = require('path'); module.exports = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js', }, };
我們可以透過output.filename
和output.path
屬性,來告訴webpack bundle 的名稱,以及bundle 產生到哪裡。
2.1、output.filename(檔案名稱和目錄)
此選項決定了每個輸出bundle 的目錄和名稱。這些bundle 將寫入到output.path
選項指定的目錄下。
對於單一入口
起點,filename 會是一個靜態名稱。然而,當透過多個入口起點(entry point)、程式碼分割(code splitting)或各種插件(plugin)建立多個bundle,應該使用其他方法來讓每個bundle 都有一個唯一的名稱。
//單入口時: module.exports = { //... output: { filename: 'js/bundle.js' } }; //多入口--使用入口名稱: module.exports = { //... output: { filename: '[name].bundle.js' } }; //多入口--使用每次建置過程中,唯一的hash 產生module.exports = { //... output: { filename: '[name].[hash].bundle.js' } }; ...
2.2、output.path(檔案目錄)
output.path 指定所有輸出檔案的目錄,即將來所有資源輸出的公共目錄。 path 必須是絕對路徑。
module.exports = { //... output: { path: path.resolve(__dirname, 'dist/assets') } };
2.3、output.publicPath(引用資源的路徑前綴)
publicPath 指定的是html 檔案中的所有資源所引入的公共路徑前綴。它不會對生成檔案的路徑造成影響,而是在html 檔案引入各種資源時,將publicPath 作為前綴加到引入資源的路徑前面。
實例:
在vue-cli 產生的webpack 配置中,生產環境下publicPath 的值預設為'/',即目前目錄的根目錄。
打包過後,我們打開html 文件,可以看到html 文件中引入的資源路徑為:
可以看到,都在路徑前面加了/ 符號。當我們開啟瀏覽器存取產生的html 檔案時,會發現報錯,資源存取不到,封包404,此時資源的存取類似如下:
在伺服器上可能會是如下,但訪問一樣可能會有問題。
我們可以將publicPath 修改為相對路徑,或直接把它註解掉也行。
2.3.1、path和publicPath的區別
打包后文件在硬盘中的存储位置,是webpack所有文件的输出的路径,必须是绝对路径。比如:输出的js、图片,HtmlWebpackPlugin生成的html文件等,都会存放在以path为基础的目录下。
2.4、output.chunkFilename(非入口chunk的名稱)
output.chunkFilename 決定了非入口(non-entry) chunk 檔案的名稱。也就是除了入口檔案產生的chunk外,其他檔案產生的chunk檔案命名。
module.exports = { //... output: { chunkFilename: 'js/[name]_chunk.js' //非入口chunk的名稱} };
webpack 本身只能打包JavaScript 和JSON 檔案( webpack3+和webpack2+
內建可處理JSON文件,但webpack1+并不支持,
需要引入json-loader
),這是webpack 開箱可用的自帶能力。 webpack 本身不支援打包其他類型文件,例如css、vue 等,但我們可以透過各種loader 來讓webpack 去處理這些類型的文件。 loader 可以將檔案從不同的語言(如TypeScript)轉換為JavaScript 或將內嵌圖片轉換為data URL,loader 甚至允許你直接在JavaScript 模組中import
CSS檔案!
透過使用不同的loader
, webpack
有能力調用外部的腳本或工具,實現對不同格式的文件的處理,比如說分析轉換scss為css,或者把下一代的JS檔(ES6,ES7)轉換為現代瀏覽器相容的JS檔。對React的開發而言,適當的Loaders可以把React的中所使用的JSX檔轉換成JS檔。
在webpack 的設定中,loader 有兩個屬性:
test
屬性,辨識出哪些檔案會被轉換。
use
屬性,定義出在進行轉換時,應該使用哪個loader。
include/exclude(可选):
手動新增必須處理的檔案(資料夾)或封鎖不需要處理的檔案(資料夾)
query(可选)
:為loaders提供額外的設定選項
//範例:webpack.config. js const path = require('path'); module.exports = { output: { filename: 'my-first-webpack.bundle.js', }, module: { rules: [ { test: /.txt$/, loader: 'raw-loader' }, { test: /.css$/, use: ['style-loader', 'css-loader'] } //使用多個loader的話應該用use ], }, };
以上配置中,對單獨的module 物件定義了rules
屬性,裡麵包含兩個必須屬性: test
和use
。這相當於告訴webpack 編譯器在碰到require()
/ import
語句中被解析為'.txt' 的路徑時,在對它打包之前,先使用raw-loader
轉換一下。
使用多個loader的話應該用use,use 數組中的loader 執行順序:從右到左,依序執行。例如上面的css 文件,首先css-loader 會將css 檔案編譯成JS 載入到JS檔案中,然後再由style-loader 建立style 標籤,將JS 中的樣式資源插入到head 標籤中。
3.1.CSS-loader
webpack提供兩個工具處理樣式表, css-loader
和style-loader
,二者處理的任務不同。 css-loader
使你能夠使用類似import
的方法來引入css 文件, style-loader
將所有的計算後的樣式加入頁面中,二者組合在一起使你能夠把樣式表嵌入webpack打包後的JS文件中,由此就可以在JS檔中引入css檔了。
//安裝npm install --save-dev style-loader css-loader //css-loader版本太高編譯可能會出錯,建議降低版本例如[email protected] 可用
//使用module.exports = { … module: { rules: [ { test: /(.jsx|.js)$/, use: { loader: "babel-loader" }, exclude: /node_modules/ }, { test: /.css$/, //對同一個檔案引入多個loader的方法。 loader的作用順序是後面的loader先開始作用use: [ { loader: "style-loader" }, { loader: "css-loader" } ] } ] } };
假設有一個main.css 檔案:
body { backgroud: green; }
為了讓webpack能找到」main.css「文件,我們把它導入」main.js 「中,如下:
//main.js import React from 'react'; import {render} from 'react-dom'; import Greeter from './Greeter'; import './main.css';//使用require導入css檔案render(<Greeter />, document.getElementById('root'));
通常情況下,css會和js打包到同一個檔案中,並不會打包為一個單獨的css檔。不過透過合適的設定webpack也可以把css打包為單獨的檔案的。
loader 用於轉換某些類型的模組,而插件則可以用於執行範圍更廣的任務,包括:打包優化、壓縮、資源管理、注入環境變數等。插件目的在於解決loader 無法實現的其他事。
要使用某個插件,我們需要透過npm
安裝它,然後在plugins 屬性下新增該插件的一個實例。由於插件可以攜帶參數/選項,你必須在webpack 配置中,向plugins
屬性傳入new
實例。多數插件可以透過選項自訂,你也可以在一個設定檔中因為不同目的而多次使用同一個插件。
//webpack.config.js const HtmlWebpackPlugin = require('html-webpack-plugin'); // 透過npm 安裝const webpack = require('webpack'); // 用於存取內建外掛module.exports = { module: { rules: [{ test: /.txt$/, use: 'raw-loader' }], }, plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })], };
在上面的範例中, html-webpack-plugin
為應用程式產生一個HTML 文件,並自動注入所有產生的bundle。
4.1、BannerPlugin插件(新增版權說明)
下面我們新增了一個為打包後程式碼添加版權聲明的插件。該插件是webpack中的內建插件不用安裝。
const webpack = require('webpack'); module.exports = { … module: { rules: [ { test: /(.jsx|.js)$/, use: { loader: "babel-loader" }, exclude: /node_modules/ }, { test: /.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true } }, { loader: "postcss-loader" } ] } ] }, plugins: [ new webpack.BannerPlugin('wenxuehai版權所有,翻版必究') ], };
4.2、Hot Module Replacement 外掛程式(熱載入)
Hot Module Replacement
(HMR)是webpack裡很有用的一個插件,它允許你在修改元件程式碼後,自動刷新即時預覽修改後的效果。熱加載和webpack-dev-server不同,熱替換在應用運行時,無需刷新頁面,便能查看程式碼更新後的效果,就跟直接在瀏覽器上修改dom樣式一樣,而webpack-dev-server是要刷新頁面的。
(1) 在webpack設定檔中加入HMR插件;
(2) 在Webpack Dev Server中新增「hot」參數;
4.2.1、react 實作熱載入
React模組可以使用Babel實作功能熱載入。 Babel有一個叫做react-transform-hrm
的插件,可以在不對React模組進行額外的配置的前提下讓HMR正常工作;
安裝react-transform-hmr
npm install --save-dev babel-plugin-react-transform react -transform-hmr
const webpack = require('webpack'); module.exports = { entry: __dirname + "/app/main.js",//已多次提及的唯一入口檔案output: { path: __dirname + "/public", filename: "bundle.js" }, devtool: 'eval-source-map', devServer: { contentBase: "./public",//本地伺服器所載入的頁面所在的目錄historyApiFallback: true,//不跳到inline: true, hot: true }, module: { rules: [ { test: /(.jsx|.js)$/, use: { loader: "babel-loader" }, exclude: /node_modules/ }, { test: /.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true } }, { loader: "postcss-loader" } ] } ] }, plugins: [ new webpack.BannerPlugin('版權所有,翻版必究'), new webpack.HotModuleReplacementPlugin() //熱載入插件], };
配置Babel
// .babelrc { "presets": ["react", "env"], "env": { "development": { "plugins": [["react-transform", { "transforms": [{ "transform": "react-transform-hmr", "imports": ["react"], "locals": ["module"] }] }]] } } }
//Greeter,js import React, { Component } from 'react' import styles from './main.css' class Greeter extends Component { render() { return ( < div> <h1> aaaf </h1> </div> ); } } export default Greeter
//main.js import React from 'react'; import { render } from 'react-dom'; import Greeter from './greeter.js'; render( < Greeter / > , document.getElementById('root'));
現在如果我們就可以實現熱加載模組了,每次保存就能在瀏覽器上直接看到更新內容,瀏覽器不必刷新也不會自動刷新。
(有時候沒有效果可能是版本問題)
4.3、ExtractTextWebpackPlugin插件(抽離css)
在預設情況下,webpack 不會將css 樣式作為一個獨立文件,而是會將css 也打包到js 文件中,打包生成的js 檔案會在渲染時透過js 語法來將樣式透過style 標籤的形式來插入頁面中。但這樣的話,打包出來的bundle 檔案可能會太大,此時我們可以透過ExtractTextWebpackPlugin 插件來將css 樣式獨立成css 檔案。
ExtractTextWebpackPlugin 外掛程式會將入口chunk 中引用到的*.css(包括引入的css檔案和vue檔案中style所寫的樣式),移到一個獨立分離的CSS 檔案中。 ExtractTextPlugin
對每個入口chunk 都會產生一個對應的css文件,也就是說一個入口對應著一個css 文件,多個入口的話就會分別生成多個對應的css 文件。
透過ExtractTextWebpackPlugin 插件,你的樣式將不再內嵌到JS bundle 中,而是會放到一個單獨的CSS 檔案(即styles.css
)當中。 如果你的樣式檔案大小較大,這會做更快提前加載,因為CSS bundle 會跟JS bundle 並行加載。
const ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { module: { rules: [ { test: /.css$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: "css-loader" }) } ] }, plugins: [ new ExtractTextPlugin({ filename: utils.assetsPath('css/[name].[contenthash].css'), //ExtractTextPlugin 對每個入口chunk 都產生一個對應的文件,所以當你配置多個入口chunk 的時候,必須使用[ name], [id] 或[contenthash] // allChunks: true, //當使用`CommonsChunkPlugin` 並且在公共chunk 中有提取的chunk(來自`ExtractTextPlugin.extract`)時,`allChunks` **必須設定為`true`。 }), ] }
4.3.1、allChunks選項(是否也將非同步載入的樣式一起擷取出來)
ExtractTextWebpackPlugin 外掛程式的allChunks 選項的預設值為false。
allChunks 選項的意思是是否需要將非同步載入的樣式一起提取出來。因為在預設情況下,就算使用了ExtractTextWebpackPlugin 插件,如果該樣式或樣式檔案是非同步載入的話,那麼這些樣式是不會被提取到獨立的css 檔案中的,而是仍然會打包到js 檔案中。
所以allChunks:false
為預設值,預設是從entry 的入口提取程式碼,但是不會提取非同步載入的程式碼; allChunks:true
則是提取所有模組的程式碼(包含非同步載入的模組)到一個檔案裡面。如果使用了非同步載入樣式,但是allChunks 又設為了false,那麼我們就需要設定ExtractTextPlugin.extract 的fallback, fallback
是在非同步程式碼載入的css 程式碼沒有被擷取的情況下, 以style-loader
的情況去載入非同步組件的樣式。
可參考:
https://github.com/sevenCon/blog-github/blob/master/articles/webpack學習筆記(2)-ExtractTextWebpackPlugin的使用.md
https://blog.csdn.net/weixin_41134409/article/details /88416356
透過選擇development
, production
或none
之中的一個,來設定mode
參數,你可以啟用webpack 內建在對應環境下的最佳化。其預設值為production
。
module.exports = { mode: 'production', };
在設定檔中直接設定mode 選項將告知webpack 使用對應模式的內建最佳化,mode選項有development、production、none。
development : 開發模式,打包的程式碼不會被壓縮,開啟程式碼調試,
production : 生產模式,則正好反之。
將mode 設為development或production,webpack會自動同時也設定process.env.NODE_ENV 的值,我們可以在任何資料夾中直接拿到該值。但如果只設定NODE_ENV
,則不會自動設定mode
。 (在node中,全域變數process 表示的是目前的node流程。process.env 屬性包含使用者環境的資訊。process.env 本身並不存在NODE_ENV這個屬性,我們一般會自己去定義NODE_ENV 屬性,用它來判斷是生產環境還是開發環境)
(請注意:mode選項是webpack4新增的,在4之前都是用DefinePlugin插件設置,webpack4把DefinePlugin刪除了)
5.1、vue-cli項目mode配置詳解
在webpack 中,一般都會在設定檔中配置NODE_ENV 的值。在使用vue-cli 預設產生的vue 專案中,NODE_ENV 配置如下:
//webpack.dev.conf.js 檔案下,引入了dev.env.js 檔案new webpack.DefinePlugin({ 'process.env': require('../config/dev.env') }),
//dev.env.js 檔案中module.exports = merge(prodEnv, { NODE_ENV: '"development"' })
//webpack.prod.conf.js 檔案下,引入了prod.env.js 檔案const env = require('../config/prod.env') new webpack.DefinePlugin({ 'process.env': env }),
//prod.env.js 檔案中module.exports = { NODE_ENV: '"production"' }
從上面可以知道,在開發環境下,設定檔將NODE_ENV 配置成了'development';在生產環境下,設定檔將NODE_ENV 配置成了'production'。
我們在執行專案時,會執行npm run dev 或npm run build,這兩個指令時使用了開發環境或生產環境的設定檔來產生執行項目,由此也對應著設定了對應的NODE_ENV 的值,我們也就能夠在專案的任一檔案中(設定檔不一定,因為要看配置了NODE_ENV 的值的設定檔有沒有生效了才行)取得到對應的NODE_ENV 的值。
5.2、process.env.NODE_ENV配置
process 是node 的全域變量,且process 有env 這個屬性,但是沒有NODE_ENV 這個屬性。 NODE_ENV 變數並不是process.env 直接就有的,而是透過設定得到的,但是NODE_ENV
變數通常約定用來定義環境類型。這個變數的作用是:我們可以透過判斷這個變數來區分開發環境或生產環境。
(1)可以透過webpack的內建外掛DefinePlugin 來設定全域變數值:
new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }),
設定完後在執行腳本上可以取到該值,例如:
// main.js console.log(process.env.NODE_ENV); //production
但是在webpack的設定檔webpack.config.js 中取不到該值。
(2)透過cross-env 套件設定
先下載cross-env 套件:
cnpm i cross-env -D
設定package.json 檔案:
"build": "cross-env NODE_ENV=test webpack --config webpack.config.js"
此時在設定檔中可以取到該值(process.env.NODE_ENV),但在可執行腳本中取不到,需要配合DefinePlugin 外掛程式使用