Vue - 项目编译速度、性能优化、打包体积优化

1 篇文章 0 订阅

GitHub Demo 地址
在线预览

webpack中文文档

文章 :
项目优化的方法以及优缺点
HardSourceWebpackPlugin配置和其他优化方案
Vue 项目性能优化方案分享
vue-cli 项目可以做的优化
Vue首评加载速度及白屏时间优化详解

webpack5 和 webpack4 的区别有哪些 ?
webpack5升级指南(附打包性能优化大全)

个人觉得优化方案选择合适的就好,没必要全上,可以把更多的心思花在业务代码的优化上。
项目环境基于webpack4x,可能有些已经过时了,自行判断
下面的方法只有部分使用,更多的是一种记录!!!

webpack版本查看 npm list webpack
或者可以找node_modules目录下面的webpack/package.json文件,查看文件中的version,如下图

在这里插入图片描述

一、编译速度优化

优化编译的文章:
vue项目编译、运行过慢如何提升编译(编译包括运行编译与打包编译)速度
开发过程中vue项目保存编译慢优化方法
webpack5 配置多线程和缓存加快构建速度

1、使用缓存

  • 在webpack3中为了提高构建速度,我们往往会用到 DllPlugin 和 DllReferencePlugin 插件,但是配置复杂,更新文件还需要手动重新生成dll,比较繁琐。还有一种autodll-webpack-plugin会好一些
  • 在webpack4之后可以使用HardSourceWebpackPlugin,它通过在磁盘中设置缓存来提升编译加载速度,第一次正常编译并缓存,第二次有缓存后能减少80%-90%的时间。需要安装使用
  • webpack5内置的cache配置。

1.1、缓存插件 - HardSourceWebpackPlugin

hard-source-webpack-plugin 插件GitHub地址

HardSourceWebpackPlugin是 webpack 的插件,为模块提供中间缓存步骤。为了查看结果,您需要使用此插件运行 webpack 两次:第一次构建将花费正常的时间。第二个构建将明显更快。
速度大概有80%-90%的提升

tips:

默认缓存位置:nodemodules/.cache,需要清除缓存的话可删除后再编译一次
HardSourceWebpackPlugin 和 speed-measure-webpack-plugin 不能一起使用 !!!

安装 hard-source-webpack-plugin

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

配置

// webpack.config.js 或 vue.config.js
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const path = require('path')

module.exports = {
  // ...
  plugins: [
     // 默认缓存位置:node_modules/.cache路径下
     new HardSourceWebpackPlugin()
     // new HardSourceWebpackPlugin({
     //   cacheDirectory: path.resolve(__dirname, 'node_modules/.cache/hard_source_cache')
     // })
  ]
}

1.2、webpack5 配置cache

webpack5 内置了 cache 缓存机制。直接配置即可。
cache 会在开发模式下被设置成 type: memory 而且会在生产模式把cache 给禁用掉。

// webpack.config.js 或 vue.config.js
const path = require('path')
module.exports = {
  transpileDependencies: true,
  configureWebpack: {
    cache: {
      type: 'filesystem',
      // cacheDirectory: path.resolve(__dirname, '.webpack_cache') // 设置缓存目录(根路径下)
      cacheDirectory: path.join(__dirname, 'node_modules/.cache/webpack_cache') // 设置缓存目录(node_modules/.cache路径下)
    }
  }
}

1.3、cache-loader 插件

和第一种类似,这里不展开说

hard-source-webpack-plugin 安装前后对比

第一次编译 36340ms
在这里插入图片描述
第二次编译 3760ms
在这里插入图片描述

2、合理使用source-map

productionSourceMap的作用在于定位问题,打包时会生成.map文件,在生产环境就可以在浏览器查看到输出的信息具体是在哪一行,但相应的包的体积也会变大,也会影响构建速度,将其设置为false则不生成.map文件
把productionSourceMap 置为false,既可以减少包大小,也可以加密源码

module.exports = {
  productionSourceMap: false
}

3、多线程打包

3.1、thread-loader

文章
【Webpack 性能优化系列(9) - 多进程打包】极大的提升项目打包构建速度

thread-loader 是一个 Webpack 的 loader,它可以将一些开销较大的工作放到 worker 池中,并在 worker 池中执行,以提高构建速度。
例如,在构建 Vue.js 项目时,由于需要编译 .vue 单文件组件、解析模板等操作,构建的时间可能会很长,特别是对于大型项目来说。可以使用 thread-loader 将这些操作放到 worker 池中执行,从而显著缩短构建时间。

使用时,需将此 loader 放置在其他 loader 之前。放置在此 loader 之后的 loader 会在一个独立的 worker 池中运行。
在 worker 池中运行的 loader 是受到限制的。例如:
这些 loader 不能生成新的文件。
这些 loader 不能使用自定义的 loader API(也就是说,不能通过插件来自定义)。
这些 loader 无法获取 webpack 的配置。
每个 worker 都是一个独立的 node.js 进程,其开销大约为 600ms 左右。同时会限制跨进程的数据交换。
请仅在耗时的操作中使用此 loader!

注意,thread-loader 并不是适用于所有场景的,它只对一些开销较大的任务有效。如果任务本身就非常快速并且非常简单,则使用 thread-loader 可能会比直接在主线程中执行更慢。

安装

 npm install thread-loader --save-dev

下面是 thread-loader 的使用示例(将会被应用到所有 .js 和.vue 文件中,除了 node_modules 中的文件。):

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve('src'),
        exclude: /node_modules/,
        use: [
          'thread-loader', // 将后续 loader 放在 worker 池中执行
          // 耗时的 loader (例如 babel-loader)
          'babel-loader'
        ]
      }
    ]
  }
}

vue.config.js

module.exports = {
  // ... 
  configureWebpack: {
    module: {
      rules: [
        {
          test: /\.js$/,
          include: path.resolve('src'),
          exclude: /node_modules/,
          use: [
            'thread-loader', // 将后续 loader 放在 worker 池中执行
            // 耗时的 loader (例如 babel-loader)
            'babel-loader'
          ]
        }
      ]
    }
  }
}

