vue3+ts+vite项目对api请求进行高度封装(使用到的热更新和注解等奇葩技巧,故弄玄虚)

chat.ts:

import { BaseService, Service } from '../../../base'

@Service({ namespace: 'chat', url: 'learn', prefix: 'pre' })
class chat extends BaseService {
  handleLoginOut(data: any) {
    console.log(1)
    return this.request({
      url: 'http://localhost:85/api/light/user/logout',
      method: 'post',
      data
    })
  }
}

export default chat

base.ts:


import axios from 'axios'

const request = (option: any) => {
  return axios(option)
}

function Service(value: { namespace?: string; url?: string; prefix?: string }) {
  return function (target: any) {
    target.prototype.namespace = value.namespace

    if (value.url) {
      target.prototype.url = value.url
    }

    if (value.prefix) {
      target.prototype.prefix = value.prefix
    }
  }
}

class BaseService {
  constructor(
    options = {} as {
      namespace?: string
      url?: string
      prefix?: string
    }
  ) {
    if (options?.namespace) {
      // @ts-ignore
      this.namespace = options.namespace
      // @ts-ignore
      this.url = options.url
      // @ts-ignore
      this.prefix = options.prefix
    }
  }

  request(options: any = {}) {
    return request(options)
  }
}

export {
  BaseService,
  Service,
}

config.ts:

import { isFunction } from 'lodash-es'
import { BaseService } from './base'

const files = import.meta.glob('./modules/**', {
  eager: true
})

let service: any

const init = (app: any) => {
  if (import.meta.hot) {
    let hmrData: any = import.meta.hot?.data.getData?.() || {}
    import.meta.hot.data.getData = () => {
      return hmrData
    }
    const hmr = {
      data: hmrData,

      setData(key: string, value: any) {
        hmrData[key] = value
      },

      getData(key: string, defaultValue?: any) {
        if (defaultValue !== undefined && !hmrData[key]) {
          this.setData(key, defaultValue)
        }
        return hmrData[key]
      }
    }

    const list = hmr.getData('modules', [])

    const module: any = {
      list,
      req: Promise.resolve(),
      dirs: [],
      get(name: string) {
        return this.list.find((e: any) => e.name === name)
      },
      add(data: any): void {
        this.list.push(data)
      },
      async wait(): Promise<void> {
        await this.req
      }
    }

    for (const key in files) {
      const [, , name] = key.split('/')
      console.log(name) //permision

      // @ts-ignore
      let FilesItem = files[key]?.default
      let filesItemInstance = new FilesItem()

      const m = module.get(name)

      const d = m || {
        name,
        value: null,
        services: [],
        directives: []
      }
      if (filesItemInstance) {
        d.services?.push({
          path: filesItemInstance.namespace,
          value: filesItemInstance
        })
      }
      if (!m) {
        module.add(d)
      }
    }

    service = hmr.getData('service', {
      request: new BaseService().request
    })

    function createModule(app: any) {
      const list = module.list.map((e: any) => {
        const d = isFunction(e.value) ? e.value(app) : e.value

        if (d) {
          Object.assign(e, d)
        }

        return e
      })

      return {
        list,
        async eventLoop() {
          const events: any = {}

          for (let i = 0; i < list.length; i++) {
            if (list[i].onLoad) {
              Object.assign(events, await list[i]?.onLoad?.(events))
            }
          }
        }
      }
    }

    const { list: listLight } = createModule(app)

    function basename(path: string): string {
      let index = path.lastIndexOf('/')
      index = index > -1 ? index : path.lastIndexOf('\\')
      if (index < 0) {
        return path
      }
      return path.substring(index + 1)
    }

    function path2Obj(list: any[]) {
      const data: any = {}

      list.forEach(({ path, value }) => {
        const arr: string[] = path.split('/')
        const parents = arr.slice(0, arr.length - 1)
        const name = basename(path).replace('.ts', '')

        let curr = data

        parents.forEach((k) => {
          if (!curr[k]) {
            curr[k] = {}
          }

          curr = curr[k]
        })

        curr[name] = value
      })

      return data
    }

    function deepMerge(a: any, b: any) {
      let k
      for (k in b) {
        a[k] = a[k] && a[k].toString() === '[object Object]' ? deepMerge(a[k], b[k]) : (a[k] = b[k])
      }
      return a
    }

    function useService(modules: any[]) {
      const s = path2Obj(
        modules.reduce((a, b) => {
          return a.concat(...((b.services as any[]) || []))
        }, [])
      )

      const list: any[] = []

      function deep(d: any) {
        if (typeof d !== 'object') {
          return
        }
        if (d.namespace) {
          const api = Array.from(new Set([...Object.getOwnPropertyNames(d.constructor.prototype)]))
            .filter((e) => !['constructor', 'namespace'].includes(e))
            .map((e) => {
              return {
                path: e
              }
            })

          list.push({
            api,
            module: d.namespace.split('/')[0],
            name: d.constructor.name,
            prefix: d.namespace
          })
        } else {
          for (const i in d) {
            deep(d[i])
          }
        }
      }
      deep(s)

      console.log(s)
      deepMerge(service, s)
    }

    useService(listLight)
  }
}

const useSystem = () => {
  return {
    service
  }
}

export { init, useSystem }

index.vue:

<script setup>
import { onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useSystem } from './config'
import './index.css'

const router = useRouter()
const route = useRoute()

const handleJumpPath = (path) => {
  router.push(path)
}

onMounted(() => {
  const { service } =  useSystem()
  service.chat.handleLoginOut()
})
</script>

<template>
  <div>
    <button @click="() => handleJumpPath('/admin')">admin</button>
  </div>
</template>

main.ts:

import './assets/main.css'

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import WujieVue from 'wujie-vue3'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

import App from './App.vue'
import router from './router'
import { init } from './views/index/config'

const app = createApp(App)

app.use(createPinia())
app.use(router)
app.use(WujieVue)
app.use(ElementPlus)
init(app)

app.mount('#app')

 tsconfig.app.json:

{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
  "exclude": ["src/**/__tests__/*"],
  "compilerOptions": {
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "experimentalDecorators": true,
    "composite": true,
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",

    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

本人并不支持这种封装方式,仅仅是整理一下

人工智能学习网站

https://chat.xutongbao.top

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐同保

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值