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前后端的实战笔记。关注我,少走弯路,一起进步!

相关推荐
程序员Sunday14 小时前
JavaScript 事件循环面试题,宏任务与微任务怎么执行|Sunday面试指南
开发语言·javascript·面试·校招·事件循环·程序员sunday
Setsuna_F_Seiei14 小时前
前端转型 Agent 开发 06 之 Agent Memory 记忆系统(让 Agent 更智能,更懂你)
前端·agent·ai编程
志尊宝14 小时前
Vue3 零基础每日笔记(073):keep-alive 页面缓存实战——列表页返回不丢状态
vue.js·笔记·缓存·#vue #前端 #前端开发·#javascript·#vue.js
zhangzeyuaaa15 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
默_笙15 小时前
🌊 向量库和 ES 都查不出"关系",我只好给奶茶建了一张人脉网
前端·javascript
优选资讯15 小时前
标签打印软件怎么对接 Excel 数据
前端·excel
思无邪6616 小时前
用 AI 做 JS 逆向:从抓包到复现的完整方法论
开发语言·javascript·人工智能
一个风轻云淡16 小时前
GCC 和 GDB命令简单解读
java·linux·前端
u01110267517 小时前
图片处理接口如何统一错误信息 前端提示与后端错误码设计
前端·状态模式
福兮说18 小时前
浏览器里把 iPhone 的 HEIC 转成 JPG:Chrome 解不开、转完大了七成、拍摄时间全丢,六个坑实测
前端·javascript·图像处理·chrome·ios·iphone·heic