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 //是否为微信浏览器
}
}
愿你走出半生,归来仍是少年

浙公网安备 33010602011771号