首页
未知分类正文内容

如何在Next.js中正确检测移动版本

2023年10月10日
阅读时长 2 分钟
阅读量 12
如何在Next.js中正确检测移动版本

最近,我被分配了一个任务,要使用NextJS从零开始构建一个电子商务应用程序。该应用程序设计有两个变体:桌面版和移动版。当我开始移动版本时,我有些沮丧。所有我最喜欢的钩子函数,如useWindowSizeuseIsMobile由于服务器端环境的限制而无法正常工作!它们依赖于全局的窗口对象,而服务器上根本没有这个对象。因此,无法获得正确的初始窗口大小,而且默认设置为null也不正确。这可能会导致UI中的布局变化。所以我必须找到一种方法,为初始标记设置适当的默认值。

我开始寻找解决方案,我找到了它。我们可以通过用户代理来检测移动设备,而不是通过窗口宽度。在本文中,我们将理解这个简单优雅的解决方案。

getServerSideProps中检测移动设备

getServerSideProps在每次页面请求时被调用。它接收上下文作为唯一参数,并返回页面所需的数据。这就是定义用户设备的确切位置。

首先,我们需要编写一个核心函数,它将接受服务器端上下文并检测移动设备。由于编写自定义实现相当困难且不必要,我们将使用一个名为mobile-detect的小型库。所以,让我们来看看代码!

import MobileDetect from "mobile-detect";
import { GetServerSidePropsContext } from "next";

export const getIsSsrMobile = (context: GetServerSidePropsContext) => {
  const md = new MobileDetect(context.req.headers["user-agent"] as string);

  return Boolean(md.mobile());
};

核心逻辑完成了,但是如何在代码中方便地使用它?我们不想一直传递这些属性,因为这可能会变得混乱。这是合适的时机来使用React Context!

创建方便使用的React Context

老实说,这没有什么复杂的。我们只需要创建一个包含布尔值的Context即可。

import { createContext } from "react";

export const IsSsrMobileContext = createContext(false);

请不要忘记用它包装应用程序。最适合的位置是_app.tsx

import type { AppProps } from "next/app";
import { IsSsrMobileContext } from "@/utils/useIsMobile";

export default function App({
  Component,
  pageProps
}: AppProps<{ isSsrMobile: boolean }>) {
  return (
    <IsSsrMobileContext.Provider value={pageProps.isSsrMobile}>
      <Component {...pageProps} />
    </IsSsrMobileContext.Provider>
  );
}

useIsMobile钩子的最终实现

我认为我们应该从钩子的骨架开始,然后在其上添加更复杂的逻辑。

export const useIsMobile = () => {
  const isSsrMobile = useContext(IsSsrMobileContext);

  return isSsrMobile;
};

目前,useIsMobile只能在服务器端检测移动设备,但是在浏览器端呢?我们来写另一个钩子,用于客户端。它可以看起来像这样,但是根据你的需要,可以自由地编写适合你的解决方案。

export const useWindowSize = () => {
  const [windowSize, setWindowSize] = useState<{
    width?: number;
    height?: number;
  }>({
    width: undefined,
    height: undefined
  });

  useEffect(() => {
    function handleResize() {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight
      });
    }

    window.addEventListener("resize", handleResize);

    // 立即调用处理程序,以便状态与初始窗口大小更新
    handleResize();

    // 不要忘记在清理时删除事件监听器
    return () => window.removeEventListener("resize", handleResize);
  }, []);

  return windowSize;
};

最后,我们将它们组合在一起...

export const useIsMobile = () => {
  const isSsrMobile = useContext(IsSsrMobileContext);
  const { width: windowWidth } = useWindowSize();
  const isBrowserMobile = !!windowWidth && windowWidth < 992;

  return isSsrMobile || isBrowserMobile;
};

如何在页面上使用它

import { getIsSsrMobile } from "@/utils/getIsSsrMobile";
import { useIsMobile } from "@/utils/useIsMobile";
import { GetServerSidePropsContext } from "next";

export default function Home() {
  const isMobile = useIsMobile();

  return isMobile ? <div>移动版</div> : <div>桌面版</div>;
}

export async function getServerSideProps(context: GetServerSidePropsContext) {
  return {
    props: {
      isSsrMobile: getIsSsrMobile(context)
    }
  };
}

请记住,始终从getServerSideProps返回isSsrMobile属性。其余部分非常简单明了。


资源


免责声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。