【问题标题】:React downloads pending images of previous renderReact 下载先前渲染的待处理图像
【发布时间】:2019-12-10 15:38:48
【问题描述】:

(更新:之前我以为这个问题是React Router引起的,但是我把React Router从代码中剥离出来了,问题依旧存在。所以我彻底修改了这个问题。)

情况:

我有分页页面,每页显示一个图像列表。 (“页面”是指可见的完整内容,而不是单独的 html 文件/url。)我想以有效的方式浏览这些页面。

问题:

如果我浏览页面的速度足够快,则在导航到下一页之前,并非所有图像都会加载到当前页面中。我希望浏览器在导航到下一页时取消所有未完成的未完成图像下载。但这不会发生,浏览器会保留所有未完成的图像以等待下载,直到它们全部下载完毕。然后我导航到的页面的图像将被下载。这会导致很大的延迟和带宽浪费。

问题:

是否可以取消上一页“待处理”图片的下载?

演示代码:

要对此进行测试,请使用浏览器开发者工具中的“网络”标签。还要选择“禁用缓存”并将油门(开发人员工具中的下载速度)设置为“慢 3G”之类的慢点,否则图像加载速度也会过快。然后浏览页面并查看待处理图像列表正在堆积,然后单击“禁用图像”按钮。然后,屏幕上将看不到任何图像,但浏览器仍然有大量未决的图像下载打开,这会浪费带宽并导致需要渲染新图像时出现延迟。

您可以在这里测试代码:https://codepen.io/Devabc/pen/PowjqwZ

//This code is using imgur images to demonstrate.
class Page extends React.Component {
  state = {
    pageNr: 1,
    imagesEnabled: true
  };

  onLinkClick = event => {
    const number = event.target.dataset.value;
    console.log("Number: " + number);
    this.setState({pageNr: number});
  };

  onButtonClick = event => {
    console.log("toggling images");
    this.setState(prevState => {
      return {imagesEnabled: ! prevState.imagesEnabled};
    });   
  };
 
  render() {
    const links = _.range(0, 5).map(number => {
      return (
        <a href="#" data-value={number} onClick={this.onLinkClick}>
          {number}
        </a>
      );
    });
    
    const pageNr = this.state.pageNr;
    const imgStart = pageNr * 100;
    const imgEnd = imgStart + 100;
    const images = this.state.imagesEnabled 
      ? <ImagesPanel imgStart={imgStart} imgEnd={imgEnd} />
      : null;
    
    return (
      <div>
        <h1>Page {this.state.pageNr}</h1>
        <div>Page links: {links}</div>
        <div><button onClick={this.onButtonClick}>{this.state.imagesEnabled ? "disable images" : "enable images"}</button></div>
        <div>Images:</div>
        <div>{images}</div>
      </div>
    );    
  }
}

function ImagesPanel(props) {  
  const images = _.range(props.imgStart, props.imgEnd).map(number => {
    return (
      <Image imageNr={number} />
    );
  });
  
  return images;
}

function imageUrl(imageNr) {
  const hash = imgurHashes[imageNr];
  return "https://i.imgur.com/" + hash + ".jpg";
}

function Image(props) {
  const url = imageUrl(props.imageNr);  
  return <img class="myImage" src={url} border='1' />
}