options

use: [
  {
    loader: "thread-loader",
    // 有同样配置的 loader 会共享一个 worker 池
    options: {
      // 产生的 worker 的数量,默认是 (cpu 核心数 - 1),或者,
      // 在 require('os').cpus() 是 undefined 时回退至 1
      workers: 2,

      // 一个 worker 进程中并行执行工作的数量
      // 默认为 20
      workerParallelJobs: 50,

      // 额外的 node.js 参数
      workerNodeArgs: ['--max-old-space-size=1024'],

      // 允许重新生成一个僵死的 work 池
      // 这个过程会降低整体编译速度
      // 并且开发环境应该设置为 false
      poolRespawn: false,

      // 闲置时定时删除 worker 进程
      // 默认为 500(ms)
      // 可以设置为无穷大,这样在监视模式(--watch)下可以保持 worker 持续存在
      poolTimeout: 2000,

      // 池分配给 worker 的工作数量
      // 默认为 200
      // 降低这个数值会降低总体的效率,但是会提升工作分布更均一
      poolParallelJobs: 50,

      // 池的名称
      // 可以修改名称来创建其余选项都一样的池
      name: "my-pool"
    },
  },
  // 耗时的 loader(例如 babel-loader)
];

3.2、parallel-webpack

parallel-webpack 是一个基于 Webpack 的插件,它能够将构建任务分解为多个子进程并行运行,以此来提高构建速度。与其他类似的插件相比,parallel-webpack 最大的特点是支持异步和同步模块的并行处理,而不仅仅局限于 Loader 和 Plugin。

npm install --save-dev parallel-webpack
// webpack.config.js
const ParallelWebpack = require('parallel-webpack').Plugin

module.exports = {
  // ...
  plugins: [
    new ParallelWebpack({
      // 配置选项
    }),
    /* 其他插件 */
  ],
  // ...
}

以上配置会启用 parallel-webpack 插件,从而实现多进程打包。您可以根据自己的需求在配置选项中对插件进行配置。

需要注意的是,使用多线程打包时可能会增加系统的 CPU 和内存消耗,因此建议在配合与机器性能相匹配的线程数,并且测试多线程打包效果时,应该同时观察构建时间和构建过程中的 CPU 和内存消耗情况。

3.3、HappyPack

HappyPack 是一个用于将 Webpack 进行多线程编译的工具,它能够将一个复杂的 Loader 或者 Plugin 任务分解成多个子进程并行执行,以此来加快构建速度。

npm install happypack --save-dev
const HappyPack = require('happypack');

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: 'happypack/loader?id=js'
      }
    ]
  },
  plugins: [
    new HappyPack({
      id: 'js',
      threads: 4,
      loaders: ['babel-loader']
    })
  ],
  // ...
};

在上述代码中,我们定义了一个名为 js 的 HappyPack 实例,使用了 4 个线程并行编译 babel-loader 处理的 JavaScript 文件。

注:2和3插件相关的代码没有经过实际使用测试效果!!!

4、开启 热更新 / 热替换

webpack 中文文档 HMR 加载样式

webpack 热加载 使用module.hot.accept和不使用的区别
模块热替换(HMR)

  • 热更新(Hot Update):在代码文件被修改后,自动编译出新的代码,并将新代码注入到浏览器运行的 JavaScript 中。热更新并没有重新加载页面,而是更新了部分改动的内容,保留了页面的状态信息,因此可以保持用户的操作状态。
  • 热重载(Hot Reload):在代码文件被修改后,以最快的速度重新编译、打包,并刷新浏览器窗口。相比于热更新,热重载不仅更新了 JavaScript 代码,还重新渲染了整个页面,因此会有一短暂的闪屏,更适用于 UI 相关的修改。
  • 模块热替换(Hot Module Replacement):类似于热更新,但是只替换变化的部分,不会重载整个模块或者应用程序,因此速度更快,体验更好。模块热替换不仅适用于 JavaScript 模块,还可以用于 CSS、图片等资源的替换。

HotModuleReplacementPlugin 是 Webpack 提供的一个插件,用于启用模块热替换(HMR)功能。HMR 是一种开发模式,它可以在不刷新整个页面的情况下,只更新修改过的部分,提高开发效率。

注:

只对js生效
修改css的时候,页面不会自动刷新,只会更新我们修改后的css

const webpack = require('webpack');

module.exports =  {
  // ...
  mode: 'development',
  devtool: 'eval-source-map', // 使用 source-map 方便调试
  devServer: {
    port: 8080,
    hot: true, // 开启 HMR 功能
    open: true // 自动打开浏览器
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin() // 启用 HMR 功能
  ]
}

其中,hot 选项设置为 true,表示开启 HMR 功能;plugins 中添加了 HotModuleReplacementPlugin 插件,以启用 HMR 功能。

在js 代码中添加如下代码:

// js 代码中添加 HMR 相关的代码
if (module.hot) {
  // 如果支持HMR,告诉 Webpack,当前模块更新不用刷新
  module.hot.accept()
  // 如果支持HMR,告诉 Webpack,当前模块更新时一定要刷新
  // module.hot.decline()
}

通过上述配置,就可以开启 Webpack 的模块热替换功能,使得在开发过程中能够实时预览修改的效果,提高开发效率。需要注意的是,HMR 功能只适用于开发环境,在生产环境中不应该使用。

在这里插入图片描述

5、使用Dll预编译(DllPlugin / autodll-webpack-plugin)

DllPlugin是webpack的一个插件,可以将一些不常变化的代码或三方库打包成一个单独的动态链接库(DLL 文件),这样在每次编译时就不需要重新打包这些代码,从而提高编译速度。

使用 DllPlugin 可以将一些稳定的、难以修改的库,如 vue、vue-router、vuex 等预先编译,并通过构建工具在项目编译时直接使用预先编译的 dll 库文件,从而提升了打包速度。

