代码之家  ›  专栏  ›  技术社区  ›  Mad Max

在React中将img标签替换为Gallery

  •  3
  • Mad Max  · 技术社区  · 8 年前

    渲染库组件有问题: 我从服务器获取html字符串

    let serverResponse = `
      <h3>Some title</h3>
      <p>Some text</p>
      <p>
        <img src="">
        <img src="">
        <img src="">
        <br>
      </p>
    ...
    `
    

    现在我用 dangerouslySetInnerHTML

    <div dangerouslySetInnerHTML={{ __html: serverResponse }} />
    

    但当我重复两次或更多的时候 <img> 标记我想用组件替换它们。 我该怎么做?我试着用 Regex 并将其替换为 <Gallery/> 但它不起作用。我想我需要在标记数组中拆分字符串,然后将图像替换为 <画廊/> 组成部分 我试着用 renderToString

    ...
    getGallery = images => {
        // Loop throw images and get sources
      let sources = [];
      if (images) {
      images.map(img => {
        let separatedImages = img.match(/<img (.*?)>/g);
        separatedImages.map(item => sources.push(...item.match(/(https?:\/\/.*\.(?:png|jpg))/)));
      });
      }
    
      if (sources.length) {
        return <Gallery items={sources}>
      }
    
      return <div/>
    }; 
    
    ...
    <div dangerouslySetInnerHTML={{__html: serverResponse.replace(/(<img (.*?)>){2,}/g,
                        renderToString(this.getGallery(serverResponse.match(/(<img (.*?)>){2,}/g))))}}/>}
    

    这不起作用,因为我得到的只是html,没有逻辑:(

    2 回复  |  直到 5 年前
        1
  •  3
  •   dfsq    8 年前

    首先, dangerouslySetInnerHTML 这不是办法,你不能把图库插入其中,然后让React处理。您需要做的是多步骤过程。

    1、将HTML解析为文档。 在此阶段,您将把字符串转换为有效的DOM文档。使用DOMParser很容易做到这一点:

    function getDOM (html) {
      const parser = new DOMParser()
      const doc = parser.parseFromString(`<div class="container">${html}</div>`, 'text/html')
      return doc.querySelector('.container')
    }
    

    我使用这个助手函数来返回包含HTML节点的容器。下一步需要它。

    2、将DOM文档转换为React JSX树。 现在您已经有了DOM树,通过从相应的DOM节点创建单独的React元素,很容易将其转换为JSX。此函数需要递归才能处理DOM树的所有级别。这样做可以:

    function getJSX(root) {
      return [...root.children].map(element => {
        const children = element.children.length ? getJSX(element) : element.textContent
        const props = [...element.attributes].reduce((prev, curr) => ({
          ...prev,
          [curr.name]: curr.value
        }), {})
    
        return React.createElement(element.tagName, props, children)
      })
    }
    

    这足以从DOM中创建JSX。可以这样使用:

    const JSX = getJSX(getDOM(htmlString))
    

    3、注入通道 . 现在,您可以改进JSX创建,将Gallery注入已创建的JSX中 element 包含多个图像标记。我会将注入函数传递到 getJSX 作为第二个参数。与上述版本的唯一区别是 children 按库大小写计算:

    if (element.querySelector('img + img') && injectGallery) {
      const imageSources = [...element.querySelectorAll('img')].map(img => img.src)
      children = injectGallery(imageSources)
    } else {
      children = element.children.length ? getJSX(element) : element.textContent
    }
    

    4、创建库组件。 现在是时候创建库组件本身了。此组件将如下所示:

    import React from 'react'
    import { func, string } from 'prop-types'
    
    function getDOM (html) {
      const parser = new DOMParser()
      const doc = parser.parseFromString(`<div class="container">${html}</div>`, 'text/html')
      return doc.querySelector('.container')
    }
    
    function getJSX(root, injectGallery) {
      return [...root.children].map(element => {
        let children
    
        if (element.querySelector('img + img') && injectGallery) {
          const imageSources = [...element.querySelectorAll('img')].map(img => img.src)
          children = injectGallery(imageSources)
        } else {
          children = element.children.length ? getJSX(element) : element.textContent
        }
    
        const props = [...element.attributes].reduce((prev, curr) => ({
          ...prev,
          [curr.name]: curr.value
        }), {})
    
        return React.createElement(element.tagName, props, children)
      })
    }
    
    const HTMLContent = ({ content, injectGallery }) => getJSX(getDOM(content), injectGallery)
    
    HTMLContent.propTypes = {
      content: string.isRequired,
      injectGallery: func.isRequired,
    }
    
    export default HTMLContent
    

    5、使用它! 以下是您将如何一起使用:

    <HTMLContent
      content={serverResponse}
      injectGallery={(images) => (
        <Gallery images={images} />
      )}
    />
    

    下面是上述代码的演示。

    演示: https://codesandbox.io/s/2w436j98n

        2
  •  0
  •   Tr1et    8 年前

    TLDR: 您可以使用 React HTML Parser 或类似的库。

    虽然JSX看起来非常相似,但它被解析为 React.createElement 所以在HTML字符串中插入React组件将不起作用。 renderToString 不会这样做,因为它用于服务器端呈现反应页面,在您的情况下不起作用。

    要用React组件替换HTML标记,需要一个解析器将HTML字符串解析到节点,将节点映射到React元素并呈现它们。幸运的是,有一些图书馆可以做到这一点,比如 反应HTML解析器 例如