You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为@emotion/cache传递nonce参数?解决Storybook中Emotion样式引发的CSP错误

解决Storybook + Preact + Emotion的CSP Nonce问题

看起来你遇到的核心问题是Emotion默认缓存没有被自定义缓存覆盖,导致部分样式标签还是用了无nonce的默认配置插入到iframe头部。结合Preact环境和Storybook的iframe隔离特性,我们可以从以下几个方向调整解决:

1. 统一Emotion依赖并适配Preact

首先建议把旧版的@emotion/core升级到最新的@emotion/react,旧版本的缓存机制与Context Provider逻辑和新版存在差异。同时,为了让@emotion/react在Preact环境正常工作,需要在Storybook的Webpack配置中添加别名:

// .storybook/main.js
export default {
  // ...其他配置
  webpackFinal: async (config) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      'react': 'preact/compat',
      'react-dom': 'preact/compat',
      'react-dom/test-utils': 'preact/test-utils',
    };
    return config;
  },
};

2. 强制覆盖Emotion的默认缓存

仅用CacheProvider包裹组件可能不够——如果你的某些依赖或组件直接调用了Emotion的默认全局缓存,会绕过Context。因此我们需要直接修改默认缓存的nonce,或者替换默认缓存:

// .storybook/preview.js
import { CacheProvider, defaultCache } from '@emotion/react';
import createCache from '@emotion/cache';
import { h } from 'preact'; // 使用Preact的JSX入口替代React

// 方法1:直接修改默认缓存的nonce
defaultCache.nonce = '你的真实Nonce值'; // 注意不能用硬编码的'nonce-id',要与Storybook的CSP配置一致

// 方法2:创建新缓存并替换全局默认(推荐,避免与其他缓存冲突)
const styleCache = createCache({
  key: 'preact-emotion',
  nonce: '你的真实Nonce值',
});

// 若你的Emotion版本支持setDefaultCache(v11+),可直接设置全局默认
// import { setDefaultCache } from '@emotion/react';
// setDefaultCache(styleCache);

export const decorators = [
  (Story) => h(CacheProvider, { value: styleCache }, h(Story))
];

3. 配置Storybook的CSP规则

确保Storybook的CSP允许你设置的nonce,否则即使样式标签添加了nonce,浏览器仍会拦截:

// .storybook/main.js
export default {
  // ...其他配置
  framework: {
    name: '@storybook/preact-webpack5', // 对应你的Storybook框架类型
    options: {
      csp: {
        directives: {
          'style-src': ["'self'", "'nonce-你的真实Nonce值'"],
          'script-src': ["'self'", "'nonce-你的真实Nonce值'"],
        },
      },
    },
  },
};

4. 验证iframe环境的Nonce一致性

Storybook的组件渲染在独立iframe中,需要确保iframe的<head>也包含对应的nonce属性,且与Emotion使用的nonce值一致。开发环境可手动检查iframe DOM;生产环境可通过preview-head.html注入nonce:

<!-- .storybook/preview-head.html -->
<head nonce="你的真实Nonce值"></head>

为什么之前的CacheProvider未生效?

你之前的代码中cache-block能渲染,但样式标签未带nonce,大概率是因为部分Emotion样式通过默认全局缓存插入,没有走Context提供的自定义缓存。直接修改默认缓存或替换全局默认缓存,就能覆盖所有Emotion的样式插入逻辑。

内容的提问来源于stack exchange,提问作者RS17

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 12:12:38