推荐使用 autodll-webpack-plugin

更推荐直接使用 hard-source-webpack-plugin

npm install autodll-webpack-plugin --save-dev

vue.config.js

const AutoDllPlugin = require('autodll-webpack-plugin');

module.exports = {
  // ...
  plugins: [
    new AutoDllPlugin({
      inject: true,
      debug: true,
      filename: '[name]_[hash].js',
      path: './dll',
      entry: {
        vendor: [
          'vue',
          'vue-router',
          'vuex'
        ]
      }
    })
  ]
}

以上配置中,我们使用了 AutoDllPlugin 插件,并传入了以下参数:

  • inject: true 表示自动将生成的 DLL 引用到 HTML 中;
  • debug: true 表示在 webpack 日志中输出调试信息;
  • filename: ‘[name]_[hash].js’ 表示生成的 DLL 文件名,其中 [name] 对应 entry 中的 key,[hash] 表示文件 hash 值;
  • path: ‘./dll’ 表示生成的 DLL 文件存放路径;
  • entry 表示要编译成 DLL 的第三方库列表。

以上配置将会生成一个名为 vendor_XXXX.js 的 DLL 文件,其中 XXXX 表示文件哈希值。该文件包含了 vue、vue-router 和 vuex 这三个库的代码。

在 HTML 中添加以下代码来引用生成的 DLL 文件, 其中,XXXX 应替换为生成的 DLL 文件的哈希值。

<script src="./dll/vendor_XXXX.js"></script>

如果使用 webpack-dev-server 进行开发,需要在 DevServer 配置中添加以下内容:
这样,在启动 DevServer 后,autodll-webpack-plugin 会自动将生成的 DLL 引入到 HTML 中,从而提升构建速度。

const AutoDllPlugin = require('autodll-webpack-plugin');

module.exports = {
  // ...
  devServer: {
    // ...
    before(app, server) {
      AutoDllPlugin({
        debug: true,
        filename: '[name]_[hash].js',
        path: './dll',
        entry: {
          vendor: [
            'vue',
            'vue-router',
            'vuex'
          ]
        }
      }).apply(new webpack.Compiler());
    }
  }
}

6、优化Loader

Loader是webpack的一个重要组成部分,它对文件进行处理的过程中可能会影响编译速度。优化Loader的配置可以大大提高编译速度。

  • 减少 Loader 数量:尽可能地避免重复的 Loader 使用,或者在必要的情况下尽可能合并多个 Loader,从而减少需要编译的 Loader 数量。

  • 缓存 Loader 的编译结果:使用 Webpack 的 cache-loader 插件可以将 Loader 的编译结果缓存到硬盘中,在不改变源文件的基础上可以大大缩短 Loader 的重新编译时间。

  • 启用多线程编译:使用多线程编译工具可以将 Webpack 构建任务分解成多个子进程并行执行,这样可以充分利用 CPU 多核并行计算的优势,加快编译速度。可以采用 happyPack 或 thread-loader 等工具来实现。

  • 避免使用正则表达式作为 Loader 规则:在定义 Loader 规则时,尽量避免使用正则表达式做为规则匹配方式,这样会导致构建过程变慢。如果必须使用,也可以尝试限制正则表达式的匹配范围,提高匹配效率。

7、exclude & include

在 Vue 项目中使用 exclude 和 include 可以帮助提升编译速度,这是因为它们可以减少不必要的文件检查和编译。

exclude 和 include 是通过 webpack 的 module.rules 配置项来设置的。其中,exclude 表示排除某些目录或文件不被编译,而 include 表示包含某些目录或文件进行编译。

下面以 Vue 项目的 Babel 编译为例,介绍如何使用 exclude 和 include。

安装依赖

npm install --save-dev babel-loader @babel/core @babel/preset-env

其中,babel-loader 是用于编译 JavaScript 代码的工具,@babel/core 是 Babel 核心库,@babel/preset-env 是一种预设包,可以根据目标环境自动选择转换插件。

配置 webpack.config.js

以下配置中,exclude: /node_modules/ 表示排除 node_modules 目录下的 JavaScript 文件不被编译,以提高编译速度。

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
}

有时候,如果我们只需要编译项目中某个目录下的 JavaScript 文件,可以使用 include 来指定相应的目录。

例如,我们只需要编译 src/components 目录下的 JavaScript 文件,可以将配置改为:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve(__dirname, 'src/components'),
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
}

以上配置中,include: path.resolve(__dirname, ‘src/components’) 表示只包含 src/components 目录下的 JavaScript 文件进行编译,以减少不必要的文件检查和编译。

8、构建区分环境

比如,在开发环境中,我们通常需要启用 source map 和热更新等功能,而在生产环境中,我们则需要启用代码压缩和 Tree Shaking 等优化技术。因此,我们可以在 Webpack 的配置文件中使用以下代码来根据环境配置不同的插件和优化:

const isProd = process.env.NODE_ENV === 'production';

module.exports = {
  // ...
  plugins: [
    isProd ? new UglifyJSPlugin() : null,
    // ...
  ].filter(Boolean),
};

9、提升webpack版本

升级 Webpack 版本是提高构建速度的一个重要方式,每个新版本都会带来新的优化和性能改进。

更新 Webpack 到最新版本:
首先需要确定当前使用的 Webpack 版本并检查是否需要更新,可以通过 npm 命令安装最新版本的 Webpack:

npm install webpack@latest --save-dev

注意事项:

  • 检查 Webpack 相关的插件和 loader 的版本:
    升级 Webpack 时,应该同时检查相关的插件和 loader 是否需要更新,需要确保其与 Webpack 的版本兼容。
  • 移除废弃的插件和 loader:
    升级可能有些插件和loader 已经随着升级废弃了,因此需要移除对应的插件和 loader。

二、打包体积优化

分析插件 webpack-bundle-analyzer

