iFrame에 HTML, CSS 및 JS 코드를 로드하는 방법

14884 단어 webdevjavascript
만약 네가 단지 이곳에 답을 찾으러 왔을 뿐이지, 이야기가 아니라면, the solution is at the bottom.
만약 JSFiddle, Codepen 또는 다른 도구를 사용한 적이 있다면, 이 문제는 당신에게 익숙합니다. 목표는 HTML, CSS, JS (문자열로 저장) 를 가져오고 코드를 불러오는 iframe를 만드는 것입니다.
이 문제는 쉬울 거예요. 하지만 아니에요. 적어도...내가 기다리고 있던 금표를 찾을 때까지.
하지만 이따가 자세히 소개해 드리겠습니다.그게 더 재미있기 때문에 우리는 작용하지 않는 모든 일부터 시작합시다.

시도 #1: srcdoc 사용


약간의 연구를 한 후에 나는iframes에 srcdoc 속성을 추가할 수 있다는 것을 감격스럽게 발견하였다.
HTML 문자열이 전송되면 iframe에서 HTML 컨텐트를 로드합니다.
<iframe srcdoc="<p>This text will appear in the iframe!</p>"></iframe>
불행하게도 이런 방법에는 두 가지 주요 문제가 있다.

1.srcdoc의 브라우저 지원은 좋지 않습니다



IE 또는 Edge를 지원하려면 다른 방법(또는 다각형 채우기)이 필요합니다.

2. CSS/JS에서 종료 가능


다음은 내가 srcdoc를 사용하여 실현한 대체적인 상황이다.
function setIframeContent(iframe, { html, css, js }) {
  const source = `
    <html>
      <head><style>${css}</style></head>
      <body>
        ${html}
        <script>${js}</script>
      </body>
    </html>
  `
  iframe.srcdoc = source
}
문제는 무엇입니까?CSS나 JS를 작성할 때 코드에 각각 포함 </style> 또는 </script> 만 하면 HTML 영역으로 탈출할 수 있습니다.
이 버그는 실제로 매우 흔하다.JSFIDLE 및 Codepen 모두 영향을 받습니다.

시도 #2: 서버 플라이백 없음


브라우저 지원 문제를 해결하기 위해 srcdoc 속성을 일반 src 속성으로 바꿉니다.이를 위해서는 코드뿐만 아니라 실제 URL을 전달해야 합니다.
HTML, CSS, JS "GET"매개 변수를 수용하고 이전과 같은 유형의 페이지를 출력하는 페이지를 설정할 수 있지만, 이번에는 실제 URL에서 불러옵니다.
서버가 없는 구조를 사용하기에 가장 좋은 시기입니다. 왜냐하면 우리는 단지 한 가지 일을 완성할 수 있는 단점이 필요하기 때문입니다.다음은 내 시도입니다.
module.exports = (req, res) => {
  // Code comes from GET params in URL
  const { html = '', css = '', js = '' } = req.query

  // Generate and send HTML page
  return res.send(`
    <html>
      <head><style>${css}</style></head>
      <body>
        ${html}
        <script>${js}</script>
      </body>
    </html>
  `)
}