const imgurHashes = ["wmk2tcs","jvqH2X4","r3dz09r","yJYRtvI","33bUPXj","cYsggBH","URAl4lS","xBpS7lq","5LMFxjU","kUrFsMB","GZf8FnO","Er2lmge","22CbMOq","vJcKGb3","U9ALJof","LxfGswQ","YzyyFHI","vin2W11","c0PQRSY","b6b2qva","6UmvLGc","oTtDO7S","LGoOzDl","XD9o83i","dMUi3dj","XpvMqXC","9JYf6o2","8IBe95g","X26sUn3","qb7Taz9","lWd5TCZ","2UKHpPZ","PMmglpV","pZ5ukGv","ymEZize","nYURuNZ","1SToTrZ","GZHTkpe","NH0qm8I","mZRTNyB","FBAoint","nJWbHb9","BI9zvXf","OeT5kWf","JZ1WPQA","6ZK3S2x","z6M8ryn","3yMODr2","bUoicZu","p3ReIJA","dybF5Sh","DH5ZBEH","fMEbpy5","UyMkbSp","EKXikAy","YG3aJm9","4JWIQhV","lgsvM63","A0MepAi","957yfQF","iNkwwNi","aaJpoxO","Vxy5RgX","jZxV1kQ","JuTUNdL","WY4e0cg","xmgTP7A","O35FJpg","VA3fFhv","oGZVPYQ","X9PRsWA","wSYxWzX","xntVddT","rDn0s52","vQPT1rH","GmlqCZt","zntCiSZ","SoEHjHB","bTFF7HW","QjJRzmx","DZxjoKZ","XdIYgsc","aBZChfb","rAIuEHZ","zt9EJD9","vaEJQA1","9c5pjUL","VXW3Ubz","315aFBb","klp7nh9","fsZktFx","x1XmXYX","8HaInVG","6jJOtkE","9aElwYX","R4dDTw7","9hgY0kI","SW8M5sw","R9jcSrP","dlSSb0P","bhxZCLX","mogQ5tz","oHxiJye","PYyOOm3","Ns89wvi","2uWIFDx","nXN5uhB","UMQn1yZ","JCAEJ3a","VTlkiOu","5JyWm3p","RVE8GoO","XVje7aY","C6qr30z","e54jc67","0X4cRbP","qZaU4lT","WUPHAQD","iILnFAb","oCsxMga","hQyN9oX","GQYDzhE","RcTO075","JlTn7jd","0jJHUWZ","iKp7JGx","YaQeN45","Ot5nFpM","8CeXfPJ","m3cQQye","JYXQaj7","pzPglwg","B4a34uo","3dHvLPv","FEn3aTc","coi9Jpy","GU5ih0m","CtnHd4Y","hbOFsRS","xNW9ED4","avCP3XP","mcbapy0","r9E6DqQ","JDjounb","xHGiHZR","LPN7uSK","QwwPGKE","OGTUcVh","OLxQHfA","Mg0QT9y","0ia7Ca3","LKKHYJn","W8nyx6Q","FjLCYY4","5YEYOe9","vLQHljC","jyL5CS9","oCuKxVQ","L2IyiSA","ffkgeN2","FF7bKmo","JpWF0LA","72kPNNw","tmdNh8K","7PBAKy4","EXMlyuO","p5ZkX5b","Iilf92H","fQbVFiU","wj1csk6","rP14xLY","1iQM4nW","XMq6P6Y","dTRijsS","B3Sz2J8","UkSipp2","eAiZQlr","JJkbcAs","sfA0TJ4","foex9pW","IoCvWfI","5yAxTX7","H6EAfeQ","k8d637d","yIQVrZH","bQdJFx5","CiPGw2A","YZLiutP","BQlEKfd","0W032dR","r4PWPJF","mGBYym5","BC7cuX5","TcoRdCZ","KsCfq9T","KaoAUfC","RdDFY7O","HFhBXSb","kOkYQua","mmT60dj","vIqZffx","r7T9b7N","y1Qtgh9","GFwT6Zc","nWUqQQ3","OZMUUjP","x4ocIKv","wy13lyN","fTj8Om8","0AgyD0C","prWbjvJ","MNJdsJk","brS1te1","4xP3P3i","IlcxIrq","bX7vGyi","U4mhrTE","FVMGYVw","zRFW4oG","jpbvgCe","ZJJEUHu","aU5C2XY","jfFsb9b","WfkChky","qNKDvPo","fhgzlkL","4uEHjGz","cRPiOZz","OzA6TSu","iEhVkeo","tXCZZQY","7DBLToo","9rrTZl5","FWO4ugI","kel6MJM","md4dsMN","kvfKaxR","JMyKehy","3jlMpQk","r1DSqFd","ywuONdJ","eCfhJ5y","vDmQ7Sm","BS6VgDy","wwa7mX9","4E1aIuK","D0zqMuZ","ovehv4U","s3yrw4S","GXm3DCm","gugRkdk","6H2WA7e","M6qdVzv","59aJXiY","As2zA50","wyc2LnT","IuhXtFA","l3V5mZS","QoUlc4T","L7XNlhe","cNk7D2j","MPF5iQY","wRjmFCp","7PZSZrU","EpSMAzy","hLaPhDJ","bS3dM0R","SOCUNXb","Q7BIVld","3Yrg85R","cX4KejE","IQbsyz1","i1qMSgy","K5cU3Qb","NeeB0tr","YHZIvUY","I4BmxjZ","BtThGp0","qWPlax5","pDFqfpR","SdET6fw","9EpcwDb","6nDPyRo","B4X4pYN","mkYU9mK","P6A7I0V","Gwb8Wtr","kSkjNsR","vlEb6D8","Gznsyvk","Vpm3QdO","949USnU","8HyKw0G","4tJmhAP","GLXxX89","X4GGU1t","wRVqclU","rEFHH4Q","vLmSGxD","gkFI4kz","kQASxFm","Cq5brtp","KmWYkSo","2IjiGnN","laGj46X","mgYgH2n","I2iwVFj","oFHxy9R","8VWomE0","y1kV08m","PqXAGBv","MNoiTk8","qEEpz2x","e4ipNRw","CfVVnG8","hHnuzRP","YthPF8u","YBigt6A","tQI8AFu","2K0TnUO","GYReGhp","F2XQStI","7j7145H","BVkgYv1","4hfVk8z","faw1ajs","27H4ogL","0a9ZQ2T","041BIAs","dilQYiq","P1e5AXA","5Tia8aA","PFNJRet","t1Nhl5f","UpfH1sp","H2zXF5f","HLdaLwV","30O8VSP","KsaM1XE","jAhqN6P","Yoi2ylE","wkrg47U","ePmLF64","KLSJPrd","aUdvQ0a","VcqZhpH","zEqDYjX","WXLTAbp","shny07g","UqlXlR5","Bpw7KnV","YeP3Zhe","C5NSYDy","Y6GzbOA","6FxTImw","6PXXJmd","FnVsEu0","Ll2zJnC","JvNbUcB","nEOXggf","t28tvPa","m2qG0pl","KjkkYG5","kCcUVrD","g2G823J","ZyD1f4Z","P3SfyR5","QsA5vHF","DpSxArz","dKt3T4S","w0GfNCC","kvyacI9","Gqdr7qd","KlHHMzZ","VVZ7HVw","hFG5mwe","D4S9tPV","SMVSpk3","MYzi6pi","BzQeber","v7E7VCw","a03HdC6","KSAiRwR","7WkzcMx","z8iVQZP","qCDNLpI","TUerfpq","X08dKd5","qkmDrys","k0MjIxD","vIgbAlY","15TeGJg","j7vx4tU","DNvqPee","2jEQHaF","5Q1M9HL","EFninXx","0VttnZV","XKgdZGL","fDSOKh8","1i5Fhk1","wmUU0tL","O7SVAVF","WMoTmty","UUVTKaF","n9EPI8R","QqnEITA","lao3U57","ITabVKK","ph97wz5","bFfdCGt","9KWJKs3","aKKQd6o","jxzaSfj","6Gv8gBL","iDS0A1T","bTAS7ej","5FvLcFu","GtIFmNQ","kJAU7gn","UZCPmpz","68yuNFU","TuO5PNi","lMV0piH","taKCu3Y","PmNa3M4","Z6gErEZ","5qrVmOD","N5yxt2l","LhrvwLr","QhBn6p8","2kpPEe8","dnxlCK3","GMDnQ32","3qH62l5","Jy6aHR2","2tIZHcB","w0zzrUJ","aSh1mwr","fwCCSBI","k5osQCu","byHHnMX","Uu9Dq9I","K9sC2OO","CsFf1Kz","G28GqCu","OOPc79G","be5NrVR","C5XAmr2","rDNSwSj","AI4BrBq","hGwueuh","EpG6zfG","QORwSKm","sWDMpiD","U2QfeTp","kUqUudt","PGMbcrN","bEaFtGN","KPrPXxO","4iFaBhm","fDcFhWG","P1M2Ld5","aPAlbHH","8ye6kdq","ztVBpFQ","SVL8ujT","5CwT2Og","nIqakeV","SM3Jcoz","QdAk2M4","zpLnMrH","fglMOex","ynj6fe7","YG1DOZh","aJ50pkC","SbvVCaf","azLfxiY","gdw8DHE","1U00sfi","p3zgLIS","h9cTNnw","Z1tt3RC","HHnBLCI","hmkeUl1","aMKRR0h","6MteQjh","PMZzXiM","v2uh5Mk","QEDz82m","70LhmSw","KEGMEbg","tlG769G","gyoNASp","AUnDdta","z1TZP9m","nVmmkCH","IIDRcHT","8m1Go3S","LsscGjy"];

