惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
人人都是产品经理
人人都是产品经理
博客园 - 聂微东
IT之家
IT之家
V
V2EX
Jina AI
Jina AI
V
Visual Studio Blog
有赞技术团队
有赞技术团队
博客园 - 司徒正美
博客园 - 叶小钗
The Cloudflare Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
小众软件
小众软件
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Google DeepMind News
Google DeepMind News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
腾讯CDC
Google Online Security Blog
Google Online Security Blog
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
N
News and Events Feed by Topic
N
News and Events Feed by Topic
The Last Watchdog
The Last Watchdog
W
WeLiveSecurity
月光博客
月光博客
Security Archives - TechRepublic
Security Archives - TechRepublic
Webroot Blog
Webroot Blog
SecWiki News
SecWiki News
博客园_首页
罗磊的独立博客
量子位
Latest news
Latest news
I
Intezer
V
Vulnerabilities – Threatpost
A
Arctic Wolf
Last Week in AI
Last Week in AI
Recent Commits to openclaw:main
Recent Commits to openclaw:main
S
SegmentFault 最新的问题
S
Security Affairs
阮一峰的网络日志
阮一峰的网络日志
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
酷 壳 – CoolShell
酷 壳 – CoolShell
P
Palo Alto Networks Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
N
News | PayPal Newsroom

博客园 - 冯叶青

OpenAI vs Anthropic API 对比:响应体、请求体、消息格式与工具调用 GitHub Actions 自动部署流程 Git分支自动合并脚本:基于时间戳的冲突解决方案 Next.js lingui.js 多语言自动提取翻译键 - ats-node React实现短信验证码输入组件 Next.js 中优雅地使用 Lottie 动画 Next.js 路由参数更新最佳实践:从 replaceState 到 nextReplaceState Jenkins构建时SWR模块导入报错解决方案 解决 Jenkins 环境下 Lingui 构建报错 "btoa is not defined" git 提交 实现大图自动压缩功能 React lingui.js 多语言自动提取翻译键 - ast node html2canvas 解决截图空白问题 react 实现前端发版监测 JS根据文件名获取文件类型 JS获取本机IP地址 适用于react、vue菜单格式化工具函数 git 内容提交 实现大图拦截功能 JS实现视频截图 Android生成签名文件及对apk进行签名 JS-SDK 配置,实现微信分享功能
next.js 利用中间件(middleware.ts)实现PC与移动路由无缝切换
冯叶青 · 2024-07-04 · via 博客园 - 冯叶青

场景描述

产品要求开发一个落地页,为了美观,他要求这个两个页面分开设计,PC页面路由是`/landingpage`,移动端页面是`/landingpage/mobile`

从用户角度出发,现在有一种访问场景,假如用户A正在访问PC页面`/landingpage`,然后他要把这个页面以微信方式分享给用户B,用户通过手机方式打开,那这个时候用户用手机看到的就是PC端页面

要解决上面的场景问题,有以下几种方案

1. 在页面上利用`getServerSideProps` 进行定向,弊端:假如我有很多这样的页面,那就要在每个页面写相同的重定向代码

2. 写通用逻辑hooks判断当前环境,根据环境跳转至不同的路由,弊端:hooks是客户端方法,要执行该方法首先的进入浏览器客户端,然后根据条件跳转不同的地址,这个时候就会出现闪烁

3. 利用中间件(middleware.ts)可实现丝滑无感跳转,原理就是重定向

hooks版本

useMobileAndPcBridging.ts

import { useRouter } from 'next/router'

const PC_MOBILE_LIST = [
  ['/landingpage/amazon', '/landingpage/amazon/mobile'],
  ['/landingpage/goOut', '/landingpage/goOut/mobile'],
  ['/landingpage/vppa', '/landingpage/vppa/mobile'],
  ['/gec/introduce', '/gec/introduce/mobile'],
  ['/greenCertificate/introduce', '/greenCertificate/introduce/mobile'],
  ['/landingpage/intention', '/landingpage/intention/mobile'],
  ['/landingpage/coscoShipping', '/landingpage/coscoShipping/mobile']
]
const useMobileAndPcBridging = () => {
  const router = useRouter()
  if (!process.browser) return
  const { pathname, query } = router
  const ITEM = PC_MOBILE_LIST.find((value) => value.includes(pathname))
  if (!ITEM?.length) return
  const [PC_PATH, MOBILE_PATH] = ITEM
  if (window.device.mobile && MOBILE_PATH !== pathname) {
    router.push({
      pathname: MOBILE_PATH,
      query
    })
    return
  }

  if (!window.device.mobile && PC_PATH !== pathname) {
    router.push({
      pathname: PC_PATH,
      query
    })
    return
  }
}

export default useMobileAndPcBridging

中间件middleware.ts版本

middleware.ts

import { NextResponse } from 'next/server'
import { type NextRequest } from 'next/server'
import { equipment } from '@/utils'

const PC_MOBILE_LIST = [
  ['/landingpage/amazon', '/landingpage/amazon/mobile'],
  ['/landingpage/goOut', '/landingpage/goOut/mobile'],
  ['/landingpage/vppa', '/landingpage/vppa/mobile'],
  ['/gec/introduce', '/gec/introduce/mobile'],
  ['/greenCertificate/introduce', '/greenCertificate/introduce/mobile'],
  ['/landingpage/intention', '/landingpage/intention/mobile'],
  ['/landingpage/coscoShipping', '/landingpage/coscoShipping/mobile']
]

export function middleware(request: NextRequest) {
  const userAgent = request.headers.get('user-agent') || ''
  const { pathname, search } = request.nextUrl
  const device = equipment(userAgent)
  const ITEM = PC_MOBILE_LIST.find((value) => value.includes(pathname))
  if (!ITEM?.length) return NextResponse.next()
  const [PC_PATH, MOBILE_PATH] = ITEM
  if (device.mobile && MOBILE_PATH !== pathname) {
    const path = new URL(MOBILE_PATH + search, request.url)
    console.log('移动端,跳转中...' + MOBILE_PATH)
    return NextResponse.redirect(path)
  }

  if (!device.mobile && PC_PATH !== pathname) {
    console.log('PC端,跳转中...' + PC_PATH)
    const path = new URL(PC_PATH + search, request.url)
    return NextResponse.redirect(path)
  }
  return NextResponse.next()
}

export const config = {
  matcher: [PC_MOBILE_LIST.flat(1)]
}

上面代码中遇到了辅助函数判断设备 `equipment`,这里贴以下方法

utils/index.ts

export const equipment = function (userAgent?: string) {
  //判断终端
  const u = userAgent || window.navigator.userAgent
  return {
    trident: u.indexOf('Trident') > -1, //IE内核
    presto: u.indexOf('Presto') > -1, //opera内核
    webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
    gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
    mobile: !!u.match(/AppleWebKit.*Mobile.*/) || u.indexOf('Samsung') > -1, //是否为移动终端
    ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
    android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器
    iPhone: u.indexOf('iPhone') > -1 || u.indexOf('Mac') > -1, //是否为iPhone或者安卓QQ浏览器
    iPad: u.indexOf('iPad') > -1, //是否为iPad
    webApp: u.indexOf('Safari') == -1, //是否为web应用程序,没有头部与底部
    weixin: u.indexOf('MicroMessenger') > -1 //是否为微信浏览器
  }
}