Как реализовать кэширование тега
Каким образом в React можно сделать кэширование тега для предотвращения повторной загрузки аудиофайла, который ранее был воспроизведён. Пользователь из select выбирает предпочитаемый звук, после чего audio его воспроизводит. Но как оптимизировать данную часть, чтобы браузер при повторном выборе пользователя звука, который ранее был загружен, просто брал бы данный звук из кэша и воспроизводил его.
function SelectNotify() {
const {sounds,activeSound} = useSelector((store) => store.timer);
const audioRef = useRef();
const selectRef = useRef();
const dispatch = useDispatch();
const [active,setActive] = useState(false);
const handelClickOutside = (event) => {
if (selectRef.current && !selectRef.current.contains(event.target)) {
setActive(false);
}
}
const handleChangeSound = (idSound) => {
dispatch(changeSound(idSound));
}
useEffect(()=>{
if (audioRef.current.completeFirstRender === true) {
audioRef.current.play();
} else {
audioRef.current.completeFirstRender = true
}
},[activeSound])
useEffect(()=>{
document.addEventListener('click',handelClickOutside)
return () => {
document.removeEventListener('click',handelClickOutside)
}
},[])
return (
<div className='d-flex justify-content-between align-items-center itemOption col-12' >
<h5>Select the sound Notification </h5>
<audio ref={audioRef} src={sounds[activeSound].src} />
<div onClick={() => setActive(!active)} ref={selectRef} className='d-flex align-items-center justify-content-between edSelect'>
{sounds[activeSound].title}
<AiFillCaretDown id='selectIcon' className={active && 'active'} />
<CustonSelectWrapper open={active} >
<ul>
{
sounds.map((item,index)=>(
<li onClick={() => handleChangeSound(index)} key={index} >{item.title}</li>
))
}
</ul>
</CustonSelectWrapper>
</div>
</div>
);
}
Ответы (1 шт):
Если хотите вручную сохранять в кеш, то можно так:
Предисловие: Весь код обёрнут в самовызывающуюся асинхронную функцию, чтобы имитировать поведение module, чтобы можно было воспользоваться top level await-ом
И так начнём с getAudioContext:
Сначала достаём определенный именованный объект Cache с помощью caches.open('audio')
Достаём оттуда, то что было запомнено, по укзанной ссылке с помощью cache.match(url)
Если там ничего не было, то нам вернётся
undefined3.1. А значит нам надо сделать запрос на указанный адрес и достать, то что нужно
3.2. Т.к. мы знаем что запрос делается впервые, то просто кладём ответ в
cacheс помощью cache.add(url)Если там уже что-то есть, то никаких запросов делать не нужно
Я использую метод blob() вместо arrayBuffer(), потому что при его использовании нужно данные отправить в new AudioContext(). Но чтобы его использовать нужно чтобы он был создан при действии пользователя, если просто так попытаться его создать, то выходит предупреждение:
An AudioContext was prevented from starting automatically. It must be created or resumed after a user gesture on the page. Чтобы не создавать всякие кнокпи и т.д. мне было легче воспользоваться методомblob
Теперь разберёмся с тем что происходит в главной функции:
Вызываем вышеописанную функцию и получаем данные нашего аудио файла
Чтобы отправить данные в new Audio() нам нужно получить ссылку на наши данные и в этом нам поможет URL.createObjectURL()
Теперь когда у нас есть ссылка на данные аудио файла, нам остаётся только его отправить в
new Audio()и вставить его на нашу страницу после того как всё будет загружено и будет готов к воспроизведениюaddEventListener('loadeddata'3.1 Я напсиал
audio.controls = true;потому что без него не показывается на странице HTMLAudioElement (как минимум это так вFirefox)
(async () => {
const getAudioContext = async (url) => {
const cache = await caches.open('audio');
let cachedAudio = await cache.match(url);
if (typeof cachedAudio === 'undefined') {
console.log('new');
const response = await fetch(url);
cache.add(url);
cachedAudio = response;
} else {
console.log('cached');
}
return cachedAudio.blob();
}
const url = 'https://v1.cdnpk.net/videvo_files/audio/premium/audio0137/watermarked/MusicComedyThe CTE02_96.3_preview.mp3';
const audioContext = await getAudioContext(url);
console.log(audioContext);
const audioLocalUrl = URL.createObjectURL(audioContext);
const audio = new Audio(audioLocalUrl);
audio.addEventListener('loadeddata', () => {
document.body.append(audio);
})
})()
Тут, конечно много чего не учтено, например обработка ошибок, но как показательный пример сойдёт :) Можете запустить на каком-нибудь localhost и посмотреть результат
P.S.
Чтобы код работал ВАЖНО чтобы до файла можно было достучаться именно из скрипта при помощи fetch. Это я к тому, что не каждый файл, который может воспроизводить тег audio, можно достать с помощью fetch
P.P.S.
Этот код тут не запустить, т.к. операции с cache тут запрещены, потому код вставлен не как запускаемый сниппет