Vue3组合式API实战:组件逻辑复用与响应式系统原理剖析

前端开发领域,Vue3组合式API(Composition API)重构了组件逻辑组织方式。相比Options API的data/methods/computed分散结构,组合式API允许按功能关注点聚合代码,配合TypeScript类型推断提升类型安全。本文从响应式原理到组件库设计,拆解Vue3生态下的工程化实践。

组合式API核心机制:ref与reactive响应式原理

Vue3响应式系统基于Proxy实现。reactive对对象做深度代理,ref对基本类型值通过.value访问器追踪依赖。理解二者的差异是正确使用组合式API的前提。

import { ref, reactive, watchEffect, toRefs } from 'vue'

// ref适用于基本类型和需要整体替换的值
const count = ref(0)
count.value++

// reactive适用于对象/数组,无需.value
const state = reactive({
  user: { name: '张三', age: 25 },
  list: []
})

// reactive的局限:解构会失去响应性
const { user } = state  // user不再是响应式
// 解决方案:toRefs
const { user: userRef } = toRefs(state)  // userRef保持响应式

watchEffect自动追踪回调函数内的响应式依赖,watch显式指定侦听源。选择依据是是否需要旧值访问和惰性执行:

// watchEffect:立即执行,自动追踪依赖
watchEffect(() => {
  console.log(`count: ${count.value}, name: ${state.user.name}`)
})

// watch:惰性执行,可获取旧值
watch(
  () => state.user.age,
  (newAge, oldAge) => {
    console.log(`age changed from ${oldAge} to ${newAge}`)
  },
  { deep: true, immediate: false }
)

computed基于响应式依赖缓存计算结果,依赖不变时返回缓存值。TypeScript实战中需标注返回类型:

import { ref, computed } from 'vue'

const price = ref(99.5)
const quantity = ref(2)

const total = computed<number>(() => {
  return Number((price.value * quantity.value).toFixed(2))
})

自定义组合式函数提取与复用

组合式函数(Composables)是Vue3逻辑复用的核心模式。将状态和操作逻辑封装为以use开头的函数,在多个组件间共享。

通用分页逻辑封装:

// composables/usePagination.ts
import { ref, computed } from 'vue'

interface PaginationOptions {
  pageSize?: number
  initialPage?: number
}

export function usePagination(totalItems: () => number, options: PaginationOptions = {}) {
  const currentPage = ref(options.initialPage ?? 1)
  const pageSize = ref(options.pageSize ?? 10)

  const totalPages = computed(() =>
    Math.ceil(totalItems() / pageSize.value)
  )

  const paginatedRange = computed(() => {
    const start = (currentPage.value - 1) * pageSize.value
    const end = start + pageSize.value
    return { start, end }
  })

  function goToPage(page: number) {
    if (page >= 1 && page <= totalPages.value) {
      currentPage.value = page
    }
  }

  function nextPage() { goToPage(currentPage.value + 1) }
  function prevPage() { goToPage(currentPage.value - 1) }

  return {
    currentPage: computed(() => currentPage.value),
    pageSize: computed(() => pageSize.value),
    totalPages,
    paginatedRange,
    goToPage,
    nextPage,
    prevPage
  }
}

组件中使用:

<script setup lang="ts">
import { ref, computed } from 'vue'
import { usePagination } from '@/composables/usePagination'

const allProducts = ref<Product[]>([])
const { currentPage, totalPages, paginatedRange, goToPage } = usePagination(
  () => allProducts.value.length,
  { pageSize: 20 }
)

const visibleProducts = computed(() =>
  allProducts.value.slice(paginatedRange.value.start, paginatedRange.value.end)
)
</script>

组件设计模式与Props校验

组件库设计需要统一的组件规范。defineProps配合TypeScript接口提供编译时类型检查和运行时校验。

// FormInput.vue
<script setup lang="ts">
import { ref, computed } from 'vue'

interface FormInputProps {
  modelValue: string
  label: string
  type?: 'text' | 'password' | 'email' | 'number'
  placeholder?: string
  error?: string
  disabled?: boolean
  rules?: Array<(value: string) => string | true>
}

const props = withDefaults(defineProps<FormInputProps>(), {
  type: 'text',
  placeholder: '',
  error: '',
  disabled: false,
  rules: () => []
})

const emit = defineEmits<{
  'update:modelValue': [value: string]
  blur: [event: FocusEvent]
}>()

const inputValue = computed({
  get: () => props.modelValue,
  set: (val: string) => emit('update:modelValue', val)
})

const validationError = ref('')
function validate() {
  for (const rule of props.rules) {
    const result = rule(props.modelValue)
    if (result !== true) {
      validationError.value = result
      return false
    }
  }
  validationError.value = ''
  return true
}

defineExpose({ validate })
</script>

<template>
  <div class="form-field">
    <label :for="label">{{ label }}</label>
    <input
      :id="label"
      v-model="inputValue"
      :type="type"
      :placeholder="placeholder"
      :disabled="disabled"
      :class="{ error: error || validationError }"
      @blur="$emit('blur', $event)"
    />
    <span v-if="validationError" class="error-msg">{{ validationError }}</span>
    <span v-else-if="error" class="error-msg">{{ error }}</span>
  </div>
</template>

defineExpose暴露validate方法给父组件调用,实现表单整体校验时统一触发子组件校验。

TypeScript类型安全与工程化配置

前端工程化配置中,Vite + TypeScript + ESLint + Prettier是Vue3项目标准工具链。vue-tsc提供模板内的类型检查。

tsconfig关键配置:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "lib": ["ESNext", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

响应式布局方面,Vue3配合Tailwind CSS或Unocss可实现高效的样式工程化。组件库设计时建议采用CSS变量定义设计令牌(Design Tokens),实现主题切换:

// design-tokens.ts
export const tokens = {
  colors: {
    primary: 'var(--color-primary, #3b82f6)',
    danger: 'var(--color-danger, #ef4444)',
    success: 'var(--color-success, #22c55e)'
  },
  spacing: {
    xs: '4px',
    sm: '8px',
    md: '16px',
    lg: '24px'
  },
  radius: {
    sm: '4px',
    md: '8px',
    lg: '12px'
  }
} as const

组合式API配合TypeScript类型系统,使Vue3项目的代码质量和可维护性达到企业级标准。跨端小程序开发场景下,相同的组合式函数逻辑可直接复用,仅需替换渲染层适配,这体现了Vue3生态架构设计的灵活性。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3-zu-he-shi-api-shi-zhan-zu-jian-luo-ji-fu-yong-yu-xiang/

(0)
小编小编
上一篇 15小时前
下一篇 15小时前

相关推荐