フロントエンドプロジェクトの極端なパフォーマンスの最適化

バックグラウンド

リーダー:この読み込みが常に行われているのはなぜですか?長い間開いています。I
:最初の読み込みが少し遅いです。ここにリマインダーテキストが表示されます。
リーダー:このエクスペリエンスはひどいです。最適化する方法を見つけて
ください。 I:このプロジェクトは大きすぎて200近く1ページのシステム...バック
エンドのクラスメート:それでは、Alibaba Cloudの淘宝網には私たちのシステムよりも多くのページがありませんか?
リーダー:(リーダーが私をちらっと見ました)
私:わかりました、リーダー(唇に微笑み、心にMMP)

WechatIMG159.jpeg 上記のコンテンツは、プログラムの目的のために純粋に架空のものです。この最適化は、テクノロジーへの愛情と仕事への熱意から完全に外れています。

次に、プロジェクトを最適化し、最初に結果を台無しにし、基本的なVueCli3プロジェクトを例として取り上げます。

最適化 最適化前 最適化
フォールドローディング時間より上 30代以上 2.5秒
パックサイズ 28.9M 1M

根据常规的操作思路,我们从以下几个方面考虑优化:

WEBPACK

sourceMap

productionSourceMap実稼働環境で閉じますcss sourceMap。これら2つは、マップされたソースファイルの2つの構成であり、ブレークポイントのデバッグに使用されるため、実稼働環境ではこのようなマッピングを行う必要はありません。

    // vue.config.js
    const isProduction = process.env.NODE_ENV === 'production'
    // 判断是否是生产环境
    module.exports = {
        productionSourceMap: !isProduction, //关闭生产环境下的SourceMap映射文件
        css: {
            sourceMap: !isProduction, // css sourceMap 配置
            loaderOptions: {
                ...
            }
        },
        ...
    }
复制代码

webpack-bundle-analyzer

webpack-bundle-analyzerこれは、webpackパッケージ分析用のアーティファクトプラグインです。 webpack-bundle-analyzer プラグインをインストールすると、パッケージ化後にローカルサービスが生成されます。これにより、パッケージ化されたファイルの包含関係とサイズが明確に示されるため、あまり直接話しません。npm install webpack-bundle-analyzer -D


    // vue.config.js
    module.exports = {     
       chainWebpack: (config) => {
            // 分析打包大小
            if (process.env.npm_config_report) {
              config.plugin('webpack-bundle-analyzer')
                .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
                .end();
            }
       }
    }
    
    // package.json
    {
      "name": "name",
      "version": "0.0.1",
      "scripts": {
        "report": "set npm_config_report=true && vue-cli-service build",
      },
      ...
     }

复制代码

次に、直接実行するnpm run reportと、ブラウザはパッケージ化時に依存関係パッケージの関係に関する次のレポートを出力します。

image.png

image.png

image.png対応するdistを開いて見てください、私のいい人、distファイルは28.9Mで、これは本当に少し大きいです。私は水を飲んでショックを受け、操作を続けました。

外観

根据分析报告,直观的看到node_modules里面有几个比较大的包,我们处理一下,比如echarts、element-ui、lodash、mock等,而externals可以用来防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)

    // vue.config.js
    ....
    chainWebpack: (config) => {
        ....
        //忽略的打包文件
        config.externals({
          'vue': 'Vue',
          'vue-router': 'VueRouter',
          'vuex': 'Vuex',
          'axios': 'axios',
          'element-ui': 'ELEMENT',
          'echarts': 'echarts',
          'lodash': 'lodash',
          'mock': 'mock'
        });
        ....
    }
复制代码

拆包

一个入口app.js好几兆,这加载起来多费劲,怪不得刚才领导打开系统的时候那么慢

// vue.config.js
....
chainWebpack: (config) => {
    ...
    config.optimization && config.optimization.splitChunks({
        // 拆包配置 
        chunks: 'all', //三选一:"initial" 初始化,"all"(默认就是all),"async"(动态加载) 
        minSize: 30000, // 形成一个新代码块最小的体积,只有 >= minSize 的bundle会被拆分出来 30000
        maxSize: 0, //拆分之前最大的数值,默认为0,即不做限制
        minChunks: 1, //引入次数,如果为2 那么一个资源最少被引用两次才可以被拆分出来
        maxAsyncRequests: 5, // 按需加载的最大并行请求数
        maxInitialRequests: 3, // 一个入口最大并行请求数
        automaticNameDelimiter: '~', // 文件名的连接符
        name: true,
        cacheGroups: {
        // node_modules模块包
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          name: 'chunk-vendors',
          // name(module) {
          //   const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
          //   return `chunk.${packageName.replace('@', '')}`;
          // },
          chunks: 'all',
          priority: -10,
        },
        // UI库单独拆包
        elementUI: {
          name: 'chunk-elementUI',
          priority: 20, //  the weight needs to be larger than libs and app or it will be packaged into libs or app
          test: /[\\/]node_modules[\\/]_?element-ui(.*)/
        },
        // 共享模块
        common: {
          name: 'chunk-common',
          minChunks: 2,
          maxSize: 1024, //拆分之前最大的数值,默认为0,即不做限制
          priority: -20,
          reuseExistingChunk: true
        }
        }
    });
    ...
}
...
复制代码

