webpack 合并压缩_webpack的运用
補充知識點 Development 與 Production 模式的區別
Development (開發階段)
使用dev-server會開啟一個本地服務器,可以本地進行服務器的運行狀態的模擬,熱更新進行實時模擬
source-map 會包含大量錯誤提醒信息,體積巨大
無需壓縮,方便直接觀看代碼
Producetion (生產環境)
source-map 非常簡潔
壓縮代碼
Development 與 Production 的分文件配置
當我們構建項目的時候,兩個模式如果分開文件配置就會導致配置代碼重復問題,我們只需要把不同的整理出來,最后打包的時候在合并一下就好了
npm install webpack-merge --save-dev 這個插件是用來合并配置的npm install webpack-dev-server --save-devwebpack.common.js 公共配置
const path = require('path');const htmlWebpackPlugin = require('html-webpack-plugin');const { CleanWebpackPlugin } = require('clean-webpack-plugin');module.exports = { entry: { index: './src/index.js' }, output: { path: path.resolve(__dirname, './bundle') }, plugins: [ new htmlWebpackPlugin(), new CleanWebpackPlugin() ]}webpack.dev.js ?開發配置
const webpack = require('webpack');const { merge } = require('webpack-merge');const commonConfig = require('./webpack.common');let devConfig = { mode: 'development', devtool: 'cheap-module-eval-source-map', devServer: { contentBase: './build', open: true, port: 8080, hot: true }, plugins: [ new webpack.HotModuleReplacementPlugin() ]}module.exports?=?merge(devConfig,?commonConfig)webpack.prod.js 生產環境配置
const { merge } = require('webpack-merge');const commonConfig = require('./webpack.common');let prodConfig = { mode: 'production', devtool: 'cheap-module-source-map', optimization: { useExports: true // 開啟 Tree Shaking }}module.exports = merge(prodConfig, commonConfig)code Splitting
在之前項目中,無論引用了多少插件,最后輸出的還是單個js文件,這樣就會造成
單文件過大
業務代碼和環境代碼壓縮到一起
單次修改業務代碼,就得重新打包所有文件
每次修改業務代碼,最終的打包出來的js就是一個新的文件(即便只是改了一個字母),就會導致用戶瀏覽器需要重新請求該文件
ps: 當第一次請求完之后,瀏覽器會將請求的文件緩存中瀏覽器環境中,即使你再次刷新網站,其實都會在本地緩存中讀取文件,不會像服務器發起請求,只有當文件的hash值不一樣的時候才會重新請求
// 例如我引入一個組件 npm install lodash -Dimport _ from 'lodash' // 假設這個組件有1Mconsole.log(_.json(['a', 'b', 'c'], '***')) // 假設業務代碼也是1M我們配置webpack optimization 選項
optimization: { splitChunks: 'all' }打包的結果就是lodash被打包進了vendors-index.js里面去了
當然我們也可以手動進行代碼分割 就是利用配置多個出口,單獨對第三方插件就行打包,但如果插件很多的話就十分不方便
improt _ from 'lodash';window._?=?_;entry: { lodash: './src/js/lodash.js'}Code Splitting 第三種方式
就是異步加載模式
function getComponent() { return import('lodash').then(({ default: _ }) => { let ele = document.createElement('div'); ele.innerText = _.json(['a', 'b', 'c'], '***'); return ele; })}getComponent().then((ele) => { document.body.appendChild(ele)})最后打包生成 異步加載的文件存放在了0.js文件中
Code Spiltting 注意點
代碼分割這個概念與webpack無關,webpack實現代碼分割的兩個方法
同步代碼:只需要在webpack.common.js 中配置optimization的配置即可
異步代碼(import函數):無需任何配置,webpack會自動進行配置,會自動放入新的文件夾中間
Code Splitting的底層插件SplitChunksPlugin
前面有講到我們使用webpack分割異步加載的組件,但是輸出文件啥的都是默認配置,現在我們要進行一些自定義配置
npm install babel-loader @babel/core --save-devnpm install @babel/preset-env --save-devnpm install @babel/polyfill --save-devnpm install @babel/plugin-syntax-dynamic-import --save-dev修改.babelrc文件
{ "presets": [ ["@babel/preset-env", { "useBuiltIns": "usage" }] ], "plugins": ["@babel/plugin-syntax-dynamic-import"]}index.js 代碼
function getComponent() { /* 使用魔法注釋 */ return import(/* webpackChunkName: "loadsh" */ 'lodash').then(({ default: _ }) => { let ele = document.createElement('div'); ele.innerText = _.json(['a', 'b', 'c'], '***'); return ele; })}getComponent().then((ele) => { document.body.appendChild(ele)})當前用魔法注釋自定義的文件名稱前面還是有一個前綴的, 這個前綴是因為webpack.common.js里面的optimization屬性影響的
修改webpack optimization的配置文件
optimzation: { splitChunks: { // webpack 自動幫我們完成代碼分割 chunks: "all", cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, // 匹配node_modules目錄下的文件 priority: -10 // 優先級配置項 }, default: { minChunks: 2, priority: -20, // 優先級配置項 reuseExistingChunk: true } } }}在默認配置中
會將 node_mudules 文件夾中的模塊打包進一個叫 vendors的bundle中,
所有引用超過兩次的模塊分配到 default bundle 中 更可以通過 priority 來設置優先級。
Code Splitting -SplitChunksPlugin 參數詳解
chunk:每一個打包出來的文件都是一個chunk, 這個文件數和minChunks的參數息息相關 意思就是,打包出來的chunk有幾個用到了某個組件,用到了,才會使用代碼分割
chunks:"all" ?針對不同的打包方式實現代碼分割 可選項:all, async, initial (同步代碼),異步一直分割就好,但如果是同步的話會繼續讀取CacheGroups的配置
示例配置如下
其他基本參數:
- optimzation: { splitChunks: { chunks: 'all', minSize: 30000, minChunks: 1, maxSayncRequest: 5 maxInitialRequest: 3, automaticNameDelimiter: '~', // 前綴和名字之間的連接符號 name: true, cacheGroups: { vendors: { // 這是一個打包的分組名稱 test: /[\\/]node_modules[\\/]/,// 匹配node_modules目錄下的文件,進行分割 priority: -10 }, default: { minChunks: 2, priority: -20, // 優先級配置項 reuseExistingChunk: true } } }}
表示從哪些chunks里面抽取代碼,除了三個可選字符串值 initial、async、all 之外,還可以通過函數來過濾所需的 chunks;
minSize:小于這個尺寸的文件, 就不再做文件分割了, 就直接合并的
maxSize: 可配可不配,如果配置了, 比如值為50000, 那么單個被獨立出來的引用包如果大于50000就會再
次被分割(但是如果這個庫是無法拆分的,那么這個maxSize就是沒啥用的了)
minChunks:當一個模塊被應用了多少次才會被分割, 一般就是1
maxAsyncRequests:最大引用的模塊數,webpack在該值設定的上限前會正確打包,后面的就不會再分割了
maxInitialRequests:最大入口文件引用的模塊數
automaticNameDelimiter:前綴和名字之間的連接符
name:一般就為true,專門用來標明下面的cacheGroups里面的基本配置是否生效
值得注意的是,如果沒有修改minSize屬性的話,而且被公用的代碼size小于30KB的話,它就不會分割成一個單獨的文件。在真實情形下,這是合理的,因為(如分割)并不能帶來性能確實的提升,反而使得瀏覽器多了一次對公共代碼的請求,而這個公用的代碼又是如此之小(不劃算)。
maxAsyncRequests:最大的按需(異步)加載次數,默認為 5;
maxInitialRequests:最大的初始化加載次數,默認為 3;
automaticNameDelimiter:抽取出來的文件的自動生成名字的分割符,默認為 ~;
name:抽取出來文件的名字,默認為 true,表示自動生成文件名;
cacheGroups: 緩存組。(這才是配置的關鍵)
緩存組會繼承splitChunks的配置,但是test、priorty和reuseExistingChunk只能用于配置緩存組。cacheGroups是一個對象,按上述介紹的鍵值對方式來配置即可,值代表對應的選項。除此之外,所有上面列出的選擇都是可以用在緩存組里的:chunks, minSize, minChunks, maxAsyncRequests,maxInitialRequests, name。可以通過optimization.splitChunks.cacheGroups.default: false禁用default緩存組。默認緩存組的優先級(priotity)是負數,因此所有自定義緩存組都可以有比它更高優先級(譯注:更高優先級的緩存組可以優先打包所選擇的模塊)(默認自定義緩存組優先級為0)
cacheGroups基本參數:vendors和default就是兩個不同的打包分組,vendors可以指定匹配規則 當某個打包的時候,兩個規則都符合的時候, 就按priority的值來,誰大按誰的 reuseExistingChunk:如果一個模塊之前已經被打包了,那么第二次打包的時候,就跳過
懶加載
lazy loading 就是懶加載,懶加載不是webpack的概念,他是JavaScript里面就被提出來了,我們結合webpack的代碼分割,可以很好的實現該功能
import 引入的組件其實是同步代碼,當home組件引入該組件,加載的時候即使你沒有用到該組件,依然會被請求,也就是我們常說的首屏優化,優化手段有很多,代碼書寫規范上可以優化,項目結構可以優化,視覺上的優化,減少不必要的請求,節流和防抖也是重要的手段等等,具體問題具體分析
打包分析
webpack.github.io/analyse/
這個工具可以幫助我們分析webpack打包的全過程和相對應的資源消耗
配置腳本命令
"bundle": "webpack --profile --json > stats.json --config webpack.dev.js"webpack.github.io/analyse/
然后在上傳JSON文件
里面會自動幫我們將各種數據進行數據化,圖形化 我們可以根據這些圖形化信息來分析我們打包的過程和相應的性能參數
webpack 還提供多種打包資源可視化工具
代碼使用率
打包后,運行html,在瀏覽器的控制臺中輸入ctrl+shift+p指令打開命令行:并輸入coverage指令
這也是webpack在幫助我們優化項目時采用的基本邏輯, 比如用于代碼分割的屬性splitChunks里面的chunks的默認值就是async,即異步,因為異步的代碼請求才可以減少首屏加載的時間
但是前面的觸發是由一個事件觸發的,比如我要實現點擊登錄按鈕,然后把登錄界面傳輸過來,那么一旦網絡不是很好,這個卡頓就會很明顯,所以我們還得繼續優化 比如說,在網絡空閑的時候,就自動發送請求,然后下載相應的文件?
Preloading,Prefetching
利用一個魔法注釋 /* webpackPrefetch:true /等主業務核心邏輯加載完再加載其他文件 or / webpackLoad:true */和主業務核心邏輯一起加載,盡可能的提前加載
webpack性能優化
可以用thread-loader或是parallel-webpack , happypack等進行多線程打包
在盡可能少的模塊上應用Loader(做好排除,tree shaking或是轉義的目標模塊)
自定義一個Loader
第三方的file-loader 作用是
在出口處生成一個圖片 該圖片的名字hash ext組成的 ?并且返回hash 和ext組成的字符串
第三方的url-loader 作用是
如果圖片資源大于limit在出口處生成一個圖片 該圖片的名字hash ext組成的 ?并且返回hash 和ext組成的字符串
如果圖片資源小于limit會生成base64格式的字符串 ?并且返回base64格式的字符串
my-url-loader
const loaderUtils = require('loader-utils')const path = require('path')function loader(source){ // 基于圖片的source生成圖片名(字符串) const {limit} = loaderUtils.getOptions(this) const extname = path.extname(this.resourcePath).slice(1) if(source.length// 如果打包的資源小于limit 那就把資源打包成base64 // console.log(source.toString('base64')) const base64 = source.toString('base64') return `module.exports="data:image/${extname};base64,${base64}"` }else{ return require('./my-file-loader').call(this,source) } // console.log(limit)}// 解析二進制文件loader.raw = truemodule.exports = loadermy-file-loader
const loaderUtils = require('loader-utils')//該函數在匹配時觸發 這個函數模擬babel-loader 把es6代碼解析成es5代碼function loader(source){ //基于圖片的source生成圖片名(字符串) const filename = loaderUtils.interpolateName(this, '[hash:6].[ext]', { content:source }); // console.log(typeof filename) //基于圖片的名字和圖片的source產生圖片 // console.log(this) this.emitFile(filename,source); return `module.exports=${JSON.stringify(filename)}`}//解析二進制文件
loader.raw = truemodule.exports = loader
webpack.config.js
{ test:/\.jpg$/, use:{ // loader:'file-loader', // loader:'my-file-loader', // loader:'url-loader', loader:'my-url-loader', options:{ limit:8192, } }}點分享點點贊點在看總結
以上是生活随笔為你收集整理的webpack 合并压缩_webpack的运用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 酷狗音乐和其他应用同步播放怎么设置
- 下一篇: 屏占比同档罕见!Redmi Note 1