背景
在React项目开发中,我们经常需要在多个SCSS文件中复用全局的变量、混合器(mixins)和函数。虽然已有的sass-resources-loader可以实现全局SCSS文件的导入,但它的实现方式是直接将全局文件的代码复制到每个SCSS文件中,这种方式太不优雅了。本文将尝试构建一个更优雅的loader,通过@import语句来实现全局SCSS文件的自动导入。
全局文件
使用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. 效果
使用
根据source-map我们可以看到没被编译为css的scss文件:
对比sass-resources-loader
| 特性 | auto-import-scss-loader | sass-resources-loader |
|---|---|---|
| 实现方式 | @import语句 | 代码注入 |
| 编译结果 | 保持SCSS原生导入机制 | 复制代码到每个文件 |
| 代码体积 | 较小 | 较大 |
| 调试友好度 | 高 | 低 |
结语
本loader还没有完善,请不要把这个loader用在生产环境中去。
作者:蒋全明