首先推荐一个分析插件,使用这个插件可以在编译和打包时打开一个分析页面,可以帮助我们优化打包后的文件大小和性能。
它可以生成一个可视化的报告,展示各个模块的大小、依赖关系和构成。通过这个报告,我们可以清晰地了解每个模块的大小和依赖关系,从而确定哪些模块对打包结果的大小贡献较大,进而进行优化。

webpack-bundle-analyzer 插件配置

安装 webpack-bundle-analyzer 插件

npm install --save-dev webpack-bundle-analyzer

在 Webpack 配置文件中使用插件

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  // ...
  plugins: [
      new BundleAnalyzerPlugin({
        openAnalyzer: true
      }),
  ]
}

vue cli 生成的项目下面的就不需要了,直接编译打包会打开一个分析页面

运行 Webpack 构建
运行 Webpack 的构建命令,例如:

webpack --config webpack.config.js

查看分析报告
上述命令执行完成之后,会生成一个报告页面,端口号会打印在控制台中,例如:

Webpack Bundle Analyzer is started at http://127.0.0.1:8888

在浏览器中打开相应的地址,即可查看分析报告。

需要注意的是,如果你的构建配置是基于 Vue CLI 或者 Create React App 等工具生成的,则无需手动添加 webpack-bundle-analyzer 插件,这些工具已经默认包含了该插件。可以直接编译打包或者通过相应的命令来查看分析报告,例如:

vue-cli-service build --mode production --report

在这里插入图片描述

1、三方库按需引用

1.1、element-ui按需引用

完整组件列表以 components.json(引用版本的)为准

  • 步骤1:安装 babel-plugin-component 插件:
npm i babel-plugin-component -D
  • 步骤2:在babel.config.js.babelrc文件中配置:
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset',
    ['@babel/preset-env', { 'modules': false }]
  ],
  plugins: [
    [
      'component',
      {
        libraryName: 'element-ui',
        styleLibraryName: 'theme-chalk'
      }
    ]
  ]
}
  • 步骤3:创建element.js

// element-ui 按需引入
// 注:完整组件列表以 components.json (引用版本的)为准
// https://github.com/ElemeFE/element/blob/master/components.json

import {
  Pagination,
  Dialog,
  Autocomplete,
  Dropdown,
  DropdownMenu,
  DropdownItem,
  Menu,
  Submenu,
  MenuItem,
  MenuItemGroup,
  Input,
  InputNumber,
  Radio,
  RadioGroup,
  RadioButton,
  Checkbox,
  CheckboxButton,
  CheckboxGroup,
  Switch,
  Select,
  Option,
  OptionGroup,
  Button,
  ButtonGroup,
  Table,
  TableColumn,
  DatePicker,
  TimeSelect,
  TimePicker,
  Popover,
  Tooltip,
  Breadcrumb,
  BreadcrumbItem,
  Form,
  FormItem,
  Tabs,
  TabPane,
  Tag,
  Tree,
  Alert,
  Slider,
  Icon,
  Row,
  Col,
  Upload,
  Progress,
  Spinner,
  Badge,
  Card,
  Rate,
  Steps,
  Step,
  Carousel,
  Scrollbar,
  CarouselItem,
  Collapse,
  CollapseItem,
  Cascader,
  ColorPicker,
  Transfer,
  Container,
  Header,
  Aside,
  Main,
  Footer,
  Timeline,
  TimelineItem,
  Link,
  Divider,
  Image,
  Calendar,
  Backtop,
  PageHeader,
  CascaderPanel,
  Avatar,
  Drawer,
  Popconfirm,
  // 单独设置
  Loading,
  MessageBox,
  Message,
  Notification
} from 'element-ui'

const components = [
  Pagination,
  Dialog,
  Autocomplete,
  Dropdown,
  DropdownMenu,
  DropdownItem,
  Menu,
  Submenu,
  MenuItem,
  MenuItemGroup,
  Input,
  InputNumber,
  Radio,
  RadioGroup,
  RadioButton,
  Checkbox,
  CheckboxButton,
  CheckboxGroup,
  Switch,
  Select,
  Option,
  OptionGroup,
  Button,
  ButtonGroup,
  Table,
  TableColumn,
  DatePicker,
  TimeSelect,
  TimePicker,
  Popover,
  Tooltip,
  Breadcrumb,
  BreadcrumbItem,
  Form,
  FormItem,
  Tabs,
  TabPane,
  Tag,
  Tree,
  Alert,
  Slider,
  Icon,
  Row,
  Col,
  Upload,
  Progress,
  Spinner,
  Badge,
  Card,
  Rate,
  Steps,
  Step,
  Carousel,
  Scrollbar,
  CarouselItem,
  Collapse,
  CollapseItem,
  Cascader,
  ColorPicker,
  Transfer,
  Container,
  Header,
  Aside,
  Main,
  Footer,
  Timeline,
  TimelineItem,
  Link,
  Divider,
  Image,
  Calendar,
  Backtop,
  PageHeader,
  CascaderPanel,
  Avatar,
  Drawer,
  Popconfirm
]

const element = {
  install: (Vue) => {
    components.forEach(component => {
      Vue.component(component.name, component)
    })

    // 单独设置
    Vue.use(Loading.directive)
    Vue.prototype.$loading = Loading.service
    Vue.prototype.$msgbox = MessageBox
    Vue.prototype.$alert = MessageBox.alert
    Vue.prototype.$confirm = MessageBox.confirm
    Vue.prototype.$prompt = MessageBox.prompt
    Vue.prototype.$notify = Notification
    Vue.prototype.$message = Message
  }
}
export default element
  • 步骤4:main.js引用(引用方式从引用官方组件换成引用自定义的element.js)
// import ElementUI from 'element-ui' // 全局引用
import ElementUI from '@/utils/element' // 按需引用
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)

之后正常使用即可

1.2、echart按需引用

这里直接创建一个echart.js文件,然后在需要使用的地方引用即可,
引用方式从引用官方组件换成引用自定义的echarts.js

// import * as echarts from 'echarts' // 全量引入
import echarts from '@/utils/echarts' // 按需引入