ReactDOM.render(
  <Page />,
  document.getElementById('root')
);

下面的屏幕截图显示了浏览器中待处理图像的列表,即使此时不应渲染任何图像,因为应该渲染图像的 React 组件已被卸载/删除。

【问题讨论】:

    标签: javascript reactjs performance react-dom


    【解决方案1】:

    问题不是由 React 引起的。我不确定这个问题应该责备谁,它可能是文档对象模型标准、HTML 或实现它的浏览器。问题是,一旦图像被附加到 DOM(当浏览器开始渲染 HTML 页面时),浏览器就会开始下载它,即使图像后来从 DOM 中删除也是如此。

    然而,有许多解决方案可以解决这个问题。

    方案一:修改src属性取消下载

    src 属性在 HTML 的 img 元素中是强制性的,但将其设置为空字符串(清除)会导致取消图像下载。 (这可能不适用于所有浏览器。)

    此处描述了此解决方案:How to cancel an image from loading 并在此处演示 https://jsbin.com/zakeqaseru/1/edit?output 和此处 https://jsfiddle.net/nw34gLgt/ (使用浏览器开发者工具监控图片下载是否被取消。)

    src 可以使用setAttribute() 更改为空字符串。将其设置为null 与将其设置为字符串"null" 具有相同的效果,这也可以,但不太干净。 删除 src 属性不适用于大多数浏览器。

    此解决方案是否有效可能因浏览器而异。旧版浏览器可能会在删除 src 时导致请求网页本身,如下所述:https://gtmetrix.com/avoid-empty-src-or-href.html 这可能会对浏览器和 Web 服务器的性能造成很大影响。

    如果浏览器无法处理此问题,类似的解决方案是在 HTML 元素上使用background-image 属性,例如div,而不是img

    此 src-clearing 解决方案在React-Image 中实现: https://github.com/mbrevda/react-image/pull/223

    this.i.src = ''
    
    try {
      delete this.i.src
    } catch (e) {
      // On Safari in Strict mode this will throw an exception,
      //  - https://github.com/mbrevda/react-image/issues/187
      // We don't need to do anything about it.
    }
    delete this.i
    

    React 中的解决方案是这样的:

    class Img extends React.Component {
        constructor(props) {
            super(props);
    
            this.imgRef = React.createRef();
        }
    
        componentWillUnmount() {
            try {
                this.imgRef.current.src = '';
                delete this.imgRef.current.src;
            } catch (e) {}
        }
    
        render() {
            return (
                <img {...this.props} ref={this.imgRef} />
            );
        }
    }
    

    那么你可以这样使用它:

    <Img src="...some image url...." />
    

    方案二:延迟加载src

    通过在编程条件下设置src 属性,您可以确保仅在需要时才加载图像。此解决方案还可以与其他解决方案结合使用,以确保在卸载关联的 React 元素时取消下载的图像。

    data-src(自定义数据)属性经常用于这种方法,当需要渲染图像时将其值复制到src

    此解决方案可能仍会下载已卸载的映像。

    解决方案 3:Chrome 的原生延迟加载

    此解决方案自 Chrome 76(2019 年 7 月?)起就存在。 Chrome 有一个用于图像和 iframe 的 loading 属性,可以将其设置为 "lazy" 以延迟下载资源,直到它到达与视口的计算距离。

    许多其他浏览器不支持loading 属性。

    有关详细信息,请参阅:

    此解决方案可能仍会下载已卸载的映像。

    其他信息来源

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-16
      • 2020-03-24
      • 2021-01-01
      • 2017-09-21
      • 1970-01-01
      • 1970-01-01
      • 2022-01-25
      相关资源
      最近更新 更多