在编写前端内容时,我们经常要面临的一个问题是做移动端适配,尤其是一些小型工具,在如今移动端使用场景远超PC端的现实环境中,为了更好的客户留存,通常都需要做一些简单的处理。
常规方案
通常情况下,我们会选择使用 useEffect
在加载时获取是否是移动端访问
// 你的组件
const [isMobileDevice, setIsMobileDevice] = useState<boolean>(false);
useEffect(() => {
const userAgent = typeof window === 'undefined' ? '' : navigator.userAgent;
setIsMobileDevice(/Mobi|Android|iPhone|iPad/i.test(userAgent))
}, []);
return (
<div>
<p>{isMobileDevice ? "is mobile user" : "is pc user"}</p>
</div>
);
这种方式,比较常规,但是始终会面临一个问题,就是只有当接口返回后,才能拿到 userAgent
。这就导致每次在移动端访问时,都会先显示1-2秒的PC端样式,然后再切换成移动端的样式。
在几乎所有公司,这都是一个BUG。
那如何解决呢?
next/headers
在 NextJS 13 及此后的版本,都已经支持了直接导入 headers
去获取请求参数。参考官网。
需要注意的是,这个方法需要在 page.tsx
目录下使用,在其他目录下面使用时,都会报如下错误
You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
注意,这个方法是因为我在非 page 页面使用了
useState
,同时又导入了next/headers
所致。每个人的情况可能不同。
当然,简化之后的代码大概如下:
// app/page.tsx
import {headers} from "next/headers";
export default function Home() {
const userAgent = headers().get('user-agent');
const isMobileDevice = /Mobi|Android|iPhone|iPad/i.test(userAgent);
return (
<div>
<HeaderCase isMobileDevice={isMobileDevice} />
</div>
);
};
// app/headerCase.tsx
const HeaderCase = ({isMobileDevice=false}) {
return (
<div>
<p>{isMobileDevice ? "is mobile user" : "is pc user"}</p>
</div>
);
}
export default HeaderCase;
如此,每次进入对应页面时就可以第一时间响应移动端的样式了。