이것은 거의 모든 브라우저에 적용되지만 그 자체의 문제도 있다.
  • CSS/JS에서 HTML로의 전환은 여전히 문제입니다
  • 전체 소스 코드는 URL에 전달되며 URL은 isn't ideal 입니다.
  • 시도 #3: 서버 리턴 없음(redux)


    우리의 첫 번째 리턴봉은 브라우저 지원 문제를 해결했지만, 여전히'탈출'문제는 처리해야 한다.
    다행히도 우리가 코드를 전달하는 방식 때문에 사실상 해결할 수 있다.우리는 서버에 CSS와 JS를 삽입하는 페이지가 아니라 클라이언트에서 완성할 수 있습니다!클라이언트의 컴퓨터가 URL GET 매개변수에 여전히 액세스할 수 있기 때문입니다.
    소스 코드가 좀 길지만 유효합니다.
    module.exports = (req, res) => {
      return res.send(`
        <html>
          <head>
            <script type="text/javascript">
              window.addEventListener('load', function() {
                function getUrlParameter(name) {
                  name = name.replace(/[\\[]/, '\\\\[').replace(/[\\]]/, '\\\\]');
                  var regex = new RegExp('[\\\\?&]' + name + '=([^&#]*)');
                  var results = regex.exec(location.search);
                  return results === null ? '' : decodeURIComponent(results[1].replace(/\\+/g, ' '));
                };
    
                // Load JS from GET params (on client)
                var js = getUrlParameter('js');
                if (js) {
                  var script = document.createElement('script');
                  script.type = 'text/javascript';
                  script.text = js;
                  document.body.appendChild(script);
                }
    
                // Load CSS from GET params (on client)
                var css = getUrlParameter('css');
                if (css) {
                  var style = document.createElement('style');
                  style.type = 'text/css';
                  if (style.styleSheet) {
                    style.styleSheet.cssText = css;
                  } else {
                    style.appendChild(document.createTextNode(css));
                  }
                  document.head.appendChild(style);
                }
    
                // Remove the currently running script tag
                document.currentScript.parentNode.removeChild(document.currentScript);
              });
            </script>
          </head>
          <body>
            ${req.query.html || ''}
          </body>
        </html>
      `)
    }
    
    
    현재 스크립트나 스타일에 무서운 HTML 문자가 포함되어 있으면 브라우저는 상기 스크립트/스타일을 문서에 삽입할 때 이 문자를 처리합니다.
    이 솔루션은...좋다기술적으로 말하자면, 이것은 실행 가능한 것이다.그러나 우리는 여전히 소프트 URL 길이 제한을 고려해야 한다.그 밖에 우리는 현재 일부 서버 측의 일을 처리하고 있으며, 클라이언트에서 발생해야 한다고 느낀다.
    더 좋은 방법이 있을 거야.

    솔루션: Blob URL


    그동안 우리는 URL에서 데이터를 로드하는 시뮬레이션을 시도해 왔습니다.
  • 우선, 우리는 srcdoc로 데이터를 불러오는 것이지 URL에서 불러오는 것이 아니다
  • 그리고 우리는 URL
  • 에서 코드를 로드하기 위해 리셋 막대를 사용합니다
  • 다음에 우리는 boomerang을 업데이트하여 "외부 URL에서 CSS/JS를 불러오기"동작을 모의하려고 합니다. 비록 모든 자원이 하나의 URL에서 왔지만.
  • Javascript에는 Blob URL이라는 기능이 있습니다.

    얼룩


    우리는 Blob 구조 함수를 사용하여 위조 파일을 만들 수 있다.디스크나 URL에서 로드된 실제 파일이 아닙니다. 메모리에만 저장됩니다.그러나 많은 면에서 그 기능은 실제로 불러온 파일과 같다.
    그리고 우리는 URL.createObjectURL(blob) 을 사용하여blob의 내용을 불러오는 URL을 만들 수 있습니다.
    다음은 실천 중인 작업 원리입니다.
    const getBlobURL = (code, type) => {
      const blob = new Blob([code], { type })
      return URL.createObjectURL(blob)
    }
    
    console.log(getBlobURL('<p>My webpage</p>', 'text/html'))
    // blob:https://dev.to/9ca05e31-05ea-48f8-838d-cc1ad0949ec8
    
    컨트롤러에서 위의 코드를 실행해 보세요. 직접 보세요!URL이 기록됩니다.시작 blob: 비트를 포함하여 새 탭에 URL을 붙여넣으면 HTML이 포함된 페이지가 로드됩니다.
    주의'text/html'getBlobURL에게 전달되었습니까?우리도 이 점을 바꿀 수 있다.CSS 또는 JS blob을 생성하는 것은 매우 쉽습니다. 각각 text/css 또는 text/javascript 만 통과하면 됩니다.
    blob URL의 또 다른 장점은 항상 존재하고 어떤 방식으로든 일반 URL에 접근할 수 있다는 것이다.이것은 우리가 사실상 하나의 단독 URL에서 CSS와 JS 파일을 불러올 수 있다는 것을 의미하기 때문에'전의'기술은 더 이상 문제가 아니다.
    이것은 실천 중의 간단한 실현이다.
    const getGeneratedPageURL = ({ html, css, js }) => {
      const getBlobURL = (code, type) => {
        const blob = new Blob([code], { type })
        return URL.createObjectURL(blob)
      }
    
      const cssURL = getBlobURL(css, 'text/css')
      const jsURL = getBlobURL(js, 'text/javascript')
    
      const source = `
        <html>
          <head>
            ${css && `<link rel="stylesheet" type="text/css" href="${cssURL}" />`}
            ${js && `<script src="${jsURL}"></script>`}
          </head>
          <body>
            ${html || ''}
          </body>
        </html>
      `
    
      return getBlobURL(source, 'text/html')
    }
    
    const url = getGeneratedPageURL({
      html: '<p>Hello, world!</p>',
      css: 'p { color: blue; }',
      js: 'console.log("hi")'
    })
    
    const iframe = document.querySelector('#iframe')
    iframe.src = url
    
    읊다, 읊조리다

    도덕


    나는 언어와 싸우지 말라고 생각한다.
    나는 내가 무엇을 하고 싶은지 안다. URL에서 데이터를 불러온다.나는 지금까지 해커가 아닌 방식을 찾아서 이 점을 해낼 생각을 해 본 적이 없다.

    좋은 웹페이지 즐겨찾기