首页 > 解决方案 > 基于环境的 Sass 动态导入

问题描述

我正在尝试根据环境切换 sass 导入 URL。我已经回顾了许多其他问题和答案,这一直在与我作斗争。@import 不允许动态字符串,因此我尝试对每个字符串使用 mixin,并基于环境变量使用 @if/@else,但是我无法从其他 sass 文件访问根环境变量。

webpack.config.js

const scssRule = config.module.rules.find(rule => rule.test.test(".scss"));
const scssLoaderConfig = scssRule.use.find(({loader}) => loader === "sass-loader");
scssLoaderConfig.options = scssLoaderConfig.options || {};
scssLoaderConfig.options["implementation"] = require("sass"); //dart-sass
scssLoaderConfig.options["additionalData"] = `$env:"${env}";`; //Pass env to sass

主题.scss

//NOTE: This $env line is not actually in the file. It gets injected by webpack via sass-loader additionalData
$env: "preprod";

@forward "icons" as icon_*;
//More @forward directives such as colors

_icons.scss

@mixin importIconsPreprod {
    @import "https://preprod.example.com/icons.css";
}
@mixin importIconsProd {
    @import "https://example.com/icons.css";
}

@mixin importIcons() {
    //SassError: Undefined variable.
    @if $env == "preprod" {
        @include importIconsPreprod;
    } @else {
        @include importIconsProd;
    }
    
    //More styles here
}

期望的实现

@use "theme-package" as theme;

//Import icons from theme.icon namespace
@include theme.icon_importIcons;

标签: csswebpacksassscss-mixinssass-loader

解决方案


一段时间后,我找到了解决方案。我最终生成了一个文件 _env.scss 作为 webpack 的一部分。该文件包含指示环境的 $env sass 变量,并由 _icons.scss 加载。下面的代码:

webpack.config.js

const writeEnvScss = require("./scripts/write-env-scss.js");

//Write _env.scss, default to "prod"
writeEnvScss("./packages/Theme/src/Component/includes/_env.scss", "prod");

_env.scss 作为构建的一部分生成

$env:"preprod";

_icons.scss

//"env" file is created as part of webpack.config.js
@use "includes/env" as *;

@mixin importIconsPreprod {
    @import "https://preprod.example.com/icons.css";
}
@mixin importIconsProd {
    @import "https://example.com/icons.css";
}

@mixin importIcons() {
    @if $env == "preprod" {
        @include importIconsPreprod;
    } @else {
        @include importIconsProd;
    }
}

写-env-scss.js

const fs = require("fs");
const path = require("path");

//looks for optional --env cli param or NODE_ENV environment variable
module.exports = function(scssPath, envDefault) {
    scssPath = scssPath || null;
    if (!scssPath) {
        throw new Error("scss path required");
    }
    envDefault = envDefault || "local";

    //Determine env
    let env = getArg("--env") || process.env.NODE_ENV || null;
    switch (env) {
        case "development":
        case "local":
            env = "local";
            break;
        case "preprod":
            break;
        case "production":
        case "prod":
            env = "prod";
            break;
        default:
            env = envDefault;
            break;
    }
    console.log("ENV =>", env);

    //Write _env.scss to Theme
    fs.writeFileSync(path.resolve(scssPath), `$env:"${env}";`);
};

function getArg(key) {
    var index = process.argv.indexOf(key);
    var next = process.argv[index + 1];
    return index < 0 ? null : !next || next[0] === "-" ? true : next;
}

推荐阅读