【webpack】探讨构建一个loader 来自动导入全局的scss文件:auto-import-scss.loader

224 阅读2分钟

背景

在React项目开发中,我们经常需要在多个SCSS文件中复用全局的变量、混合器(mixins)和函数。虽然已有的sass-resources-loader可以实现全局SCSS文件的导入,但它的实现方式是直接将全局文件的代码复制到每个SCSS文件中,这种方式太不优雅了。本文将尝试构建一个更优雅的loader,通过@import语句来实现全局SCSS文件的自动导入。

image.png

全局文件

image.png

使用sass-resources-loader导入的文件

实现

Webpack loader的本质是一个函数,它接收文件的源代码作为输入。基于这一特性,我们可以在源代码前添加@import语句来导入全局样式文件,从而实现自动导入的功能。

1. 实现loader

/**
 * 自动导入scss文件的loader
 * @param {string} source - 源代码内容
 * @returns {string} 处理后的scss文件
 */
module.exports = function autoImportScssLoader(
  this: LoaderContext<AutoImportScssLoaderOptions>,
  source: string,
): string {
  // 检查源文件是否为scss/css文件
  if (!this.resourcePath.match(/\.(scss|css)$/)) {
    return source;
  }
  // 获取webpack配置中的alias
  const options = this.getOptions();
  if (!options.globalScssPath) {
    warn("globalScssPath 不能为空");
    return source;
  }

  const alias = (this._compiler?.options?.resolve?.alias || {}) as Record<string, string>;
  let globalScssPath: string[] = Array.isArray(options.globalScssPath)
    ? options.globalScssPath
    : [options.globalScssPath];

  globalScssPath = globalScssPath
    .map((path) => resolveGlobPath(resolvePathToRealPath(path, alias)))
    .flat();

  const importStatement = globalScssPath.map((path) => `@import '${path}';`).join("\n") + "\n";
  const newSource = importStatement + source;
  return newSource;
};

工具函数:处理webpack中的alias

/**
 * 将路径转换为实际路径
 * @param path 路径
 * @param alias 别名映射
 * @returns 实际路径
 */
const resolvePathToRealPath = (path: string, alias: Record<string, string>) => {
  const paths = path.split("/");
  const keys = Object.keys(alias);
  paths.forEach((path, index) => {
    if (keys.includes(path)) {
      paths[index] = alias[path];
    }
  });
  return paths.join("/");
};

工具函数:处理glob路径

/**
 * 解析glob路径
 * @param path
 */
const resolveGlobPath = (path: string): string[] => {
  const files = glob.sync(path);
  const res = files
    .filter((file) => fs.statSync(file).isFile())
    .map((file) => file.replace(/\\/g, "/"));
  return res;
};

2. 配置webpack

{
  test: /\.module\.(css|scss)$/,
  use: [
    "style-loader",
    {
      loader: "css-loader",
      options: {
        modules: true,
        sourceMap: true,
      },
    },
    "postcss-loader",
    {
      loader: "sass-loader",
      options: {
        sourceMap: true,
      },
    },
    {
      loader: path.resolve(__dirname, "./loaders/auto-import-scss.loader.js"),
      options: {
        globalScssPath: "@/Style/**/*",  // 支持glob模式
      },
    },
  ],
}

3. 效果

使用

image.png

根据source-map我们可以看到没被编译为css的scss文件:

image.png

对比sass-resources-loader

特性auto-import-scss-loadersass-resources-loader
实现方式@import语句代码注入
编译结果保持SCSS原生导入机制复制代码到每个文件
代码体积较小较大
调试友好度

结语

本loader还没有完善,请不要把这个loader用在生产环境中去。

源代码:gitee.com/qm-jj/autom…

作者:蒋全明