问题
Suppose I have a list of url's like so :
[ '/images/1', '/images/2', ... ]
And I want to prefetch n
of those so that transitioning between images is faster. What I am doing now in componentWillMount
is the following:
componentWillMount() {
const { props } = this;
const { prefetchLimit = 1, document = dummyDocument, imgNodes } = props;
const { images } = document;
const toPrefecth = take(prefetchLimit, images);
const merged = zip(toPrefecth, imgNodes);
merged.forEach(([url, node]) => {
node.src = url;
});
}
with imgNodes
being defined like so:
imgNodes: times(_ => new window.Image(), props.prefetchLimit),
and times
, zip
, and take
coming from ramda.
Now when I use those urls inside of react like so:
<img src={url} />
it hits the browser cache according to the Etag
and Expire
tags regardless of where the url is used. I also plan on using this to prefetch the next n
images whenever we hit n - 1
inside of the view, reusing imgNodes
in the same manner.
My question are:
Is this even a valid idea give 100+ components that will use this idea but only 1 will be visible at a time?
Will I run into memory issues by doing this? I am assuming that
imgNodes
will be garbage collected when the component is unmounted.
We are using redux
so I could save these images in the store but that seems like I am handling the caching instead of leveraging the browser's natural cache.
How bad of an idea is this?
回答1:
You don't need to do it in all of your components. As soon as an image is downloaded it gets cached by the browser and will be accessible in all components, so you can do this only once somewhere in a high-level component.
I don't know what exactly UX you are trying to create by caching images, however, your code only initiates downloading images but doesn't know whether an image is being downloaded, has been downloaded successfully or even failed. So, for example, you want to show a button to change images or add a class to a component only when the images have been downloaded (to make it smooth), your current code may let you down.
You may want to resolve this with Promises.
// create an utility function somewhere
const checkImage = path =>
new Promise(resolve => {
const img = new Image()
img.onload = () => resolve(path)
img.onerror = () => reject()
img.src = path
})
...
// then in your component
class YourComponent extends Component {
this.state = { imagesLoaded: false }
componentDidMount = () =>
Promise.all(
R.take(limit, imgUrls).map(checkImage)
).then(() => this.setState(() => ({ imagesLoaded: true })),
() => console.error('could not load images'))
render = () =>
this.state.imagesLoaded
? <BeautifulComponent />
: <Skeleton />
}
Regarding memory consumption — I don't think anything bad will happen. Browsers normally limit the number of parallel xhr requests, so you won't be able to create a gigantic heap usage spike to crash anything, as unused images will garbage collected (yet, preserved in browser cache).
Redux store is a place to store the app state, not the app assets, but anyway you won't be able to store any actual images there.
来源:https://stackoverflow.com/questions/43644633/how-to-cache-images-in-react