Skip to content

Ant-design 打包后样式错误分析

统计信息:字数 7222 阅读15分钟

错误现象

某项目本地开发样式正常(npm run dev),上线后样式不正常(npm run build)

错误分析

本地开发中,antd-mobile 对应的CSS在前,自定义的 CSS 在后,界面可以正常显示;

打包集成到线上,自定义的CSS 编译后为 commons.buldle.css 在前,antd-mobile 直接由 less 编译成 css 未打包,在后面。

所以自定义的样式被已有的 antd-mobile 的样式覆盖,样式错误。

下面是错误的代码 webpack-config-dev.js

// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
  const loaders = [
    // 这个函数中,如果是开发环境,直接使用 style-loader;如果是生产环境,需要miniCss压缩后操作
    isEnvDevelopment && {
      loader: require.resolve('style-loader')
    },
    isEnvProduction && {
      loader: MiniCssExtractPlugin.loader,
      options: shouldUseRelativeAssetPaths ? { publicPath: '../../' } : {},
    },
    {
      loader: require.resolve('css-loader'),
      options: cssOptions,
    },
    {
      // Options for PostCSS as we reference these options twice
      // Adds vendor prefixing based on your specified browser support in
      // package.json
      loader: require.resolve('postcss-loader'),
      options: {
        // Necessary for external CSS imports to work
        // https://github.com/facebook/create-react-app/issues/2677
        ident: 'postcss',
        plugins: () => [
          require('postcss-flexbugs-fixes'),
          require('postcss-preset-env')({
            autoprefixer: {
              flexbox: 'no-2009',
            },
            stage: 3,
          }),
          // Adds PostCSS Normalize as the reset css with default options,
          // so that it honors browserslist config in package.json
          // which in turn let's users customize the target behavior as per their needs.
          postcssNormalize(),
        ],
        sourceMap: isEnvProduction && shouldUseSourceMap,
      },
    },
  ].filter(Boolean);
  if (preProcessor) {
    loaders.push(
      {
        loader: require.resolve('resolve-url-loader'),
        options: {
          sourceMap: isEnvProduction && shouldUseSourceMap,
        },
      },
      {
        loader: require.resolve(preProcessor),
        options: {
          sourceMap: true,
        },
      }
    );
  }
  return loaders;
};


module: {
  rules: [
    // js loader
    {
      oneOf: [
        {
          test: cssRegex,
          exclude: cssModuleRegex,
          use: getStyleLoaders({
            importLoaders: 0,
            sourceMap: isEnvProduction && shouldUseSourceMap,
          }),
          sideEffects: true,
        },
        // Adds support for CSS Modules 
        {
          test: cssModuleRegex,
          use: getStyleLoaders({
            importLoaders: 1,
            sourceMap: isEnvProduction && shouldUseSourceMap,
            modules: true,
            getLocalIdent: getCSSModuleLocalIdent,
          }),
        },
        // 处理 sass 文件(webpack默认配置)已经使用getStyleLoaders这个函数,可以在开发环境中使用未压缩的CSS,在生产环境中使用压缩的CSS文件。
        {
          test: sassRegex,
          exclude: sassModuleRegex,
          use: getStyleLoaders(
            {
              importLoaders: 2,
              sourceMap: isEnvProduction && shouldUseSourceMap,
            },
            'sass-loader'
          ),
          // Don't consider CSS imports dead code even if the
          // containing package claims to have no side effects.
          // Remove this when webpack adds a warning or an error for this.
          // See https://github.com/webpack/webpack/issues/6571
          sideEffects: true,
        },
        // Adds support for CSS Modules, but using SASS
        // using the extension .module.scss or .module.sass
        {
          test: sassModuleRegex,
          use: getStyleLoaders(
            {
              importLoaders: 2,
              sourceMap: isEnvProduction && shouldUseSourceMap,
              modules: true,
              getLocalIdent: getCSSModuleLocalIdent,
            },
            'sass-loader'
          ),
        },

        // 默认的Less处理,只使用三个loader处理less文件。在生产环境中,会渲染成多个style文件,不会把antd-mobile 文件打包到 commons.trunk.css 中。
        {
          test: /\.less$/,
          use:  [
            {loader: 'style-loader'},
            {loader: 'css-loader'}, 
            {
              loader: 'less-loader',
              options: {
                javascriptEnabled: true
              }
            }
          ],
        },

      ],
    },
  ],
},

问题解决

const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;

module: {
  rule: [
    loaders.push(
      {
        loader: require.resolve('resolve-url-loader'),
        options: {
          sourceMap: isEnvProduction && shouldUseSourceMap,
        },
      },
      {
        loader: require.resolve(preProcessor),
        options: {
          sourceMap: true,
          // 增加 javascriptEnabled: true,
        },
      }
    );
  ]
}

// 修改下面的 less loader,在生产环境中可以判断,把 less 文件打包到 trunk.css 文件中。
{
  test: lessRegex,
    exclude: lessModuleRegex,
      use: getStyleLoaders(
        {
          importLoaders: 2,
          sourceMap: isEnvProduction && shouldUseSourceMap
        },
        'less-loader'
      ),
},
  {
    test: lessModuleRegex,
      use: getStyleLoaders(
        {
          importLoaders: 2,
          sourceMap: isEnvProduction && shouldUseSourceMap,
          modules: true,
          getLocalIdent: getCSSModuleLocalIdent,
        },
        'less-loader'
      ),
  },

现在生产环境中部署后,样式正常

PR: https://github.com/seafileltd/dtable-web/pull/228/files

参考文件

官网文档 https://webpack.docschina.org/concepts/entry-points/

React+less https://blog.csdn.net/qwe502763576/article/details/83242823

https://www.jianshu.com/p/6b0b80eb3e2e

https://github.com/ant-design/ant-design/issues/3442#issuecomment-374877201

https://www.cnblogs.com/mydxy/articles/10157215.html

https://www.cnblogs.com/ldld/p/6488830.html

进一步思考

打包到 bundle 中,那么其他的界面也会引用这个 bundle.css,就会影响其他界面的样式(body 的背景色)。

现在还是原始的问题:按需加载,加载的部分中,不能影响其他自定义的样式。

antd-mobile 中引用任何一个组件时,都会引用 style/index.css 文件。所以,不管使用 less 还是使用 css,都会把这部分公共组件引入。目前没有特别好的办法(除非把每一个组件单独应用,然后把公共样式中不影响默认样式的部分拿过来)。

less 部分有一个对应的按需加载的配置。这个插件根据需要的部分把CSS引入。但是这样打包后的CSS的顺序可能存在问题。所以现在不使用这个方法。如果自己日常使用时,可以使用部分引入的方法。


Last update: November 9, 2024