echart.js文件

// echarts 按需引入

// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
import * as echarts from 'echarts/core'

// 引入图表类型,图表后缀都为 Chart
import { BarChart, LineChart, PieChart } from 'echarts/charts'
// 自定义类型的图表
import { CustomChart } from 'echarts/charts'

// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  LegendComponent,
  DatasetComponent,
  TransformComponent,
  DataZoomComponent
} from 'echarts/components'

// 标签自动布局、全局过渡动画等特性
import { LabelLayout, UniversalTransition } from 'echarts/features'
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
// import { CanvasRenderer } from 'echarts/renderers'
import { SVGRenderer } from 'echarts/renderers'

// 将以上引入的组件使用use()方法注册
echarts.use([
  BarChart,
  LineChart,
  PieChart,
  CustomChart,
  // 提示框,标题,直角坐标系,数据集 等组件
  TitleComponent,
  TooltipComponent,
  GridComponent,
  LegendComponent,
  DatasetComponent,
  TransformComponent,
  DataZoomComponent,
  // 标签自动布局、全局过渡动画等特性
  LabelLayout,
  UniversalTransition,
  // Canvas 渲染器
  // CanvasRenderer
  SVGRenderer
])

// 导出
export default echarts

1.3、lodash按需引用

方法一:使用’lodash-es’代替’lodash’

import { cloneDeep } from 'lodash-es' // 按需引入
import { throttle, debounce } from 'lodash-es' // 按需引入
// import lodash from 'lodash-es' // 全量引入

方法二:使用’lodash’时注意按需引用

// import cloneDeep from 'lodash/cloneDeep' // 按需引入
// import lodash from 'lodash' // 全量引入

2、生产环境去除console.log

开发环境需要调试加的控制台日志,在生产环境也打印的话,一方面容易造成数据泄漏,一方面也会占用体积

2.1、 babel-plugin-transform-remove-console

这是一个Babel插件,可以在构建期间删除所有console.*语句。

npm install --save-dev babel-plugin-transform-remove-console

然后,在babel.config.js.babelrc文件中添加以下代码:

const plugins = []
// remove console.* in production
if (process.env.NODE_ENV === 'production') {
  plugins.push('transform-remove-console')
  // plugins.push(['transform-remove-console', { 'exclude': ['error', 'warn'] }])
}

module.exports = {
  //...
  plugins: plugins
}

2.2、terser-webpack-plugin

这个插件来压缩js代码的,它自带一个删除 console.log 的功能
webpack4 上需要下载安装 terser-webpack-plugin 插件,并且需要以下配置:

npm install terser-webpack-plugin --save-dev
const TerserPlugin = require('terser-webpack-plugin')
 
module.exports = { 
// ...other config
optimization: {
  minimize: !isDev,
  minimizer: [
    new TerserPlugin({
      extractComments: false, 
      terserOptions: { 
        compress: { 
          pure_funcs: ['console.log'] 
        }
      }
    }) ]
 }

webpack5 内部本身就自带 js 压缩功能,他内置了terser-webpack-plugin插件,我们不用再下载安装。而且在 mode=“production” 的时候会自动开启 js 压缩功能。

如果你要在开发环境使用,就用下面:

  // webpack.config.js中
  module.exports = {
     optimization: {
       usedExports: true, //只导出被使用的模块
       minimize : true // 启动压缩
     }
  }
 

2.3、 uglifyjs-webpack-plugin

uglifyjs-webpack-plugin 是一个用于压缩 JavaScript 代码的 Webpack 插件,它是以 UglifyJS 为基础,用于对 JavaScript 进行代码压缩和混淆。

npm install --save-dev uglifyjs-webpack-plugin
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
  // ...
  optimization: {
    minimize: true,
    minimizer: [
      new UglifyJsPlugin({
        // 开启并发
        parallel: true,
        // 多进程压缩
        uglifyOptions: {
          compress: {},
          output: {
            comments: false,
          },
        },
      }),
    ],
  }
}

注:2和3插件相关的代码没有经过实际使用测试效果!!!

3、合理使用source-map

productionSourceMap的作用在于定位问题,打包时会生成.map文件,在生产环境就可以在浏览器查看到输出的信息具体是在哪一行,但相应的包的体积也会变大,也会影响构建速度,将其设置为false则不生成.map文件
把productionSourceMap 置为false,既可以减少包大小,也可以加密源码

module.exports = {
  productionSourceMap: false
}

4、Code Splitting(代码拆分)

Code Splitting 是一种优化 Web 应用程序性能的技术,它将应用程序的 JavaScript 代码拆分成多个较小的部分,并根据需要按需加载,减少首页的加载时间,加快响应速度。

代码在没分包之前,整个工程的代码全部被打包到一个main.js里面,那么在访问某一个页面时,必然就会造成资源的浪费,和延长页面的加载时间。
代码分包之后,各个模块分开进行打包,main.js被拆分之后变的体积很小,页面在进行加载的时候,也会按需进行加载。

作用:

  • 加速页面加载
    当应用程序的 JavaScript 代码被拆分为多个较小的部分并按需加载时,可以减少页面加载时间并加快网站的响应速度。

  • 减少资源浪费
    对于大型的 Web 应用程序,往往包含了大量的 JavaScript 代码,这些代码不会在每个页面中都被用到。通过 Code Splitting 技术,可以将不必要的代码延迟加载,从而减少资源浪费。

  • 提高用户体验
    在使用 Code Splitting 技术时,用户只需要加载实际需要的代码,而不是整个应用程序的所有代码。这样可以提高页面响应速度和用户的体验,同时降低应用程序的加载时间和资源利用率。

我这里把elementUI ,echarts,xlsx,和项目中使用的components单独拆分成文件,如chunk-echarts.js,chunk-elementUI.js等,其他的按配置的规则分配到不同的chunk js文件

vue.config.js

