Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] tree shaking 没有把未被使用的图片删除掉 #12881

Closed
RRDAWLX opened this issue Jan 5, 2025 · 2 comments
Closed

[Bug] tree shaking 没有把未被使用的图片删除掉 #12881

RRDAWLX opened this issue Jan 5, 2025 · 2 comments

Comments

@RRDAWLX
Copy link

RRDAWLX commented Jan 5, 2025

What happens?

有个组件 ComponentA,其样式文件引用了图片资源。这个组件未被使用,tree shaking 之后组件的 js 和 css 代码都被删除了,但仅被 ComponentA 所引用的图片却出现在了产物当中。

Mini Showcase Repository(REQUIRED)

Please provide a minimal reproduction then upload to your GitHub. 请提供 最小重现,并上传到你的 GitHub 仓库

查看这个项目

How To Reproduce

Steps to reproduce the behavior: 1. 2.

执行 build 命令,发现产物中存在未被使用的图片。
image

Expected behavior 1. 2.

未被使用的图片不应该出现中构建产物中。

Context

  • Umi Version: 3.5.43
  • Node Version: 16.16.0
  • Platform: MacOS
@RRDAWLX RRDAWLX changed the title tree shaking 没有把未被使用的图片删除掉 [Bug] tree shaking 没有把未被使用的图片删除掉 Jan 5, 2025
@RRDAWLX
Copy link
Author

RRDAWLX commented Jan 10, 2025

官方的回复是等不到了,分享我自己的解决方案,希望对其他人能有帮助。

import { rm } from 'fs/promises';
import path from 'path';

/**
 * 删除未使用的产物。
 * 文件名不以 .js、.css、.html、.map、.json 结尾的产物,如果在以 .js、.css、.html 结尾的产物中未找到对其引用,则删除该产物。
 */
export default class RemoveUnusedAssetsPlugin {
  /**
   * 将 RemoveUnusedAssetsPlugin 插件应用于 webpack 配置对象,做两件事:
   * 1. 将 optimization.sideEffects 设置为 true。
   * 2. 当 process.env.NODE_ENV === 'production' 时添加 RemoveUnusedAssetsPlugin 插件。
   * @param config webpack-chain config 对象。
   */
  static applyToWebpack(config: any) {
    config.merge({
      optimization: {
        sideEffects: true,
      },
    });

    if (process.env.NODE_ENV !== 'production') {
      console.log(
        '[RemoveUnusedAssetsPlugin] 当前为非生产模式,不会应用 RemoveUnusedAssetsPlugin。',
      );
      return;
    }

    config.plugin('RemoveUnusedAssetsPlugin').use(RemoveUnusedAssetsPlugin);
  }

  apply(compiler: any) {
    const unusedAssetRelativePaths: string[] = [];

    // 查找未使用的产物
    compiler.hooks.emit.tap('RemoveUnusedAssetsPlugin', (compilation: any) => {
      const assets = compilation.getAssets();
      const codes: string[] = [];
      const assetRelativePaths: string[] = [];

      assets.forEach((asset: any) => {
        const { name } = asset;

        // .js、.css、.html、.map、.json 产物肯定不删除
        if (
          name.endsWith('.js') ||
          name.endsWith('.css') ||
          name.endsWith('.html')
        ) {
          codes.push(asset.source.source());
        } else if (!name.endsWith('.map') && !name.endsWith('.json')) {
          assetRelativePaths.push(name);
        }
      });

      assetRelativePaths.forEach((relativePath) => {
        const indexOfLastSlash = relativePath.lastIndexOf('/');
        const fileName =
          indexOfLastSlash === -1
            ? relativePath
            : relativePath.slice(indexOfLastSlash + 1);

        if (codes.every((code) => !code.includes(fileName))) {
          unusedAssetRelativePaths.push(relativePath);
        }
      });
    });

    // 删除未使用的产物
    // 按理在上一个 hook 中调用 compilation.deleteAssets 删除产物更好,但 umi3 中不支持这个 API。
    compiler.hooks.afterEmit.tapPromise(
      'RemoveUnusedAssetsPlugin',
      async (compilation: any) => {
        const outputPath = compilation.outputOptions.path;
        const tasks = unusedAssetRelativePaths.map((relativePath) => {
          const absolutePath = path.join(outputPath, relativePath);
          return rm(absolutePath);
        });
        await Promise.all(tasks);
      },
    );
  }
}

@RRDAWLX RRDAWLX closed this as completed Jan 10, 2025
@xiaohuoni
Copy link
Member

动态拼接的图片引用呢?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants