90、使用webpack打包的項目優(yōu)化方案

一、根據(jù)可視化工具查看項目打包

webpack-bundle-analyzer可視化工具

Webpack進行打包,到底打了多少包,每個包有多大?webpack-bundle-analyzer這款插件可以幫助我們清晰展示。針對多余的包文件過大,剔除首次影響加載的效率問題進行剔除修改。


模塊功能:

  • 意識到你的文件打包壓縮后中真正的內(nèi)容
  • 找出哪些模塊組成最大的大小
  • 找到錯誤的模塊
  • 優(yōu)化它!
  • 最好的事情是它支持縮小捆綁!它解析它們以獲得實際大小的捆綁模塊。它也顯示他們的gzipped大??!

安裝和使用:

npm install --save-dev webpack-bundle-analyzer
//在webpack.config.js中:
let BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
    plugins: [new BundleAnalyzerPlugin()]
}

在vue-cli3中配置如下

//vue.config.js
let BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
configureWebpack: {
   plugins: [
      new BundleAnalyzerPlugin()
    ]
  },
}

默認的可選配置對象:

new BundleAnalyzerPlugin({
    // 可以是`server`,`static`或`disabled`。
    // 在`server`模式下,分析器將啟動HTTP服務器來顯示軟件包報告。
    // 在“靜態(tài)”模式下,會生成帶有報告的單個HTML文件。
    // 在`disabled`模式下,你可以使用這個插件來將`generateStatsFile`設(shè)置為`true`來生成Webpack Stats JSON文件。
    analyzerMode: 'server',
    // 將在“服務器”模式下使用的主機啟動HTTP服務器。
    analyzerHost: '127.0.0.1',
    // 將在“服務器”模式下使用的端口啟動HTTP服務器。
    analyzerPort: 8888,
    // 路徑捆綁,將在`static`模式下生成的報告文件。
    // 相對于捆綁輸出目錄。
    reportFilename: 'report.html',
    // 模塊大小默認顯示在報告中。
    // 應該是`stat`,`parsed`或者`gzip`中的一個。
    // 有關(guān)更多信息,請參見“定義”一節(jié)。
    defaultSizes: 'parsed',
    // 在默認瀏覽器中自動打開報告
    openAnalyzer: true,
    // 如果為true,則Webpack Stats JSON文件將在bundle輸出目錄中生成
    generateStatsFile: false,
    // 如果`generateStatsFile`為`true`,將會生成Webpack Stats JSON文件的名字。
    // 相對于捆綁輸出目錄。
    statsFilename: 'stats.json',
    // stats.toJson()方法的選項。
    // 例如,您可以使用`source:false`選項排除統(tǒng)計文件中模塊的來源。
    // 在這里查看更多選項:https: //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
    statsOptions: null,
    logLevel: 'info' 日志級別??梢允?信息','警告','錯誤'或'沉默'。
})

啟動服務:

生產(chǎn)環(huán)境查看:npm run build --report 或 正常build 即可啟動查看器

開發(fā)環(huán)境查看:webpack -p --progress 或啟動正常devServer服務即可啟動查看器!

官方請看:webpack-bundle-analyzer

Speed-Measure-Plugin可視化工具

通過這個插件,我們可以清楚的看到,打包整體的時間,各plugin,loader處理時間,以有利于我們針對打包速度取做優(yōu)化。

安裝和使用:

npm install --save-dev speed-measure-webpack-plugin
//webpack配置
 
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
const webpackConfig = smp.wrap({
    plugins: [
        new MyPlugin(),
        new MyOtherPlugin()
    ]
});

官方請看:Speed Measure Plugin

analyse

先使用 Webpack 打包指令生成 Webpack stats 分析文件

webpack --profile --json > stats.json

然后到官方提供的網(wǎng)頁上傳stats.json

Webpack analyse

就可以通過一些可視化資料來分析打包結(jié)果


官方請看:webpack

二、webpack外部擴展

列出了項目中較大的包,剩下的事情就是想辦法如何減小這些包的體積(將一個大包拆成多個小包)。
項目中產(chǎn)生較大的包的原因可以從兩個方面去考慮:

    1. 項目中引入的依賴包過于龐大;
    2. 業(yè)務代碼集中在一塊寫,或者是業(yè)務代碼寫的比較繁瑣;

對于這兩個問題,我們可以從兩個方面著手解決:

    1. 抽離項目中公共依賴的、不常變動的、體積較大的包;
    2. 將一個較大的業(yè)務代碼文件,拆成多個較小的文件,異步加載(或者優(yōu)化業(yè)務代碼)。

我們來討論第一種方法,在不改動業(yè)務代碼的情況下,如何減小公共依賴。
要知道這些依賴是我們需要的,不可能排除不引入。
但是他們都是全局依賴的,萬年不變的,可以使用瀏覽器自己的緩存來實現(xiàn)不重復加載。
具體做法就是:
將項目中需要的一些公共依賴包,并且不常變動的,單獨取出,不再每次都打包編譯(如React,Redux等)。
而是通過使用script標簽形式cdn引入,這樣在有緩存的情況下,這些資源均走緩存,不必加載。

具體做法:

