webpack 是代碼編譯工具,有入口、出口、loader 和插件;webpack 是一個(gè)用于 JavasScript 應(yīng)用程序的靜態(tài)模塊打包工具;當(dāng) webpack 處理應(yīng)用程序時(shí)會(huì)遞歸構(gòu)建一個(gè)依賴(lài)關(guān)系圖(dependency graph),其中包含應(yīng)用程序的每一個(gè)模塊,然后將這個(gè)模塊打包成一個(gè)或者多個(gè) bundle。
webpack 的本質(zhì)是模塊化打包工具,前端所有的資源都應(yīng)當(dāng)看成是一個(gè)模塊,通過(guò) webpack 的核心機(jī)制 loader 來(lái)處理,然后借助插件機(jī)制 plugin 來(lái)形成一個(gè)繁榮的生態(tài);
學(xué)習(xí)可參考 webpack 中文文檔;webpack 版本:5.73.0
一、為什么使用 webpack
1、解決作用域問(wèn)題
問(wèn)題原因:
傳統(tǒng)的 JavaScript 文件引入方式會(huì)在 window 對(duì)象上面綁定全局的變量,這也會(huì)嚴(yán)重的污染 window 對(duì)象,使得 window 對(duì)象變的臃腫;
解決辦法:
1、我們?cè)缙谑褂霉芾眄?xiàng)目資源的工具 Grunt 和 Gulp ,他們是將所有項(xiàng)目文件拼接在一起,其實(shí)是使用了 JavaScript 的立即執(zhí)行函數(shù)來(lái)解決作用域的問(wèn)題,立即執(zhí)行函數(shù)簡(jiǎn)稱(chēng) IIFE ;當(dāng)腳本被封裝在 IIFE 內(nèi)部時(shí),我們可以安全的拼接和組合文件而不必?fù)?dān)心作用域問(wèn)題。
;(function(){
let test = 'xxx'
})()
console.log(test) //is not defined
let res = (function(){
return 'xxx'
})()
console.log(res) //xxx
上面是一個(gè)立即執(zhí)行函數(shù),如果在立即執(zhí)行函數(shù)外面調(diào)用 test,test 是 ‘is not defined’ ,說(shuō)明在立即執(zhí)行函數(shù)中的變量是不能夠在外部訪問(wèn)的,這樣就不會(huì)污染到 window;如果想暴露一些東西給 window,則可以使用一個(gè)變量,將自執(zhí)行函數(shù)賦值給這個(gè)變量,然后就可以在 window 中訪問(wèn)自執(zhí)行函數(shù)的返回值;
2、代碼拆分問(wèn)題
問(wèn)題原因:
使用立即執(zhí)行函數(shù),將所有代碼整合到同一個(gè)文件中,會(huì)造成代碼體積過(guò)大,構(gòu)建和加載的適合效率很慢,所有不得不對(duì)代碼進(jìn)行拆分;
解決辦法:
commonJS 運(yùn)行是基于 node.js 環(huán)境的,commonJS 的問(wèn)世引入了一個(gè) require 的機(jī)制,它允許我們?cè)诋?dāng)前文件中去加載和使用某個(gè)模塊,只導(dǎo)入需要的模塊;
const add = (a,b) => {
return a+b
}
const minus = (a,b) => {
return a-b
}
module.exports = {
add,
minus
}
//引入 serve.js
const math = require(./math.js)
console.log(math.add(1,2)) //3
上面代碼我們通過(guò) module.exports
來(lái)暴露對(duì)象,使用頁(yè)面通過(guò) require 來(lái)引入整個(gè) module.exports
暴露出來(lái)的對(duì)象;需要注意的是 node 需要在 node.js 環(huán)境中運(yùn)行,所以想查看這個(gè)效果需要開(kāi)啟 node 服務(wù):打開(kāi)控制臺(tái),找到需要運(yùn)行的 JavaScript 文件 serve.js,執(zhí)行 node serve.js
就可以在控制臺(tái)看到結(jié)果;
3、讓瀏覽器支持模塊
1、借助 require.js (不夠簡(jiǎn)潔)
定義 add 方法,通過(guò) define 暴露出來(lái),define 第一個(gè)參數(shù)是一個(gè)數(shù)組,里面填寫(xiě)所需要依賴(lài)的文件路徑;在 html 頁(yè)面引入 require.js 文件,通過(guò) data-main 來(lái)綁定入口文件;在入口文件 main 中,使用require 方法,第一個(gè)參數(shù)是所需要的依賴(lài)文件,第二個(gè)參數(shù)是個(gè)方法,你可以在方法中使用add方法,并返回,這個(gè)返回值在瀏覽器中是可以查看的;
2、借助 ECMAScript 標(biāo)準(zhǔn)(瀏覽器支持不完整)
//add.jsconst add =(a,b)=> {return a+b}export default add;//html <script type="module>import add from './add.js'</script>
或者
//add.jsexport const add =(a,b)=> {return a+b}//html <script type="module">import { add } from './add.js'</script>
這里需要聲明 script 的 type = 'module'
;同時(shí)使用 export(輸出多個(gè))和 export default (輸出單個(gè))暴露出來(lái)的方法引入時(shí)也有一點(diǎn)差別;
上面這些手段雖然都能解決對(duì)應(yīng)的問(wèn)題,但是會(huì)比較麻煩,這里我們就引出 了更加強(qiáng)大的工具 webpack ;它可以打包 JavaScript 應(yīng)用程序,支持 ES 模塊化標(biāo)準(zhǔn)和 commonJS,可以擴(kuò)展支持圖片、字體文件、樣式文件等靜態(tài)資源打包;
4、構(gòu)建工具對(duì)比
1、Webpack:適合一些復(fù)雜的應(yīng)用,可以集成很多第三方庫(kù),可以拆分代碼,使用靜態(tài)資源文件,支持 commonJS、esmodule 等模塊化模式;
2、Parcel:零配置,用戶(hù)無(wú)需做其他的配置,開(kāi)箱即用;適合簡(jiǎn)單的應(yīng)用,并且可以快速的運(yùn)行起來(lái);
3、Rollup:用標(biāo)準(zhǔn)化的格式來(lái)編寫(xiě)代碼(ES6)通過(guò)減少無(wú)用的代碼來(lái)縮小包的體積;一般只能用來(lái)打包 JavaScript;適合一些類(lèi)庫(kù)并且只需要引入很少的第三方庫(kù);(Vue,React 框架)
4、Vite:基于 esmodule 構(gòu)建,可以按需編譯,熱模塊更新;可以和 Vue3 完美結(jié)合;
二、webpack 學(xué)習(xí)起步
1、安裝
安裝 webpack 之前需要確保已經(jīng)安裝了 node.js 的最新版本(參考:node版本升級(jí));然后使用 npm 包管理工具來(lái)安裝 webpack:
1、全局安裝
//全局安裝webpack webpack-clinpm install webpack webpack-cli --global
//查看webpack是否安裝成功webpack -v
不建議使用全局安裝 webpack,那樣不利于不同項(xiàng)目中使用不同版本的 webpack,也不利于項(xiàng)目的協(xié)調(diào)開(kāi)發(fā);
2、本地項(xiàng)目安裝
//安裝npm包管理配置文件package.jsonnpm init -y//局部安裝webpack webpack-clinpm install webpack webpack-cli --save-dev
本地項(xiàng)目安裝之前需要閑創(chuàng)建一個(gè) npm 包管理配置文件;安裝好 webpack 之后本地目錄中會(huì)生成 node_modules 文件夾,里面就我們引入的依賴(lài)包;(切記,文件名不可以是webpack)
webpack-cli 不是必須的,只是用來(lái)處理命令行參數(shù)的工具;
2、運(yùn)行打包
1、如果是在全局安裝的 webpack 直接在控制臺(tái)執(zhí)行 webpack
就可以開(kāi)始打包了;
webpack
2、如果是局部安裝的 webpack,上面的運(yùn)行命令就不行了,因?yàn)榫植堪惭b的并沒(méi)有加入到系統(tǒng)環(huán)境變量中,所以控制臺(tái)找不到 webpack
指令;這個(gè)時(shí)候我們需要使用下面的命令來(lái)運(yùn)行;
npx webpack
npx 依托于 npm ,有了 npm 就可以直接使用 npx;npx 的作用是表示我們可以觀察當(dāng)前文件夾里面是否有我們想要去運(yùn)行的命令,如果沒(méi)有就會(huì)在這個(gè)目錄的上一層目錄中查找;
注意:運(yùn)行打包可以在任意文件夾下面運(yùn)行,運(yùn)行之后生成的 dist 文件夾會(huì)在運(yùn)行打包的文件夾下面;
3、自定義 webpack 配置
在根目錄下創(chuàng)建 webpack.config.js 文件,用來(lái)配置 webpack 的配置項(xiàng);
const path = require('path')module.exports = {
entry:'./src/index.js', //入口文件路徑
output:{
filename:'bundle.js',//打包后的文件名
path: path.resolve(__dirname,'./dist'),//打包后文件放置的位置
clean:true //每次打包前清空dist文件夾}}
為了獲取絕對(duì)路徑,我們需要引入 node.js 的 path 模塊;通過(guò) resolve 來(lái)解析路徑,_dirname (兩個(gè)下劃線)表示當(dāng)前文件的物理路徑,也就是 webpack.config.js 文件的上一級(jí)文件夾;第二個(gè)參數(shù)是指定打包文件保存的文件夾;
打包之后的 bundle.js 在 html 文件中通過(guò)標(biāo)簽引入就可以正常使用了;
4、自動(dòng)引入資源
我們可以使用 webpack 插件 HtmlWebpackPlugin
來(lái)自動(dòng)引入打包后的文件,這也就可以避免手動(dòng)修改文件的路徑; HtmlWebpackPlugin
插件會(huì)為你生成一個(gè)新的 HTML 文件在 dist 文件夾下,并且自動(dòng)引入打包后的入口文件(script 標(biāo)簽),以及 CSS( head中的標(biāo)簽內(nèi));
1、安裝插件
npm install --save-dev html-webpack-plugin
2、配置
在 webpack.config.js 文件中加新的配置:
const path = require('path')var HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {
entry:'./src/index.js',
output:{
filename:'bundle.js',//打包后的文件名
path: path.resolve(__dirname,'./dist'),//打包后文件放置的位置
clean:true //每次打包前清空dist文件夾},
plugins:[new HtmlWebpackPlugin({
tempalte:'./index.html',// 模板,根據(jù)指定模板生成新的html
filename:'app.html',//生成文件的名稱(chēng)
inject:'body'//指定script標(biāo)簽位置})]}
這樣 dist 文件夾中就會(huì)生成 bundle.js 的同時(shí)還會(huì)生成一個(gè) app.html 文件,這個(gè) app.html 文件就是根據(jù) index.html 文件為模板生成的,并且文件自動(dòng)引入 bundle.js ;
5、mode 選項(xiàng)
為了能在每次修改之后能夠自動(dòng)編譯,并且讓瀏覽器自動(dòng)刷新,我們可以搭建一個(gè)開(kāi)發(fā)環(huán)境來(lái)實(shí)現(xiàn);
1、source map 實(shí)現(xiàn)代碼調(diào)試
在 webpack.config.js 添加下面配置項(xiàng),實(shí)現(xiàn)精準(zhǔn)定位 bug 行數(shù);
devtool:'inline-source-map'
2、自動(dòng)編譯
在初次編譯的時(shí)候在命令行后面加一個(gè) watch,這也內(nèi)容修改后控制臺(tái)就會(huì)自動(dòng)編譯了;
npx webpack --watch
3、webpack-dev-server
webpack-dev-server
提供了一個(gè)基本的 web server 并且具有實(shí)時(shí)重新加載的功能;
安裝
npm install webpack-dev-server -D
配置
devServer:{
statis:"./dist" //server根目錄}
啟動(dòng)
npx webpack server//或者npx webpack-dev-server//自動(dòng)打開(kāi)瀏覽器npx webpack-dev-server --open
這里可以啟動(dòng)一個(gè)服務(wù),一般是 http://localhost:8080/,然后在瀏覽器訪問(wèn)這個(gè)地址就可以實(shí)現(xiàn)自動(dòng)更新瀏覽器了;
webpack-dev-server 實(shí)際上并沒(méi)有輸出任何的物理文件,它把打包后的 bundle 文件保存在內(nèi)存里面,這也我們的開(kāi)發(fā)效率提高了,webpack 的編譯效率也提高了;
6、資源模塊 module
在 webpack 出現(xiàn)之前,前端人員會(huì)使用 Grunt 、Gulp 等工具來(lái)處理資源,將 src 文件夾的文件移動(dòng)到 dist 或者 build 目錄中;然而 webpack 最出色的功能除了引入 js 還可以使用內(nèi)置的資源模塊;asset modules 來(lái)引入任何的其他類(lèi)型資源,它允許 webpack 打包其他的文件(字體、圖標(biāo));
資源模塊有四種類(lèi)型:asset modules type
asset/resource:發(fā)送一個(gè)單獨(dú)的文件并導(dǎo)出URL;
asset/inline:導(dǎo)出一個(gè)資源的 Data URL;
asset/source:導(dǎo)出資源的源代碼;
asset:導(dǎo)出一個(gè)資源的 Data URL 和發(fā)送一個(gè)單獨(dú)文件之間自動(dòng)選擇;
6.1、resource 資源
在 webpack.config.js 新增 module 配置項(xiàng);添加 rules 規(guī)則,通過(guò) test 加上正則匹配指定類(lèi)型的文件;
module:{
rules:[{//規(guī)則
test:/\.png$/, //正則定義加載文件的類(lèi)型
type:'asset/resource'}]}//頁(yè)面使用import imgSrc from './assets/test.png'
這個(gè)時(shí)候在頁(yè)面上引用的時(shí)候就會(huì)獲取到圖片的路徑;并且在 dist 文件夾下面可以看到我們導(dǎo)出的圖片資源;
如果想修改圖片存放位置和文件名可以進(jìn)行如下操作:
output:{
filename:'bundle.js',//打包后的文件名
path: path.resolve(__dirname,'./dist'),//打包后文件放置的位置
clean:true, //每次打包前清空dist文件夾
assetModuleFilename:'images/[contenthash][ext]' //contenthash 根據(jù)文件的內(nèi)容生成一個(gè)hash字符串,ext表示擴(kuò)展名},
或者在 module 中加一個(gè) generator
module:{
rules:[{//規(guī)則
test:/\.png$/, //正則定義加載文件的類(lèi)型
type:'asset/resource',
generator:{
filename:'images/[contenthash][ext]'}}]}
注意:如果兩處同時(shí)設(shè)置了,那么 generator 的優(yōu)先級(jí)會(huì)更高;
6.2、inline 資源
在 dist 文件夾下面是看不到圖片資源的,因?yàn)檫@種模式只導(dǎo)出了資源的 URL;這個(gè) URL 是 base64 格式的資源路徑;
6.3、source 資源
可以獲取文本的內(nèi)容,常用來(lái)獲取 txt 文件的內(nèi)容;
6.4、通用資源類(lèi)型 asset
在 inline 和 resource 之間自由選擇,默認(rèn)情況下小于 8kb 的文件將會(huì)視為 inline 模塊類(lèi)型,否則視為 resource 模塊類(lèi)型;也可以通過(guò)設(shè)置 parser.dataUrlCondition.maxSize
來(lái)修改默認(rèn)文件大小;
module:{
rules:[{//規(guī)則
test:/\.png$/, //正則定義加載文件的類(lèi)型
type:'asset',
parser:{//自定義解析器里面的時(shí)間
dataUrlCondition:{
maxSize:4*1024*1024}},
generator:{
filename:'images/[contenthash][ext]'}}]}
7、loader
webpack 除了可以使用資源模塊來(lái)引入外部資源,還可以使用 loader 來(lái)引入其他類(lèi)型的文件;webpack 只能理解 js 和 json 類(lèi)型的文件,這是 webpack 自帶的能力, loader 可以讓 webpack 去解析其他類(lèi)型的文件并且將這些文件轉(zhuǎn)化為有效的模塊,供應(yīng)用程序使用;
loader 的定義在 module rules 下面定義一個(gè) test 來(lái)識(shí)別那些文件被轉(zhuǎn)換,use 屬性定義在轉(zhuǎn)化的時(shí)候使用那個(gè) loader 來(lái)進(jìn)行轉(zhuǎn)化;
module:{
rules:[{
test:/\.text/,
use:'raw-loader'}]}
上面這段配置的意思是:webpack 在通過(guò) import、require 去解析一個(gè) .test 文件的時(shí)候,在對(duì)文件進(jìn)行打包之前先使用 row-loader 轉(zhuǎn)化一下;
7.1、加載 css
1、處理 css
安裝 css-loader style-loader
//將css識(shí)別轉(zhuǎn)化,讓webpack可識(shí)別npm i css-loader -D//把css放置到頁(yè)面 header 標(biāo)簽里面npm i style-loader -D
安裝成功之后在 webpack.config.js 文件的 module 下新增 rules :
rules:[{
test:/\.css$/,
use:['style-loader','css-loader']}]
多個(gè) loader 可以在 use 里面以數(shù)組的形成傳入,loader 執(zhí)行順序從 use 數(shù)組的后面往前面執(zhí)行,先執(zhí)行的 loader 會(huì)將結(jié)果返回傳遞給下一個(gè) loader;并且這個(gè)先后執(zhí)行順序必須正確,否則不生效;需要先轉(zhuǎn)化 css ,然后將 css 放到頁(yè)面上面;
2、處理 less
安裝 less-loader
npm i less-loader less -D
安裝成功之后在 webpack.config.js 文件的 module 下新增 rules :
rules:[{
test:/\.(css|less)$/,
use:['style-loader','css-loader','less-loader']}]
7.2、抽離和壓縮 css
1、抽離
上面我們通過(guò) loader 將 css 放到了 HTML 中,下面我們看一下將 css 單獨(dú)放在一個(gè)文件中,然后通過(guò) link 標(biāo)簽去加載;
安裝插件 mini-css-extract-plugin
//webpack5 下才有這個(gè)插件npm i mini-css-extract-plugin -D
在 webpack.config.js 引入插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')//插件使用plugins:{new MiniCssExtractPlugin({
filename:'styles/[contenthash].css' //制定打包后的css存放位置})},module:{
rules:[{
test:/\.(css|less)$/,
use:['MiniCssExtractPlugin.loader','css-loader','less-loader']}]}
使用插件的 loader MiniCssExtractPlugin.loader
替換掉 style-loader
,這也 dist 文件夾會(huì)新增一個(gè) styles 文件夾 ,打包后的 css 就會(huì)放在這個(gè)文件夾內(nèi),并且在 dist/index.html
文件內(nèi)自動(dòng)引入;
2、壓縮
安裝插件 css-minimizer-webpack-plugin
npm i css-minimizer-webpack-plugin -D
在 webpack.config.js 引入插件:
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')//這個(gè)插件不是在plugins中,而是在優(yōu)化配置中做設(shè)置mode:'production',optimization:{
minimizer:[new CssMinimizerPlugin()]}
注意:這個(gè)時(shí)候的 mode 必須是 production;
7.3、加載 fonts 字體
可以直接借助 asset module 來(lái)接收和載入任何類(lèi)型的資源;
module:{
rules:[{
test:/\.(woff|woff2|eot|ttf|otf)$/i,
type:'asset/resource'}]}
7.4、加載數(shù)據(jù)
json 是默認(rèn)可以正常導(dǎo)入的,但是要導(dǎo)入 CSV、TSV 和 XML 類(lèi)型的文件數(shù)據(jù)則需要 loader 來(lái)幫忙;
安裝 csv-loader xml-loader
npm i csv-loader xml-loader
安裝成功之后在 webpack.config.js 文件的 module 下新增 rules :
module:{
rules:[{
test:/\.(csv|tsv)$/i,
use:['csv-loader']},{
test:/\.xml$/i,
use:['xml-loader']}]}
然后頁(yè)面引入這些類(lèi)型的文件,就可以正常訪問(wèn)了,XML文件會(huì)轉(zhuǎn)化成 js 對(duì)象,CSV 文件會(huì)轉(zhuǎn)化成一個(gè)數(shù)組;
7.5、自定義 JSON 模塊 parser
通過(guò)使用自定義 parser 替換特定的 webpack loader ,將 toml、yaml、json5 文件作為 json 模塊導(dǎo)入;
安裝 toml yaml json5
npm i toml yaml json5 -D
安裝成功之后在 webpack.config.js 文件的 module 下新增 rules :
const toml = require('toml')const yaml= require('yaml')const json5 = require('json5')module:{
rules:[{
test:/\.toml$/i,
type:'json',
parser:{
parse: toml.parse }},{
test:/\.yaml$/i,
type:'json',
parser:{
parse: yaml.parse }},{
test:/\.json5$/i,
type:'json',
parser:{
parse: json5.parse }}]}
8、babel-loader
webpack 只能做 js 打包,但是無(wú)法轉(zhuǎn)化 js 代碼; babel-loader 的主要任務(wù)是將 ES6 轉(zhuǎn)化成低版本瀏覽器可以使用的代碼;這里需要先安裝三個(gè)包:
babel-loader:在 webpack 里面使用 babel 解析 ES6 的橋梁
@babel/core:babel 的核心模塊
@babel/parset-env:babel 預(yù)設(shè),一組 babel 插件的集合(將很多插件安裝到一個(gè)插件里)
npm i babel-loader @babel/core @babel/parset-env -D
安裝成功之后還需要安裝 regeneratorRuntime
插件,這個(gè)是 webpack 打包生成的全局輔助函數(shù),由 babel 生成,用于兼容 async/await 語(yǔ)法;
//包含regeneratorRuntime 插件運(yùn)行的時(shí)候需要的內(nèi)容npm i @babel/runtime -D//需要regeneratorRuntime 的地方自動(dòng)require導(dǎo)包,然后編譯的時(shí)候需要它npm i @babel/plugin-transform-runtime -D
在 webpack.config.js 文件的 module 下新增 rules :
module:{
rules:[{
test:/\.js$/i,
exclude:/node_module/,//不打包node_module里面的js
use:{
loader: 'babel-loader',
options:{//參數(shù)
presets:['@babel/preset-env'],
plugins:[['@babel/plugin-transform-runtime']]}}}]}
9、代碼分離
代碼分離是 webpack 最主要的特性之一,可以將代碼分離到不同的 bundle 中;分離后的文件我們可以按需加載、并行加載;代碼分離可以獲取最小的 bundle ,可以控制資源加載的優(yōu)先級(jí),如果使用合理可以極大的節(jié)省加載時(shí)間;常用分離方式有三種:
9.1、配置入口節(jié)點(diǎn)
使用 entry 配置手動(dòng)的分離代碼;這種方法的問(wèn)題是如果有多個(gè)入口,那么這些多個(gè)入口共享的文件會(huì)分別在每個(gè)包里重復(fù)打包;
const path = require('path')entry:{
index:'./src/index.js',
other:'./src/other.js'},output:{
filename:'[name].bundle.js', //name可以獲取到entry里面入口的key
path:path.resolve(__dirname,'./dist'),}
多個(gè)入口,對(duì)應(yīng)打包就會(huì)打包出多個(gè)出口,但是如果 index 和 other 同時(shí)使用了 lodash 包,那么在打包的時(shí)候會(huì)分別將 lodash 包加到 index 和 other 文件中;
9.2、防止重復(fù)
使用 Entry dependencies 或者 SplitChunkPlugin
去重和分離代碼,配置 dependOn option 選項(xiàng),這也可以在多個(gè)模塊之間直接共享模塊;
1、 Entry dependencies
const path = require('path')entry:{
index:{import:'./src/index.js',
dependOn:'shared'},
other:{import:'./src/other.js',
dependOn:'shared'},
shared:'lodash' //配置需要共享的模塊},output:{
filename:'[name].bundle.js', //name可以獲取到entry里面入口的key
path:path.resolve(__dirname,'./dist'),}
將 lodash 模塊單獨(dú)打包在 shared 包中,讓 index 和 other 共享;
2、SplitChunkPlugin
const path = require('path')entry:{
index:'./src/index.js',
other:'./src/other.js'},output:{
filename:'[name].bundle.js', //name可以獲取到entry里面入口的key
path:path.resolve(__dirname,'./dist'),},optimization:{
splitChunks:{
chunks:'all'}}
這種方法會(huì)自動(dòng)幫們做代碼分割處理;
9.3、動(dòng)態(tài)導(dǎo)入
當(dāng)涉及到動(dòng)態(tài)代碼拆分時(shí),webpack 提供了兩種方法:import()
語(yǔ)法實(shí)現(xiàn)動(dòng)態(tài)導(dǎo)入、webpack 遺留功能 require.ensure
;這里推薦使用第一種,所以也只介紹第一種方法;
1、import
function get(){return import('lodash').then((default:_)=>{return _.join(['hello','webpack'],' ')})}get.then(res=>{
console.log(res)})
import 函數(shù)調(diào)用完成之后返回的是一個(gè) Promise,所以可以直接使用 then 來(lái)鏈?zhǔn)秸{(diào)用;靜態(tài)導(dǎo)入和動(dòng)態(tài)導(dǎo)入是可以同時(shí)工作的;
下面是動(dòng)態(tài)導(dǎo)入的兩個(gè)比較好的應(yīng)用:
9.3.1、懶加載
懶加載也叫按需加載,是優(yōu)化網(wǎng)頁(yè)的一種方式;它主要是將代碼在一些邏輯斷點(diǎn)處分離開(kāi),在完成某些操作之后立即引入需要的代碼模塊;這也能加快應(yīng)用程序初始加載速度,也能減輕代碼的體積;
//math.jsexport add(a,b){return a+b}//頁(yè)面使用const button = document.createElement('button')button.textContent = '+'button.addEventListener('click',()=>{import(/*webpackChunkName:'math'*/'./math.js').then({add}=>{ //注釋這一段是修改打包后文件的名稱(chēng)
console.log(add(1,2))})})document.body.appendChild(button)
上面這個(gè)例子,webpack 會(huì)將 math.js 打包成一個(gè)公共文件 math.bundle.js,但是在頁(yè)面初始化的時(shí)候這個(gè)文件不回被加載,當(dāng)點(diǎn)擊按鈕的時(shí)候才會(huì)被加載出來(lái);
注意:我們可以在 import 引入資源的時(shí)候添加注釋來(lái)為這個(gè)文件打包的時(shí)候命名:webpackChunkName:'math'
9.3.2、預(yù)加載模塊
webpack4.6.0 以上版本增加了對(duì)預(yù)獲取和預(yù)加載的支持;在聲明 import 時(shí),使用下面指令可以讓 webpack 輸出資源提示,來(lái)告訴瀏覽器:
prefetch:預(yù)獲取,將來(lái)某些導(dǎo)航下可能需要的資源
preload: 預(yù)加載,當(dāng)前導(dǎo)航下可能需要的資源
//math.jsexport add(a,b){return a+b}//頁(yè)面使用const button = document.createElement('button')button.textContent = '+'button.addEventListener('click',()=>{import(/*webpackChunkName:'math', webpackPrefetch:true*/'./math.js').then({add}=>{ //注釋這一段是修改打包后文件的名稱(chēng)
console.log(add(1,2))})})document.body.appendChild(button)
在引入注釋處加上webpackPrefetch:true
,這也在打包的時(shí)候就會(huì)將 math.bundle.js 文件放到頁(yè)面的 link 標(biāo)簽里面,并且標(biāo)明是 prefetch 類(lèi)型的引入文件;這也瀏覽器就會(huì)在首頁(yè)內(nèi)容都加載完畢之后網(wǎng)絡(luò)空閑的時(shí)候去加載 math.bundle.js;
preload 方法加載的效果和懶加載的差不多,都是在操作之后需要的時(shí)候才會(huì)下載對(duì)應(yīng)資源;
prefetch 方法是在瀏覽器空閑的時(shí)候預(yù)先加載好可能需要的資源,與操作無(wú)關(guān);
10、緩存
由于獲取資源比較耗費(fèi)時(shí)間,瀏覽器會(huì)使用一個(gè)緩存機(jī)制,通過(guò)命中緩存以降低網(wǎng)絡(luò)流量,是網(wǎng)站加載速度更快;然而在部署新版本的時(shí)候不改變資源文件名瀏覽器可能會(huì)認(rèn)為你沒(méi)有更新,就會(huì)使用緩存版本;
10.1、配置輸出文件名
output:{
filename:'[name].[contenthash].js'}
在打包時(shí)輸出文件名增加一個(gè)動(dòng)態(tài) hash 字符串,這也每次打包的文件名就不回重復(fù)了;
10.2、緩存第三方庫(kù)
將第三方庫(kù)(lodash)單獨(dú)提取到一個(gè)固定名稱(chēng)的文件中,因?yàn)檫@些庫(kù)一般不回做修改,所以可以利用緩存機(jī)制消除請(qǐng)求,減少向 server 獲取資源;(目標(biāo)是第三方共享文件)
optimization:{
splitChunks:{
cacheGroups:{
vendor:{
test:/[\\/]node_module[\\/]/,
name:'vendors',
chunks:'all'}}}}
這樣所有第三方的包就都被放到 vindors.bundle.js 中了;
10.3、將所有的 js 文件放到一個(gè)文件夾中
output:{
filename:'scripts/[name].[contenthash].js'}
11、拆分開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境的配置
11.1、公共路徑(publicPath)
我們可以使用公共路徑來(lái)指定應(yīng)用程序中所有資源的基礎(chǔ)路徑;默認(rèn)值是空字符串:“” ,webpack-dev-server 也會(huì)默認(rèn)從 publicPath 為基準(zhǔn),使用它來(lái)決定在哪個(gè)目錄下啟用服務(wù),來(lái)訪問(wèn) webpack 輸出的文件。
output:{
publishPath:'/' //也可以是其他路徑}
11.2、環(huán)境變量
環(huán)境變量可以消除 webpack.config.js 在開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境之間的差異;webpack 的命令行 npx webpack --env 參數(shù)
允許你傳入任意數(shù)量的環(huán)境變量,在 webpack.config.js 文件中可以訪問(wèn)到這個(gè)變量;想要訪問(wèn)環(huán)境變量 env 必須將 module.exports 轉(zhuǎn)換成一個(gè)函數(shù);
將當(dāng)前打包環(huán)境變量設(shè)置為 production:
npx webpack --env production//也可以攜帶一個(gè) key 、valuenpx webpack --env production --env global=local
在 webpack.config.js 文件中獲取環(huán)境變量:
module.exports = (env) =>{
console.log(env)return {
mode: env.production ? 'production' : 'development'}}
結(jié)果:
{ WEBPACK_BUNDLE: true, WEBPACK_BUILD: true, production: true }//攜帶參數(shù){
WEBPACK_BUNDLE: true,
WEBPACK_BUILD: true,
production: true,
global: 'local'}
js 壓縮
webpack 本身可以對(duì) js 文件進(jìn)行壓縮,但是如果我們配置了 css 壓縮,那么原有的 js 壓縮就會(huì)失效,這里我們看一下怎么配置 js 壓縮:
安裝插件 terser-weboack-plugin
npm i terser-weboack-plugin -D
在 webpack.config.js 文件中引入使用:
//壓縮const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')const TerserPlugin = require('terser-weboack-plugin')optimization:{
minimizer:{new CssMinimizerPlugin()new TerserPlugin()}}
上面這倆壓縮插件只會(huì)在生產(chǎn)環(huán)境中生效,開(kāi)發(fā)環(huán)境中不會(huì)壓縮;
11.3、拆分配置文件
拆分配置文件的目的就是將生產(chǎn)環(huán)境和開(kāi)發(fā)環(huán)境的配置文件分開(kāi),單獨(dú)配置:webpack.config.dev.js、webpack.config.prod.js ;然后統(tǒng)一放到配置文件夾中;
1、開(kāi)發(fā)環(huán)境
//webpack.config.dev.jsmodule.exports = {
ebtry:{
index:'./src/index.js'},
output:{
filename:'scripys/[name].js',
path:path.resolve(__dirname,'../dist'),
clean:true,
assetModuleFilename:'images/[contenthash][ext]'},
mode:'development',
devtool:'inline-source-map',
devServer:{static:'./dist'},
optimization:{
splitChunks:{
cacheGroups:{
vendor:{
test:/[\\/]node_module[\\/]/,
name:'vendors',
chunks:'all'}}}}}12345678910111213141516171819202122232425262728
開(kāi)發(fā)環(huán)境不需要清理服務(wù)器緩存,不需要 publicPath,mode 可以直接設(shè)置為 development,需要 devtool,不需要壓縮相關(guān)配置;
我們可以在控制臺(tái)運(yùn)行這個(gè)配置:
npx webpack -c ./config/webpack.config.dev.js
由于提前設(shè)置的 output.path = path.resolve(__dirname,'../dist')
這個(gè)時(shí)候打包的 dist 文件夾會(huì)替換 config 同級(jí)的 dist 文件夾;這個(gè) dist 就可以用于開(kāi)發(fā)環(huán)境代碼的部署;
2、生產(chǎn)環(huán)境
//webpack.config.prod.jsconst CssMinimizerPlugin = require('css-minimizer-webpack-plugin')const TerserPlugin = require('terser-weboack-plugin')module.exports = {
ebtry:{
index:'./src/index.js'},
output:{
filename:'scripys/[name].js',
path:path.resolve(__dirname,'../dist'),
clean:true,
assetModuleFilename:'images/[contenthash][ext]',
publicPath:'/'},
mode:'production',
optimization:{
minimizer:{new CssMinimizerPlugin()new TerserPlugin()},
splitChunks:{
cacheGroups:{
vendor:{
test:/[\\/]node_module[\\/]/,
name:'vendors',
chunks:'all'}}}},
performance:{//關(guān)閉提示信息
hints:false}}12345678910111213141516171819202122232425262728293031323334
11.4、合并配置文件
由于配置文件開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境中有很多相同的配置,我們可以把相同配置提取到 webpack.config.common.js 文件中;然后再將配置合并:
安裝包 webpack-merge
npx i webpack-merge -D
在 config 文件夾下新增 webpack.config.js 文件:
const { merge } = require('webpack-merge')const common = require('./webpack.config.common.js')const prod= require('./webpack.config.prod.js')const dev= require('./webpack.config.dev.js')module.exports = (env) => {switch(true){case env.development:return merge(common,dev);case env.production:return merge(common,prod);}}
11.5、npm 腳本
每次打包或者啟動(dòng)服務(wù)時(shí),都需要在命令行輸入一長(zhǎng)串的命令,這里我們配置 npm 來(lái)簡(jiǎn)化命令行;
1、在項(xiàng)目中的 package.json 文件中
"scripts":{"start": "npx webpack serve -c ./config/webpak.config.js --env development"}
在配置 npm 的時(shí)候,我們可以省略 npm 或者 npx
"scripts":{"start": "webpack serve -c ./config/webpak.config.js --env development"}
2、在命令行運(yùn)行
npm run start
由于改寫(xiě)命令行的時(shí)候在后面?zhèn)魅肓谁h(huán)境變量,所以這個(gè)時(shí)候 start 就代表執(zhí)行 development 環(huán)境的打包;
入門(mén)部分就到這里了,后面會(huì)繼續(xù)深入學(xué)習(xí) webpack!
該文章在 2024/4/3 14:25:59 編輯過(guò)