压缩js和css

如果你使用的是 webpack v5 或更高版本,是开箱机带的功能,但是你的webpack是5以下或则希望自定义配置,那么需要安装 terser-webpack-plugin。如果使用 webpack v4,则必须安装 terser-webpack-plugin v4 的版本。

// vue.config.js
const TerserJSPlugin = require('terser-webpack-plugin');
....
chainWebpack: (config) => {
    // 开启js、css压缩
    config.plugin('TerserJSPlugin')
      .use(new TerserJSPlugin({
        terserOptions: {
          output: {
            comments: false // 去掉注释
          },
          warnings: false,
          compress: {
            // eslint-disable-next-line camelcase
            drop_console: true,
            // eslint-disable-next-line camelcase
            drop_debugger: true,
            // pure_funcs: ['console.log'] // 移除console
          }
        }
      }));
}
复制代码

开启Gzip压缩

// 打包压缩静态文件插件
const CompressionWebpackPlugin = require("compression-webpack-plugin")
...
module.exports = {
    ...
    chainWebpack: config => {
        //生产环境开启js/css压缩
        if (isProduction) {
            config.plugin('CompressionWebpackPlugin').use(new CompressionWebpackPlugin({
                test: /\.(js)$/, // 匹配文件名
                threshold: 10240, // 对超过10k的数据压缩
                minRatio: 0.8,
                deleteOriginalAssets: true // 删除源文件
            }))
        }
    }
    ...
}
复制代码

对用服务端Nginx配置

# nginx前端静态资源配置  // data/docker/nginx/conf.d 
server { 
    listen 8080; 
    server_name _;
    gzip_static on; // 开启gzip压缩
    client_max_body_size 500m; 
    root /data/****/web/dist; 
    index index.html; 
    location ^~ /api { 
        proxy_pass http://***.**.**.***:8080/; 
        proxy_set_header Host ***.**.**.***; 
        proxy_set_header X-Real-IP $remote_addr; 
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    } 
}


复制代码

CDN

对于项目用到的静态资源,比如图片,静态资源库,我们直接把文件给甩到CDN上

<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.10/vue.min.js" charset="utf-8"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vuex/3.1.1/vuex.min.js" charset="utf-8"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.0.1/vue-router.min.js" charset="utf-8"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.17.1/axios.min.js" charset="utf-8"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.3/index.min.js" charset="utf-8"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.1.2/echarts.min.js" charset="utf-8"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js" charset="utf-8"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/Mock.js/1.0.1-beta3/mock-min.js" charset="utf-8"></script>
复制代码

按需加载

比如我们引用某一些第三方,使用少量功能,可以选择按需加载,举个例子: 比如引用lodash的时候,如果想要按需加载,可以考虑使用webpack-lodash-pluginbabel-plugin-lodash,按需加载可以节省近1M的空间大小

    // 使用前,需要手动引入指定的模块
    const isElement = require('lodash/isElement');
    const debounce = require('lodash/debounce');

    // 使用后,放心引入
    import { isElement, debounce } from 'lodash'
复制代码

缓存

さらに、ブラウザキャッシュ、サーバーキャッシュ、Redisキャッシュを使用して、システムの応答速度を上げることができます。たとえば、強力なキャッシュやバックエンドの学生とのネゴシエーションキャッシュを使用して、クライアントリクエストを合理的にキャッシュできますが、そうする必要があります。サーバーがWebリソースをプロキシしている場合、エントリのhtml.indexをキャッシュしてはならないことに注意してください。これは、エントリが更新されるたびに、このエントリが依存する一部のjsとcssがハッシュフィンガープリントに従って変更されるためです。キャッシュがある場合、最新のコンテンツをロードするためにブラウザのキャッシュをクリアする必要があるたびに、ユーザーが公開する原因になります

# nginx前端静态资源配置  // data/docker/nginx/conf.d 
server { 
    listen 8080; 
    server_name _;
    gzip_static on; // 开启gzip压缩
    client_max_body_size 500m; 
    root /data/****/web/dist; 
    index index.html; 
    location ^~ /api { 
        proxy_pass http://***.**.**.***:8080/; 
        proxy_set_header Host ***.**.**.***; 
        proxy_set_header X-Real-IP $remote_addr; 
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    } 
    location = /index.html {
        add_header Cache-Control "no-cache,no-store";
    }
}

复制代码

エピローグ

最適化の前後の比較を確認しましょう

最適化 最適化前 最適化
フォールドローディング時間より上 26秒 2.5秒
パックサイズ 28.9M 1M

download.jpegこれは、ウォークビハインドトラクターからランボルギーニへの飛躍です。手に水ガラスを手に取り、一口飲んでください。今月のパフォーマンスボーナスは安定しているはずです☕️

image.png

image.png

image.png

おすすめ

転載: juejin.im/post/7080066104613142559