最近開了一個讀者回饋表單郵箱,無論是對文章的感想或是對部落格的感想,有什麼想回饋的都可以發郵箱跟我說:i_kkkp@163.com

Webpack 性能优化-1

前言

先来说说为什么要优化?当然如果你的项目很小,构建很快,其实不需要特别关注性能方面的问题。

但是随着项目涉及到的页面越来越多,功能和业务代码也会越来越多,相应的 webpack 的构建时间也会越来越久,这个时候我们就不得不考虑性能优化的事情了。

webpack 的性能优化较多,我们考虑从两方面入手:
优化一:打包后的结果,上线时的性能优化。(比如分包处理、减小包体积、CDN服务器等)
优化二:优化打包速度,开发或者构建时优化打包速度。(比如 excludecache-loader等)

因为这个上线时的性能是直接影响到用户使用体验的,而构建时间与我们的日常开发是密切相关,当我们本地开发启动 devServer 或者 build 的时候,如果时间过长,会大大降低我们的工作效率。

性能优化 - 代码分离

代码分离(Code Splitting)是 webpack 一个非常重要的特性:

它主要的目的是将代码分离到不同的 bundle 中,之后我们可以按需加载,或者并行加载这些文件;
比如默认情况下,所有的 JavaScript 代码(业务代码、第三方依赖、暂时没有用到的模块)在首页全部都加载,就会影响首页的加载速度;
代码分离可以分出更小的 bundle ,以及控制资源加载优先级,提供代码的加载性能;

Webpack中常用的代码分离有三种:

  • 入口起点:使用entry配置手动分离代码
  • 防止重复:使用Entry Dependencies或者SplitChunksPlugin去重和分离代码;
  • 动态导入:通过模块的内联函数调用来分离代码;

入口起点优化-Entry Dependencies(入口依赖)

当项目拥有多个入口点(entry points)时,可能会遇到一些重复依赖的问题。某些模块可能在多个入口点中被引用,导致这些模块被重复打包,增加了最终输出文件的体积。
dependon-shared模块解决重复依赖

module.exports = {
  entry: {
    page1: {
      import: './src/page1.js',
      dependOn: 'shared',
    },
    page2: {
      import: './src/page2.js',
      dependOn: 'shared',
    },
    shared: './src/shared.js',
  },
  output: {
    filename: '[name].bundle.js',
    path: __dirname + '/dist',
  },
}; 

动态导入(dynamic import)

动态导入是一种在Webpack中实现按需加载(Lazy Loading)的技术,允许在运行时异步加载模块,而不是在应用初始化时就把所有模块打包到一个大文件中。可以提高应用的初始加载速度,并且减小了初始包的体积。

const path = require('path');

module.exports = {
  entry: {
    main: './src/index.js',
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/',
  },
  module: {
    rules: [
      // 添加你的Loader规则
    ],
  },
  optimization: {
    splitChunks: {
      chunks: 'all',
    },
  },
};

在上述配置中,通过 optimization.splitChunks 进行代码分割,它的 chunks: 'all' 选项表示对所有模块进行代码分割。

然后,在代码中使用 import() 函数进行动态导入:

// 在需要的地方使用动态导入
const loadModule = () => import('./Module');

loadModule().then(module => {
  // 使用加载的模块
});

Webpack会将使用 import() 函数引入的模块进行代码分割,生成单独的文件。
在运行时,这些文件会在需要的时候异步加载。

自定义分包-SplitChunks

分包(code splitting)是一种优化策略,它允许将代码分割成小块,使得应用在加载时能够更快地显示内容。

Webpack提供了多种分包的模式,其中一种是使用SplitChunksPlugin插件来实现的,这个模式叫做splitChunks

module.exports = {
  // ...其他配置
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30000, // 模块的最小体积
      minChunks: 1, // 模块的最小被引用次数
      maxAsyncRequests: 5, // 按需加载时的最大并行请求数
      maxInitialRequests: 3, // 入口点的最大并行请求数
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};

webpack-split-chunks-plugin

性能优化-CDN

CDN称之为内容分发网络(Content Delivery Network或Content Distribution Network,缩写:CDN), 它是指通过相互连接的网络系统,利用最靠近每个用户的服务器; 更快、更可靠地将音乐、图片、视频、应用程序及其他文件发送给用户; 来提供高性能、可扩展性及低成本的网络内容传递给用户;

在开发中,我们使用CDN主要是两种方式:

  • 打包的所有静态资源,放到CDN服务器, 用户所有资源都是通过CDN服务器加载的;
  • 一些第三方资源放到CDN服务器上;

使用CDN(Content Delivery Network,内容分发网络)是一种非常有效的性能优化策略,特别是在Webpack中。CDN可以加速网站的加载速度,减轻服务器负担,并提高用户体验。以下是如何在Webpack中配置和使用CDN的方法:

