WebNuxt工程中,如何实现子域名绑定子路由?
nuxt
子域名

如你所见,当前所在的域名前缀为ask,当您跳转到会员中心时,域名前缀变成了uni,然而,它们其实都是同一个Nuxt工程,不相信你可以访问 https://uni.buildadmin.com/ask 一样可以打开本社区,我们将uni.buildadmin.com/ask简化为了ask.buildadmin.com,这就是本贴要讲的子域名绑定子路由,建议先收藏再细看~

  • 本方案纯前端代码实现,域名解析过来即可正常显示,无需配置反向代理等
  • 理解实现原理后,本方案同时适用于普通的vue项目
  • 以下示例代码,只适用于BuildAdmin的WebNuxt工程,且因侵入较多,无法以模块或系统内置实现,同时示例代码在你的项目中,需做出一定调整

实现原理

获取当前子域名 -> 修改路由。对的,核心原理就这么简单!
打个比方,假设我们已经有以下静态路由:

{
    // 首页
    path: '/',
    name: '/',
    component: () => import('/@/views/frontend/index.vue'),
    meta: {
        title: pageTitle('home'),
    },
},
{
    // 社区首页
    path: '/ask',
    name: 'ask',
    component: () => import('/@/views/frontend/ask.vue'),
    meta: {
        title: pageTitle('ask'),
    },
},

用户访问的URL为:ask.buildadmin.com,获取到前缀ask,修改原有的静态路由/ask改为/即可。

实现

静态路由

Nuxt工程中,静态路由是由Nuxt自动分析目录结构并注册的,但是它为我们提供了app/router.options.ts

// app/router.options.ts 文件,没有请自行建立
import type { RouterOptions } from '@nuxt/schema'

// https://router.vuejs.org/api/interfaces/routeroptions.html
export default <RouterOptions>{
    routes: (routes) => {
        console.log(routes)
    }
}

这样就拿到我们需要修改的静态路由数据了~

当前域名

我们还需要拿到当前用户访问的域名。Nuxt中,用户的请求是先到node的渲染服务器上,我们需要在此时就拿到静态路由,并完成修改,这样才能保证服务端渲染和前台激活的一致,好在,这也不难。

// 从nuxt实例内取到ssr上下文数据
// 域名是不带协议,同时有端口号的,所以我们直接以:号切割,并只取域名部分
const { ssrContext } = useNuxtApp()
const domain = ssrContext!.event.node.req.headers.host!.split(':')[0]

这种方式可以正确取到用户访问的域名,但是页面被发送给用户浏览器激活期间,值会发生改变(或丢失),所以,我们需要使用一种能够在服务端渲染后,前端激活期间能够保留变量值的方式来存储域名信息,以免到了前端,用户还是自动跳转到原本的/路由,你想到了什么?

// 建立 stores/subDomain.ts 文件

// 用于保存完整域名
export const useNodeReqHost = () => useState<string>('nodeReqHost', () => '')

// 用于保存匹配到的子域名部分
export const useSubDomain = () => useState<string>('subDomain', () => '')
修改路由
// 回到之前建立的 app/router.options.ts 文件
import { cloneDeep } from 'lodash-es'
import type { RouterOptions } from '@nuxt/schema'
import { RouteRecordRaw } from 'vue-router'

// 定义一个可将只读属性去掉的 Writeable
type Writeable<T> = { -readonly [P in keyof T]: T[P] }

/**
 * 修改路由 path,建议先看本函数之下的实现
 * 
 * /ask => /
 * /ask/info => /info
 * 
 */
const editPath = (routes: RouteRecordRaw[], subDomainPath: string) => {
    for (const key in routes) {
        if (routes[key].path.includes(subDomainPath)) {
            if (routes[key].path === subDomainPath) {
                routes[key].path = '/'
                routes[key].name = '/'
            } else {
                routes[key].path = routes[key].path.replace(subDomainPath + '/', '/')
            }
        }

        if (routes[key].children?.length) {
            routes[key].children = editPath(routes[key].children!, subDomainPath)
        }
    }
    return routes
}

export default <RouterOptions>{
    routes: (routes) => {

        // routes 是只读的,通过 Writeable 将其改为可写,并且进行深克隆,避免影响到原始数据(渲染->激活过程的数据可能受影响)
        let routesTemp = cloneDeep(routes as Writeable<RouteRecordRaw[]>)

        // 记录当前域名 - 只服务端记录,客户端直接访问就行
        const nodeReqHost = useNodeReqHost()
        if (process.server) {
            const { ssrContext } = useNuxtApp()
            nodeReqHost.value = ssrContext!.event.node.req.headers.host!.split(':')[0]
        }

        // 根据子域名完成路由的修改
        if (nodeReqHost.value == 'ask.buildadmin.com') {
            // 修改原本的首页路由
            const oldIndex = getArrayKey(routesTemp, 'path', '/')
            routesTemp[oldIndex].path = '/home'
            routesTemp[oldIndex].name = '/home'

            // 修改 /ask 路由为首页
            routesTemp = editPath(routesTemp, '/ask')

            // 记录下当前已经通过子域名访问
            const subDomain = useSubDomain()
            subDomain.value = 'ask'
        }

        return routesTemp
    }
}

至此,通过ask.buildadmin.com访问,已经可以自动将/ask作为首页了~

已采纳
ˉ 随心所欲〃
ˉ 随心所欲〃
这家伙很懒,什么也没写~
1年前

点赞学习了

2个回答默认排序 投票数排序
YANG001
YANG001
这家伙很懒,什么也没写~
1年前

消灭0回答~

elwlb
elwlb
这家伙很懒,什么也没写~
5月前

这很实用

请先登录
1
1
2
3