module.exports = {
  // ...
  chainWebpack(config) {
  
    config
      .when(process.env.NODE_ENV !== 'development',
        config => {
          config
            .optimization.splitChunks({
              chunks: 'all',
              cacheGroups: {
                libs: {
                  name: 'chunk-libs',
                  test: /[\\/]node_modules[\\/]/,
                  priority: 10,
                  chunks: 'initial' // only package third parties that are initially dependent
                },
                elementUI: {
                  name: 'chunk-elementUI', // split elementUI into a single package
                  priority: 25, // the weight needs to be larger than libs and app or it will be packaged into libs or app
                  test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
                },
                echarts: {
                  name: 'chunk-echarts', // split echarts into a single package
                  priority: 30, // the weight needs to be larger than libs and app or it will be packaged into libs or app
                  test: /[\\/]node_modules[\\/]_?echarts(.*)/ // in order to adapt to cnpm
                },
                xlsx: {
                  name: 'chunk-xlsx', // split xlsx into a single package
                  priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
                  test: /[\\/]node_modules[\\/](xlsx|file-saver)[\\/]/ // in order to adapt to cnpm
                },
                commons: {
                  name: 'chunk-commons',
                  test: resolve('src/components'), // can customize your rules
                  minChunks: 3, //  minimum common number
                  priority: 5,
                  reuseExistingChunk: true
                }
              }
            })
        }
      )

  }
}

5、Tree shaking技术

webpack 中文网 Tree Shaking

前端体验优化tree-shaking
1分钟了解 Tree-shaking
浅析 Tree Shaking

使用Tree shaking技术,剔除无用的代码,减小打包后文件大小,提高页面加载速度。
Tree Shaking 是一种用于清除 JavaScript 中未被使用代码(dead-code)的技术。它依赖于 ES6 模块语法中的静态结构,并通过识别和移除项目中没有使用到的部分来减少 bundle 的体积,从而提高应用程序的性能。

在 Webpack 中启用 Tree Shaking 需要做如下配置:

使用 ES6 模块语法:
Tree Shaking 依赖于 ES6 模块语法的静态结构,因此需要确保在项目中使用 ES6 模块语法,并避免使用 CommonJS / AMD 等非静态的模块语法。

在 Webpack 的配置文件中设置 mode:

odule.exports = {
  // ...
  mode: 'production',
  optimization: {
    usedExports: true, // 开启 Tree shaking
  },
};

在 package.json 文件中设置 sideEffects 属性:

{
  "name": "example",
  "sideEffects": [
    "*.css",
    "*.scss",
    "*.jpg",
  ]
}

在 package.json 文件中添加 sideEffects 属性,告诉 Webpack 哪些模块不应该被 Tree Shaking,如 CSS / SCSS 文件等有副作用的文件。

确保使用了 UglifyJSPlugin 插件:
UglifyJSPlugin 是 Webpack 内置的一个插件,用于对 JavaScript 代码进行压缩和混淆,同时也可以用于移除未被使用的代码。在 Webpack 4 中,由于 mode 为 production 时,会自动使用 TerserWebpackPlugin 进行 JavaScript 代码压缩和优化,因此不需要单独添加 UglifyJSPlugin。

启用 Tree Shaking 后,Webpack 会分析项目代码,将所有没有被引用到的模块和代码块删除掉。这样做不仅可以减少打包后的体积,还可以提高应用程序的性能,因为没有被使用的无效代码不再需要解析和执行。

在设置好上述配置后,Webapck 就会自动进行 Tree shaking,删除未使用的代码。

下面给出一个简单的示例:

// index.js
import { add } from './math.js';
console.log(add(1, 2));
// math.js
export function add(x, y) {
  return x + y;
}

export function sub(x, y) {
  return x - y;
}

以上代码中,sub 函数没有被使用,因此 Tree shaking 后会被删除。最终输出的代码中,只包含 add 函数的代码,sub 函数的代码被成功删除。

一般情况下,Tree Shaking 需要结合其他优化技术一起使用,例如代码分割(Code Splitting)、懒加载(Lazy Loading)、预加载(Preloading)等,以进一步提高应用程序的性能和用户体验。

6、大模块cdn引入

CDN 提供商
https://unpkg.com/
https://www.jsdelivr.com/
https://www.bootcdn.cn/
http://www.staticfile.org/

unpkg 使用,比如搜xlsx

在浏览器录入unpkg.com/xlsx 会跳到最新版本的源码

在这里插入图片描述

退几格,在浏览器录入unpkg.com/xlsx/dist/ 会跳到最新版本的dist列表

在这里插入图片描述

文章

正确使用externals,vue工程构建性能提升67%
Vue项目CDN引入ElementUI

缺点:线上使用 cdn 可能会因为网络问题不稳定,除非公司有自己的 cdn 库。
不过这确实也是一种优化方案,效果也还不错。它的配置也很简单,在 externals 中配置如下:

module.exports = {
    // 设置通过cdn引入的库,不打包到vendor中(需要去掉导入的相关代码),需要在index.html中引入cdn资源
    externals: {
      vue: 'Vue', // 使用cdn引用element-ui时,必须设置vue和element-ui为外部依赖。
      'element-ui': 'ELEMENT', // 不去 node_modules 中找,而是去找 全局变量 ELEMENT
      // 'vue-router': 'VueRouter',
      // axios: 'axios',
      echarts: 'echarts'
      // XLSX: 'XLSX'
    }
}

如何得知第三方包对应的keyvalue是啥,keypackage.json中安装的包名,value时包真实注册或者说暴露的全局变量的值,比如element-uivalueELEMENT,打开elememt-ui的源码,格式化可以看到如下,注册的值是ELEMENT,且依赖了vue;其他包同样思路

在这里插入图片描述
在public/index.html 引入 CDN

<!-- vue必须在element-ui前面 -->
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script> 
<!-- unpkg element-ui 源 -->
<link href="https://unpkg.com/element-ui@2.15.13/lib/theme-chalk/index.css" rel="stylesheet">
<script src="https://unpkg.com/element-ui@2.15.13/lib/index.js"></script>
<!-- bootcdn element-ui 源 -->
<!-- <link href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.13/theme-chalk/index.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.13/index.min.js"></script> -->
    