将第三方库引入CDN

将你的项目中用到的第三方库(例如React、Vue、jQuery等)引入CDN。可以选择在HTML文件中直接引入CDN链接:

<script src="https://cdn.jsdelivr.net/npm/react@版本号/dist/react.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react-dom@版本号/dist/react-dom.min.js"></script>

在Webpack中配置externals

在Webpack的配置中使用externals字段,告诉Webpack哪些模块是外部引入的,不需要打包。

module.exports = {
  // ...其他配置
  externals: {
    react: 'React',
    'react-dom': 'ReactDOM',
  },
};

然后在HTML文件中通过script标签引入CDN:

<script src="https://cdn.jsdelivr.net/npm/react@版本号/dist/react.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react-dom@版本号/dist/react-dom.min.js"></script>

配置CDN的publicPath

在Webpack的output字段中配置publicPath,指定在引入资源时使用的URL前缀,通常设置为CDN的地址:

module.exports = {
  // ...其他配置
  output: {
    // ...其他output配置
    publicPath: 'https://cdn.example.com/',
  },
};

这样在Webpack构建时,所有的资源引用路径都会加上CDN的地址前缀。

性能优化-提取css文件

将CSS文件从JavaScript打包文件中提取出来是一种常见的性能优化策略。这样做的好处是可以减小JavaScript文件的体积,加快页面加载速度,并且使浏览器能够并行下载CSS和JavaScript文件,提高加载性能。在Webpack中,你可以使用mini-css-extract-plugin插件来实现CSS文件的提取。

配置Webpack

在Webpack配置文件中引入mini-css-extract-plugin插件,然后配置module.rules来处理CSS文件。

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  // ...其他配置

  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          // 可以加入其他的CSS处理loader,比如postcss-loader和sass-loader
        ],
      },
    ],
  },

  plugins: [
    new MiniCssExtractPlugin({
      filename: 'styles.css', // 提取出的CSS文件的文件名
    }),
  ],
};

引入CSS文件

在JavaScript文件或者入口文件中引入CSS文件:

import './styles.css';

或者在HTML文件中使用link标签引入提取出来的CSS文件:

<link rel="stylesheet" href="styles.css">

性能优化-打包文件命名(Hash,ContentHash,ChunkHash)

在Webpack中,打包文件的命名是一个重要的性能优化策略。合适的命名方案可以确保浏览器能够正确地缓存文件,避免不必要的网络请求,提高应用的加载速度。以下是三种常见的打包文件命名方式:Hash、ContentHash 和 ChunkHash。

Hash(哈希)

Hash 是根据文件内容生成的哈希值,当文件内容发生改变时,其对应的 Hash 值也会改变。在Webpack中,可以使用 [hash] 占位符来表示 Hash 值。

output: {
  filename: 'bundle.[hash].js',
}

ContentHash(内容哈希)

ContentHash 是根据文件内容生成的哈希值,但是不同于 Hash 的是,ContentHash 只会受到文件内容的影响,不会受到文件名或路径等其他因素的影响。在Webpack中,可以使用 [contenthash] 占位符来表示 ContentHash 值。

output: {
  filename: 'bundle.[contenthash].js',
}

ChunkHash(块哈希)

ChunkHash 是根据模块内容生成的哈希值,不同模块的内容不同,它们的 ChunkHash 值也会不同。在Webpack中,可以使用 [chunkhash] 占位符来表示 ChunkHash 值。

output: {
  filename: '[name].[chunkhash].js',
}

性能优化-webpack实现Tree Shaking

JavaScript的Tree Shaking:

对JavaScript进行Tree Shaking是源自打包工具rollup(后面我们也会讲的构建工具);

这是因为Tree Shaking依赖于ES Module的静态语法分析(不执行任何的代码,可以明确知道模块的依赖关系);

webpack2正式内置支持了ES2015模块,和检测未使用模块的能力;

在webpack4正式扩展了这个能力,并且通过 package.json的 sideEffects属性作为标记,告知webpack在编译时,哪里文 件可以安全的删除掉;

webpack5中,也提供了对部分CommonJS的tree shaking的支持;

commonjs-tree-shaking

JS实现Tree Shaking

webpack实现Tree Shaking采用了两种不同的方案:

  • usedExports:通过标记某些函数是否被使用,之后通过Terser来进行优化的;
  • sideEffects:跳过整个模块/文件,直接查看该文件是否有副作用;

CSS进行Tree Shaking

CSS的Tree Shaking需要借助于一些其他的插件;

在早期的时候,我们使用PurifyCss插件来完成CSS的tree shaking,但是目前该库已经不再维护;

目前我们可以使用另外一个库来完成CSS的Tree Shaking:PurgeCSS,也是一个帮助我们删除未使用的CSS的工具: PurgeCss

构建工具之Rspack和Vite 輕鬆理解 Ajax 與跨來源請求

評論