1. 程式人生 > 其它 >【專項學習】 —— Webpack5從入門到精通課程學習(六)

【專項學習】 —— Webpack5從入門到精通課程學習(六)

這篇主要介紹《webpack優化環境配置(下)》。

知識點包括:

  1. 懶載入
  2. 預載入

 

一、懶載入和預載入

懶載入

懶載入就是,在實際專案中,某個.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實戰課程