<script src="https://unpkg.com/echarts@5.4.2/dist/echarts.min.js"></script>
<!-- <script src="https://unpkg.com/xlsx@0.18.5/dist/xlsx.full.min.js"></script> -->

如果在main.js 使用了下面的代码可以去掉

import ElementUI from 'element-ui'
Vue.use(ElementUI)

关于echarts 使用还是通过下面的代码

import * as echarts from 'echarts'

7、压缩css、js、图片资源,删除无用代码

8、gzip压缩

Gzip 是一种常用的文件压缩算法,可以将文本文件压缩成更小的体积,提高网站的加载速度和用户体验。在 Web 开发中,常用的压缩工具有 Gzip 和 Brotli 等。

要开启 Gzip 压缩,你可以使用 webpack 内置的 compression-webpack-plugin 插件。该插件会自动将打包后的 js、css 文件进行 Gzip 压缩,并生成对应的 .gz 文件。

以下是如何在 项目中开启 Gzip 压缩的步骤:

1、安装 compression-webpack-plugin 插件。

npm install compression-webpack-plugin --save-dev

在 vue.config.js 中添加配置,启用 compression-webpack-plugin

const CompressionWebpackPlugin = require('compression-webpack-plugin');

module.exports = {
  configureWebpack: {
    plugins: [
      // 启用 Gzip 压缩
      new CompressionWebpackPlugin({
        filename: '[path][base].gz',
        algorithm: 'gzip',
        test: /\.(js|css)$/,
        threshold: 10240,
        minRatio: 0.8,
        deleteOriginalAssets: false,
      }),
    ],
  },
}

以上配置中,CompressionWebpackPlugin 的参数含义如下:

filename:压缩后的文件名,[path] 表示文件路径,[base] 表示文件名。
algorithm:压缩算法,这里选用 Gzip。
test:匹配需要压缩的文件类型。
threshold:文件大小超过这个值才会被压缩,单位是字节。
minRatio:压缩后的文件大小和原文件大小的比率,小于这个值才会生成压缩文件。
deleteOriginalAssets:是否删除原文件。若为 true,则原文件将会被删除,只保留压缩后的文件。
执行 npm run build 构建生产环境代码,可以看到在 dist 目录下生成了 .gz 的压缩文件。

在服务器上启用 Gzip 压缩。如果你是使用 Nginx 作为 Web 服务器,可以在 nginx.conf 文件中设置:

gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_vary on;
gzip_proxied any;

这样就可以在服务器端启用 Gzip 压缩,提高网站的加载速度和用户体验。

三、性能、用户体验优化

3.1、路由懒加载

路由懒加载是指在需要的时候才加载对应的路由模块,而不是在项目初始化时就加载所有的路由模块。这样可以减少初始加载时间和资源消耗,提高页面加载速度和用户体验。在 Vue 项目中,可以使用动态导入的方式来实现路由懒加载。

下面是如何使用路由懒加载的步骤:

安装 @babel/plugin-syntax-dynamic-import 插件

npm install @babel/plugin-syntax-dynamic-import --save-dev

在 .babelrc 文件中添加插件

{
  "plugins": ["@babel/plugin-syntax-dynamic-import"]
}

在路由配置文件(如 router.js)中定义路由时,使用 import 函数懒加载路由组件

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

const Home = () => import('./views/Home.vue')

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
  ]
})

以上代码中,通过使用 import 函数来动态加载组件,在访问对应路由时,才会加载该组件。这样就实现了路由懒加载。

3.2、vue-lazyload插件,图片懒加载

vue-lazyload 是一个 Vue.js 的插件,用于实现图片的懒加载。它可以自动监听页面的滚动事件,在图片出现在用户可见区域时再进行加载,从而减轻页面加载压力,优化用户体验和加快页面渲染速度。

以下是使用 vue-lazyload 实现图片懒加载的基本步骤:

1). 安装 vue-lazyload 插件

npm install vue-lazyload --save

2). 编写 Vue 组件,并在模板中引入待懒加载的图片

<template>
  <div class="container">
    <img v-lazy="imgSrc" alt="图片描述">
  </div>
</template>

<script>
export default {
  data() {
    return {
      imgSrc: 'path/to/image.jpg'
    }
  }
}
</script>

3). 在 Vue 中注册 vue-lazyload 插件

import Vue from 'vue'
import VueLazyLoad from 'vue-lazyload'

Vue.use(VueLazyLoad)

4). 配置 vue-lazyload 的全局参数

import Vue from 'vue'
import VueLazyLoad from 'vue-lazyload'

Vue.use(VueLazyLoad, {
  loading: 'loading.gif', // 加载中的占位图
  error: 'error.gif' // 加载失败的占位图
})

通过以上步骤,就可以实现对 Vue 应用程序中图片的简单懒加载,提高页面性能表现和用户体验。

需要注意的是,在移动端浏览器上,为了提高页面的滚动流畅度和用户体验,建议在 vue-lazyload 中启用 listenEvents 选项,使用 Touch 事件代替 scroll 事件来监听页面滚动。同时,考虑到用户可能会在滚动过程中快速滑动界面,所以建议开启 adapter 选项,其作用是在页面滚动时自动调整懒加载图片的加载时机和顺序,从而避免某些图片由于滚动过快而无法加载。

3.3、使用缓存

使用缓存和资源重用技术,减少不必要的重复请求和渲染,如使用keep-alive组件缓存组件状态。

缓存技术是一种优化 Web 应用程序性能和用户体验的有效方式,Vue.js 项目也可以通过缓存技术进一步提高应用程序的响应速度、减轻服务器负担和增强用户体验。下面是几种常见的 Vue.js 项目中使用缓存技术提高用户体验的方法:

