使用 React Hooks 对 React 上的视频使用动态导入

Using dynamic import with videos on React with React Hooks

我正在制作一种背景为全屏视频的个人网站。 我需要为不同的屏幕尺寸加载不同的视频。我正在使用 React,并基于 React Hooks 制作组件。

这些是我的依赖版本:

    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-router-dom": "^5.1.2",
    "react-scripts": "3.4.1",

在我的组件 "VideoBackground" 中,我使用 useState 创建了一个状态,我将在其中存储视频源和海报图像的源,如下所示:

  const [video, setVideo] = useState({
    poster: null,
    src: null,
  });

我正在使用 useEffect 钩子作为 componentDidMount 函数,我将在其中根据 windows 宽度动态加载视频,并向浏览器中的事件侦听器添加一个函数以重新加载每次调整大小时的视频:

  useEffect(() => {
    async function handleResize() {
      setVideo(await loadVideo());
    }

    handleResize();

    window.addEventListener("resize", handleResize);
    window.addEventListener("orientationchange", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
      window.removeEventListener("orientationchange", handleResize);
    };
  }, []);

根据 windows 宽度动态加载视频的函数 "loadVideo" 是一个异步函数,因为我使用的是 React 提供的 Dinamic Import 语法,这是一个 Promise(这是最大的我猜的问题)。这是我的完整组件,"loadVideo"函数在开头:

The "./styles" file is a component based on styled-component lib, I am using it to style my html, it doesn't matter (I guess). But the "Video" component is the video tag from html 5.

import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";

import { Video, Overlay, Content } from "./styles";

const VideoBackground = ({ children }) => {
  /*
   * Media query of the resource's intended media; this should be used only in a <picture> element
   *
   * Extra large devices (large laptops and desktops, 1200px and up)
   * Large devices (laptops/desktops, 992px and up)
   * Medium devices (landscape tablets, 768px and up)
   * Small devices (portrait tablets and large phones, 600px and up)
   * Extra small devices (phones, 600px and down)
   */
  async function loadVideo() {
    const { width } = getWindowDimensions();

    let posterPromise;
    let srcPromise;

    if (width > 1280) {
      posterPromise = import("../../assets/images/code720.jpg");
      srcPromise = import("../../assets/videos/code720.mp4");
    } else if (width > 720) {
      posterPromise = import("../../assets/images/code480.jpg");
      srcPromise = import("../../assets/videos/code480.mp4");
    } else if (width > 480) {
      posterPromise = import("../../assets/images/code320.jpg");
      srcPromise = import("../../assets/videos/code320.mp4");
    } else {
      posterPromise = import("../../assets/images/code240.jpg");
      srcPromise = import("../../assets/videos/code240.mp4");
    }

    const [{ default: poster }, { default: src }] = await Promise.all([
      posterPromise,
      srcPromise,
    ]);

    return {
      poster,
      src,
    };
  }

  /*
   * It returns the width of a window's content area
   */
  function getWindowDimensions() {
    const { innerWidth: width } = window;

    return {
      width,
    };
  }

  const [video, setVideo] = useState({
    poster: null,
    src: null,
  });

  useEffect(() => {
    async function handleResize() {
      setVideo(await loadVideo());
    }

    handleResize();

    window.addEventListener("resize", handleResize);
    window.addEventListener("orientationchange", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
      window.removeEventListener("orientationchange", handleResize);
    };
  }, []);

  console.log("BEFORE RETURN", video);

  return (
    <>
      <Video playsInline autoPlay muted loop poster={video.poster}>
        <source src={video.src} type="video/mp4" />
      </Video>
      <Overlay />
      <Content>{children}</Content>
    </>
  );
};

VideoBackground.propTypes = {
  children: PropTypes.node.isRequired,
};

export default VideoBackground;

问题是背景视频没有播放。但是出现海报图片,我不知道为什么只有海报图片有效。 我把 console.log 放在 return 之前,试图找出发生了什么,这是记录的内容:

BEFORE RETURN {poster: null, src: null}
BEFORE RETURN {poster: null, src: null}
BEFORE RETURN {poster: "/static/media/code720.e66bf519.jpg", src: "/static/media/code720.83f62f35.mp4"}
BEFORE RETURN {poster: "/static/media/code720.e66bf519.jpg", src: "/static/media/code720.83f62f35.mp4"}

如您所见,视频和图片加载正常(但需要一段时间)。我认为问题是 "useState" 第一次为我的状态的属性分配了一个空值 ,紧接着,"useEffect" 调用了 "loadVideo"功能。

我第一次尝试将视频导入并分配给 "useState",并且正常工作! 但是,我无法使用 "useState",因为我需要先知道屏幕尺寸,然后下载合适的视频。

我还尝试将 "loadVideo" 函数与 "useState" 一起使用,如下所示:

const [video, setVideo] = useState(loadVideo());

但是,它不起作用,因为 "loadVideo" 是异步的。

有没有人能解决这个问题,或者有更好的方法来解决我想做的事情?

谢谢!

import() 没有加载视频,它只是获取 URL。您可以同步进行。

import videoSrc720 from "../../assets/videos/code720.mp4";

if (width>1280) {
    src = videoSrc720;
} ...