Loading

next.js 利用中间件(middleware.ts)实现PC与移动路由无缝切换

场景描述

产品要求开发一个落地页,为了美观,他要求这个两个页面分开设计,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 //是否为微信浏览器
  }
}

 

posted @ 2024-07-04 17:45  冯叶青  阅读(8)  评论(0编辑  收藏  举报