總結(jié)需要抽離的公共依賴

這些依賴需要滿足一定的條件:

 * 體積較大;
 * 不常更新;
 * 依賴較多;
 * 是前置依賴;

常見的滿足這類條件的包有:

 * react
 * react-dom
 * redux
 * react-redux
 * moment
 * jquery

另外一些包文件如 Antd庫文件,整個包較大,但是每次用什么取什么的話,庫文件也會按需加載,不必單獨取出。
還有這類庫文件不建議單獨取出,因為里面可能會有bug,需要更新。

使用CDN引入資源

將抽離出來文件放到cnd上,注意,這些文件要是壓縮版本,并且是用ES5編寫的,否則瀏覽器報錯。

<head>
    <title>React Starter Kit</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 體積較大的包 -->
    <script src="https://cdn.bootcss.com/react/15.0.0/react-with-addons.min.js"></script>
    <script src="https://cdn.bootcss.com/react/15.0.0/react-dom.min.js"></script>
    <script src="https://cdn.bootcss.com/react-router/3.0.0/ReactRouter.min.js"></script>
    <script src="https://cdn.bootcss.com/redux/3.6.0/redux.min.js"></script>
    <script src="https://cdn.bootcss.com/react-redux/5.0.1/react-redux.min.js"></script>
    <script src="https://cdn.bootcss.com/history/4.5.0/history.min.js"></script>
</head>

配置webpack.conf.js

資源已經(jīng)引入,接下來需要配置webpack,使其打包的時候不在將這些資源打包。

const webpackConfig = {
    name: 'client',
    target: 'web',
    devtool: config.compiler_devtool,
    resolve: {
        root: paths.client(),
        extensions: ['', '.js', '.jsx', '.json'],
    },
    externals: {
        'react': 'React',
        'react-dom': 'ReactDOM',
        'react-router': 'ReactRouter',
        'redux': 'Redux',
        'history': 'History'
    },
    module: {},
}

這里externals告訴webpack那些資源從哪里尋找。

該對象的鍵表示 require 或者 import 時候的字符串

值表示的當前環(huán)境下的變量,比如引入React之后,React被作為全局對象,webpack就回去尋找React對象。

如果其中有一個找不到,打包就會失敗。

配置vendor.js
接下來配置vendor,使vendor也不打包該些JS

compiler_vendors : [
    // 'react',
    // 'react-redux',
    // 'react-router',
    // 'redux',
],

三、DLL方式

dll 全稱是:dynamic link library(動態(tài)鏈接庫)

dll方式也就是通過配置,告訴webpack指定庫在項目中的位置,從而直接引入,不將其打包在內(nèi)。

上面介紹的方式是將包放到cdn上,build的時候不在引入對應的包;

dll方式就是指定包在項目中,build的時候不在打包對應的包,使用的時候引入。

webpack通過webpack.DllPlugin與webpack.DllReferencePlugin兩個內(nèi)嵌插件實現(xiàn)此功能。

新建webpack.dll.config.js

const webpack = require('webpack');
module.exports = {
    entry: {
        bundle: [
            'react',
            'react-dom',
            //其他庫
        ],
    },
    output: {
        path: './build',
        filename: '[name].js',
        library: '[name]_library'
    },
    plugins: [
        new webpack.DllPlugin({
            path: './build/bundle.manifest.json',
            name: '[name]_library',
        })
    ]
};

webpack.DllPlugin選項:

    * path:manifest.json文件的輸出路徑,這個文件會用于后續(xù)的業(yè)務代碼打包;
    * name:dll暴露的對象名,要跟output.library保持一致;
    * context:解析包路徑的上下文,這個要跟接下來配置的 webpack.config.js 一致。

運行:

webpack --config webpack.dll.config.js

生成兩個文件,一個是打包好的bundlejs,另外一個是bundle.mainifest.json,大致內(nèi)容如下:

{
    "name": "bundle_library",
    "content": {
    "./node_modules/react/react.js": 1,
    "./node_modules/react/lib/React.js": 2,
    "./node_modules/process/browser.js": 3,
    "./node_modules/object-assign/index.js": 4,
    "./node_modules/react/lib/ReactChildren.js": 5,
    "./node_modules/react/lib/PooledClass.js": 6,
    "./node_modules/react/lib/reactProdInvariant.js": 7,
    //其他引用
}

配置webpack.config.js

const webpack = require('webpack');
var path = require('path');
 
module.exports = {
    entry: {
        main: './main.js',
    },
    output: {
        path: path.join(__dirname, "build"),
        publicPath: './',
        filename: '[name].js'
    },
    module: {
        loaders:[
            { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'},
            {
                test: /\.jsx?$/,
                loaders: ['babel-loader?presets[]=es2015&presets[]=react'],
                include: path.join(__dirname, '.')
            }
        ]
    },
    plugins: [
        new webpack.DllReferencePlugin({
            context: '.',
            manifest: require("./build/bundle.manifest.json"),
        }),
    ]
};

webpack.DllReferencePlugin的選項中:

   * context:需要跟之前保持一致,這個用來指導webpack匹配
   * manifest.json中庫的路徑;
   * manifest:用來引入剛才輸出的manifest.json文件。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容