yanyue404 / blog

Just blog and not just blog.
https://yanyue404.github.io/blog/
Other
87 stars 13 forks source link

从前端工程的角度进行性能优化 #241

Open yanyue404 opened 1 year ago

yanyue404 commented 1 year ago

举例以一个 Vue 全家桶 项目为例~

构建体积相关 + 首屏性能优化

0. 优化前使用 LightHouse(灯塔)评测各项性能指标

各项指标说明:https://mp.weixin.qq.com/s?__biz=Mzg5MDY1MjIxMA==&mid=2247496148&idx=1&sn=e7afda57d67a1d87ee0a42a907c6ef52&source=41#wechat_redirect

1. 使用 webpack-bundle-analyzer 查看冗余代码

2. 使用 splitChunks 插件拆分公共代码

『Webpack 系列』—— SplitChunks 插件用法详解 :https://juejin.cn/post/6844904198023168013

用 SplitChunks 插件来控制 Webpack 打包生成的 js 文件的内容的精髓就在于,防止模块被重复打包,拆分过大的 js 文件,合并零散的 js 文件。最终的目的就是减少请求资源的大小和请求次数。因这两者是互相矛盾的,故要以项目实际的情况去使用 SplitChunks 插件,需切记中庸之道。

{
  optimization: {
  splitChunks: {
    cacheGroups: {
      vendors: {
        name: 'chunk-vendors',
        test: /[\\/]node_modules[\\/]/,
        priority: -10,
        chunks: 'initial'
      },
      common: {
        name: 'chunk-common',
        minChunks: 2,
        priority: -20,
        chunks: 'initial',
        reuseExistingChunk: true
      }
    }
  }
}

3. 关闭 prefetch、preload

prefetch:用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容 preload:用来指定页面加载后很快会被用到的资源,所以在页面加载的过程中,我们希望在浏览器开始主体渲染之前尽早 preload

// 移除 preload(预载) 插件
config.plugins.delete("preload");
// 移除 prefetch(预取) 插件
config.plugins.delete("prefetch");

4. 前端开启 gzip(双端开启指南:https://cloud.tencent.com/developer/article/1507153

打包产物新增 gzip 资源,页面加载优先使用(network 请求多出 gzip 资源)

const CompressionPlugin = require("compression-webpack-plugin");
new CompressionPlugin({
  algorithm: "gzip",
  test: /\.js$|\.html$|\.css$/, // 匹配文件名
  minRatio: 1, // 压缩率小于1才会压缩
  threshold: 10240, // 对超过10k的数据压缩
  deleteOriginalAssets: false,
});

5. 图片资源优化

6. 资源加载

7. 生产环境

建议 js、css、图片 单个文件资源大小不超过 100k

构建速度相关

1. 查看构建时间

说明:https://www.npmjs.com/package/speed-measure-webpack-plugin

const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
module.exports = {
  configureWebpack: (config) => {
    config.plugins.push(new SpeedMeasurePlugin());
  },
};

2. vue-cli-plugin-dll

npm install --save-dev vue-cli-plugin-dll

接下来就是 dll 的相关配置,将我们项目中的依赖使用 dll 插件进行动态链接,这样依赖就不会进行编译,从而极大地提高编译速度,因为这些插件没有编译,在 vue.config.js 中进行配置,也很简单

const path = require("path");
module.exports = {
  pluginOptions: {
    dll: {
      //这里放的是你的依赖插件,就是你项目安装的其他的插件,将这些插件的名字依次加在后面,我建议将所有项目依赖插件全部放在后面
      //注意这里不能放webpack,gulp等需要node环境的插件,我尝试将babel等放到这里报错提示没有V8环境
      entry: ["vue", "vue-router", "view-design"],
      //dll 编译后的链接库的地址
      cacheFilePath: path.resolve(__dirname, "./public"),
      // 是否开启 DllReferencePlugin,
      open: true,
      // 在执行 `dev` , `build` 等其他指令时,程序会自动将 `dll` 指令生成的 `*.dll.js` 等文件自动注入到 index.html 中。
      inject: true,
    },
  },
};

多入口

const path = require("path");
module.exports = {
  pluginOptions: {
    dll: {
      // 入口配置
      entry: {
        vue: ["vue", "vue-router", "vuex"],
        ui: ["view-design"],
      },
      // 输出目录
      output: {
        path: path.join(__dirname, "public/dll"),
        filename: "[name].dll.js",
        // vendor.dll.js中暴露出的全局变量名
        // 保持与 webpack.DllPlugin 中名称一致
        library: "[name]_[hash]",
      },
      // 是否开启 DllReferencePlugin,
      open: true,

      // 在执行 `dev` , `build` 等其他指令时,程序会自动将 `dll` 指令生成的 `*.dll.js` 等文件自动注入到 index.html 中。
      inject: true,
    },
  },
};

配置好之后然后运行,进行你上面配置插件动态链接库的编译

npx vue-cli-service dll

dll 编译完成后会在上面配置的目录下生成 dll 文件夹,就可以开始跑项目了,因为这些插件都不需要编译,跑起来很流畅,修改后的热更新速度更是显著提升。我以前修改一行代码热更新编译在 30 秒以上,使用这个以后基本十秒以内搞定。

npm run serve

3. thread-loader 多进程打包

Vue-Cli

Vue-Cli 已经内置,开启

module.exports = { parallel: true };

parallel

是否为 Babel 或 TypeScript 使用 thread-loader。 该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。

Webpack

npm install --save-dev thread-loader

配置

const path = require("path");
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve("src"),
        use: [
          "thread-loader",
          // 耗时的 loader (例如 babel-loader)
        ],
      },
    ],
  },
};

