柳燕 發表於 2024-7-4 17:45:00

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

<h2>场景描述</h2>
<p>产品要求开发一个落地页,为了美观,他要求这个两个页面分开设计,PC页面路由是`/landingpage`,移动端页面是`/landingpage/mobile`</p>
<p>从用户角度出发,现在有一种访问场景,假如用户A正在访问PC页面`/landingpage`,然后他要把这个页面以微信方式分享给用户B,用户通过手机方式打开,那这个时候用户用手机看到的就是PC端页面</p>
<p><strong>要解决上面的场景问题,有以下几种方案</strong></p>
<p>1. 在页面上利用`getServerSideProps` 进行定向,弊端:假如我有很多这样的页面,那就要在每个页面写相同的重定向代码</p>
<p>2. 写通用逻辑hooks判断当前环境,根据环境跳转至不同的路由,弊端:hooks是客户端方法,要执行该方法首先的进入浏览器客户端,然后根据条件跳转不同的地址,这个时候就会出现闪烁</p>
<p>3. 利用中间件(middleware.ts)可实现丝滑无感跳转,原理就是重定向</p>
<h2>hooks版本</h2>
<p>useMobileAndPcBridging.ts</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">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 = () =&gt; {
const router = useRouter()
if (!process.browser) return
const { pathname, query } = router
const ITEM = PC_MOBILE_LIST.find((value) =&gt; value.includes(pathname))
if (!ITEM?.length) return
const = ITEM
if (window.device.mobile &amp;&amp; MOBILE_PATH !== pathname) {
    router.push({
      pathname: MOBILE_PATH,
      query
    })
    return
}

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

export default useMobileAndPcBridging
</pre>
</div>
<h2>&nbsp;</h2>
<h2><strong>中间件middleware.ts版本</strong></h2>
<p>middleware.ts</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">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) =&gt; value.includes(pathname))
if (!ITEM?.length) return NextResponse.next()
const = ITEM
if (device.mobile &amp;&amp; MOBILE_PATH !== pathname) {
    const path = new URL(MOBILE_PATH + search, request.url)
    console.log('移动端,跳转中...' + MOBILE_PATH)
    return NextResponse.redirect(path)
}

if (!device.mobile &amp;&amp; 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:
}
</pre>
</div>
<p>&nbsp;</p>
<p>上面代码中遇到了辅助函数判断设备 `equipment`,这里贴以下方法</p>
<p>utils/index.ts</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">export const equipment = function (userAgent?: string) {
//判断终端
const u = userAgent || window.navigator.userAgent
return {
    trident: u.indexOf('Trident') &gt; -1, //IE内核
    presto: u.indexOf('Presto') &gt; -1, //opera内核
    webKit: u.indexOf('AppleWebKit') &gt; -1, //苹果、谷歌内核
    gecko: u.indexOf('Gecko') &gt; -1 &amp;&amp; u.indexOf('KHTML') == -1, //火狐内核
    mobile: !!u.match(/AppleWebKit.*Mobile.*/) || u.indexOf('Samsung') &gt; -1, //是否为移动终端
    ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
    android: u.indexOf('Android') &gt; -1 || u.indexOf('Linux') &gt; -1, //android终端或者uc浏览器
    iPhone: u.indexOf('iPhone') &gt; -1 || u.indexOf('Mac') &gt; -1, //是否为iPhone或者安卓QQ浏览器
    iPad: u.indexOf('iPad') &gt; -1, //是否为iPad
    webApp: u.indexOf('Safari') == -1, //是否为web应用程序,没有头部与底部
    weixin: u.indexOf('MicroMessenger') &gt; -1 //是否为微信浏览器
}
}
</pre>
</div>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    愿你走出半生,归来仍是少年<br><br>
来源:https://www.cnblogs.com/yz-blog/p/18284330
頁: [1]
查看完整版本: next.js 利用中间件(middleware.ts)实现PC与移动路由无缝切换