1). 浏览器缓存
浏览器缓存是一种常见且易于实现的缓存技术,通过将资源文件(例如图片、CSS、JS 等)缓存到客户端浏览器中,可以减少服务器请求次数,加快页面加载速度并减轻服务器负担。在 Vue.js 中,你可以使用 Vue Router 提供的 keep-alive 组件来实现组件缓存,当组件被缓存后,每次访问该组件时,都会从浏览器缓存中加载,从而减少服务器的请求次数,提高用户体验。

2). 服务端缓存
在 Vue.js 项目中,我们通常会使用 AJAX 或者 Axios 发送 HTTP 请求以获取数据,如果每次请求都要重新向服务器请求数据,会消耗大量的时间和带宽资源,影响用户体验。因此,可以使用服务端缓存技术来缓存数据,例如使用 Redis 来缓存常用数据或者使用 CDN 存储静态资源文件,从而减轻服务器的请求负担,提高数据访问速度和响应时间。

3). 懒加载
如果一个页面中包含大量的图片或者其他资源文件,会极大地影响页面的加载速度和用户体验。为了避免这种情况,可以采用懒加载技术,即在用户需要浏览某个区域时才加载该区域的资源文件。在 Vue.js 中,你可以使用 vue-lazyload 插件实现图片的懒加载。

4). 数据缓存
在 Vue.js 项目中,可以使用 Vuex 来管理应用程序的状态,在应用程序中经常需要获取相同的数据,因此可以使用 Vuex 的缓存技术来缓存请求过的数据,在下次请求时直接从缓存中获取数据,从而减少服务器的请求次数,提高用户体验。

3.4、watch & computed

watchcomputed 是 Vue 中非常常用的两种响应式数据处理方式,它们都能够监听数据变化并执行相应的操作。在提高 Vue 项目的性能和用户体验方面,watchcomputed 也有着不同的应用场景和优化方法。

1). 优化 watch 的性能

  • 尽量避免在 watch 中进行异步操作,因为异步操作通常会阻塞 JS 线程,影响页面性能。如果必须进行异步操作,可以使用 vm.$nextTickPromise.then 方法,将异步操作推迟到下一次 DOM 更新时执行。
  • 在监听数组或对象等复杂数据类型时,尽量使用 deep 选项来监听其子属性的变化,而不是使用 immediate 选项来立即执行回调函数。因为 immediate 会立即执行回调函数,导致监听的数据被多次计算和渲染,影响页面性能。
  • 避免在 watch 中执行过多的计算和渲染操作,尽量将这些操作放在 computed 属性中进行处理。
  1. 优化 computed 的性能
  • 避免在 computed 中引用其他 computed,因为这样会导致多余的计算和渲染,降低页面性能。
  • 对于数据量较大或计算量较大的 computed,可以使用 lazy 选项,将其设置为惰性计算,只有在需要计算时才会重新计算。
  • 对于一些频繁变化的响应式数据,可以考虑使用 watch 来监听其变化,而不是使用 computed 来计算。

3.5、防抖 & 节流

函数节流(throttle)与 函数防抖(debounce)都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象
是应对频繁触发事件的优化方案。

Vue - 使用Lodash实现防抖和节流

3.6、v-if与v-show的使用

v-ifv-show 是 Vue 中两种常用的条件渲染指令。它们都可以根据表达式的值来控制元素的显示和隐藏,但是在具体使用上存在一些差异。

1). v-if
v-if 指令的作用是,根据绑定的表达式的真假值来决定是否渲染 DOM 元素。当绑定的表达式为真值时,该元素会被渲染到页面中;当绑定的表达式为假值时,该元素不会被渲染到页面中,相当于从 DOM 树中移除该元素及其子元素。
优点:在条件不成立时,可以减少不必要的 DOM 元素的渲染和加载,从而提高页面的加载速度和性能表现。
缺点:每次切换条件,都会重新创建和销毁对应的组件或元素,因此在程序运行时可能会比 v-show 慢。

2). v-show
v-show 指令的作用是,根据绑定的表达式的真假值来控制元素的显示和隐藏。与 v-if 不同,v-show 并不会从 DOM 树中移除元素,而是通过修改其 CSS 样式来控制元素的显示和隐藏。
优点:在条件不成立时,并不会从 DOM 树中移除该元素及其子元素,因此在切换条件时,可以保留该元素的状态和属性,提高页面切换的平滑度和体验。
缺点:在条件不成立时,该元素仍会被加载到页面中,因此可能会增加页面的渲染和加载时间。所以,对于需要频繁切换的元素或组件,建议使用 v-show

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
对于Vue项目打包速度优化,可以尝试以下几种方法: 1. 使用CDN:将一些常用的第三方库(如VueVuex、Vue Router等)从本地打包中移除,改为通过CDN引入。这样可以减少打包体积和加快打包速度。 2. 按需引入组件:在使用UI组件库时,可以考虑按需引入组件,而不是全部引入。这可以通过babel-plugin-component等工具来实现,减少不必要的打包编译时间。 3. 代码拆分:将大型的代码块拆分成更小的模块,利用Webpack的代码分割功能(如异步加载、按需加载)来实现懒加载。这样可以减少初始加载时间,提升用户体验。 4. 优化图片:对于项目中的图片资源,可以使用压缩工具来减小图片大小,如使用imagemin-webpack-plugin等插件进行图片压缩。 5. 缓存和持久化:合理利用浏览器缓存和服务端缓存,减少不必要的请求和加载时间。另外,可以考虑使用localStorage或IndexedDB等技术进行数据持久化,减少数据加载时间。 6. 使用Tree Shaking:通过配置Webpack的tree shaking功能,可以剔除项目中未使用的代码,减少打包体积和提升加载速度。 7. 配置合理的Webpack:根据项目需求,合理配置Webpack的各项参数,如使用cache-loader、thread-loader等插件来提升构建速度。 需要注意的是,优化策略要根据具体项目情况进行选择,不同的项目可能有不同的瓶颈和优化空间。可以通过Webpack Bundle Analyzer等工具来分析打包结果,找出优化的重点。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值