Angular:如何将JSON变量解析为SCSS

Angular: How to parse JSON variables into SCSS

在 Angular 应用程序中,我通过任一普通 D3 or Vega 制作 D3 视觉效果。还有 SCSS 样式正在进行中。

我希望能够从 Javascript 和 SCSS 引用相同的全局变量来进行样式设置。 JSON 文件可以很好地存储我通过简单的 import 语句加载到 Typescript 中的设置。但是如何从 SCSS 做同样的事情呢?

node-sass-json-importer 似乎是一个不错的候选者,但将其添加到 Angular 9 CLI 应用程序对我来说并不明显。

This Whosebug post前段时间刷过这个话题,但是涉及到修改node_modules下的资源,很难持续。

原始文档中也有一些关于 how one can go about tweaking webpack in a non-Angular app 的输入。我不知道如何将它与通过 CLI 构建的 Angular 应用联系起来。

Webpack / sass-loader Blockquote

Webpack v1

import jsonImporter from 'node-sass-json-importer';

// Webpack config
export default {
  module: {
    loaders: [{
      test: /\.scss$/,
      loaders: ["style", "css", "sass"]
    }],
  },
  // Apply the JSON importer via sass-loader's options.
  sassLoader: {
    importer: jsonImporter()
  }
};

Webpack v2

import jsonImporter from 'node-sass-json-importer';

// Webpack config
export default {
  module: {
    rules: [
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 1
          },
        },
        {
          loader: 'sass-loader',
          // Apply the JSON importer via sass-loader's options.
          options: {
            importer: jsonImporter(),
          },
        },
      ],
    ],
  },
};

您可以在不更改任何 node_modules 文件的情况下使用 @angular-builders/custom-webpack to setup custom Webpack rules and as you mention node-sass-json-importer 将 JSON 文件导入 SCSS 文件。

您必须安装 node-sass for the implementation option because node-sass-json-importer is compatible with node-sass

  1. 安装包@angular-builders/custom-webpack, node-sass-json-importer and node-sass:

    npm i -D @angular-builders/custom-webpack node-sass-json-importer node-sass
    
  2. 创建Webpack配置文件(webpack.config.js)到项目根目录,内容如下:

    const jsonImporter = require('node-sass-json-importer');
    
    module.exports = {
      module: {
        rules: [{
          test: /\.scss$|\.sass$/,
          use: [
            {
              loader: require.resolve('sass-loader'),
              options: {
                implementation: require('node-sass'),
                sassOptions: {
                  // bootstrap-sass requires a minimum precision of 8
                  precision: 8,
                  importer: jsonImporter(),
                  outputStyle: 'expanded'
                }
              },
            }
          ],
        }],
      },
    }
    
  3. builder 更改为 @angular-builders/custom-webpack:[architect-target] 并将 customWebpackConfig 选项添加到 buildservetest 构建目标里面 angular.json:

    "projects": {
      ...
      "[project]": {
        ...
        "architect": {
          "build": {
            "builder: "@angular-builders/custom-webpack:browser",
            "options": {
              "customWebpackConfig": {
                "path": "webpack.config.js"
              },
              ...
            },
            ...
          },
          "serve": {
            "builder: "@angular-builders/custom-webpack:dev-server",
            "customWebpackConfig": {
              "path": "webpack.config.js"
            },
            ...
          },
          "test": {
            "builder: "@angular-builders/custom-webpack:karma",
            "customWebpackConfig": {
              "path": "webpack.config.js"
            },
            ...
          },
        },
        ...
      },
      ...
    }
    

现在您可以在任何组件 .scss 文件中包含任何 .json 文件:

hello-world.component.scss:

@import 'hello-world.vars.json';

.hello-bg {
  padding: 20px;
  background-color: $bg-color;
  border: 2px solid $border-color;
}

hello-world.vars.json:

{
  "bg-color": "yellow",
  "border-color": "red"
}

我创建了一个 Github 存储库,您可以从这里克隆和测试所有这些工作:https://github.com/clytras/angular-json-scss

git clone https://github.com/clytras/angular-json-scss.git
cd angular-json-scss
npm install
ng serve

我有一个比较简单的方法给你:

