【專項學習】 —— Webpack5從入門到精通課程學習(六)
這篇主要介紹《webpack優化環境配置(下)》。
知識點包括:
一、懶載入和預載入
懶載入
懶載入就是,在實際專案中,某個.js檔案,還沒有用到,此時不進行載入,當網頁中進行某個功能,有需要時在載入。
1、複製程式碼分割工程檔案,修改其中的webpack.config.js,進行精簡。
const { resolve } = require('path'); const Htmlwebpackplugin = require('html-webpack-plugin'); module.exports = { entry: './src/js/index.js', output: { filename: 'js/[name].[contenthash:10].js', path: resolve(__dirname, 'build') }, plugins: [ new Htmlwebpackplugin({ template: './src/index.html', minify: { collapseWhitespace: true, removeComments: true } }), ], optimization: { splitChunks: { chunks: 'all' } }, mode: 'production' }
2、然後修改index.js檔案
console.log('index.js檔案被載入了'); // 給首頁的按鈕增加一個點選事件,為了實現懶載入,即用到某個js檔案時才載入該檔案 // 引入方式改為動態引入 document.getElementById('btn').onclick = function () { import('./test').then(({ mul }) => { console.log(mul(4, 5)); }); }
3、修改test.js程式碼
console.log('test.js檔案被載入了'); export function mul(x, y) { return x * y; } export function count(x, y) { return x - y; }
4、修改index.html程式碼,增加一個按鈕,當點選該按鈕時,test.js中的功能被需要,然後被載入。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h1>懶載入</h1> <button id="btn">按鈕</button> </body> </html>
5、然後終端執行npm run build
,開啟打包後的index.html。
發現,點選按鈕後,test.js檔案才被載入。
預載入
開啟網頁的時候,所有的js檔案都載入了,快取到記憶體裡,然後網頁中某個功能實現需要js檔案時,直接從記憶體中讀取。
1、修改index.js程式碼,增加webpackPrefetch: true
。
console.log('index.js檔案被載入了'); // 給首頁的按鈕增加一個點選事件,為了實現懶載入,即用到某個js檔案時才載入該檔案 // 引入方式改為動態引入 document.getElementById('btn').onclick = function () { // webpackPrefetch: true開啟預載入 import(/*webpackChunkName:'test',webpackPrefetch: true*/'./test').then(({ mul }) => { console.log(mul(4, 5)); }); }
2、然後輸入npm run build
重新打包。
開啟生成的index.html,可以看到,網頁一開啟,全部被載入了,點選按鈕後,test.js檔案開始被呼叫。
總結
- 懶載入:當檔案需要使用時才載入~
- 預載入prefetch:會在使用之前,提前載入js檔案
- 正常常載入可以認為是並行載入(同一時間載入多個檔案)
- 預載入prefetch:等其他資源載入完畢,瀏覽器空閒了,再偷偷載入資源
二、PWA(離線可訪問)
漸進式網路應用程式(progressive web application - PWA),是一種可以提供類似於native app(原生應用程式) 體驗的 web app(網路應用程式)。
1、複製tree shaking工程檔案。
2、實現該功能需要一個外掛,輸入npm i workbox-webpack-plugin -D
下載。然後在webpack.config.js中使用
const { resolve } = require('path'); const minicssextractplugin = require('mini-css-extract-plugin'); process.env.NODE_ENV = 'production' const cssminimizerwebpackplugin = require('css-minimizer-webpack-plugin'); const Htmlwebpackplugin = require('html-webpack-plugin'); const workboxwebpackplugin = require('workbox-webpack-plugin') // PWA:漸進式網路開發應用程式(離線可訪問) // 通過一個外掛workbox-webpack-plugin module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.[contenthash:10].js', path: resolve(__dirname, 'build') }, module: { rules: [{ oneOf: [ { test: /\.css$/, use: [ minicssextractplugin.loader, 'css-loader', { loader: 'postcss-loader', options: { postcssOptions: { plugins: [require('postcss-preset-env')()] } } } ] }, { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { useBuiltIns: 'usage', corejs: { version: 3 }, targets: { chrome: '60', firefox: '50' } } ] ], cacheDirectory: true, } }, { test: /\.(jpg|png|gif)$/, loader: 'url-loader', options: { limit: 8 * 1024, outputPath: 'imgs', esModule: false }, type: 'javascript/auto' }, { test: /\.html$/, loader: 'html-loader', options: { esModule: false, } }, { exclude: /\.(js|css|less|html|jpg|png|gif)$/, loader: 'file-loader', options: { outputPath: 'media', esModule: false, }, type: 'javascript/auto' } ] } ] }, plugins: [ new minicssextractplugin({ filename: 'css/built.[contenthash:10].css' }), new cssminimizerwebpackplugin( ), new Htmlwebpackplugin({ template: './src/index.html', minify: { collapseWhitespace: true, removeComments: true } }), // 使用PWA new workboxwebpackplugin.GenerateSW({ // 進行兩個設定,分別: // 1.幫助serviceworker快速啟動 //2.刪除舊的serviceworkerl // 最後生成一個serviceworker配置檔案 clientsClaim: true, skipWaiting: true }) ], mode: 'production' }
3、然後輸入npm run build
進行打包。打包後看到生成兩個.js檔案
生成的service-worker程式碼必須執行在伺服器上,有三種方法,一是通過nodejs編寫程式碼,二是輸入npm install http-server --save-dev安裝一個包,還要修改 package.json 的 scripts 部分,增加"start": "http-server dist",然後輸入npm start 啟動伺服器,將build目錄下所有資源作為靜態資源暴露出去。第三種方法是輸入npm install -D webpack-dev-server,然後npx webpack serve。最後點選訪問生成的網址。
【!!!注:我在測試後發現,第二種生成的路徑打不開,第三種執行後報了錯。不知道什麼原因~~~下面是原博主的測試執行結果】
把網路設定為離線,看是否還能訪問。
訪問正常。
三、多程序打包
1、複製上一小節工程檔案。
同一時間多個程序同時打包,優化打包時間。
2、需要下載一個loader。終端輸入命令npm i thread-loader -D
,修改config.js程式碼。
const { resolve } = require('path'); const minicssextractplugin = require('mini-css-extract-plugin'); process.env.NODE_ENV = 'production' const cssminimizerwebpackplugin = require('css-minimizer-webpack-plugin'); const Htmlwebpackplugin = require('html-webpack-plugin'); const workboxwebpackplugin = require('workbox-webpack-plugin') module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.[contenthash:10].js', path: resolve(__dirname, 'build') }, module: { rules: [{ oneOf: [ { test: /\.css$/, use: [ minicssextractplugin.loader, 'css-loader', { loader: 'postcss-loader', options: { postcssOptions: { plugins: [require('postcss-preset-env')()] } } } ] }, { test: /\.js$/, exclude: /node_modules/, use: [ // 開啟多程序打包,程序啟動大概為600ms,程序通訊也有開銷。只有工作消耗時間比較長,才需要 // 一般與babel loader結合使用 'thread-loader', { loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { useBuiltIns: 'usage', corejs: { version: 3 }, targets: { chrome: '60', firefox: '50' } } ] ], cacheDirectory: true, } } ] }, { test: /\.(jpg|png|gif)$/, loader: 'url-loader', options: { limit: 8 * 1024, outputPath: 'imgs', esModule: false }, type: 'javascript/auto' }, { test: /\.html$/, loader: 'html-loader', options: { esModule: false, } }, { exclude: /\.(js|css|less|html|jpg|png|gif)$/, loader: 'file-loader', options: { outputPath: 'media', esModule: false, }, type: 'javascript/auto' } ] } ] }, plugins: [ new minicssextractplugin({ filename: 'css/built.[contenthash:10].css' }), new cssminimizerwebpackplugin( ), new Htmlwebpackplugin({ template: './src/index.html', minify: { collapseWhitespace: true, removeComments: true } }), // 使用PWA new workboxwebpackplugin.GenerateSW({ // 進行兩個設定,分別: // 1.幫助serviceworker快速啟動 //2.刪除舊的serviceworkerl // 最後生成一個serviceworker配置檔案 clientsClaim: true, skipWaiting: true }) ], mode: 'production' }
3、終端輸入npm run build
進行打包,一般當專案檔案比較大時,這個功能的優勢才會更明顯。
四、externals
externals是防止將某些 import 的包(package)打包到 build(存放打包後文件的地方)中,是在執行時(runtime)再去從外部獲取這些擴充套件依賴(external dependencies)。
例如,從 CDN 引入 jQuery,而不是把它打包。
1、複製打包html資源工程,並重命名。複製好的工程檔案目錄如下
2、修改webpack.config.js程式碼
const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: './src/index.js', output: { filename: 'built.js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ], mode: 'production', // 外部擴充套件(externals) // 防止將某些 import 的包(package)打包到 built 中, externals: { jquery: 'jQuery' } }
3、修改index.js程式碼,使用jquery
import $ from 'jquery'; console.log($); function add(x, y) { return x + y; } console.log(add(1, 2));
4、然後輸入npm run build
我們發現生成的built.js檔案大小是312bytes。
如果把jquery也打包的話,檔案大小肯定遠遠大於這個值。
5、最後記得要在index.html中手動引入jquery。
因為我們沒有打包jquery,被externals設定排除了,手動引入後,才能正常使用。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h1 id="title">hello html</h1> <script src="https://code.jquery.com/jquery-3.1.0.js" integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk=" crossorigin="anonymous"> </script> </body> </html>
6、再重新打包一次。在瀏覽器開啟生成的index.html檔案。可以看到此時的built.js檔案是312bytes,網頁的功能也正常。
五、DLL(動態連結庫)
1、複製打包html資源工程。並重命名。
DLL功能就是:單獨打包,把不同的檔案最後打包到不同的檔案,即多對多的關係。
2、在複製的工程資料夾下新增webpack.dll.js檔案。其程式碼如下
/* 使用dll技術,對某些庫(第三方庫:jquery、react、vue. . . )進行單獨打包 當你執行webpack時,預設查詢webpack.config.js配置檔案 而我們需要執行webpack.dll.js檔案 所以輸入命令: webpack --config webpack.dll.js,進行修改 */ const { resolve } = require('path'); // webpack自帶的外掛 const webpack = require('webpack') module.exports = { entry: { //最終打包生成的[name] --> jquery // ['jquery']-- > 要打包的庫是jquery jquery: ['jquery'] }, output: { filename: '[name].js', path: resolve(__dirname, 'dll'), library: '[name]_[hash:10]'// 打包的庫裡面向外暴露出去的內容叫什麼名字 }, plugins: [ // 使用webpack自帶的外掛,打包生成一個manifest.json檔案,提供和jquery的對映 new webpack.DllPlugin({ name: '[name]_[hash:10]',//對映庫的暴露的內容名稱 path: resolve(__dirname, 'dll/manifest.json')//輸出檔案路徑 }) ], mode: 'production' }
3、終端輸入npm i jquery --save
下載jquery包。然後修改package.json中程式碼
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack --mode development", "build": "webpack --mode production", "dill":"webpack --config webpack.dll.js" },
4、然後終端輸入:npm run dill
。這樣就修改了打包時預設的配置檔案,變成了webpack.dll.js。
5、至此, 我們已經把jquery單獨打包出來了到一個資料夾中,那麼以後再打包時,就可以不用在打包jquery了。
想打包其他非官方modules時,需要再修改webpack.config.js程式碼。
/*工作流程 loader: 1下載 2使用(配置loader) plugins: 1.下載 2.引入 3使用 */ const { resolve } = require('path'); // 引入外掛 const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack') module.exports = { entry: './src/index.js', output: { filename: 'built.js', path: resolve(__dirname, 'build') }, module: { rules: [ // loader的配置 ] }, plugins: [ //plugins的配置 // html-webpack-plugin配置 // 功能:預設會建立一個空的HTML,自動引入打包輸出的所有資源(S/cSs) new HtmlWebpackPlugin({ //複製'./src/index.html’檔案,並自動引入打包輸出的所有資源(JS/cSs) template: './src/index.html' }), // 告訴webpack哪些庫不參與打包,同時使用時的名稱也得變~ new webpack.DllReferencePlugin({ manifest: resolve(__dirname, 'dll/manifest.json') }) ], mode: 'development' }
6、然後我們在index.js引入jquery程式碼。
import $ from 'jquery' console.log($); function add(x, y) { return x + y; } console.log(add(1, 2));
如果此時不修改config.js中程式碼,直接進行生產環境下的打包,npm run build,則最後的打包檔案還是會把jquery與自己寫的程式碼雜糅起來。
7、使用了webpack.DllReferencePlugin外掛後,輸入npm run build,檢視效果。
此時的built.js中沒有柔和jquery程式碼,體積很小。
那麼我們需要用jquery,該怎麼辦呢?
8、此時需要另一個外掛,輸入npm i add-asset-html-webpack-plugin -D
.
該外掛將某個檔案打包輸出去,並在html中自動引入該資源。
然後在config.js中使用。
/*工作流程 loader: 1下載 2使用(配置loader) plugins: 1.下載 2.引入 3使用 */ const { resolve } = require('path'); // 引入外掛 const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin') module.exports = { entry: './src/index.js', output: { filename: 'built.js', path: resolve(__dirname, 'build') }, module: { rules: [ // loader的配置 ] }, plugins: [ //plugins的配置 // html-webpack-plugin配置 // 功能:預設會建立一個空的HTML,自動引入打包輸出的所有資源(S/cSs) new HtmlWebpackPlugin({ //複製'./src/index.html’檔案,並自動引入打包輸出的所有資源(JS/cSs) template: './src/index.html' }), // 告訴webpack哪些庫不參與打包,同時使用時的名稱也得變~ new webpack.DllReferencePlugin({ manifest: resolve(__dirname, 'dll/manifest.json') }), // 將某個檔案打包輸出去,並在html中自動引入該資源 new AddAssetHtmlWebpackPlugin({ filepath: resolve(__dirname, 'dll/jquery.js'), outputPath: "auto" }) ], mode: 'development' }
9、此時在重新打包一次,npm run build
。
此時在執行打包後的html檔案就沒問題了。
總結
我們通過一個webpack.dll.js先單獨打包jquery檔案,然後在webpack.config.js中使用了外掛webpack.DllReferencePlugin,告訴webpack,在生產環境打包時,不需要再對jquery打包了,然後又使用了外掛AddAssetHtmlWebpackPlugin,告訴webpack,將之前單獨打包的jquery自動輸出並引入到html檔案中去。就可以避免在修改配置後再打包時,還會重複打包jquery,節省了時間。
注:筆記轉載自瘋子的夢想@部落格,課程來自尚矽谷b站Webpack5實戰課程