reactjs - 如何在 Gatsby 站点的 iframe 中渲染带有 Emotion 样式的 React 组件?

标签 reactjs webpack iframe gatsby emotion

我在一个组件库和一个演示站点上工作。

组件的样式使用 Emotion并且演示站点是用 Gatsby 构建的.

出于预览目的,我想在 iframe 中呈现组件。这将确保来自网站的样式不会级联到组件,更容易处理响应式布局等。

我还想在 iframe 中保留热重载。

Here , 你可以看到一个例子 line-height从网站级联到Button组件使其非常高。

enter image description here

我如何渲染 Button它的所有样式都在 iframe 中?

最佳答案

我认为这里的问题是应用 emotion 生成的样式到放置在 iframe 内的按钮。

我找到了 Mitchell(情感核心团队)的这个很好的例子,它完全符合你的需要:github

这是您的代码和复制代码的 fork ,带有基本的自制<Iframe>元素:codesandbox

result

这是相关的代码:

// src/components/Iframe.js

import React, { useRef, useEffect, useState } from 'react'
import { createPortal } from 'react-dom'

import { CacheProvider } from '@emotion/core'
import createCache from '@emotion/cache'
import weakMemoize from '@emotion/weak-memoize'

// literally copied from Mitchell's codesandbox
// https://github.com/emotion-js/emotion/issues/760#issuecomment-404353706
let memoizedCreateCacheWithContainer = weakMemoize(container => {
  let newCache = createCache({ container });
  return newCache;
});


/* render Emotion style to iframe's head element */
function EmotionProvider({ children, $head }) {
  return (
    <CacheProvider value={memoizedCreateCacheWithContainer($head)}>
      {children}
    </CacheProvider>
  )
}

/* hack-ish: force iframe to update */
function useForceUpdate(){
  const [_, setValue] = useState()
  return () => setValue(0)
}

/* rudimentary Iframe component with Portal */
export function Iframe({ children, ...props }) {
  const iFrameRef = useRef(null)
  const [$iFrameBody, setIframeBody] = useState(null)
  const [$iFrameHead, setIframeHead] = useState(null)
  const forceUpdate = useForceUpdate()

  useEffect(function(){
    if (!iFrameRef.current) return

    const $iframe = iFrameRef.current
    $iframe.addEventListener('load', onLoad)

    function onLoad() {
      // TODO can probably attach these to ref itself?
      setIframeBody($iframe.contentDocument.body)
      setIframeHead($iframe.contentDocument.head)

      // force update, otherwise portal children won't show up
      forceUpdate()
    }

    return function() {
      // eslint-disable-next-line no-restricted-globals
      $iframe.removeEventListener('load', onload)
    }
  })

  return (<iframe {...props} title="s" ref={iFrameRef}>
      {$iFrameBody && $iFrameHead && createPortal((
        <EmotionProvider $head={$iFrameHead}>{children}</EmotionProvider>
      ), $iFrameBody)}
    </iframe>)
}

如果您希望在 gatsby build 期间预渲染 iFrame,这需要更多的工作。 .

对于 styled-components用户,我通过 Stephen Haney 找到了这个片段看起来比emotion优雅多了:

[...] styled-components includes a StyleSheetManager component that can take a target prop. The target expects a DOM node, and it will attach its dynamically created stylesheets to that node.

react-frame-component uses React’s new version of its Context API to expose a FrameContextProvider. It includes the IFrame document and window in the context.

You can combine these two APIs as follows to use styled-components inside your IFrames:

    {
      frameContext => (
        <StyleSheetManager target={frameContext.document.head}>
          <React.Fragment>
            {/* your children here */}
          </React.Fragment>
        </StyleSheetManager>
      )
    }   </FrameContextConsumer> </Frame> 

This works perfectly with react v16.4.1, styled-components v3.3.3, and react-frame-component v4.0.0.

关于reactjs - 如何在 Gatsby 站点的 iframe 中渲染带有 Emotion 样式的 React 组件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59098769/

相关文章:

javascript - React.js : Set a Default value into a prop

npm - Webpack:如何覆盖 package.json 主字段?

html - 在容器中垂直居中 YouTube 视频

html - 为什么 &lt;iframe&gt; 元素不能在 HTML 4.01 中验证?

javascript - 使用 FirebaseUI 创建通用登录应用程序

javascript - 使用reactjs在crud应用程序中验证部分的建议

javascript - 如何在 ReactJS JSX 中执行嵌套的 if else 语句?

css - 如何使用 webpack 在 ReactJS 中编译和加载纯 CSS?

javascript - 为什么使用 webpack 来打包运行在 node 环境下的应用

javascript - 添加具有自定义协议(protocol)的 iframe 会导致 IE 导航到 iframe 源