webpack基础学习,各个loader和plugin的具体配置
一、邂逅Webpack
Webpack是什么
webpack是一個靜態的模塊化打包工具,為現代的JavaScript應用程序;
-
打包bundler:webpack可以將幫助我們進行打包,所以它是一個打包工具
-
靜態的static:這樣表述的原因是我們最終可以將代碼打包成最終的靜態資源(部署到靜態服務器);
-
模塊化module:webpack默認支持各種模塊化開發,ES Module、CommonJS、AMD等;
-
現代的modern:我們前端說過,正是因為現代前端開發面臨各種各樣的問題,才催生了webpack的出現和發展;
二、webpack配置和css處理
webpack配置文件
1、出口、入口的配置
我們可以在根目錄下創建一個webpack.config.js文件,來作為webpack的配置文件:
module.exports = {entry: " 指定入口路徑",output: {filename:"bundle.js", // 出口名字path: '出口路徑'} }2、css-loader的使用
loader是什么
-
loader 可以用于對模塊的源代碼進行轉換;
-
我們可以將css文件也看成是一個模塊,我們是通過import來加載這個模塊的;
-
在加載這個模塊時,webpack其實并不知道如何對其進行加載,我們必須制定對應的loader來完成這個功能;
module.rules的配置如下:
-
test屬性:用于對resource(資源)進行匹配的,通常會設置成正則表達式;
-
use屬性:對應的值時一個數組:[UseEntry]
-
UseEntry是一個對象,可以通過對象的屬性來設置一些其他屬性
-
loader:必須有一個loader屬性,對應的值是一個字符串;
-
options:可選的屬性,值是一個字符串或者對象,值會被傳入到loader中;
-
query:目前已經使用options來替代;
-
-
傳遞字符串(如:use: [ 'style-loader' ])是loader 屬性的簡寫方式(如:use: [ { loader: 'style-loader'} ])
-
-
loader屬性:Rule.use: [ { loader } ] 的簡寫。
3、style-loader
當我們通過css-loader來加載css文件時,代碼沒有生效。這是因為css-loader只是將.css文件進行解析,并不會將解析之后的css插入到頁面中,而style-loader將完成插入style的操作
注意:因為loader的執行順序是從右向左(或者說從下到上,或者說從后到前的),所以我們需要將styleloader寫到css-loader的前面;
? ? ? ?use:[{loader:"style-loader"},{loader:"css-loader"}]4、less-loader
? ? ? use:[{loader:"style-loader"},{loader:"css-loader"}{loader:"less-loader"}]5、瀏覽器的兼容性
認識browserslist工具
-
Browserslist編寫規則一:
-
defaults:Browserslist的默認瀏覽器(> 0.5%, last 2 versions, Firefox ESR, not dead)。
-
5%:通過全局使用情況統計信息選擇的瀏覽器版本。>=,<和<=工作過。
-
dead:24個月內沒有官方支持或更新的瀏覽器。現在是IE 10,IE_Mob11,BlackBerry 10,BlackBerry 7,Samsung 4和OperaMobile12.1。
-
last 2 versions:每個瀏覽器的最后2個版本。
-
配置browserslist
-
方案一:在package.json中配置;
"browserslist": ["last 2 version","not dead","> 0.2%" ] -
方案二:單獨的一個配置文件.browserslistrc文件;
last 2 versionnot dead> 0.2%
6、認識postCss工具
PostCSS是一個通過JavaScript來轉換樣式的工具,這個工具可以幫助我們進行一些CSS的轉換和適配,比如自動添加瀏覽器前綴、css樣式的重置;
如何使用
安裝工具:postcss、postcss-cli
npm install postcss postcss-cli -D插件autoprefixer
添加瀏覽器前綴需要安裝autoprefixer
npm install autoprefixer -D直接使用使用postcss工具,并且制定使用autoprefixer
npx postcss --use autoprefixer -o end.css ./src/css/style.csspostcss-loader
-
借助構建工具進行css處理
-
單獨的postcss配置
在跟目錄下創建postcss.config.js
module.exports = {plugins: [require('autoprefixer')] }-
postcss-preset-env
在項目中配置postcss-loader時,我們一般不使用autoprefixer。而是使用另一插件postcss-preset-env
-
postcss-preset-env也是一個postcss的插件;
-
它可以幫助我們將一些現代的CSS特性,轉成大多數瀏覽器認識的CSS,并且會根據目標瀏覽器或者運行時環境添加所需的polyfill;
-
也包括會自動幫助我們添加autoprefixer(所以相當于已經內置了autoprefixer);
安裝
npm install postcss-preset-env -D使用
module.exports = {plugins: [require('postcss-preset-env')] }三、加載和處理其他資源
1、file-loader
用來處理jpg、png等格式的圖片
-
file-loader的作用就是幫助我們處理import/require()方式引入的一個文件資源,并且會將它放到我們輸出的文件夾中;
安裝
npm install file-loader -D配置:
{test:/\.(png|jpe?g|svg|gif)$/i,use: {loader: "file-loader"} ?}1、文件名稱規則
對處理后的文件名稱按照一定的規則進行顯示,一般使用PlaceHolders來完成,webpack給我們提供了大量的PlaceHolders來顯示不同的內容:
介紹幾個最常用的placeholder:
-
[ext]:處理文件的擴展名;
-
[name]:處理文件的名稱;
-
[hash]:文件的內容,使用MD4的散列函數處理,生成的一個128位的hash值(32個十六進制);
-
[contentHash]:在file-loader中和[hash]結果是一致的(在webpack的一些其他地方不一樣,后面會講到);
-
[hash:<length>]:截圖hash的長度,默認32個字符太長了;
-
[path]:文件相對于webpack配置文件的路徑;
2、設置文件名稱和存放路徑
{test:/\.(png|jpe?g|svg|gif)$/i,use: {loader: "file-loader"options: {name: "img/[name].[hash:8].[ext]",outputPath: "img"}} ?}2、url-loader
將較小的文件轉換為base64的URI
安裝:
npm install url-loader -D配置:
{test:/\.(png|jpe?g|svg|gif)$/i,use: {loader: "url-loader"options: {name: "img/[name].[hash:8].[ext]",outputPath: "img"}} ?}打包之后的顯示結果跟file-loader一樣,但是在打包好的dist文件夾中,看不到圖片文件,而是轉換為base64格式存儲。
limit屬性
限制轉換base64格式的圖片大小(比如小于100kb)
{test:/\.(png|jpe?g|svg|gif)$/i,use: {loader: "url-loader"options: {limit:100 * 1024,name: "img/[name].[hash:8].[ext]",outputPath: "img"}} ?}3、asset module type
1、介紹
-
webpack5之前,加載這些資源我們需要使用一些loader,比如raw-loader 、url-loader、file-loader
-
webpack5之后,我們可以直接使用資源模塊類型(asset module type),來替代上面的這些loader;
資源模塊類型(asset module type),通過添加4 種新的模塊類型,來替換所有這些loader
-
asset/resource發送一個單獨的文件并導出URL。之前通過使用file-loader 實現
-
asset/inline導出一個資源的data URI。之前通過使用url-loader 實現;
-
asset/source導出資源的源代碼。之前通過使用raw-loader 實現;
-
asset在導出一個data URI 和發送一個單獨的文件之間自動選擇。之前通過使用url-loader,并且配置資源體積限制實現;
2、使用
? ? {test: /\.(png|jpe?g|svg|gif)$/i,type: "asset/resource",},-
如何自定義文件的輸出路徑和文件名
-
url-loader中的limit限制圖片大小效果
4、加載字體文件
處理特殊字體或者字體圖標的使用
我們可以選擇使用file-loader來處理,也可以選擇直接使用webpack5的資源模塊類型來處理;
{test: /\.(woff2?|eot|ttf)$/,type: "asset/resource",generator: {filename: "img/[name].[hash:6][ext]",},},四、認識plugin
-
Loader是用于特定的模塊類型進行轉換;
-
Plugin可以用于執行更加廣泛的任務,比如打包優化、資源管理、環境變量注入等;
1、CleanWebpackPlugin
每次修改了一些配置,重新打包時,都需要手動刪除dist文件夾,CleanWebpackPlugin可以幫助我們完成這個功能。
安裝:
npm install clean-webpack-plugin -D配置:
const { CleanWebpackPlugin } = require("clean-webpack-plugin");module.exports = {plugins : [new CleanWebpackPlugin()]};2、HtmlWebpackPlugin
HtmlWebpackPlugin用來對HTML進行打包處理
安裝:
npm install html-webpack-plugin -D配置:
const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = {plugins: [new CleanWebpackPlugin(),new HtmlWebpackPlugin({titile: "webpack案例",}),], };3、自定義HTML模板
如果我們想在自己的模塊中加入一些比較特別的內容:
-
添加一個noscript標簽,在用戶的JavaScript被關閉時,給予響應的提示;
-
比如在開發vue或者react項目時,我們需要一個可以掛載后續組件的根標簽<div id="app"></div>;
自定義模板數據填充
上面的代碼中,會有一些類似這樣的語法<%變量%>,這個是EJS模塊填充數據的方式。
在配置HtmlWebpackPlugin時,我們可以添加如下配置:
-
template:指定我們要使用的模塊所在的路徑;
-
title:在進行htmlWebpackPlugin.options.title讀取時,就會讀到該信息;
4、DefinePlugin的介紹
當在我們的模塊中還使用到一個BASE_URL的常量,我們需要設置這個常量,這個時候我們可以使用DefinePlugin插件;
使用:
DefinePlugin允許在編譯時創建配置的全局常量,是一個webpack內置的插件(不需要單獨安裝):
const { DefinePlugin } = require("webpack"); module.exports = {plugins: [new DefinePlugin({BASE_URL:'"./"' ?// 注意需要多一層包裹})], };5、CopyWebpackPlugin
vue的打包過程中,如果我們將一些文件放到public的目錄下,那么這個目錄會被復制到dist文件夾中。這個復制的功能,我們可以使用CopyWebpackPlugin來完成;
安裝:
npm install copy-webpack-plugin -D配置:
-
from:設置從哪一個源中開始復制;
-
to:復制到的位置,可以省略,會默認復制到打包的目錄下;
-
globOptions:設置一些額外的選項,其中可以編寫需要忽略的文件:
-
.DS_Store:mac目錄下回自動生成的一個文件;
-
index.html:也不需要復制,因為我們已經通過HtmlWebpackPlugin完成了index.html的生成;
-
五、source-map
source-map是從已轉換的代碼,映射到原始的源文件。使瀏覽器可以重構原始源并在調試器中顯示重建的原始源
使用:
第一步:根據源文件,生成source-map文件,webpack在打包時,可以通過配置生成source-map;
第二步:在轉換后的代碼,最后添加一個注釋,它指向sourcemap;
//# sourceMappingURL=common.bundle.js.map瀏覽器會根據我們的注釋,查找響應的source-map,并且根據source-map還原我們的代碼,方便進行調試。
分析source-map
-
version:當前使用的版本,也就是最新的第三版;
-
sources:從哪些文件轉換過來的source-map和打包的代碼(最初始的文件);
-
names:轉換前的變量和屬性名稱(因為我目前使用的是development模式,所以不需要保留轉換前的名稱);
-
mappings:source-map用來和源文件映射的信息(比如位置信息等),一串base64VLQ(veriablelengthquantity可變長度值)編碼;
-
file:打包后的文件(瀏覽器加載的文件);
-
sourceContent:轉換前的具體代碼信息(和sources是對應的關系);
-
sourceRoot:所有的sources相對的根目錄;
生成source-map
webpack為我們提供了非常多的選項,目前為止是26個,來處理source-map,選擇不同的值,打包形成的代碼會有性能的差異,可以根據不同情況進行選擇
-
不會生成source-map的配置項
-
false:不使用source-map,也就是沒有任何和source-map相關的內容。
-
nnone:production模式下的默認值,不生成source-map。
-
eval:development模式下的默認值,不生成source-map
-
但是它會在eval執行的代碼中,添加//#sourceURL=;
-
它會被瀏覽器在執行時解析,并且在調試面板中生成對應的一些文件目錄,方便我們調試代碼;
-
-
source-map值
生成一個獨立的source-map文件,并且在bundle文件中有一個注釋,指向source-map文件;
bundle文件中有如下的注釋:
//# sourceMappingURL=bundle.js.mapeval-source-map值
eval-source-map:會生成sourcemap,但是source-map是以DataUrl添加到eval函數的后面
inline-source-map值
inline-source-map:會生成sourcemap,但是source-map是以DataUrl添加到bundle文件的后面
cheap-source-map
cheap-source-map:
-
會生成sourcemap,但是會更加高效一些(cheap低開銷),因為它沒有生成列映射(Column Mapping)
cheap-module-source-map值
會生成sourcemap,類似于cheap-source-map,但是對源自loader的sourcemap處理會更好。
hidden-source-map值
-
會生成sourcemap,但是不會對source-map文件進行引用;
-
相當于刪除了打包文件中對sourcemap的引用注釋;
nosources-source-map值
會生成sourcemap,但是生成的sourcemap只有錯誤信息的提示,不會生成源代碼文件;
多個值的組合(重要)
事實上,webpack提供給我們的26個值,是可以進行多組合的。
組合的規則如下:
-
inline-|hidden-|eval:三個值時三選一;
-
nosources:可選值;
-
cheap可選值,并且可以跟隨module的值;
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
在開發中,最佳的實踐是什么呢?
-
開發階段:推薦使用source-map或者cheap-module-source-map
-
測試階段:推薦使用source-map或者cheap-module-source-map
-
發布階段:false、缺省值(不寫)
六、Babel深入理解
1、babel是什么東西,用來做什么的
-
Babel是一個工具鏈,主要用于舊瀏覽器或者緩解中將ECMAScript 2015+代碼轉換為向后兼容版本的JavaScript;
-
語法轉換、源代碼轉換、Polyfill實現目標緩解缺少的功能等;
Babel命令行使用
-
@babel/core:babel的核心代碼,必須安裝;
-
@babel/cli:可以讓我們在命令行使用babel;
使用babel來處理我們的源代碼:
-
src:是源文件的目錄;
-
--out-dir:指定要輸出的文件夾dist;
插件的使用
比如我們需要轉換箭頭函數,那么我們就可以使用箭頭函數轉換相關的插件:
npm install @babel/plugin-transform-arrow-functions -D npx babel src--out-dirdist--plugins=@babel/plugin-transform-arrow-functions查看轉換后的結果,我們會發現const 并沒有轉成var,這是因為plugin-transform-arrow-functions,并沒有提供這樣的功能,我們需要使用plugin-transform-block-scoping 來完成這樣的功能。
npm install @babel/plugin-transform-block-scoping -D npx babel src--out-dirdist--plugins=@babel/plugin-transform-block-scoping @babel/plugin-transform-arrow-functionsBabel的預設preset
如果要轉換的內容過多,一個個設置是比較麻煩的,我們可以使用預設(preset)
安裝 :
npm install @babel/preset-env -D執行:
npx babel src--out-dirdist--presets=@babel/preset-env2、Babel的底層原理
工作流程:
-
解析階段(Parsing)
-
轉換階段(Transformation)
-
生成階段(CodeGeneration)
流程圖:
3、babel-loader
安裝:
npm install babel-loader @babel/core使用:
module.exports = {module: {rules: [{test:/\.js$/,use: {loader: "babel-loader"}}]} }指定使用的插件
我們必須指定使用的插件才會生效
module: {rules: [{test:/\.js$/,use: {loader: "babel-loader",options: {plugins: ["@babel/plugin-transform-block-scoping","@babel/plugin-transform-arrow-functions"]}}}]}babel-preset
如果我們一個個去安裝使用插件,那么需要手動來管理大量的babel插件,我們可以直接給webpack提供一個preset,webpack會根據我們的預設來加載對應的插件列表,并且將其傳遞給babel。
常見的預設:
-
env
-
react
-
TypeScript
安裝preset-env:
npm install @babel/preset-env {test:/\.js$/,use: {loader: "babel-loader",options: {plugins: ["@babel/plugin-transform-block-scoping","@babel/plugin-transform-arrow-functions"]}} }Babel的Stage-X設置
在babel7之前(比如babel6中),我們會經常看到這種設置方式:
-
它表達的含義是使用對應的babel-preset-stage-x預設;
-
從babel7開始,已經不建議使用了,建議使用preset-env來設置;
4、Babel的配置文件
我們可以將babel的配置信息放到一個獨立的文件中,babel給我們提供了兩種配置文件的編寫:
-
babel.config.json(或者.js,.cjs,.mjs)文件;
-
.babelrc.json(或者.babelrc,.js,.cjs,.mjs)文件;
區別:
-
.babelrc.json:早期使用較多的配置方式,但是對于配置Monorepos項目是比較麻煩的;
-
babel.config.json(babel7):可以直接作用于Monorepos項目的子包,更加推薦;
5、認識polyfill
更像是應該填充物(墊片),一個補丁,可以幫助我們更好的使用JavaScript;
使用場景:
比如我們使用了一些語法特性(例如:Promise,Generator,Symbol等以及實例方法例如Array.prototype.includes等),但是某些瀏覽器壓根不認識這些特性,必然會報錯,我們可以使用polyfill來填充或者說打一個補丁,那么就會包含該特性了;
可以通過單獨引入core-js和regenerator-runtime來完成polyfill的使用:
npm install core-js regenerator-runtime --save {test:/\.m?js$/,exclude:/node_modules/,use:"babel-loader" }配置babel.config.js
我們需要在babel.config.js文件中進行配置,給preset-env配置一些屬性:
-
useBuiltIns:設置以什么樣的方式來使用polyfill;
-
corejs:設置corejs的版本
-
另外corejs可以設置是否對提議階段的特性進行支持;
-
設置proposals屬性為true即可;
-
useBuiltIns屬性設置
-
useBuiltIns屬性有三個常見的值
第一個值:false
-
打包后的文件不使用polyfill來進行適配;
-
并且這個時候是不需要設置corejs屬性的;
第二個值:usage
-
會根據源代碼中出現的語言特性,自動檢測所需要的polyfill;
-
這樣可以確保最終包里的polyfill數量的最小化,打包的包相對會小一些;
-
可以設置corejs屬性來確定使用的corejs的版本;
第三個值:entry
-
如果我們依賴的某一個庫本身使用了某些polyfill的特性,但是因為我們使用的是usage,所以之后用戶瀏覽器可能會報錯,果你擔心出現這種情況,可以使用entry;
-
需要在入口文件中添加`import 'core-js/stable'; import 'regenerator-runtime/runtime';
-
這樣做會根據browserslist目標導入所有的polyfill,但是對應的包也會變大;
-
6、認識Plugin-transform-runtime(了解)
在前面我們使用的polyfill,默認情況是添加的所有特性都是全局的,如果我們正在編寫一個工具庫,這個工具庫需要使用polyfill,別人在使用我們工具時,工具庫通過polyfill添加的特性,可能會污染它們的代碼,所以,當編寫工具時,babel更推薦我們使用一個插件:@babel/plugin-transform-runtime來完成polyfill的功能;
7、React的jsx支持
安裝:
npm install @babel/preset-react -D使用:
presets: [["@babel/preset-env", {useBuiltIns:"usage",corejs: 3.8}],["@babel/preset-react"] ]8、TypeScript的編譯
TypeScript通過compiler來轉換成JavaScript
安裝:
npm install typescript -DTypeScript的編譯配置信息我們通常會編寫一個tsconfig.json文件:
tsc --init之后我們可以運行npxtsc來編譯自己的ts代碼:
npx tsc1、使用ts-loader編譯TS
安裝:
npm install ts-loader -D配置:
{test:'/\.ts$/',exclude: /node_modules/,use: ["ts-loader"] }2、使用babel-loader編譯TS
Babel是有對TypeScript進行支持
-
我們可以使用插件:@babel/tranform-typescript;
-
但是更推薦直接使用preset:@babel/preset-typescript;
安裝:
npm install @babel/preset-typescript -D配置:
{test:'/\.ts$/',exclude: /node_modules/,use: ["babel-loader"] }七、大數據中關于代碼格式校驗(Eslint和prettierrc )
大數據中關于eslint的配置
/** @Description: eslint 配置* @ 規則依賴于 @umijs/fabric,在此基礎上,可自行添加自己的規則進行配置* @Author: 賈永昌* @Date: 2022-05-01 13:55:14* @LastEditTime: 2022-05-02 17:35:45*/ module.exports = {extends: [require.resolve('@umijs/fabric/dist/eslint')], ?// in antd-design-proglobals: {ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true,page: true,}, ?rules: {// 強制語句有分號結尾semi: [2, 'always'],// ? 操作符前后必須有空格 bad: 1||2 good: 1 || 2'space-infix-ops': 2,// ? 對象字面量中冒號前面禁止有空格,后面必須有空格 bad: {a :'a'} good:{a: 'a'}'key-spacing': 2,// ? 花括號首尾必須有空格'object-curly-spacing': [2, 'always'],// ? 語句塊(if、function、class、try...catch等的大括號) 的前面必須要有空格'space-before-blocks': 2,// ? 箭頭函數的箭頭與后面的{}之間需要空格'arrow-spacing': 2,// ? 禁止多余的空格'no-multi-spaces': 2,// ? 禁止代碼行結束后面有多余空格'no-trailing-spaces': 2,// ? 禁止多余空行'no-multiple-empty-lines': ['error', { max: 1, maxBOF: 1, maxEOF: 1 }],// ? 允許標識符中使用懸空下劃線(標識符的開頭或末尾的下劃線)'no-underscore-dangle': 0,// ? 允許邏輯短路、三元運算符等表達式求值'no-unused-expressions': 0,// ? 禁止使用嵌套的三元表達式'no-nested-ternary': 2,// ? 禁止對函數參數再賦值(保證react函數式編程純函數的概念)'no-param-reassign': 2,// ? 禁止使用 var 定義變量'no-var': 2,// ? 禁止修改const聲明的變量'no-const-assign': 2,// ? 函數調用時 函數名與()之間不能有空格'no-spaced-func': 2, ?// ? jsx 屬性中強制使用雙引號'jsx-quotes': [2, 'prefer-double'],// ? 禁止 jsx 屬性對象的引用括號里 兩邊加空格'react/jsx-curly-spacing': [2, 'never'],// ? JSX 中前標簽傳有屬性換行展示的話,其后面的 > 也需換行對齊展示'react/jsx-closing-bracket-location': 2,// ? 校驗 jsx 中所有換行屬性值縮進'react/jsx-indent-props': [2, 2],// ? jsx 中傳入屬性值是Boolean值且為true時,省略傳入'react/jsx-boolean-value': 2,// ? 在 JSX 屬性中禁止等號前后存在空格'react/jsx-equals-spacing': 2, ?// ? 關閉此規則,允許 useEffect 的依賴為空數組'react-hooks/exhaustive-deps': 0, ?// ? 未使用的變量警告提醒'@typescript-eslint/no-unused-vars': ['warn'],// ? 禁用使用在前,保證 useEffct 使用在最前面,這時候里面如果使用了外部的函數就會報這錯'@typescript-eslint/no-use-before-define': 0,// ? 允許空的 ts 接口定義 eg: interface IProps {}'@typescript-eslint/no-empty-interface': 0,}, }; 大數據中 .prettierrc 配置 {"printWidth": 80,"tabWidth": 2,"singleQuote": true,"useTabs": false,"semi": true,"jsxSingleQuote": false,"trailingComma": "all","bracketSpacing": true,"jsxBracketSameLine": false,"arrowParens": "always","requirePragma": false,"insertPragma": false,"proseWrap": "preserve", "htmlWhitespaceSensitivity": "css","overrides": [{"files": ".prettierrc","options": { "parser": "json" }}] }八、DevServer
為什么需要搭建本地服務器?
我們希望可以做到,當文件發生變化時,可以自動完成編譯和展示
-
webpack watch mode
-
webpack-dev-server
-
webpack-dev-middleware
1、Webpack watch(基本不用)
webpack給我們提供了watch模式:
-
在該模式下,webpack依賴圖中的所有文件,只要有一個發生了更新,那么代碼將被重新編譯(損耗性能)
開啟watch的兩種方式
-
方式一:在導出的配置中,添加watch: true;
-
方式二:在啟動webpack的命令中,添加--watch的標識;
2、webpack-dev-server(必會,常用)
除了可以監聽到文件的變化,還可以具備實時重新加載的功能
安裝:
npm install --save-dev webpack-dev-server配置:
"scripts" : {"watch":"webpack --watch""serve":"webpack serve --config wk.config.js" }webpack-dev-server 在編譯之后不會寫入到任何輸出文件。而是將bundle 文件保留在內存中:
-
事實上webpack-dev-server使用了一個庫叫memfs(memory-fswebpack自己寫的)
3、webpack-dev-middleware(基本不用)
如果我們想要有更好的自由度,可以使用webpack-dev-middleware;
定義:
webpack-dev-middleware 是一個封裝器(wrapper),它可以把webpack處理過的文件發送到一個server,webpack-dev-server 在內部使用了它,然而它也可以作為一個單獨的package 來使用,以便根據需求進行更多自定義設置;
4、output的publicPath(outPut中的配置)
output中還有一個publicPath屬性,該屬性是指定index.html文件打包引用的一個基本路徑
-
它的默認值是一個空字符串,所以我們打包后引入js文件時,路徑是bundle.js;
-
在開發中,我們也將其設置為/,路徑是/bundle.js,那么瀏覽器會根據所在的域名+路徑去請求對應的資源;
-
如果我們希望在本地直接打開html文件來運行,會將其設置為./,路徑時./bundle.js,可以根據相對路徑去查找資源;
module.exports = {entry: " 指定入口路徑",output: {filename:"bundle.js", // 出口名字path: '出口路徑'publicPath:'./'} }
5、devServer的publicPath
devServer中也有一個publicPath的屬性,該屬性是指定本地服務所在的文件夾
-
它的默認值是/,也就是我們直接訪問端口即可訪問其中的資源http://localhost:8080
-
如果我們將其設置為了/abc,那么我們需要通過http://localhost:8080/abc才能訪問到對應的打包后的資源
-
并且這個時候,我們其中的bundle.js通過http://localhost:8080/bundle.js也是無法訪問的:
-
所以必須將output.publicPath也設置為/abc;
-
官方其實有提到,建議devServer.publicPath與output.publicPath相同;
-
6、devServer的contentBase(不常用)
主要作用是如果我們打包后的資源,又依賴于其他的一些資源,那么就需要指定從哪里來查找這個內容
-
比如在index.html中,我們需要依賴一個abc.js文件,這個文件我們存放在public文件中
-
在index.html中,我們應該如何去引入這個文件
-
比如代碼是這樣的:<script src="./public/abc.js"></script>
-
但是這樣打包后瀏覽器是無法通過相對路徑去找到這個文件夾的;
-
所以代碼是這樣的:<script src="/abc.js"></script>;
-
但是我們如何讓它去查找到這個文件的存在呢?設置contentBase即可
-
7、hotOnly、hos、port、open、compress配置
-
hotOnly是當代碼編譯失敗時,是否刷新整個頁面
-
port設置監聽的端口,默認情況下是8080
-
host設置主機地址
-
open是否打開瀏覽器
-
compress是否為靜態文件開啟gzip compression
8、Proxy代理(解決跨域問題)注意:在開發環境中使用
我們可以將請求先發送到一個代理服務器,代理服務器和API服務器沒有跨域的問題,就可以解決我們的跨域問題了
配置:
-
target:表示的是代理到的目標地址,比如/api-hy/moment會被代理到http://localhost:8888/api-hy/moment;
-
pathRewrite:默認情況下,我們的/api-hy也會被寫入到URL中,如果希望刪除,可以使用pathRewrite;
-
secure:默認情況下不接收轉發到https的服務器上,如果希望支持,可以設置為false;
-
changeOrigin:它表示是否更新代理后請求的headers中host地址;
9、historyApiFallback
-
historyApiFallback是開發中一個非常常見的屬性,它主要的作用是解決SPA頁面在路由跳轉之后,進行頁面刷新時,返回404的錯誤。
-
boolean值:默認是false
-
如果設置為true,那么在刷新時,返回404錯誤時,會自動返回index.html的內容;
-
-
object類型的值,可以配置rewrites屬性:
-
可以配置from來匹配路徑,決定要跳轉到哪一個頁面;
-
九、模塊熱替換(HMR)
什么是HMR
-
HMR的全稱是Hot Module Replacement,翻譯為模塊熱替換;
-
模塊熱替換是指在應用程序運行過程中,替換、添加、刪除模塊,而無需重新刷新整個頁面;
如何使用HMR
-
默認情況下,webpack-dev-server已經支持HMR,我們只需要開啟即可;
-
在不開啟HMR的情況下,當我們修改了源代碼之后,整個頁面會自動刷新,使用的是live reloading;
1、開啟HMR
// 在webpack.config.js 中添加以下配置 devserver: {hot:true }同時還需要指定發生更新的模塊
if(module.hot) {module.hot.accept("./*文件路徑",() => {console.log()}) }2、框架中的HMR
項目中已經有非常成熟的方案,別操心了
-
vue開發中,我們使用vue-loader,此loader支持vue組件的HMR,提供開箱即用的體驗
-
react開發中,有React HotLoader,實時調整react組件(目前React官方已經棄用了,改成使用reactrefresh);
3、HMR的原理
HMR的原理是什么
-
webpack-dev-server會創建兩個服務:提供靜態資源的服務(express)和Socket服務(net.Socket)
-
express server負責直接提供靜態資源的服務(打包后的資源直接被瀏覽器請求和解析);
HMR Socket Server,是一個socket的長連接:(想想webSocket,需要及時通信)
-
長連接有一個最好的好處是建立連接后雙方可以通信(服務器可以直接發送文件到客戶端)
-
當服務器監聽到對應的模塊發生變化時,會生成兩個文件.json(manifest文件)和.js文件(update chunk);
-
通過長連接,可以直接將這兩個文件主動發送給客戶端(瀏覽器)
-
瀏覽器拿到兩個新的文件后,通過HMR runtime機制,加載這兩個文件,并且針對修改的模塊進行更新;
十、resolve模塊解析
resolve用于設置模塊如何被解析:
-
resolve可以幫助webpack從每個require/import 語句中,找到需要引入到合適的模塊代碼;
-
webpack 使用enhanced-resolve來解析文件路徑;
webpack能解析三種文件路徑:
-
絕對路徑
-
相對路徑
-
模塊路徑:在resolve.modules中指定的所有目錄檢索模塊,默認值是['node_modules'],所以默認會從node_modules中查找文件;
1、extensions和alias配置
extensions是解析到文件時自動添加擴展名:
-
默認值是['.wasm','.mjs','.js','.json'];
配置:
module.exports = {entry: " 指定入口路徑",output: {filename:"bundle.js", // 出口名字path: '出口路徑'},resolve:{extensions:['.wasm','.mjs','.js','.json','.jsx','.ts'],} }
我們可以使用alias給某些常見的路徑起一個別名;
resolve:{extensions:['.wasm','.mjs','.js','.json','.jsx','.ts'],alias: {"@":resolveApp('./src'),pages:resolveApp('./src/pages')}}十一、環境分離和代碼分離
1、入口文件解析
context的作用是用于解析入口(entry point)和加載器(loader)
默認是webpack的啟動目錄
module.exports = {context:path.resolve(__dirname,'./')entry:"../src/index.js" }2、配置文件的分離
-
將原來的webpack.config.js劃分為webpack.comm.conf.js(通用配置)、webpack.dev.conf.js(開發環境)、webpack.prod.conf.js(生產環境)三部分
-
利用mode配置項,區分開發環境和生產環境。用利用merge將用到的環境配置和通用配置合并
3、認識代碼分離
代碼分離(CodeSplitting)主要的目的是將代碼分離到不同的bundle中,之后我們可以按需加載,或者并行加載這些文件;
Webpack中常用的代碼分離有三種
-
入口起點:使用entry配置手動分離代碼;
-
防止重復:使用EntryDependencies或者SplitChunksPlugin去重和分離代碼;
-
動態導入:通過模塊的內聯函數調用來分離代碼;
1、多入口起點
entry: {index:"./src/index.js",main:"./src/main.js" } output: {filename:"[name].bundle.js",path:resolveApp("./build") }2、EntryDependencies(入口依賴)
假如我們的index.js和main.js都依賴兩個庫:lodash、dayjs
-
如果我們單純的進行入口分離,那么打包后的兩個bunlde都有會有一份lodash和dayjs;
entry: {index: {import:"./src/index.js",dependOn:"shared"},main:{import:"./src/main.js",dependOn:"shared"},shared:['lodash','axios'] } output: {filename:"[name].bundle.js",path:resolveApp("./build"),publicPath: "" }
3、SplitChunks
另外一種分包的模式是splitChunk,它是使用SplitChunksPlugin來實現的:
Webpack提供了SplitChunksPlugin默認的配置,我們也可以手動來修改它的配置:
-
比如默認配置中,chunks僅僅針對于異步(async)請求,我們可以設置為initial或者all;
4、SplitChunks自定義配置
SplitChunks: {chunks:'all',// 拆分包的大小,至少為minsize// 如果一個包拆分出來不到minsize,那么將不會被拆分minsize:100,// 將大于maxMize的包,拆分成不小于minSize 的包maxsize:1000,// 至少包被引入的次數minChunks:2,// 最大異步請求數量maxAsyncRequests:30,// 最大的初始化請求數量cacheGroups: {venders: {test:/[\\/]node_modules[\\/]/,priority: -10,filename: "[id]_[hash:6]_vendor.js"},foo: {test:/foo/,priority: -20,filename: "foo_[id]_[name]_.js"}} }配置解析:
-
Chunks
-
默認值是async
-
另一個值是initial,表示對通過的代碼進行處理
-
all表示對同步和異步代碼都進行處理
-
-
minSize
-
拆分包的大小, 至少為minSize;
-
如果一個包拆分出來達不到minSize,那么這個包就不會拆分;
-
-
maxSize
-
將大于maxSize的包,拆分為不小于minSize的包;
-
-
minChunks
-
至少被引入的次數,默認是1;
-
如果我們寫一個2,但是引入了一次,那么不會被單獨拆分;
-
-
name:設置拆包的名稱
-
可以設置一個名稱,也可以設置為false;
-
設置為false后,需要在cacheGroups中設置名稱;
-
-
cacheGroups
-
用于對拆分的包就行分組,比如一個lodash在拆分之后,并不會立即打包,而是會等到有沒有其他符合規則的包一起來打包;
-
test屬性:匹配符合規則的包;
-
name屬性:拆分包的name屬性;
-
filename屬性:拆分包的名稱,可以自己使用placeholder屬性;
-
5、動態導入(dynamic import)
使用ECMAScript中的import()語法來完成,也是目前推薦的方式;
注意:
-
在webpack中,通過動態導入獲取到一個對象;
-
真正導出的內容,在改對象的default屬性中,所以我們需要做一個簡單的解構;
動態導入的文件命名
它的命名我們通常會在output中,通過chunkFilename屬性來命名
output: {filename: "[name].bundle.js",path:resolveApp("./build"),chunkFilename: "chunk_[id]_[name].js" }默認情況下我們獲取到的[name]是和id的名稱保持一致的
-
我們希望修改name的值,可以通過magic comments(魔法注釋)的方式
6、optimization.chunkIds配置
optimization.chunkIds配置用于告知webpack模塊的id采用什么算法生成。
-
natural:按照數字的順序使用id;
-
named:development下的默認值,一個可讀的名稱的id;
-
deterministic:確定性的,在不同的編譯中不變的短數字id
最佳實踐:
-
開發過程中,我們推薦使用named;
-
打包過程中,我們推薦使用deterministic;
7、optimization. runtimeChunk配置
配置runtime相關的代碼是否抽取到一個單獨的chunk中:
-
抽離出來后,有利于瀏覽器緩存的策略:
-
設置的值
-
true/multiple:針對每個入口打包一個runtime文件;
-
single:打包一個runtime文件;
-
對象:name屬性決定runtimeChunk的名稱;
-
8、Prefetch和Preload
-
webpack v4.6.0+增加了對預獲取和預加載的支持。
-
prefetch(預獲取):將來某些導航下可能需要的資源
-
preload(預加載):當前導航下可能需要資源
-
-
區別
-
preload chunk 會在父chunk 加載時,以并行方式開始加載。prefetch chunk 會在父chunk 加載結束后開始加載。
-
preload chunk 具有中等優先級,并立即下載。prefetch chunk 在瀏覽器閑置時下載。
-
preload chunk 會在父chunk 中立即請求,用于當下時刻。prefetch chunk 會用于未來的某個時刻。
-
9、CDN
CDN稱之為內容分發網絡(ContentDeliveryNetwork或ContentDistributionNetwork,縮寫:CDN)
開發中的應用方式:
-
方式一:打包的所有靜態資源,放到CDN服務器,用戶所有資源都是通過CDN服務器加載的;
-
方式二:一些第三方資源放到CDN服務器上;
方式一花錢,直接說方式二
一些比較出名的開源框架都會將打包后的源碼放到一些比較出名的、免費的CDN服務器上
使用方法:
-
第一步,我們可以通過webpack配置,來排除一些庫的打包:
externals: {lodash: "_",dayjs: "dayjs" } -
第二步,在html模塊中,加入CDN服務器地址:
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.core.js"></script>
10、認識shimming
-
shimming是一個概念,是某一類功能的統稱:
-
比如我們現在依賴一個第三方的庫,這個第三方的庫本身依賴lodash,但是默認沒有對lodash進行導入(認為全局存在lodash),那么我們就可以通過ProvidePlugin來實現shimming的效果;
-
-
注意:webpack并不推薦隨意的使用shimming
-
Webpack背后的整個理念是使前端開發更加模塊化;
-
也就是說,需要編寫具有封閉性的、不存在隱含依賴(比如全局變量)的彼此隔離的模塊;
-
11、MiniCssExtractPlugin
MiniCssExtractPlugin可以幫助我們將css提取到一個獨立的css文件中,該插件需要在webpack4+才可以使用。
安裝:
npm install mini-css-extract-plugin -D配置:
plugins: [new MiniCssExtractPlugin({filename:"css/[name].[contenthash:8].css",chunkfilename: "css/[name].[contenthash:8].css"}) ], module:{rules: [{test:/\.css$/i,use:[MiniCssExtractPlugin.loader,'css-loader']}] }12、Hash、ContentHash、ChunkHash
-
hash值的生成和整個項目有關系:
-
比如我們現在有兩個入口index.js和main.js;
-
它們分別會輸出到不同的bundle文件中,并且在文件名稱中我們有使用hash;
-
這個時候,如果修改了index.js文件中的內容,那么hash會發生變化;
-
那就意味著兩個文件的名稱都會發生變化;
-
-
chunkhash可以有效的解決上面的問題,它會根據不同的入口進行借來解析來生成hash值:
比如我們修改了index.js,那么main.js的chunkhash是不會發生改變的;
-
contenthash表示生成的文件hash名稱,只和內容有關系:
-
比如我們的index.js,引入了一個style.css,style.css有被抽取到一個獨立的css文件中;
-
這個css文件在命名時,如果我們使用的是chunkhash;
-
那么當index.js文件的內容發生變化時,css文件的命名也會發生變化;
-
這個時候我們可以使用contenthash;
-
十二、DLL_Tree Shaking
認識DLL庫(了解一下)
-
DLL全程是動態鏈接庫(Dynamic Link Library),是為軟件在Windows中實現共享函數庫的一種實現方式;
-
webpack中也有內置DLL的功能,它指的是我們可以將可以共享,并且不經常改變的代碼,抽取成一個共享的庫;
Terser介紹和安裝(一般使用默認配置)
-
Terser是一個JavaScript的解釋(Parser)、Mangler(絞肉機)/Compressor(壓縮機)的工具集;
-
早期我們會使用uglify-js來壓縮、丑化我們的JavaScript代碼,但是目前已經不再維護,并且不支持ES6+的語法;
-
-
Terser可以幫助我們壓縮、丑化我們的代碼,讓我們的bundle變得更小。
安裝:
npm install terser -g // 可以進行局部安裝,也可以全局安裝命令行使用
terser [input files] [options]常見的配置項:
Compress option
-
arrows:class或者object中的函數,轉換成箭頭函數;
-
arguments:將函數中使用arguments[index]轉成對應的形參名稱;
-
dead_code:移除不可達的代碼(tree shaking);
-
等等其他屬性,詳情看官方文檔
Mangle option
-
toplevel:默認值是false,頂層作用域中的變量名稱,進行丑化(轉換)
-
keep_classnames:默認值是false,是否保持依賴的類名稱;
-
keep_fnames:默認值是false,是否保持原來的函數名稱;
Terser在webpack中配置使用
-
(注意)真實開發中,我們不需要手動的通過terser來處理我們的代碼,我們可以直接通過webpack來處理:
-
在webpack中有一個minimizer屬性,在production模式下,默認就是使用TerserPlugin來處理我們的代碼的;
-
如果我們對默認的配置不滿意,也可以自己來創建TerserPlugin的實例,并且覆蓋相關的配置;(基本不會手動配置)
-
CSS的壓縮
安裝:
npm install css-minimizer-webpack-plugin -D在optimization.minimizer中配置:
minimizer: [new CssMinimizerplugin({parallel: true}) ]提升作用域 Scope Hoisting
-
Scope Hoisting從webpack3開始增加的一個新功能,功能是對作用域進行提升,并且讓webpack打包后的代碼更小、運行更快;
-
默認情況下webpack打包會有很多的函數作用域,Scope Hoisting可以將函數合并到一個模塊中來運行
-
使用:
-
在production模式下,默認這個模塊就會啟用;
-
在development模式下,我們需要自己來打開該模塊;
-
Tree Shaking
定義:最早的想法起源于LISP,用于消除未調用的代碼(純函數無副作用,可以放心的消除,這也是為什么要求我們在進行函數式編程時,盡量使用純函數的原因之一)
webpack實現TreeShaking
兩種方法:
-
在optimization中配置usedExports為true,來幫助Terser進行優化;
-
在package.json中配置sideEffects,直接對模塊進行優化;
usedExports
在usedExports設置為true時,會有一段注釋:unused harmony export mul,這段注釋告知Terser在優化時,可以刪除掉這段代碼
注意:
-
配置該屬性時,需要將mode設置為development模式
-
usedExports實現tree Shaking是結合terse來完成的
sideEffects
sideEffects用于告知webpack compiler哪些模塊時有副作用的(副作用的意思是這里面的代碼有執行一些特殊的任務,不能僅僅通過export來判斷這段代碼的意義;)
-
在package.json中設置sideEffects的值:
-
false:告知webpack可以安全的刪除未用到的exports;
-
如果有一些希望保留,可以設置數組
-
CSS實現TreeShaking
我們可以使用一個庫來完成CSS的Tree Shaking:PurgeCSS,幫助我們刪除未使用的CSS的工具
安裝:
npm install purgecss-webpack-plugin -D配置:
-
paths:表示要檢測哪些目錄下的內容需要被分析,這里我們可以使用glob;
-
默認情況下,Purgecss會將我們的html標簽的樣式移除掉,如果我們希望保留,可以添加一個safelist的屬性;
-
purgecss也可以對less文件進行處理(所以它是對打包后的css進行tree shaking操作)
HTTP壓縮
定義:HTTP壓縮是一種內置在服務器和客戶端之間的,以改進傳輸速度和帶寬利用率的方式
流程:
第一步:HTTP數據在服務器發送前就已經被壓縮了
第二步:兼容的瀏覽器在向服務器發送請求時,會告知服務器自己支持哪些壓縮格式
第三步:服務器在瀏覽器支持的壓縮格式下,直接返回對應的壓縮后的文件,并且在響應頭中告知瀏覽器;
目前的壓縮格式
-
compress–UNIX的“compress”程序的方法(歷史性原因,不推薦大多數應用使用,應該使用gzip或deflate);
-
deflate–基于deflate算法(定義于RFC1951)的壓縮,使用zlib數據格式封裝;
-
gzip–GNUzip格式(定義于RFC1952),是目前使用比較廣泛的壓縮算法;
-
br–一種新的開源壓縮算法,專為HTTP內容的編碼而設計;
Webpack對文件壓縮
webpack中相當于是實現了HTTP壓縮的第一步操作,我們可以使用CompressionPlugin。
安裝:
npm install compression-webpack-plugin -D配置:
new CompressionPlugin({test:/\.(css|js)$/, ?// 匹配哪些文件需要壓縮threshold:500, // 設置文件多大開始壓縮minRatio: 0.7, // 至少采用的壓縮比例algorithm: "gzip" // 采用的壓縮算法 })HTML文件中代碼的壓縮
我們之前使用了HtmlWebpackPlugin插件來生成HTML的模板,事實上它還有一些其他的配置:
-
inject:設置打包的資源插入的位置
-
true、false、body、head
-
-
cache:設置為true,只有當文件改變時,才會生成新的文件(默認值也是true)
-
minify:默認會使用一個插件html-minifier-terser
InlineChunkHtmlPlugin
可以輔助將一些chunk出來的模塊,內聯到html中
安裝
npm install react-dev-utils -D在production的plugins中進行配置:
module.exports = {plugin:[new InlineChunkHtmlPlugin(HtmlWebpackPlugin,[/runtime.+\.js/])] }十三、webpack打包分析
分析一:打包的時間分析
speed-measure-webpack-plugin 可以幫助我們看到每一個loader、每一個plugin的打包時間
安裝:
npm install speed-measure-webpack-plugin -D配置:
const smp = new SpeedMeasurePlugin(); cpnst webpackConfig = smp.wrap({plugins: [new MyPlugin(),new MyOtherPlugin()] })分析二:打包后文件分析
使用webpack-bundle-analyzer工具
安裝:
npm install webpack-bundle-analyzer -D配置
module.exports = {plugins: [new BundleAnalyzerPlugin()] }Compiler和Compilation的區別(面試題)
-
Compiler中webpack構建的之初就會創建的一個對象, 并且在webpack的整個生命周期都會存在(before -run -beforeCompiler-compile -make -finishMake-afterCompiler-done)
-
只要是做webpack的編譯, 都會先創建一個Compiler
-
-
Compilation是到準備編譯模塊(比如main.js), 才會創建Compilation對象
-
watch -> 源代碼發生改變就需要重新編譯模塊
-
主要是存在于compile -make 階段主要使用的對象
-
-
Compiler可以繼續使用(如果我修改webpack的配置, 那么需要重新執行run run build)
-
Compilation需要創建一個新的Compilation對象
總結
以上是生活随笔為你收集整理的webpack基础学习,各个loader和plugin的具体配置的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务器系统清理工具,服务器清理内存工具
- 下一篇: 【BMI指数计算器V4.0】项目实战