hawx1993 / tech-blog

📦My personal tech blog,not regularly update
http://sf.gg/u/trigkit4/articles
339 stars 30 forks source link

Webpack 5.0 项目优化方案实践 #27

Open hawx1993 opened 1 year ago

hawx1993 commented 1 year ago

webpack 5.0新特性

借鉴网上的图,大致窥探一下webpack5.0的一些新特性。

image.png

包依赖分析

在拆包之前,我们通常都会使用一些插件去分析我们的包模块的依赖,webpack-bundle-analyzer就是一款不错的插件,通过webpack-bundle-analyzer 我们可以合理分析依赖情况

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

module.exports = merge(common, {
  mode: 'development',
  devtool: 'eval-cheap-module-source-map',
  entry: ['./src/index.tsx'],
  plugins: [
    new Webpack.HotModuleReplacementPlugin(),
    new BundleAnalyzerPlugin(),
  ],
});

image.png

Webpack 代码拆分

我们可以使用splitChunks 进行精细控制代码分割,也就是俗称的拆包。 使用splitChunks 我们可以提取公共代码,防止代码被重复打包,拆分过大的js文件,合并零散的js文件等:

splitChunks拆包

1、使用splitChunks 提取共有代码,拆分业务代码与第三方库

 optimization: {
    splitChunks: {
      chunks: 'all',
      maxInitialRequests: Infinity,
      minSize: 0,// 分割出去的代码最小体积 0为不限制
      cacheGroups: {
        styles: {
          name: 'styles',
          test: /\.css$|.less$/,
          chunks: 'all',// 将所有的重复模块抽离出来
          enforce: true,
        },
        vendor: {
          name: 'vendor',
          test: (module) =>
            /(react|react-dom|react-router-dom/.test(
              module.context,
            ),
          chunks: 'initial',// 引入组件的方式:同步加载,异步加载:'async'
          priority: 11,
        },
        libs: {
          name: 'libs',
          test: (module) => /(moment|antd|lodash)/.test(module.context),
          chunks: 'all',
          priority: 14,
        },
        common: {
          chunks: 'async',
          test: /[\\/] node_modules[\\ / ] /,
          name: 'common',
          minChunks: 3,// 被几个代码块引用才会分割
          maxAsyncRequests: 5,
          maxSize: 1000000,
          priority: 10,
        },
      },
    },
  },

动态加载

import('./math').then(math => {
  console.log(math.add(1,2))
})

const math = lazy(() => import('./math'))

拆分tailwindcss

未移除之前,vendors.js有22mb大小:

image.png

tailwind.config.js 中配置移除项:

// tailwind.config.js
 purge: {
    // Learn more on https://tailwindcss.com/docs/controlling-file-size/#removing-unused-css
    enabled: true,
    content: [
      './src/components/**/*.tsx',
      './src/context/**/*.tsx',
      './src/layouts/**/*.tsx',
      './src/pages/**/*.tsx',
    ],
  },

移除之后,bundle文件体积大小如下:

image.png

Tree shaking移除未使用的js代码

production模式实际上是由TerserPlugin来处理的,默认开启压缩也是基于此插件处理。tree-shaking 需要基于es6模块化语法。我们可以直接通过手动的方式告诉webpack, 哪些东西是不需要处理的,可以通过package.json去配置副作用:

{
  "sideEffects": [
    "*.css" // 指定不需要shaking处理的文件
  ]
}

为什么需要基于es6 模块语法呢?

因为ES Module在js编译阶段就可以确定模块之间的依赖关系(import),并不需要执行就可以确认模块的导入、导出。所以我们并不需要代码执行就可以根据ESM确定模块之间的规则从而实现Tree Shaking,我们称之为静态分析特性。

Webpack 资源持久化缓存

每个打包的资源文件有唯一的hash值,即“文件指纹”。修改后只有受影响的文件hash变化,就可以做到“增量式更新”。可以避免部署过程中,存在更新的时间间隔。也不会出现用户浏览器某些使用到的文件是旧的缓存文件,某些使用的是新的文件。

{
    plugins: [
       new MiniCssExtractPlugin({
          filename: '[name]_[contenthash:8].css',// contenthash 根据css内容去计算hash值
          chunkFilename: '[name].[contenthash].css',
      }),
    ],
   output: {
       filename: '[name].[hash].bundle.js',
       chunkFilename: '[name].[chunkhash:8].bundle.js'
   }   
}

使用contenthash,这样每个资源文件都会有自己的独立的hash值。这样我们对局部的修改只会影响到局部文件,而不需要进行整体的变更和部署