首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Gatsby站点的iframe中呈现带有情感的反应性组件?

问如何在Gatsby站点的iframe中呈现带有情感的反应性组件?
EN

Stack Overflow用户
提问于 2019-11-29 04:25:18
回答 2查看 4.2K关注 0票数 4

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

组件使用情感进行样式化,演示站点使用盖茨比构建。

为了预览目的,我想在iframe中呈现组件。这将确保从网站的样式不级联到组件,使它更容易处理响应布局,等等。

我也想保持热重装在iframe内。

这里,您可以看到来自网站的line-height是如何级联到Button组件导致其非常高的一个例子。

​

​

我如何在iframe中呈现Button及其所有样式?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-12-02 15:06:53

我认为这里的问题是将emotion生成的样式应用于放在iframe中的按钮。

我发现米切尔(情感核心团队)做了你需要的事情:github,这是一个很好的例子。

下面是代码框的一个分叉,代码被复制过来,带有一个基本的自制<Iframe>元素:码箱

​

​

以下是相关代码:

代码语言:javascript
复制
// 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>)
}

如果您希望在iFrames期间预先呈现gatsby build,这需要更多的工作。

对于styled-components用户,我发现斯蒂芬·哈尼的这个片段看起来比emotion要优雅得多

. styled-components包括一个StyleSheetManager组件,它可以接收目标道具。目标需要一个DOM节点,并将其动态创建的样式表附加到该节点。 react-frame-component使用React的新版本的上下文API来公开FrameContextProvider。它包括上下文中的IFrame文档和窗口。 您可以将这两个API组合在一起,如下所示,以便在styled-components中使用IFrames: { frameContext => ( {/*,您的孩子在这里*/} )} 这与react v16.4.1,styled components v3.3.3和react frame-component v4.0.0完美地工作。

票数 7
EN

Stack Overflow用户

发布于 2019-12-01 09:01:45

我叉形您的沙箱,以显示一个解决方案。

步骤:

  1. 为iframe使用或编写组件。在沙箱中,我使用react-frame-component (https://github.com/ryanseddon/react-frame-component)。它将为我们呈现任何传递内容的iframe。
  2. 找到一种方法来获得由情感创造的风格。情感只会创建style节点,所以我们只需复制它。在沙箱中,我编写了非常原始的代码来检查这个想法,它正在工作,但是在生产过程中,您应该编写一些更高级的代码,我认为:
代码语言:javascript
复制
        <Frame>
          <FrameContextConsumer>
            {// Callback is invoked with iframe's window and document instances
            ({ document }) => {
              if (isFirstRender) {
                setTimeout(() => {
                  // find styles in main document
                  const styles = Array.from(
                    window.document.head.querySelectorAll("style[data-emotion]")
                  )
                  // and add it to the child
                  styles.forEach(s =>
                    document.head.appendChild(s.cloneNode(true))
                  )
                  isFirstRender = false
                }, 100)
              }
              // Render Children
              return <Button>Primary</Button>
            }}
          </FrameContextConsumer>
        </Frame>

注意:我不熟悉emotion,但我认为它不会在生产中创建style节点(通过webpack ofc),而是创建一个文件,类似于styles.css。然后,应该将链接添加到子文档中:

代码语言:javascript
复制
              if (isFirstRender) {
                setTimeout(() => {
                  const link = document.createElement("link");
                  link.href = "styles.scss";
                  link.rel = "stylesheet";

                  document.head.appendChild(link);

                  isFirstRender = false
                }, 100)
              }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59098769

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档