webpack5入門基礎
當前位置:點晴教程→知識管理交流
→『 技術文檔交流 』
webpack 是代碼編譯工具,有入口、出口、loader 和插件;webpack 是一個用于 JavasScript 應用程序的靜態模塊打包工具;當 webpack 處理應用程序時會遞歸構建一個依賴關系圖(dependency graph),其中包含應用程序的每一個模塊,然后將這個模塊打包成一個或者多個 bundle。 webpack 的本質是模塊化打包工具,前端所有的資源都應當看成是一個模塊,通過 webpack 的核心機制 loader 來處理,然后借助插件機制 plugin 來形成一個繁榮的生態; 學習可參考 webpack 中文文檔;webpack 版本:5.73.0 文章目錄
一、為什么使用 webpack1、解決作用域問題問題原因:
上面是一個立即執行函數,如果在立即執行函數外面調用 test,test 是 ‘is not defined’ ,說明在立即執行函數中的變量是不能夠在外部訪問的,這樣就不會污染到 window;如果想暴露一些東西給 window,則可以使用一個變量,將自執行函數賦值給這個變量,然后就可以在 window 中訪問自執行函數的返回值; 2、代碼拆分問題問題原因:
上面代碼我們通過 3、讓瀏覽器支持模塊1、借助 require.js (不夠簡潔) 定義 add 方法,通過 define 暴露出來,define 第一個參數是一個數組,里面填寫所需要依賴的文件路徑;在 html 頁面引入 require.js 文件,通過 data-main 來綁定入口文件;在入口文件 main 中,使用require 方法,第一個參數是所需要的依賴文件,第二個參數是個方法,你可以在方法中使用add方法,并返回,這個返回值在瀏覽器中是可以查看的; //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 的 上面這些手段雖然都能解決對應的問題,但是會比較麻煩,這里我們就引出 了更加強大的工具 webpack ;它可以打包 JavaScript 應用程序,支持 ES 模塊化標準和 commonJS,可以擴展支持圖片、字體文件、樣式文件等靜態資源打包; 4、構建工具對比1、Webpack:適合一些復雜的應用,可以集成很多第三方庫,可以拆分代碼,使用靜態資源文件,支持 commonJS、esmodule 等模塊化模式; 二、webpack 學習起步1、安裝安裝 webpack 之前需要確保已經安裝了 node.js 的最新版本(參考:node版本升級);然后使用 npm 包管理工具來安裝 webpack: //全局安裝webpack webpack-clinpm install webpack webpack-cli --global //查看webpack是否安裝成功webpack -v 不建議使用全局安裝 webpack,那樣不利于不同項目中使用不同版本的 webpack,也不利于項目的協調開發; //安裝npm包管理配置文件package.jsonnpm init -y//局部安裝webpack webpack-clinpm install webpack webpack-cli --save-dev 本地項目安裝之前需要閑創建一個 npm 包管理配置文件;安裝好 webpack 之后本地目錄中會生成 node_modules 文件夾,里面就我們引入的依賴包;(切記,文件名不可以是webpack) webpack-cli 不是必須的,只是用來處理命令行參數的工具; 2、運行打包1、如果是在全局安裝的 webpack 直接在控制臺執行 webpack 2、如果是局部安裝的 webpack,上面的運行命令就不行了,因為局部安裝的并沒有加入到系統環境變量中,所以控制臺找不到 npx webpack npx 依托于 npm ,有了 npm 就可以直接使用 npx;npx 的作用是表示我們可以觀察當前文件夾里面是否有我們想要去運行的命令,如果沒有就會在這個目錄的上一層目錄中查找; 注意:運行打包可以在任意文件夾下面運行,運行之后生成的 dist 文件夾會在運行打包的文件夾下面; 3、自定義 webpack 配置在根目錄下創建 webpack.config.js 文件,用來配置 webpack 的配置項; const path = require('path')module.exports = { entry:'./src/index.js', //入口文件路徑 output:{ filename:'bundle.js',//打包后的文件名 path: path.resolve(__dirname,'./dist'),//打包后文件放置的位置 clean:true //每次打包前清空dist文件夾}} 為了獲取絕對路徑,我們需要引入 node.js 的 path 模塊;通過 resolve 來解析路徑,_dirname (兩個下劃線)表示當前文件的物理路徑,也就是 webpack.config.js 文件的上一級文件夾;第二個參數是指定打包文件保存的文件夾; 打包之后的 bundle.js 在 html 文件中通過標簽引入就可以正常使用了; 4、自動引入資源我們可以使用 webpack 插件 npm install --save-dev html-webpack-plugin 2、配置 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',// 模板,根據指定模板生成新的html filename:'app.html',//生成文件的名稱 inject:'body'//指定script標簽位置})]} 這樣 dist 文件夾中就會生成 bundle.js 的同時還會生成一個 app.html 文件,這個 app.html 文件就是根據 index.html 文件為模板生成的,并且文件自動引入 bundle.js ; 5、mode 選項為了能在每次修改之后能夠自動編譯,并且讓瀏覽器自動刷新,我們可以搭建一個開發環境來實現; devtool:'inline-source-map' 2、自動編譯 npx webpack --watch 3、webpack-dev-server npm install webpack-dev-server -D 配置 devServer:{ statis:"./dist" //server根目錄} 啟動 npx webpack server//或者npx webpack-dev-server//自動打開瀏覽器npx webpack-dev-server --open 這里可以啟動一個服務,一般是 http://localhost:8080/,然后在瀏覽器訪問這個地址就可以實現自動更新瀏覽器了; webpack-dev-server 實際上并沒有輸出任何的物理文件,它把打包后的 bundle 文件保存在內存里面,這也我們的開發效率提高了,webpack 的編譯效率也提高了; 6、資源模塊 module在 webpack 出現之前,前端人員會使用 Grunt 、Gulp 等工具來處理資源,將 src 文件夾的文件移動到 dist 或者 build 目錄中;然而 webpack 最出色的功能除了引入 js 還可以使用內置的資源模塊;asset modules 來引入任何的其他類型資源,它允許 webpack 打包其他的文件(字體、圖標); 資源模塊有四種類型:asset modules type
6.1、resource 資源在 webpack.config.js 新增 module 配置項;添加 rules 規則,通過 test 加上正則匹配指定類型的文件; module:{ rules:[{//規則 test:/\.png$/, //正則定義加載文件的類型 type:'asset/resource'}]}//頁面使用import imgSrc from './assets/test.png' 這個時候在頁面上引用的時候就會獲取到圖片的路徑;并且在 dist 文件夾下面可以看到我們導出的圖片資源; 如果想修改圖片存放位置和文件名可以進行如下操作: output:{ filename:'bundle.js',//打包后的文件名 path: path.resolve(__dirname,'./dist'),//打包后文件放置的位置 clean:true, //每次打包前清空dist文件夾 assetModuleFilename:'images/[contenthash][ext]' //contenthash 根據文件的內容生成一個hash字符串,ext表示擴展名}, 或者在 module 中加一個 generator module:{ rules:[{//規則 test:/\.png$/, //正則定義加載文件的類型 type:'asset/resource', generator:{ filename:'images/[contenthash][ext]'}}]} 注意:如果兩處同時設置了,那么 generator 的優先級會更高; 6.2、inline 資源在 dist 文件夾下面是看不到圖片資源的,因為這種模式只導出了資源的 URL;這個 URL 是 base64 格式的資源路徑; 6.3、source 資源可以獲取文本的內容,常用來獲取 txt 文件的內容; 6.4、通用資源類型 asset在 inline 和 resource 之間自由選擇,默認情況下小于 8kb 的文件將會視為 inline 模塊類型,否則視為 resource 模塊類型;也可以通過設置 module:{ rules:[{//規則 test:/\.png$/, //正則定義加載文件的類型 type:'asset', parser:{//自定義解析器里面的時間 dataUrlCondition:{ maxSize:4*1024*1024}}, generator:{ filename:'images/[contenthash][ext]'}}]} 7、loaderwebpack 除了可以使用資源模塊來引入外部資源,還可以使用 loader 來引入其他類型的文件;webpack 只能理解 js 和 json 類型的文件,這是 webpack 自帶的能力, loader 可以讓 webpack 去解析其他類型的文件并且將這些文件轉化為有效的模塊,供應用程序使用; loader 的定義在 module rules 下面定義一個 test 來識別那些文件被轉換,use 屬性定義在轉化的時候使用那個 loader 來進行轉化; module:{ rules:[{ test:/\.text/, use:'raw-loader'}]} 上面這段配置的意思是:webpack 在通過 import、require 去解析一個 .test 文件的時候,在對文件進行打包之前先使用 row-loader 轉化一下; 7.1、加載 css1、處理 css //將css識別轉化,讓webpack可識別npm i css-loader -D//把css放置到頁面 header 標簽里面npm i style-loader -D 安裝成功之后在 webpack.config.js 文件的 module 下新增 rules : rules:[{ test:/\.css$/, use:['style-loader','css-loader']}] 多個 loader 可以在 use 里面以數組的形成傳入,loader 執行順序從 use 數組的后面往前面執行,先執行的 loader 會將結果返回傳遞給下一個 loader;并且這個先后執行順序必須正確,否則不生效;需要先轉化 css ,然后將 css 放到頁面上面; 2、處理 less npm i less-loader less -D 安裝成功之后在 webpack.config.js 文件的 module 下新增 rules : rules:[{ test:/\.(css|less)$/, use:['style-loader','css-loader','less-loader']}] 7.2、抽離和壓縮 css1、抽離 //webpack5 下才有這個插件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 2、壓縮 npm i css-minimizer-webpack-plugin -D 在 webpack.config.js 引入插件: const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')//這個插件不是在plugins中,而是在優化配置中做設置mode:'production',optimization:{ minimizer:[new CssMinimizerPlugin()]} 注意:這個時候的 mode 必須是 production; 7.3、加載 fonts 字體可以直接借助 asset module 來接收和載入任何類型的資源; module:{ rules:[{ test:/\.(woff|woff2|eot|ttf|otf)$/i, type:'asset/resource'}]} 7.4、加載數據json 是默認可以正常導入的,但是要導入 CSV、TSV 和 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']}]} 然后頁面引入這些類型的文件,就可以正常訪問了,XML文件會轉化成 js 對象,CSV 文件會轉化成一個數組; 7.5、自定義 JSON 模塊 parser通過使用自定義 parser 替換特定的 webpack loader ,將 toml、yaml、json5 文件作為 json 模塊導入; 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-loaderwebpack 只能做 js 打包,但是無法轉化 js 代碼; babel-loader 的主要任務是將 ES6 轉化成低版本瀏覽器可以使用的代碼;這里需要先安裝三個包:
npm i babel-loader @babel/core @babel/parset-env -D 安裝成功之后還需要安裝 //包含regeneratorRuntime 插件運行的時候需要的內容npm i @babel/runtime -D//需要regeneratorRuntime 的地方自動require導包,然后編譯的時候需要它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:{//參數 presets:['@babel/preset-env'], plugins:[['@babel/plugin-transform-runtime']]}}}]} 9、代碼分離代碼分離是 webpack 最主要的特性之一,可以將代碼分離到不同的 bundle 中;分離后的文件我們可以按需加載、并行加載;代碼分離可以獲取最小的 bundle ,可以控制資源加載的優先級,如果使用合理可以極大的節省加載時間;常用分離方式有三種: 9.1、配置入口節點使用 entry 配置手動的分離代碼;這種方法的問題是如果有多個入口,那么這些多個入口共享的文件會分別在每個包里重復打包; 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'),} 多個入口,對應打包就會打包出多個出口,但是如果 index 和 other 同時使用了 lodash 包,那么在打包的時候會分別將 lodash 包加到 index 和 other 文件中; 9.2、防止重復使用 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 模塊單獨打包在 shared 包中,讓 index 和 other 共享; 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'}} 這種方法會自動幫們做代碼分割處理; 9.3、動態導入當涉及到動態代碼拆分時,webpack 提供了兩種方法: function get(){return import('lodash').then((default:_)=>{return _.join(['hello','webpack'],' ')})}get.then(res=>{ console.log(res)}) import 函數調用完成之后返回的是一個 Promise,所以可以直接使用 then 來鏈式調用;靜態導入和動態導入是可以同時工作的; 下面是動態導入的兩個比較好的應用: 9.3.1、懶加載懶加載也叫按需加載,是優化網頁的一種方式;它主要是將代碼在一些邏輯斷點處分離開,在完成某些操作之后立即引入需要的代碼模塊;這也能加快應用程序初始加載速度,也能減輕代碼的體積; //math.jsexport add(a,b){return a+b}//頁面使用const button = document.createElement('button')button.textContent = '+'button.addEventListener('click',()=>{import(/*webpackChunkName:'math'*/'./math.js').then({add}=>{ //注釋這一段是修改打包后文件的名稱 console.log(add(1,2))})})document.body.appendChild(button) 上面這個例子,webpack 會將 math.js 打包成一個公共文件 math.bundle.js,但是在頁面初始化的時候這個文件不回被加載,當點擊按鈕的時候才會被加載出來; 9.3.2、預加載模塊webpack4.6.0 以上版本增加了對預獲取和預加載的支持;在聲明 import 時,使用下面指令可以讓 webpack 輸出資源提示,來告訴瀏覽器:
//math.jsexport add(a,b){return a+b}//頁面使用const button = document.createElement('button')button.textContent = '+'button.addEventListener('click',()=>{import(/*webpackChunkName:'math', webpackPrefetch:true*/'./math.js').then({add}=>{ //注釋這一段是修改打包后文件的名稱 console.log(add(1,2))})})document.body.appendChild(button) 在引入注釋處加上
10、緩存由于獲取資源比較耗費時間,瀏覽器會使用一個緩存機制,通過命中緩存以降低網絡流量,是網站加載速度更快;然而在部署新版本的時候不改變資源文件名瀏覽器可能會認為你沒有更新,就會使用緩存版本; 10.1、配置輸出文件名output:{ filename:'[name].[contenthash].js'} 在打包時輸出文件名增加一個動態 hash 字符串,這也每次打包的文件名就不回重復了; 10.2、緩存第三方庫將第三方庫(lodash)單獨提取到一個固定名稱的文件中,因為這些庫一般不回做修改,所以可以利用緩存機制消除請求,減少向 server 獲取資源;(目標是第三方共享文件) optimization:{ splitChunks:{ cacheGroups:{ vendor:{ test:/[\\/]node_module[\\/]/, name:'vendors', chunks:'all'}}}} 這樣所有第三方的包就都被放到 vindors.bundle.js 中了; 10.3、將所有的 js 文件放到一個文件夾中output:{ filename:'scripts/[name].[contenthash].js'} 11、拆分開發環境和生產環境的配置11.1、公共路徑(publicPath)我們可以使用公共路徑來指定應用程序中所有資源的基礎路徑;默認值是空字符串:“” ,webpack-dev-server 也會默認從 publicPath 為基準,使用它來決定在哪個目錄下啟用服務,來訪問 webpack 輸出的文件。 output:{ publishPath:'/' //也可以是其他路徑} 11.2、環境變量環境變量可以消除 webpack.config.js 在開發環境和生產環境之間的差異;webpack 的命令行 npx webpack --env production//也可以攜帶一個 key 、valuenpx webpack --env production --env global=local 在 webpack.config.js 文件中獲取環境變量: module.exports = (env) =>{ console.log(env)return { mode: env.production ? 'production' : 'development'}} 結果: { WEBPACK_BUNDLE: true, WEBPACK_BUILD: true, production: true }//攜帶參數{ WEBPACK_BUNDLE: true, WEBPACK_BUILD: true, production: true, global: 'local'} js 壓縮 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()}} 上面這倆壓縮插件只會在生產環境中生效,開發環境中不會壓縮; 11.3、拆分配置文件拆分配置文件的目的就是將生產環境和開發環境的配置文件分開,單獨配置:webpack.config.dev.js、webpack.config.prod.js ;然后統一放到配置文件夾中; //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 開發環境不需要清理服務器緩存,不需要 publicPath,mode 可以直接設置為 development,需要 devtool,不需要壓縮相關配置; 我們可以在控制臺運行這個配置: npx webpack -c ./config/webpack.config.dev.js 由于提前設置的 //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:{//關閉提示信息 hints:false}}12345678910111213141516171819202122232425262728293031323334 11.4、合并配置文件由于配置文件開發環境和生產環境中有很多相同的配置,我們可以把相同配置提取到 webpack.config.common.js 文件中;然后再將配置合并: 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 腳本每次打包或者啟動服務時,都需要在命令行輸入一長串的命令,這里我們配置 npm 來簡化命令行; "scripts":{"start": "npx webpack serve -c ./config/webpak.config.js --env development"} 在配置 npm 的時候,我們可以省略 npm 或者 npx "scripts":{"start": "webpack serve -c ./config/webpak.config.js --env development"} 2、在命令行運行 npm run start 由于改寫命令行的時候在后面傳入了環境變量,所以這個時候 start 就代表執行 development 環境的打包; 入門部分就到這里了,后面會繼續深入學習 webpack! 該文章在 2024/4/3 14:25:59 編輯過 |
關鍵字查詢
相關文章
正在查詢... |