4. import 优化

运用这个插件能在代码使用了 import 语法的情况下,大大提高代码的编译速度。

安装 babel-plugin-dynamic-import-node

npm install --save-dev babel-plugin-dynamic-import-node

vue-cli3

修改 babel.config.js 文件

module.exports = {
  presets: ["@vue/cli-plugin-babel/preset"],
  env: { development: { plugins: ["dynamic-import-node"] } },
};

5. 利用缓存提升二次构建速度

Webpack 中几种缓存方式:

以上这些缓存方式都有首次启动时的开销,即它们会让 “冷启动” 时间会更长,但是二次启动能够节省很多时间.

Vue-Cli 已经内置了 cache-loader 进行以下两个的缓存,未添加 hard-source-webpack-plugin

[
  {
    test: /\.vue$/,
    use: [
      {
        loader: "project_path\\node_modules\\cache-loader\\dist\\cjs.js",
        options: {
          cacheDirectory: "project_path\\node_modules\\.cache\\vue-loader",
          cacheIdentifier: "036da057",
        },
      },
      {
        loader: "project_path\\node_modules\\vue-loader\\lib\\index.js",
        options: {
          compilerOptions: {
            whitespace: "condense",
          },
          cacheDirectory: "project_path\\node_modules\\.cache\\vue-loader",
          cacheIdentifier: "036da057",
        },
      },
    ],
  },
  {
    test: /\.m?jsx?$/,
    include: ["/packages"],
    exclude: [
      function () {
        /* omitted long function */
      },
    ],
    use: [
      {
        loader: "project_path\\node_modules\\cache-loader\\dist\\cjs.js",
        options: {
          cacheDirectory: "project_path\\node_modules\\.cache\\babel-loader",
          cacheIdentifier: "5217b3ec",
        },
      },
      {
        loader: "project_path\\node_modules\\babel-loader\\lib\\index.js",
      },
      {
        loader: "babel-loader",
      },
    ],
  },
];

提示:Vue-Cli 自带的 cache-loader 会默认为 Vue/Babel/TypeScript 编译开启。文件会缓存在 node_modules/.cache 中。如果你遇到了编译方面的问题,记得先清缓存目录之后再试试看。

HardSourceWebpackPlugin

详细说明 :https://www.npmjs.com/package/hard-source-webpack-plugin

在启动项目时会针对项目生成缓存,若是项目无 package 或其他变化,下次就不用花费时间重新构建,直接复用缓存。

安装

npm install --save-dev hard-source-webpack-plugin

配置 vue.config.js

为模块提供中间缓存,缓存路径是:node_modules/.cache/hard-source

const HardSourceWebpackPlugin = require("hard-source-webpack-plugin");
module.exports = {
  configureWebpack: (config) => {
    config.plugin.push(
      new HardSourceWebpackPlugin({
        root: process.cwd(),
        directories: [],
        environmentHash: {
          root: process.cwd(),
          directories: [],
          // 配置了files的主要原因是解决配置更新,cache不生效了的问题,配置后有包的变化,plugin会重新构建一部分cache
          files: ["package.json", "yarn.lock"],
        },
      })
    );
  },
};

参考