开发者问题收集

如何最好地在汇总包中包含 scss 文件中引用的资产(图像和字体)

2020-03-24
12502

我正在用 typescript、sass 和 rollup 编写一个 react 组件库,我希望它尽可能独立。

有人对如何最好地包含 scss 文件中引用的资产(图像和字体)有什么建议吗?

一种解决方案可能是某种加载器(例如 postcss 处理器)用 base64 版本替换 scss 文件中引用的所有图像和字体资产。

有人有以有效方式完成此操作的示例吗?任何解决方案或建议都将不胜感激🙏🏻

我的汇总配置如下所示:

import peerDepsExternal from "rollup-plugin-peer-deps-external";
import resolve from "rollup-plugin-node-resolve";
import typescript from "rollup-plugin-typescript2";
import scss from 'rollup-plugin-scss'
import sass from "rollup-plugin-sass";
import commonjs from "rollup-plugin-commonjs";
import copy from "rollup-plugin-copy";
import url from '@rollup/plugin-url';

import packageJson from "./package.json";

export default {
  input: "src/index.tsx",
  output: [
    {
      file: packageJson.main,
      format: "cjs",
      sourcemap: true
    },
    {
      file: packageJson.module,
      format: "esm",
      sourcemap: true
    }
  ],
  plugins: [
    peerDepsExternal(),
    resolve({
      browser: true
    }),
    typescript({ objectHashIgnoreUnknownHack: true }),
    commonjs({
      include: ["node_modules/**"],
      exclude: ["**/*.stories.js"],
      namedExports: {
        "node_modules/react/react.js": [
          "Children",
          "Component",
          "PropTypes",
          "createElement"
        ],
        "node_modules/react-dom/index.js": ["render"]
      }
    }),
    scss({
    }),
    sass({
      insert: true
    }),
    copy({
      targets: [
        {
          src: "src/variables.scss",
          dest: "build",
          rename: "variables.scss"
        },
        {
          src: "src/typography.scss",
          dest: "build",
          rename: "typography.scss"
        },
        {
          src: "src/assets",
          dest: "build/",
        },
      ]
    })
  ]
};

更新:

因此,我所做的可能有些不妥,但它解决了我的问题。

我在 rollup.config.js 中的插件数组中添加了一个 postcss-plugin, 一个 commonjs 插件之后。

postcss({
      inject: true,
      plugins: [
        postcssInlineBase64({
          baseDir: 'src/assets/',
        }),
        postcssUrl({
          url: 'inline',
        }),
        atImport({
          path: path.resolve(__dirname, '../'),
        }),
      ],
    }),

我也使用 storybook,它在内部使用 webpack,因此我不得不在 .storybook/webpack.config.js 中重新创建相同的内容:

config.module.rules.push({
    test: /\.scss$/,
    use: [
      'style-loader',
      'css-loader',
      {
        loader: 'postcss-loader',
        options: {
          inject: true,
          ident: 'postcss',
          plugins: [
            postcssInlineBase64({
              baseDir: 'src/assets/',
            }),
            postCssUrl({ url: 'inline' }),
            atImport({
              path: path.resolve(__dirname, '../'),
            }),
          ],
        },
      },
      'sass-loader',
    ],
    include: path.resolve(__dirname, '../'),
  });

现在,在 scss 中使用 url 指令时(或可能在其他地方),我可以使用以下方式包围任何路径:

b64---<SOME_PATH>---

对于实例:

@font-face {
  font-family: 'Open Sans';
  font-display: swap;
  font-style: normal;
  font-weight: 300;
  src: url('b64---./fonts/open-sans/open-sans-v15-latin-ext_latin-300.woff2---') format('woff2'),
    url('b64---./fonts/open-sans/open-sans-v15-latin-ext_latin-300.woff---') format('woff');
}

这使得 post css 将资产打包为 base64。

致任何可能看到这篇文章的人。祝你好运!希望这能有所帮助!

1个回答

使用 postcss-url 资源函数使其工作,使用真实的图像文件而不是 base64。

这个想法是,包将 CSS 提取到单独的文件中,并且 url() 中引用的所有资源都被拦截、散列并放入文件夹 ( _assets ),然后 postcss-url 将原始 url 更改为该文件夹。

消费者应用程序(例如使用 css-loader 等工具的 webpack)导入 CSS

import 'pkg-name/dist/index.css'

其中所有 url() 看起来都像 background-image: url(_assets/<hash>.png) ,并且该加载器还负责将这些资源带入包中,并将 url() 替换为具有公共路径的本地 url,例如 url(/static/<app-hash>.png)

由于无法从该回调访问插件实例,它不使用内置于 emitFile 的汇总,尽管这是理想的选择。

import fs from "fs-extra";
import path from "path";
import hasha from "hasha";

const IMAGES_RX = /\.(png|jpe?g|gif|webp|svg)$/;

// rollup config

plugins: [
  postcss({
    extract: true,
    plugins: [
      // extracts all url() assets into _assets folder, and replaces the url() to a relative path
      // consumers of this package (e.g. webpack apps) will import the css and handle getting assets as well
      postcssUrl({
        url: (asset: any) => {
          if (!IMAGES_RX.test(asset.url)) return asset.url;

          const file = fs.readFileSync(asset.absolutePath);
          const hash = hasha(file, { algorithm: "md5" });

          const extname = path.extname(asset.absolutePath);

          const hashedFileName = `${hash}${extname}`;
          fs.ensureDirSync(path.join(OUT_DIR, "_assets"));
          const hashedFilePath = path.join("_assets", hashedFileName);

          fs.writeFileSync(path.join(OUT_DIR, hashedFilePath), file);

          return hashedFilePath;
        },
      }),
    ],
  }),
]
elado
2021-04-01