Vue3+TS设计模式:5个真实场景让你代码更优雅

大家好,我是大华。今天分享我在前端项目中怎么用设计模式解决实际问题的。都是简单实用的例子可以进行参考!

1. 单例模式:全局唯一的"管家"

场景:用户登录状态

用户登录后,整个网站都要知道这个状态。

typescript 复制代码
// stores/user.ts
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', () => {
  // 用户信息
  const userInfo = ref({
    id: 0,
    name: '',
    email: '',
    isLoggedIn: false
  })
  
  // 登录
  const login = (name: string, email: string) => {
    userInfo.value = {
      id: Date.now(),
      name,
      email,
      isLoggedIn: true
    }
    // 保存到本地
    localStorage.setItem('user', JSON.stringify(userInfo.value))
  }
  
  // 退出
  const logout = () => {
    userInfo.value = {
      id: 0,
      name: '',
      email: '',
      isLoggedIn: false
    }
    localStorage.removeItem('user')
  }
  
  // 页面刷新时恢复状态
  const init = () => {
    const saved = localStorage.getItem('user')
    if (saved) {
      userInfo.value = JSON.parse(saved)
    }
  }
  
  init()
  
  return {
    userInfo,
    login,
    logout
  }
})
vue 复制代码
<!-- 任意组件中使用 -->
<script setup lang="ts">
import { useUserStore } from '@/stores/user'

const userStore = useUserStore()

// 登录
const handleLogin = () => {
  userStore.login('张三', 'zhangsan@email.com')
}

// 退出
const handleLogout = () => {
  userStore.logout()
}
</script>

更多使用场景

  • 用户登录状态
  • 购物车
  • 主题设置
  • 全局配置

2. 工厂模式:动态创建组件

场景:不同类型的弹窗

网站有多种弹窗:提示框、确认框、表单框...

typescript 复制代码
// components/Alert.vue
<template>
  <div class="alert">
    <h3>{{ title }}</h3>
    <p>{{ message }}</p>
    <button @click="onClose">确定</button>
  </div>
</template>

<script setup lang="ts">
defineProps<{
  title: string
  message: string
  onClose: () => void
}>()
</script>
typescript 复制代码
// components/Confirm.vue
<template>
  <div class="confirm">
    <h3>{{ title }}</h3>
    <p>{{ message }}</p>
    <button @click="onCancel">取消</button>
    <button @click="onConfirm">确定</button>
  </div>
</template>

<script setup lang="ts">
defineProps<{
  title: string
  message: string
  onConfirm: () => void
  onCancel: () => void
}>()
</script>
typescript 复制代码
// factories/modal-factory.ts
import { shallowRef } from 'vue'
import Alert from '@/components/Alert.vue'
import Confirm from '@/components/Confirm.vue'

// 弹窗类型
type ModalType = 'alert' | 'confirm'

// 创建弹窗
export function createModal(type: ModalType) {
  const ModalComponent = shallowRef()
  
  if (type === 'alert') {
    ModalComponent.value = Alert
  } else if (type === 'confirm') {
    ModalComponent.value = Confirm
  }
  
  return {
    ModalComponent
  }
}
vue 复制代码
<!-- 使用弹窗 -->
<script setup lang="ts">
import { createModal } from '@/factories/modal-factory'

const { ModalComponent } = createModal('alert')

const showAlert = () => {
  // 显示提示框
}
</script>

更多使用场景

  • 不同类型的弹窗
  • 动态表单
  • 表格列渲染
  • 消息通知

3. 观察者模式:组件间通信

场景:购物车数量同步

商品页加购,顶部购物车图标要显示数量。

typescript 复制代码
// utils/event-bus.ts
// 事件中心
const eventBus = {
  events: {}
}

// 发布消息
export function emit(event: string, data: any) {
  if (eventBus.events[event]) {
    eventBus.events[event].forEach(callback => callback(data))
  }
}

// 订阅消息
export function on(event: string, callback: Function) {
  if (!eventBus.events[event]) {
    eventBus.events[event] = []
  }
  eventBus.events[event].push(callback)
}
vue 复制代码
<!-- 商品详情页 -->
<script setup lang="ts">
import { on } from '@/utils/event-bus'