node-sass 步骤与 ng build 步骤分开,并使用生成的 .css 文件代替 angular 应用中的 sass 文件。

这有两个好处:

  • 无需大量调整和编辑任何节点模块
  • 完全控制 sass 的编译方式

对于实施,我将按如下方式进行:

  1. ./node_modules/.bin/node-sass --importer node_modules/node-sass-json-importer/dist/cli.js --recursive ./src --output ./src 添加到 package.json 中的脚本,例如名字 build-sass。您可以调整此命令以仅包含需要递归模式的 sass 文件(确保在 angular.json 中忽略它们,这样它们就不会被编译两次)。
  2. 运行 命令一次npm run build-sass 以便在项目中生成.css 个文件。
  3. 将 angular 组件中需要的引用从 .sass 更改为 .css
  4. (可选)在您的 package.json 中添加一个方便的命令 npm run build-sass && ng build 作为 compile,您可以随时 运行 构建整个项目。

虽然这种方法相对简单,但也有一些缺点

  • 对于您要使用 css 的任何组件,ng serve 的热重载功能将要求您 运行 您的 npm run build-sass 以查看任何更改你实时制作了你的风格
  • 您的项目看起来会有点乱,因为在生成 .css/src 文件夹中有相同组件的 .scss.css 文件.您需要确保(例如通过添加后缀或注释)没有人意外编辑生成的 .css 并且这些更改会被覆盖。

其他方法几乎总是涉及大量编辑现有的或编写您自己的 webpack 编译器。

另一种选择是使用 raw-loader,它在 angular-cli 中默认可用。这样你就可以将原始 scss 文件加载到 ts 组件中:

// Adding `!!` to a request will disable all loaders specified in the configuration
import scss from '!!raw-loader!./config.scss';

console.info('scss', scss);

您必须在 .d.ts 文件中声明模块:

declare module '!!raw-loader!./*.scss';

然后您可以提取任何您想要的信息。

因此,您可以将 scss 加载到 ts 中,而不是将 JSON 加载到 scss 中。

工作于 angular 10: 正如其他人提到的那样使用自定义 webpack,但我在 angular 10 中唯一可行的方法是编辑一个函数,最初由 Kishorevarma.

建议

webpack.config.js

const jsonImporter = require("node-sass-json-importer");

const webpack = require("webpack");

const mode =
  process.env.NODE_ENV === "production" ? "production" : "development";

module.exports = function (config) {
  const rules = config.module.rules || [];
  rules.forEach((rule) => {
    if (String(rule.test) === String(/\.scss$|\.sass$/)) {
      const loaders = rule.use;
      const transformedLoaders = loaders.map((l) => {
        if (l.loader && l.loader.search("sass-loader") !== -1) {
          l.options.sassOptions.importer = jsonImporter();
        }
        return l;
      });
      rule.use = transformedLoaders;
    }
  });
  return config;
};

我一直在导入最初为 angular flexlayout 创建的断点。

breakpoints.json

{
  "breakpoints": [
    {
      "alias": "xs",
// Notice the quoting
      "mediaQuery": "'screen and (min-width: 0px) and (max-width: 599.98px)'",
      "priority": 1000
    },
    ...
]
}

breakpoints.scss

@import "./breakpoints.json";

$bps: ();

// Reformat from json
@each $breakpoint in $breakpoints {
  $i: index($breakpoints, $breakpoint);
  $bps: map-merge(
    $bps,
    (
      map-get($breakpoint, alias): map-get($breakpoint, mediaQuery),
    )
  );
}

// Can be used with
// @media #{(map-get($bps, xs))} {
//      margin: 0 -15px;
//    }

breakpoints.ts

import { BreakPoint } from '@angular/flex-layout';

// Custom breakpoints
const brkpnts: BreakPoint[] = require('./breakpoints.json').breakpoints;
export const JSONBreakpoints = brkpnts.map((bp) => {
  // Remove the single qoutes needed for the sass variables
  return { ...bp, mediaQuery: bp.mediaQuery.substring(1).slice(0, -1) };
});

app.module.ts

import { JSONBreakpoints } from './breakpoints';

@NgModule({
  imports: [
FlexLayoutModule.withConfig({ disableDefaultBps: true }, JSONBreakpoints)
  ]
})