日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

webpack基础学习,各个loader和plugin的具体配置

發(fā)布時間:2023/12/29 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 webpack基础学习,各个loader和plugin的具体配置 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、邂逅Webpack

Webpack是什么

webpack是一個靜態(tài)的模塊化打包工具,為現(xiàn)代的JavaScript應(yīng)用程序;

  • 打包bundler:webpack可以將幫助我們進(jìn)行打包,所以它是一個打包工具

  • 靜態(tài)的static:這樣表述的原因是我們最終可以將代碼打包成最終的靜態(tài)資源(部署到靜態(tài)服務(wù)器);

  • 模塊化module:webpack默認(rèn)支持各種模塊化開發(fā),ES Module、CommonJS、AMD等;

  • 現(xiàn)代的modern:我們前端說過,正是因為現(xiàn)代前端開發(fā)面臨各種各樣的問題,才催生了webpack的出現(xiàn)和發(fā)展;

二、webpack配置和css處理

webpack配置文件

1、出口、入口的配置

我們可以在根目錄下創(chuàng)建一個webpack.config.js文件,來作為webpack的配置文件:

module.exports = {entry: " 指定入口路徑",output: {filename:"bundle.js", // 出口名字path: '出口路徑'} }

2、css-loader的使用

loader是什么

  • loader 可以用于對模塊的源代碼進(jìn)行轉(zhuǎn)換;

  • 我們可以將css文件也看成是一個模塊,我們是通過import來加載這個模塊的;

  • 在加載這個模塊時,webpack其實并不知道如何對其進(jìn)行加載,我們必須制定對應(yīng)的loader來完成這個功能;

module.rules的配置如下:

  • test屬性:用于對resource(資源)進(jìn)行匹配的,通常會設(shè)置成正則表達(dá)式;

  • use屬性:對應(yīng)的值時一個數(shù)組:[UseEntry]

    • UseEntry是一個對象,可以通過對象的屬性來設(shè)置一些其他屬性

      • loader:必須有一個loader屬性,對應(yīng)的值是一個字符串;

      • options:可選的屬性,值是一個字符串或者對象,值會被傳入到loader中;

      • query:目前已經(jīng)使用options來替代;

    • 傳遞字符串(如:use: [ 'style-loader' ])是loader 屬性的簡寫方式(如:use: [ { loader: 'style-loader'} ])

  • loader屬性:Rule.use: [ { loader } ] 的簡寫。

module.exports = {entry: " 指定入口路徑",output: {filename:"bundle.js", // 出口名字path: '出口路徑'},module: {rules: [{test:/\.css$/,// loader:"css-loader" // 寫法一// use:["css-loader"] ? //寫法二// 寫法三use:[{loader:"css-loader"}]}]} }

3、style-loader

當(dāng)我們通過css-loader來加載css文件時,代碼沒有生效。這是因為css-loader只是將.css文件進(jìn)行解析,并不會將解析之后的css插入到頁面中,而style-loader將完成插入style的操作

注意:因為loader的執(zhí)行順序是從右向左(或者說從下到上,或者說從后到前的),所以我們需要將styleloader寫到css-loader的前面

? ? ? ?use:[{loader:"style-loader"},{loader:"css-loader"}]

4、less-loader

? ? ? use:[{loader:"style-loader"},{loader:"css-loader"}{loader:"less-loader"}]

5、瀏覽器的兼容性

認(rèn)識browserslist工具

  • Browserslist編寫規(guī)則一:

    • defaults:Browserslist的默認(rèn)瀏覽器(> 0.5%, last 2 versions, Firefox ESR, not dead)。

    • 5%:通過全局使用情況統(tǒng)計信息選擇的瀏覽器版本。>=,<和<=工作過。

    • dead:24個月內(nèi)沒有官方支持或更新的瀏覽器。現(xiàn)在是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、認(rèn)識postCss工具

PostCSS是一個通過JavaScript來轉(zhuǎn)換樣式的工具,這個工具可以幫助我們進(jìn)行一些CSS的轉(zhuǎn)換和適配,比如自動添加瀏覽器前綴、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.css

postcss-loader

  • 借助構(gòu)建工具進(jìn)行css處理

npm install postcss-loader -D {loader:"postcss-loader",options: {postcssOptions: {plugins: [require('autoprefixer')]}}},
  • 單獨的postcss配置

在跟目錄下創(chuàng)建postcss.config.js

module.exports = {plugins: [require('autoprefixer')] }
  • postcss-preset-env

在項目中配置postcss-loader時,我們一般不使用autoprefixer。而是使用另一插件postcss-preset-env

  • postcss-preset-env也是一個postcss的插件;

  • 它可以幫助我們將一些現(xiàn)代的CSS特性,轉(zhuǎn)成大多數(shù)瀏覽器認(rèn)識的CSS,并且會根據(jù)目標(biāo)瀏覽器或者運行時環(huán)境添加所需的polyfill;

  • 也包括會自動幫助我們添加autoprefixer(所以相當(dāng)于已經(jīng)內(nèi)置了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、文件名稱規(guī)則

對處理后的文件名稱按照一定的規(guī)則進(jìn)行顯示,一般使用PlaceHolders來完成,webpack給我們提供了大量的PlaceHolders來顯示不同的內(nèi)容:

介紹幾個最常用的placeholder:

  • [ext]:處理文件的擴(kuò)展名;

  • [name]:處理文件的名稱;

  • [hash]:文件的內(nèi)容,使用MD4的散列函數(shù)處理,生成的一個128位的hash值(32個十六進(jìn)制);

  • [contentHash]:在file-loader中和[hash]結(jié)果是一致的(在webpack的一些其他地方不一樣,后面會講到);

  • [hash:<length>]:截圖hash的長度,默認(rèn)32個字符太長了;

  • [path]:文件相對于webpack配置文件的路徑;

2、設(shè)置文件名稱和存放路徑

{test:/\.(png|jpe?g|svg|gif)$/i,use: {loader: "file-loader"options: {name: "img/[name].[hash:8].[ext]",outputPath: "img"}} ?}

2、url-loader

將較小的文件轉(zhuǎn)換為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"}} ?}

打包之后的顯示結(jié)果跟file-loader一樣,但是在打包好的dist文件夾中,看不到圖片文件,而是轉(zhuǎn)換為base64格式存儲。

limit屬性

限制轉(zhuǎn)換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發(fā)送一個單獨的文件并導(dǎo)出URL。之前通過使用file-loader 實現(xiàn)

  • asset/inline導(dǎo)出一個資源的data URI。之前通過使用url-loader 實現(xiàn);

  • asset/source導(dǎo)出資源的源代碼。之前通過使用raw-loader 實現(xiàn);

  • asset在導(dǎo)出一個data URI 和發(fā)送一個單獨的文件之間自動選擇。之前通過使用url-loader,并且配置資源體積限制實現(xiàn);

2、使用

? ? {test: /\.(png|jpe?g|svg|gif)$/i,type: "asset/resource",},
  • 如何自定義文件的輸出路徑和文件名

方式一:修改output,添加assetModuleFilename屬性; ?output: {filename: "bundle.js", // 出口名字path: "出口路徑",assetModuleFilename:"img/[name].[hash:6][ext]"}, 方式二:在Rule中,添加一個generator屬性,并且設(shè)置filename; ? ? ? ? ? ? ? {test: /\.(png|jpe?g|svg|gif)$/i,type: "asset/resource",generator: {filename: "img/[name].[hash:6][ext]",},},
  • url-loader中的limit限制圖片大小效果

? ? ? ? ? {test: /\.(png|jpe?g|svg|gif)$/i,type: "asset", ? // 注意generator: {filename: "img/[name].[hash:6][ext]",},parser: {dataUrlCondition: {maxSize: 100 *1024}}},

4、加載字體文件

處理特殊字體或者字體圖標(biāo)的使用

我們可以選擇使用file-loader來處理,也可以選擇直接使用webpack5的資源模塊類型來處理;

{test: /\.(woff2?|eot|ttf)$/,type: "asset/resource",generator: {filename: "img/[name].[hash:6][ext]",},},

四、認(rèn)識plugin

  • Loader是用于特定的模塊類型進(jìn)行轉(zhuǎn)換;

  • Plugin可以用于執(zhí)行更加廣泛的任務(wù),比如打包優(yōu)化、資源管理、環(huán)境變量注入等;

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進(jìn)行打包處理

安裝:

npm install html-webpack-plugin -D

配置:

const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = {plugins: [new CleanWebpackPlugin(),new HtmlWebpackPlugin({titile: "webpack案例",}),], };

3、自定義HTML模板

如果我們想在自己的模塊中加入一些比較特別的內(nèi)容:

  • 添加一個noscript標(biāo)簽,在用戶的JavaScript被關(guān)閉時,給予響應(yīng)的提示;

  • 比如在開發(fā)vue或者react項目時,我們需要一個可以掛載后續(xù)組件的根標(biāo)簽<div id="app"></div>;

自定義模板數(shù)據(jù)填充

上面的代碼中,會有一些類似這樣的語法<%變量%>,這個是EJS模塊填充數(shù)據(jù)的方式。

在配置HtmlWebpackPlugin時,我們可以添加如下配置:

  • template:指定我們要使用的模塊所在的路徑;

  • title:在進(jìn)行htmlWebpackPlugin.options.title讀取時,就會讀到該信息;

const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = {plugins: [new CleanWebpackPlugin(),new HtmlWebpackPlugin({titile: "webpack案例",template: "./public/index.html"}),], };

4、DefinePlugin的介紹

當(dāng)在我們的模塊中還使用到一個BASE_URL的常量,我們需要設(shè)置這個常量,這個時候我們可以使用DefinePlugin插件;

使用:

DefinePlugin允許在編譯時創(chuàng)建配置的全局常量,是一個webpack內(nèi)置的插件(不需要單獨安裝):

const { DefinePlugin } = require("webpack"); module.exports = {plugins: [new DefinePlugin({BASE_URL:'"./"' ?// 注意需要多一層包裹})], };

5、CopyWebpackPlugin

vue的打包過程中,如果我們將一些文件放到public的目錄下,那么這個目錄會被復(fù)制到dist文件夾中。這個復(fù)制的功能,我們可以使用CopyWebpackPlugin來完成;

安裝:

npm install copy-webpack-plugin -D

配置:

  • from:設(shè)置從哪一個源中開始復(fù)制;

  • to:復(fù)制到的位置,可以省略,會默認(rèn)復(fù)制到打包的目錄下;

  • globOptions:設(shè)置一些額外的選項,其中可以編寫需要忽略的文件:

    • .DS_Store:mac目錄下回自動生成的一個文件;

    • index.html:也不需要復(fù)制,因為我們已經(jīng)通過HtmlWebpackPlugin完成了index.html的生成;

? ?new CopyWebpackPlugin({patterns: [{from:"public",globOptions: {ignore: ['**/.DS_Store','**/index.html']}}]})

五、source-map

source-map是從已轉(zhuǎn)換的代碼,映射到原始的源文件。使瀏覽器可以重構(gòu)原始源并在調(diào)試器中顯示重建的原始源

使用:

第一步:根據(jù)源文件,生成source-map文件,webpack在打包時,可以通過配置生成source-map;

第二步:在轉(zhuǎn)換后的代碼,最后添加一個注釋,它指向sourcemap;

//# sourceMappingURL=common.bundle.js.map

瀏覽器會根據(jù)我們的注釋,查找響應(yīng)的source-map,并且根據(jù)source-map還原我們的代碼,方便進(jìn)行調(diào)試。

分析source-map

  • version:當(dāng)前使用的版本,也就是最新的第三版;

  • sources:從哪些文件轉(zhuǎn)換過來的source-map和打包的代碼(最初始的文件);

  • names:轉(zhuǎn)換前的變量和屬性名稱(因為我目前使用的是development模式,所以不需要保留轉(zhuǎn)換前的名稱);

  • mappings:source-map用來和源文件映射的信息(比如位置信息等),一串base64VLQ(veriablelengthquantity可變長度值)編碼;

  • file:打包后的文件(瀏覽器加載的文件);

  • sourceContent:轉(zhuǎn)換前的具體代碼信息(和sources是對應(yīng)的關(guān)系);

  • sourceRoot:所有的sources相對的根目錄;

生成source-map

webpack為我們提供了非常多的選項,目前為止是26個,來處理source-map,選擇不同的值,打包形成的代碼會有性能的差異,可以根據(jù)不同情況進(jìn)行選擇

  • 不會生成source-map的配置項

    • false:不使用source-map,也就是沒有任何和source-map相關(guān)的內(nèi)容。

    • nnone:production模式下的默認(rèn)值,不生成source-map。

    • eval:development模式下的默認(rèn)值,不生成source-map

      • 但是它會在eval執(zhí)行的代碼中,添加//#sourceURL=;

      • 它會被瀏覽器在執(zhí)行時解析,并且在調(diào)試面板中生成對應(yīng)的一些文件目錄,方便我們調(diào)試代碼;

source-map值

生成一個獨立的source-map文件,并且在bundle文件中有一個注釋,指向source-map文件;

bundle文件中有如下的注釋:

//# sourceMappingURL=bundle.js.map

eval-source-map值

eval-source-map:會生成sourcemap,但是source-map是以DataUrl添加到eval函數(shù)的后面

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文件進(jìn)行引用;

  • 相當(dāng)于刪除了打包文件中對sourcemap的引用注釋;

// 被刪除掉的 //# sourceMappingURL=bundle.js.map

nosources-source-map值

會生成sourcemap,但是生成的sourcemap只有錯誤信息的提示,不會生成源代碼文件;

多個值的組合(重要)

事實上,webpack提供給我們的26個值,是可以進(jìn)行多組合的。

組合的規(guī)則如下:

  • inline-|hidden-|eval:三個值時三選一;

  • nosources:可選值;

  • cheap可選值,并且可以跟隨module的值;

    [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

在開發(fā)中,最佳的實踐是什么呢?

  • 開發(fā)階段:推薦使用source-map或者cheap-module-source-map

  • 測試階段:推薦使用source-map或者cheap-module-source-map

  • 發(fā)布階段:false、缺省值(不寫)

六、Babel深入理解

1、babel是什么東西,用來做什么的

  • Babel是一個工具鏈,主要用于舊瀏覽器或者緩解中將ECMAScript 2015+代碼轉(zhuǎn)換為向后兼容版本的JavaScript;

  • 語法轉(zhuǎn)換、源代碼轉(zhuǎn)換、Polyfill實現(xiàn)目標(biāo)緩解缺少的功能等;

Babel命令行使用

  • @babel/core:babel的核心代碼,必須安裝;

  • @babel/cli:可以讓我們在命令行使用babel;

npm install @babel/cli @babel/core

使用babel來處理我們的源代碼:

  • src:是源文件的目錄;

  • --out-dir:指定要輸出的文件夾dist;

npx babel src--out-dirdist

插件的使用

比如我們需要轉(zhuǎn)換箭頭函數(shù),那么我們就可以使用箭頭函數(shù)轉(zhuǎn)換相關(guān)的插件:

npm install @babel/plugin-transform-arrow-functions -D npx babel src--out-dirdist--plugins=@babel/plugin-transform-arrow-functions

查看轉(zhuǎn)換后的結(jié)果,我們會發(fā)現(xiàn)const 并沒有轉(zhuǎn)成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-functions

Babel的預(yù)設(shè)preset

如果要轉(zhuǎn)換的內(nèi)容過多,一個個設(shè)置是比較麻煩的,我們可以使用預(yù)設(shè)(preset)

安裝 :

npm install @babel/preset-env -D

執(zhí)行:

npx babel src--out-dirdist--presets=@babel/preset-env

2、Babel的底層原理

工作流程:

  • 解析階段(Parsing)

  • 轉(zhuǎn)換階段(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會根據(jù)我們的預(yù)設(shè)來加載對應(yīng)的插件列表,并且將其傳遞給babel。

常見的預(yù)設(shè):

  • 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設(shè)置

在babel7之前(比如babel6中),我們會經(jīng)常看到這種設(shè)置方式:

  • 它表達(dá)的含義是使用對應(yīng)的babel-preset-stage-x預(yù)設(shè);

  • 從babel7開始,已經(jīng)不建議使用了,建議使用preset-env來設(shè)置;

4、Babel的配置文件

我們可以將babel的配置信息放到一個獨立的文件中,babel給我們提供了兩種配置文件的編寫:

  • babel.config.json(或者.js,.cjs,.mjs)文件;

  • .babelrc.json(或者.babelrc,.js,.cjs,.mjs)文件;

區(qū)別:

  • .babelrc.json:早期使用較多的配置方式,但是對于配置Monorepos項目是比較麻煩的;

  • babel.config.json(babel7):可以直接作用于Monorepos項目的子包,更加推薦;

5、認(rèn)識polyfill

更像是應(yīng)該填充物(墊片),一個補(bǔ)丁,可以幫助我們更好的使用JavaScript;

使用場景:

比如我們使用了一些語法特性(例如:Promise,Generator,Symbol等以及實例方法例如Array.prototype.includes等),但是某些瀏覽器壓根不認(rèn)識這些特性,必然會報錯,我們可以使用polyfill來填充或者說打一個補(bǔ)丁,那么就會包含該特性了;

可以通過單獨引入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文件中進(jìn)行配置,給preset-env配置一些屬性:

  • useBuiltIns:設(shè)置以什么樣的方式來使用polyfill;

  • corejs:設(shè)置corejs的版本

    • 另外corejs可以設(shè)置是否對提議階段的特性進(jìn)行支持;

    • 設(shè)置proposals屬性為true即可;

useBuiltIns屬性設(shè)置

  • useBuiltIns屬性有三個常見的值

    第一個值:false

    • 打包后的文件不使用polyfill來進(jìn)行適配;

    • 并且這個時候是不需要設(shè)置corejs屬性的;

    第二個值:usage

    • 會根據(jù)源代碼中出現(xiàn)的語言特性,自動檢測所需要的polyfill;

    • 這樣可以確保最終包里的polyfill數(shù)量的最小化,打包的包相對會小一些;

    • 可以設(shè)置corejs屬性來確定使用的corejs的版本;

    第三個值:entry

    • 如果我們依賴的某一個庫本身使用了某些polyfill的特性,但是因為我們使用的是usage,所以之后用戶瀏覽器可能會報錯,果你擔(dān)心出現(xiàn)這種情況,可以使用entry;

    • 需要在入口文件中添加`import 'core-js/stable'; import 'regenerator-runtime/runtime';

    • 這樣做會根據(jù)browserslist目標(biāo)導(dǎo)入所有的polyfill,但是對應(yīng)的包也會變大;

6、認(rèn)識Plugin-transform-runtime(了解)

在前面我們使用的polyfill,默認(rèn)情況是添加的所有特性都是全局的,如果我們正在編寫一個工具庫,這個工具庫需要使用polyfill,別人在使用我們工具時,工具庫通過polyfill添加的特性,可能會污染它們的代碼,所以,當(dāng)編寫工具時,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來轉(zhuǎn)換成JavaScript

安裝:

npm install typescript -D

TypeScript的編譯配置信息我們通常會編寫一個tsconfig.json文件:

tsc --init

之后我們可以運行npxtsc來編譯自己的ts代碼:

npx tsc

1、使用ts-loader編譯TS

安裝:

npm install ts-loader -D

配置:

{test:'/\.ts$/',exclude: /node_modules/,use: ["ts-loader"] }

2、使用babel-loader編譯TS

Babel是有對TypeScript進(jìn)行支持

  • 我們可以使用插件:@babel/tranform-typescript;

  • 但是更推薦直接使用preset:@babel/preset-typescript;

安裝:

npm install @babel/preset-typescript -D

配置:

{test:'/\.ts$/',exclude: /node_modules/,use: ["babel-loader"] }

七、大數(shù)據(jù)中關(guān)于代碼格式校驗(Eslint和prettierrc )

大數(shù)據(jù)中關(guān)于eslint的配置

/** @Description: eslint 配置* @ 規(guī)則依賴于 @umijs/fabric,在此基礎(chǔ)上,可自行添加自己的規(guī)則進(jìn)行配置* @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: {// 強(qiáng)制語句有分號結(jié)尾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,// ? 箭頭函數(shù)的箭頭與后面的{}之間需要空格'arrow-spacing': 2,// ? 禁止多余的空格'no-multi-spaces': 2,// ? 禁止代碼行結(jié)束后面有多余空格'no-trailing-spaces': 2,// ? 禁止多余空行'no-multiple-empty-lines': ['error', { max: 1, maxBOF: 1, maxEOF: 1 }],// ? 允許標(biāo)識符中使用懸空下劃線(標(biāo)識符的開頭或末尾的下劃線)'no-underscore-dangle': 0,// ? 允許邏輯短路、三元運算符等表達(dá)式求值'no-unused-expressions': 0,// ? 禁止使用嵌套的三元表達(dá)式'no-nested-ternary': 2,// ? 禁止對函數(shù)參數(shù)再賦值(保證react函數(shù)式編程純函數(shù)的概念)'no-param-reassign': 2,// ? 禁止使用 var 定義變量'no-var': 2,// ? 禁止修改const聲明的變量'no-const-assign': 2,// ? 函數(shù)調(diào)用時 函數(shù)名與()之間不能有空格'no-spaced-func': 2, ?// ? jsx 屬性中強(qiáng)制使用雙引號'jsx-quotes': [2, 'prefer-double'],// ? 禁止 jsx 屬性對象的引用括號里 兩邊加空格'react/jsx-curly-spacing': [2, 'never'],// ? JSX 中前標(biāo)簽傳有屬性換行展示的話,其后面的 > 也需換行對齊展示'react/jsx-closing-bracket-location': 2,// ? 校驗 jsx 中所有換行屬性值縮進(jìn)'react/jsx-indent-props': [2, 2],// ? jsx 中傳入屬性值是Boolean值且為true時,省略傳入'react/jsx-boolean-value': 2,// ? 在 JSX 屬性中禁止等號前后存在空格'react/jsx-equals-spacing': 2, ?// ? 關(guān)閉此規(guī)則,允許 useEffect 的依賴為空數(shù)組'react-hooks/exhaustive-deps': 0, ?// ? 未使用的變量警告提醒'@typescript-eslint/no-unused-vars': ['warn'],// ? 禁用使用在前,保證 useEffct 使用在最前面,這時候里面如果使用了外部的函數(shù)就會報這錯'@typescript-eslint/no-use-before-define': 0,// ? 允許空的 ts 接口定義 eg: interface IProps {}'@typescript-eslint/no-empty-interface': 0,}, }; 大數(shù)據(jù)中 .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

為什么需要搭建本地服務(wù)器?

我們希望可以做到,當(dāng)文件發(fā)生變化時,可以自動完成編譯和展示

  • webpack watch mode

  • webpack-dev-server

  • webpack-dev-middleware

1、Webpack watch(基本不用)

webpack給我們提供了watch模式:

  • 在該模式下,webpack依賴圖中的所有文件,只要有一個發(fā)生了更新,那么代碼將被重新編譯(損耗性能)

開啟watch的兩種方式

  • 方式一:在導(dǎo)出的配置中,添加watch: true;

  • 方式二:在啟動webpack的命令中,添加--watch的標(biāo)識;

"scripts" : {"watch":"webpack --watch" }

2、webpack-dev-server(必會,常用)

除了可以監(jiān)聽到文件的變化,還可以具備實時重新加載的功能

安裝:

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

配置:

"scripts" : {"watch":"webpack --watch""serve":"webpack serve --config wk.config.js" }

webpack-dev-server 在編譯之后不會寫入到任何輸出文件。而是將bundle 文件保留在內(nèi)存中:

  • 事實上webpack-dev-server使用了一個庫叫memfs(memory-fswebpack自己寫的)

3、webpack-dev-middleware(基本不用)

如果我們想要有更好的自由度,可以使用webpack-dev-middleware;

定義:

webpack-dev-middleware 是一個封裝器(wrapper),它可以把webpack處理過的文件發(fā)送到一個server,webpack-dev-server 在內(nèi)部使用了它,然而它也可以作為一個單獨的package 來使用,以便根據(jù)需求進(jìn)行更多自定義設(shè)置;

4、output的publicPath(outPut中的配置)

output中還有一個publicPath屬性,該屬性是指定index.html文件打包引用的一個基本路徑

  • 它的默認(rèn)值是一個空字符串,所以我們打包后引入js文件時,路徑是bundle.js;

  • 在開發(fā)中,我們也將其設(shè)置為/,路徑是/bundle.js,那么瀏覽器會根據(jù)所在的域名+路徑去請求對應(yīng)的資源;

  • 如果我們希望在本地直接打開html文件來運行,會將其設(shè)置為./,路徑時./bundle.js,可以根據(jù)相對路徑去查找資源;

    module.exports = {entry: " 指定入口路徑",output: {filename:"bundle.js", // 出口名字path: '出口路徑'publicPath:'./'} }

5、devServer的publicPath

devServer中也有一個publicPath的屬性,該屬性是指定本地服務(wù)所在的文件夾

  • 它的默認(rèn)值是/,也就是我們直接訪問端口即可訪問其中的資源http://localhost:8080

  • 如果我們將其設(shè)置為了/abc,那么我們需要通過http://localhost:8080/abc才能訪問到對應(yīng)的打包后的資源

  • 并且這個時候,我們其中的bundle.js通過http://localhost:8080/bundle.js也是無法訪問的:

    • 所以必須將output.publicPath也設(shè)置為/abc;

    • 官方其實有提到,建議devServer.publicPath與output.publicPath相同

6、devServer的contentBase(不常用)

主要作用是如果我們打包后的資源,又依賴于其他的一些資源,那么就需要指定從哪里來查找這個內(nèi)容

  • 比如在index.html中,我們需要依賴一個abc.js文件,這個文件我們存放在public文件中

  • 在index.html中,我們應(yīng)該如何去引入這個文件

    • 比如代碼是這樣的:<script src="./public/abc.js"></script>

    • 但是這樣打包后瀏覽器是無法通過相對路徑去找到這個文件夾的;

    • 所以代碼是這樣的:<script src="/abc.js"></script>;

    • 但是我們?nèi)绾巫屗ゲ檎业竭@個文件的存在呢?設(shè)置contentBase即可

devserver: {contentBase: path.resolve(__dirname,"why"),watchContentBase:true //監(jiān)聽contentBase發(fā)生變化后重新編譯 }

7、hotOnly、hos、port、open、compress配置

  • hotOnly是當(dāng)代碼編譯失敗時,是否刷新整個頁面

  • port設(shè)置監(jiān)聽的端口,默認(rèn)情況下是8080

  • host設(shè)置主機(jī)地址

  • open是否打開瀏覽器

  • compress是否為靜態(tài)文件開啟gzip compression

devserver: {contentBase: path.resolve(__dirname,"why"),watchContentBase:true, //監(jiān)聽contentBase發(fā)生變化后重新編譯hotOnly:true,host: 0.0.0.0,port: 3000,open: true,compress:true }

8、Proxy代理(解決跨域問題)注意:在開發(fā)環(huán)境中使用

我們可以將請求先發(fā)送到一個代理服務(wù)器,代理服務(wù)器和API服務(wù)器沒有跨域的問題,就可以解決我們的跨域問題了

配置:

  • target:表示的是代理到的目標(biāo)地址,比如/api-hy/moment會被代理到http://localhost:8888/api-hy/moment;

  • pathRewrite:默認(rèn)情況下,我們的/api-hy也會被寫入到URL中,如果希望刪除,可以使用pathRewrite;

  • secure:默認(rèn)情況下不接收轉(zhuǎn)發(fā)到https的服務(wù)器上,如果希望支持,可以設(shè)置為false;

  • changeOrigin:它表示是否更新代理后請求的headers中host地址;

devserver: {'/api': {// 標(biāo)識需要進(jìn)行轉(zhuǎn)換的請求的urltarget: 'http://172.16.188.188:8000', // 服務(wù)端域名changeOrigin: true, // 允許域名進(jìn)行轉(zhuǎn)換pathRewrite: {// 將請求url里的ci去掉'^/api': '',},logLevel: 'debug',secure: false, // 將該屬性設(shè)置為false,將允許在https上運行或者運行在證書無效的后端服務(wù)器},},

9、historyApiFallback

  • historyApiFallback是開發(fā)中一個非常常見的屬性,它主要的作用是解決SPA頁面在路由跳轉(zhuǎn)之后,進(jìn)行頁面刷新時,返回404的錯誤。

  • boolean值:默認(rèn)是false

    • 如果設(shè)置為true,那么在刷新時,返回404錯誤時,會自動返回index.html的內(nèi)容;

  • object類型的值,可以配置rewrites屬性:

    • 可以配置from來匹配路徑,決定要跳轉(zhuǎn)到哪一個頁面;

?devserver: {'/api': {// 標(biāo)識需要進(jìn)行轉(zhuǎn)換的請求的urltarget: 'http://172.16.188.188:8000', // 服務(wù)端域名changeOrigin: true, // 允許域名進(jìn)行轉(zhuǎn)換pathRewrite: {// 將請求url里的ci去掉'^/api': '',},logLevel: 'debug',secure: false, // 將該屬性設(shè)置為false,將允許在https上運行或者運行在證書無效的后端服務(wù)器},historyApiFallback: {rewrites:[{from: /abc/, to:"/index.html"}]}},

九、模塊熱替換(HMR)

什么是HMR

  • HMR的全稱是Hot Module Replacement,翻譯為模塊熱替換;

  • 模塊熱替換是指在應(yīng)用程序運行過程中,替換、添加、刪除模塊,而無需重新刷新整個頁面;

如何使用HMR

  • 默認(rèn)情況下,webpack-dev-server已經(jīng)支持HMR,我們只需要開啟即可;

  • 在不開啟HMR的情況下,當(dāng)我們修改了源代碼之后,整個頁面會自動刷新,使用的是live reloading;

1、開啟HMR

// 在webpack.config.js 中添加以下配置 devserver: {hot:true }

同時還需要指定發(fā)生更新的模塊

if(module.hot) {module.hot.accept("./*文件路徑",() => {console.log()}) }

2、框架中的HMR

項目中已經(jīng)有非常成熟的方案,別操心了

  • vue開發(fā)中,我們使用vue-loader,此loader支持vue組件的HMR,提供開箱即用的體驗

  • react開發(fā)中,有React HotLoader,實時調(diào)整react組件(目前React官方已經(jīng)棄用了,改成使用reactrefresh);

3、HMR的原理

HMR的原理是什么

  • webpack-dev-server會創(chuàng)建兩個服務(wù):提供靜態(tài)資源的服務(wù)(express)和Socket服務(wù)(net.Socket)

  • express server負(fù)責(zé)直接提供靜態(tài)資源的服務(wù)(打包后的資源直接被瀏覽器請求和解析);

HMR Socket Server,是一個socket的長連接:(想想webSocket,需要及時通信)

  • 長連接有一個最好的好處是建立連接后雙方可以通信(服務(wù)器可以直接發(fā)送文件到客戶端)

  • 當(dāng)服務(wù)器監(jiān)聽到對應(yīng)的模塊發(fā)生變化時,會生成兩個文件.json(manifest文件)和.js文件(update chunk);

  • 通過長連接,可以直接將這兩個文件主動發(fā)送給客戶端(瀏覽器)

  • 瀏覽器拿到兩個新的文件后,通過HMR runtime機(jī)制,加載這兩個文件,并且針對修改的模塊進(jìn)行更新;

十、resolve模塊解析

resolve用于設(shè)置模塊如何被解析:

  • resolve可以幫助webpack從每個require/import 語句中,找到需要引入到合適的模塊代碼;

  • webpack 使用enhanced-resolve來解析文件路徑;

webpack能解析三種文件路徑:

  • 絕對路徑

  • 相對路徑

  • 模塊路徑:在resolve.modules中指定的所有目錄檢索模塊,默認(rèn)值是['node_modules'],所以默認(rèn)會從node_modules中查找文件;

1、extensions和alias配置

extensions是解析到文件時自動添加擴(kuò)展名:

  • 默認(rèn)值是['.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')}}

十一、環(huán)境分離和代碼分離

1、入口文件解析

context的作用是用于解析入口(entry point)和加載器(loader)

默認(rèn)是webpack的啟動目錄

module.exports = {context:path.resolve(__dirname,'./')entry:"../src/index.js" }

2、配置文件的分離

  • 將原來的webpack.config.js劃分為webpack.comm.conf.js(通用配置)、webpack.dev.conf.js(開發(fā)環(huán)境)、webpack.prod.conf.js(生產(chǎn)環(huán)境)三部分

  • 利用mode配置項,區(qū)分開發(fā)環(huán)境和生產(chǎn)環(huán)境。用利用merge將用到的環(huán)境配置和通用配置合并

3、認(rèn)識代碼分離

代碼分離(CodeSplitting)主要的目的是將代碼分離到不同的bundle中,之后我們可以按需加載,或者并行加載這些文件;

Webpack中常用的代碼分離有三種

  • 入口起點:使用entry配置手動分離代碼;

  • 防止重復(fù):使用EntryDependencies或者SplitChunksPlugin去重和分離代碼;

  • 動態(tài)導(dǎo)入:通過模塊的內(nèi)聯(lián)函數(shù)調(diào)用來分離代碼;

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

  • 如果我們單純的進(jìn)行入口分離,那么打包后的兩個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來實現(xiàn)的:

Webpack提供了SplitChunksPlugin默認(rèn)的配置,我們也可以手動來修改它的配置:

  • 比如默認(rèn)配置中,chunks僅僅針對于異步(async)請求,我們可以設(shè)置為initial或者all;

optimization: {splitChunks: {chunks:'all'} }

4、SplitChunks自定義配置

SplitChunks: {chunks:'all',// 拆分包的大小,至少為minsize// 如果一個包拆分出來不到minsize,那么將不會被拆分minsize:100,// 將大于maxMize的包,拆分成不小于minSize 的包maxsize:1000,// 至少包被引入的次數(shù)minChunks:2,// 最大異步請求數(shù)量maxAsyncRequests:30,// 最大的初始化請求數(shù)量cacheGroups: {venders: {test:/[\\/]node_modules[\\/]/,priority: -10,filename: "[id]_[hash:6]_vendor.js"},foo: {test:/foo/,priority: -20,filename: "foo_[id]_[name]_.js"}} }

配置解析:

  • Chunks

    • 默認(rèn)值是async

    • 另一個值是initial,表示對通過的代碼進(jìn)行處理

    • all表示對同步和異步代碼都進(jìn)行處理

  • minSize

    • 拆分包的大小, 至少為minSize;

    • 如果一個包拆分出來達(dá)不到minSize,那么這個包就不會拆分;

  • maxSize

    • 將大于maxSize的包,拆分為不小于minSize的包;

  • minChunks

    • 至少被引入的次數(shù),默認(rèn)是1;

    • 如果我們寫一個2,但是引入了一次,那么不會被單獨拆分;

  • name:設(shè)置拆包的名稱

    • 可以設(shè)置一個名稱,也可以設(shè)置為false;

    • 設(shè)置為false后,需要在cacheGroups中設(shè)置名稱;

  • cacheGroups

    • 用于對拆分的包就行分組,比如一個lodash在拆分之后,并不會立即打包,而是會等到有沒有其他符合規(guī)則的包一起來打包;

    • test屬性:匹配符合規(guī)則的包;

    • name屬性:拆分包的name屬性;

    • filename屬性:拆分包的名稱,可以自己使用placeholder屬性;

5、動態(tài)導(dǎo)入(dynamic import)

使用ECMAScript中的import()語法來完成,也是目前推薦的方式;

注意:

  • 在webpack中,通過動態(tài)導(dǎo)入獲取到一個對象;

  • 真正導(dǎo)出的內(nèi)容,在改對象的default屬性中,所以我們需要做一個簡單的解構(gòu);

動態(tài)導(dǎo)入的文件命名

它的命名我們通常會在output中,通過chunkFilename屬性來命名

output: {filename: "[name].bundle.js",path:resolveApp("./build"),chunkFilename: "chunk_[id]_[name].js" }

默認(rèn)情況下我們獲取到的[name]是和id的名稱保持一致的

  • 我們希望修改name的值,可以通過magic comments(魔法注釋)的方式

import(/* webpackChunkName:"bar"*/ "./bar").then(({default:bar}) => {bar() })

6、optimization.chunkIds配置

optimization.chunkIds配置用于告知webpack模塊的id采用什么算法生成。

  • natural:按照數(shù)字的順序使用id;

  • named:development下的默認(rèn)值,一個可讀的名稱的id;

  • deterministic:確定性的,在不同的編譯中不變的短數(shù)字id

最佳實踐:

  • 開發(fā)過程中,我們推薦使用named;

  • 打包過程中,我們推薦使用deterministic;

7、optimization. runtimeChunk配置

配置runtime相關(guān)的代碼是否抽取到一個單獨的chunk中:

  • 抽離出來后,有利于瀏覽器緩存的策略:

  • 設(shè)置的值

    • true/multiple:針對每個入口打包一個runtime文件;

    • single:打包一個runtime文件;

    • 對象:name屬性決定runtimeChunk的名稱;

    optimization:{chunkIds:"deterministic",runtimeChunk: {name:"runtime"} }

8、Prefetch和Preload

  • webpack v4.6.0+增加了對預(yù)獲取和預(yù)加載的支持。

    • prefetch(預(yù)獲取):將來某些導(dǎo)航下可能需要的資源

    • preload(預(yù)加載):當(dāng)前導(dǎo)航下可能需要資源

    import(/* webpackChunkName:"bar"*/ /* webpackpeload:true */"./bar").then(({default:bar}) => {bar() })
  • 區(qū)別

    • preload chunk 會在父chunk 加載時,以并行方式開始加載。prefetch chunk 會在父chunk 加載結(jié)束后開始加載。

    • preload chunk 具有中等優(yōu)先級,并立即下載。prefetch chunk 在瀏覽器閑置時下載。

    • preload chunk 會在父chunk 中立即請求,用于當(dāng)下時刻。prefetch chunk 會用于未來的某個時刻。

9、CDN

CDN稱之為內(nèi)容分發(fā)網(wǎng)絡(luò)(ContentDeliveryNetwork或ContentDistributionNetwork,縮寫:CDN)

開發(fā)中的應(yīng)用方式:

  • 方式一:打包的所有靜態(tài)資源,放到CDN服務(wù)器,用戶所有資源都是通過CDN服務(wù)器加載的;

  • 方式二:一些第三方資源放到CDN服務(wù)器上;

方式一花錢,直接說方式二

一些比較出名的開源框架都會將打包后的源碼放到一些比較出名的、免費的CDN服務(wù)器上

使用方法:

  • 第一步,我們可以通過webpack配置,來排除一些庫的打包:

    externals: {lodash: "_",dayjs: "dayjs" }
  • 第二步,在html模塊中,加入CDN服務(wù)器地址:

    <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.core.js"></script>

10、認(rèn)識shimming

  • shimming是一個概念,是某一類功能的統(tǒng)稱:

    • 比如我們現(xiàn)在依賴一個第三方的庫,這個第三方的庫本身依賴lodash,但是默認(rèn)沒有對lodash進(jìn)行導(dǎo)入(認(rèn)為全局存在lodash),那么我們就可以通過ProvidePlugin來實現(xiàn)shimming的效果;

  • 注意:webpack并不推薦隨意的使用shimming

    • Webpack背后的整個理念是使前端開發(fā)更加模塊化;

    • 也就是說,需要編寫具有封閉性的、不存在隱含依賴(比如全局變量)的彼此隔離的模塊;

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值的生成和整個項目有關(guān)系:

    • 比如我們現(xiàn)在有兩個入口index.js和main.js;

    • 它們分別會輸出到不同的bundle文件中,并且在文件名稱中我們有使用hash;

    • 這個時候,如果修改了index.js文件中的內(nèi)容,那么hash會發(fā)生變化;

    • 那就意味著兩個文件的名稱都會發(fā)生變化;

  • chunkhash可以有效的解決上面的問題,它會根據(jù)不同的入口進(jìn)行借來解析來生成hash值:

比如我們修改了index.js,那么main.js的chunkhash是不會發(fā)生改變的;

  • contenthash表示生成的文件hash名稱,只和內(nèi)容有關(guān)系:

    • 比如我們的index.js,引入了一個style.css,style.css有被抽取到一個獨立的css文件中;

    • 這個css文件在命名時,如果我們使用的是chunkhash;

    • 那么當(dāng)index.js文件的內(nèi)容發(fā)生變化時,css文件的命名也會發(fā)生變化;

    • 這個時候我們可以使用contenthash;

十二、DLL_Tree Shaking

認(rèn)識DLL庫(了解一下)

  • DLL全程是動態(tài)鏈接庫(Dynamic Link Library),是為軟件在Windows中實現(xiàn)共享函數(shù)庫的一種實現(xiàn)方式;

  • webpack中也有內(nèi)置DLL的功能,它指的是我們可以將可以共享,并且不經(jīng)常改變的代碼,抽取成一個共享的庫;

Terser介紹和安裝(一般使用默認(rèn)配置)

  • Terser是一個JavaScript的解釋(Parser)、Mangler(絞肉機(jī))/Compressor(壓縮機(jī))的工具集;

    • 早期我們會使用uglify-js來壓縮、丑化我們的JavaScript代碼,但是目前已經(jīng)不再維護(hù),并且不支持ES6+的語法;

  • Terser可以幫助我們壓縮、丑化我們的代碼,讓我們的bundle變得更小。

安裝:

npm install terser -g // 可以進(jìn)行局部安裝,也可以全局安裝

命令行使用

terser [input files] [options]

常見的配置項:

Compress option

  • arrows:class或者object中的函數(shù),轉(zhuǎn)換成箭頭函數(shù);

  • arguments:將函數(shù)中使用arguments[index]轉(zhuǎn)成對應(yīng)的形參名稱;

  • dead_code:移除不可達(dá)的代碼(tree shaking);

  • 等等其他屬性,詳情看官方文檔

Mangle option

  • toplevel:默認(rèn)值是false,頂層作用域中的變量名稱,進(jìn)行丑化(轉(zhuǎn)換)

  • keep_classnames:默認(rèn)值是false,是否保持依賴的類名稱;

  • keep_fnames:默認(rèn)值是false,是否保持原來的函數(shù)名稱;

Terser在webpack中配置使用

  • (注意)真實開發(fā)中,我們不需要手動的通過terser來處理我們的代碼,我們可以直接通過webpack來處理:

    • 在webpack中有一個minimizer屬性,在production模式下,默認(rèn)就是使用TerserPlugin來處理我們的代碼的;

    • 如果我們對默認(rèn)的配置不滿意,也可以自己來創(chuàng)建TerserPlugin的實例,并且覆蓋相關(guān)的配置;(基本不會手動配置)

module.exports = {optimization: {minimize:true,minimizer: [new TerserPlugin({parallel:true, // 使用多進(jìn)程并發(fā)運行提高構(gòu)建的速度,默認(rèn)值是true,extractComments:false, // 默認(rèn)值為true,表示會將注釋抽取到一個單獨的文件中terserOptions: { // 設(shè)置我們的terser相關(guān)的配置compress: { // 設(shè)置壓縮相關(guān)的選項;arguments:true,dead_code:true},mangle:true, // 設(shè)置丑化相關(guān)的選項,可以直接設(shè)置為true;toplevel:true, // 底層變量是否進(jìn)行轉(zhuǎn)換keep_classnames:false,// 保留類的名稱keep_fnames:false// 保留函數(shù)的名稱;}})]} }

CSS的壓縮

安裝:

npm install css-minimizer-webpack-plugin -D

在optimization.minimizer中配置:

minimizer: [new CssMinimizerplugin({parallel: true}) ]

提升作用域 Scope Hoisting

  • Scope Hoisting從webpack3開始增加的一個新功能,功能是對作用域進(jìn)行提升,并且讓webpack打包后的代碼更小、運行更快;

  • 默認(rèn)情況下webpack打包會有很多的函數(shù)作用域,Scope Hoisting可以將函數(shù)合并到一個模塊中來運行

  • 使用:

    • 在production模式下,默認(rèn)這個模塊就會啟用;

    • 在development模式下,我們需要自己來打開該模塊;

    new webpack.optimize.ModuleConcatenationPlugin()

Tree Shaking

定義:最早的想法起源于LISP,用于消除未調(diào)用的代碼(純函數(shù)無副作用,可以放心的消除,這也是為什么要求我們在進(jìn)行函數(shù)式編程時,盡量使用純函數(shù)的原因之一)

webpack實現(xiàn)TreeShaking

兩種方法:

  • 在optimization中配置usedExports為true,來幫助Terser進(jìn)行優(yōu)化;

  • 在package.json中配置sideEffects,直接對模塊進(jìn)行優(yōu)化;

usedExports

在usedExports設(shè)置為true時,會有一段注釋:unused harmony export mul,這段注釋告知Terser在優(yōu)化時,可以刪除掉這段代碼

注意:

  • 配置該屬性時,需要將mode設(shè)置為development模式

  • usedExports實現(xiàn)tree Shaking是結(jié)合terse來完成的

sideEffects

sideEffects用于告知webpack compiler哪些模塊時有副作用的(副作用的意思是這里面的代碼有執(zhí)行一些特殊的任務(wù),不能僅僅通過export來判斷這段代碼的意義;)

  • 在package.json中設(shè)置sideEffects的值:

    • false:告知webpack可以安全的刪除未用到的exports;

    • 如果有一些希望保留,可以設(shè)置數(shù)組

"sideEffects": ["./src/util/format.js","*.css" ]

CSS實現(xiàn)TreeShaking

我們可以使用一個庫來完成CSS的Tree Shaking:PurgeCSS,幫助我們刪除未使用的CSS的工具

安裝:

npm install purgecss-webpack-plugin -D

配置:

  • paths:表示要檢測哪些目錄下的內(nèi)容需要被分析,這里我們可以使用glob;

  • 默認(rèn)情況下,Purgecss會將我們的html標(biāo)簽的樣式移除掉,如果我們希望保留,可以添加一個safelist的屬性;

  • purgecss也可以對less文件進(jìn)行處理(所以它是對打包后的css進(jìn)行tree shaking操作)

new ?PurgecssPlugin({paths:glob.sync(`${resolveApp(./src)}/**/*`,{nodir:true}),safelist: function() {return {standard:['html']}} })

HTTP壓縮

定義:HTTP壓縮是一種內(nèi)置在服務(wù)器和客戶端之間的,以改進(jìn)傳輸速度和帶寬利用率的方式

流程:

第一步:HTTP數(shù)據(jù)在服務(wù)器發(fā)送前就已經(jīng)被壓縮了

第二步:兼容的瀏覽器在向服務(wù)器發(fā)送請求時,會告知服務(wù)器自己支持哪些壓縮格式

第三步:服務(wù)器在瀏覽器支持的壓縮格式下,直接返回對應(yīng)的壓縮后的文件,并且在響應(yīng)頭中告知瀏覽器;

目前的壓縮格式

  • compress–UNIX的“compress”程序的方法(歷史性原因,不推薦大多數(shù)應(yīng)用使用,應(yīng)該使用gzip或deflate);

  • deflate–基于deflate算法(定義于RFC1951)的壓縮,使用zlib數(shù)據(jù)格式封裝;

  • gzip–GNUzip格式(定義于RFC1952),是目前使用比較廣泛的壓縮算法;

  • br–一種新的開源壓縮算法,專為HTTP內(nèi)容的編碼而設(shè)計;

Webpack對文件壓縮

webpack中相當(dāng)于是實現(xiàn)了HTTP壓縮的第一步操作,我們可以使用CompressionPlugin。

安裝:

npm install compression-webpack-plugin -D

配置:

new CompressionPlugin({test:/\.(css|js)$/, ?// 匹配哪些文件需要壓縮threshold:500, // 設(shè)置文件多大開始壓縮minRatio: 0.7, // 至少采用的壓縮比例algorithm: "gzip" // 采用的壓縮算法 })

HTML文件中代碼的壓縮

我們之前使用了HtmlWebpackPlugin插件來生成HTML的模板,事實上它還有一些其他的配置:

  • inject:設(shè)置打包的資源插入的位置

    • true、false、body、head

  • cache:設(shè)置為true,只有當(dāng)文件改變時,才會生成新的文件(默認(rèn)值也是true)

  • minify:默認(rèn)會使用一個插件html-minifier-terser

InlineChunkHtmlPlugin

可以輔助將一些chunk出來的模塊,內(nèi)聯(lián)到html中

安裝

npm install react-dev-utils -D

在production的plugins中進(jìn)行配置:

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的區(qū)別(面試題)

  • Compiler中webpack構(gòu)建的之初就會創(chuàng)建的一個對象, 并且在webpack的整個生命周期都會存在(before -run -beforeCompiler-compile -make -finishMake-afterCompiler-done)

    • 只要是做webpack的編譯, 都會先創(chuàng)建一個Compiler

  • Compilation是到準(zhǔn)備編譯模塊(比如main.js), 才會創(chuàng)建Compilation對象

    • watch -> 源代碼發(fā)生改變就需要重新編譯模塊

    • 主要是存在于compile -make 階段主要使用的對象

  • Compiler可以繼續(xù)使用(如果我修改webpack的配置, 那么需要重新執(zhí)行run run build)

  • Compilation需要創(chuàng)建一個新的Compilation對象

總結(jié)

以上是生活随笔為你收集整理的webpack基础学习,各个loader和plugin的具体配置的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。