const addToCart = () => {
  // 加购逻辑...
  
  // 通知购物车更新
  emit('cart-updated', { count: 5 })
}
</script>
vue 复制代码
<!-- 头部导航 -->
<script setup lang="ts">
import { ref } from 'vue'
import { on } from '@/utils/event-bus'

const cartCount = ref(0)

// 监听购物车更新
on('cart-updated', (data) => {
  cartCount.value = data.count
})
</script>

更多使用场景

  • 跨组件通信
  • 状态同步
  • 用户行为通知
  • 数据更新

4. 策略模式:灵活的计算方式

实战场景:不同折扣计算

普通用户9折,VIP用户8折,SVIP用户7折。

typescript 复制代码
// utils/discount.ts
// 折扣策略
const discountStrategies = {
  // 普通用户 - 9折
  normal: (price: number) => price * 0.9,
  // VIP用户 - 8折
  vip: (price: number) => price * 0.8,
  // SVIP用户 - 7折
  svip: (price: number) => price * 0.7
}

// 计算折扣后价格
export function calculatePrice(price: number, level: string) {
  const strategy = discountStrategies[level]
  if (strategy) {
    return strategy(price)
  }
  return price // 默认不打折
}
vue 复制代码
<!-- 商品页 -->
<script setup lang="ts">
import { calculatePrice } from '@/utils/discount'

const originalPrice = 100
const userLevel = 'vip'

// 计算最终价格
const finalPrice = calculatePrice(originalPrice, userLevel)
</script>

更多使用场景

  • 不同的计算方式
  • 多种验证规则
  • 排序/过滤
  • 支付方式

5. 代理模式:统一处理请求

实战场景:API请求处理

每个接口都要加token,处理错误。

typescript 复制代码
// utils/api.ts
// 创建API代理
export const api = new Proxy({}, {
  get(target, prop) {
    return async (data: any) => {
      try {
        // 显示loading
        showLoading()
        
        const response = await fetch(`/api/${prop}`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            // 自动加token
            'Authorization': `Bearer ${getToken()}`
          },
          body: JSON.stringify(data)
        })
        
        const result = await response.json()
        
        if (result.code !== 0) {
          throw new Error(result.message)
        }
        
        return result.data
      } catch (error) {
        // 统一错误处理
        handleError(error)
        throw error
      } finally {
        // 隐藏loading
        hideLoading()
      }
    }
  }
})

// 获取token
function getToken() {
  return localStorage.getItem('token') || ''
}

// 显示loading
function showLoading() {
  console.log('loading...')
}

// 隐藏loading
function hideLoading() {
  console.log('hide loading')
}

// 错误处理
function handleError(error: any) {
  alert(error.message)
}
vue 复制代码
<!-- 使用API -->
<script setup lang="ts">
import { api } from '@/utils/api'

// 获取用户信息
const getUser = async () => {
  try {
    const user = await api.getUser({ id: 123 })
    console.log(user)
  } catch (error) {
    // 错误已经统一处理了
  }
}
</script>

更多使用场景

  • 统一API处理
  • 权限控制
  • 数据缓存
  • 日志记录

总结

设计模式很简单:

  • 单例:全局唯一,比如用户状态
  • 工厂:动态创建,比如不同弹窗
  • 观察者:消息通知,比如状态同步
  • 策略:多种方式,比如不同折扣
  • 代理:统一处理,比如API请求

先解决问题,再想模式。代码简单最重要!

我是大华,专注分享Java、Vue前后端的实战笔记。关注我,少走弯路,一起进步!

相关推荐
青红光硫化黑7 分钟前
React-native之组件
javascript·react native·react.js
菠萝+冰9 分钟前
在 React 中,父子组件之间的通信(传参和传方法)
前端·javascript·react.js
庚云12 分钟前
一套代码如何同时适配移动端和pc端
前端
Jinuss13 分钟前
Vue3源码reactivity响应式篇Reflect和Proxy详解
前端·vue3
海天胜景21 分钟前
vue3 el-select 默认选中第一个
前端·javascript·vue.js
小小怪下士_---_41 分钟前
uniapp开发微信小程序自定义导航栏
前端·vue.js·微信小程序·小程序·uni-app
前端W42 分钟前
腾讯地图组件使用说明文档
前端
页面魔术44 分钟前
无虚拟dom怎么又流行起来了?
前端·javascript·vue.js
胡gh1 小时前
如何聊懒加载,只说个懒可不行
前端·react.js·面试