首页 / 速查手册 / 在线

Vue速查手册

Vue3指令/组合式API/组件通信速查

速查手册 · 1 次 · 2026-10-04 · 分享 · 全屏

🟢 模板与 SFC

单文件组件结构

Vue 3 推荐 <script setup>,顶层声明可直接在模板使用。

<script setup>
import { ref } from 'vue'

const msg = ref('Hello Vue 3')
const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>
  <button @click="count++">+1 {{ count }}</button>
</template>

<style scoped>
h1 { color: #42b883; }
</style>
插值 / 属性 / 事件绑定

v-bind(:)绑属性,v-on(@)绑事件,{{ }} 插文本。

<template>
  <p>{{ name }} - {{ age + 1 }}</p>
  <a v-bind:href="url">完整写法</a>
  <a :href="url">缩写写法</a>
  <button v-on:click="say">完整写法</button>
  <button @click="say">缩写写法</button>
  <button @click="count++">内联语句 {{ count }}</button>
</template>
v-html 与属性透传

v-html 渲染原始 HTML,注意 XSS;$attrs 接收未声明透传属性。

<div>{{ raw }}</div>
<!-- 原样输出 <b>hi</b> -->
<div v-html="raw"></div>
<!-- 渲染为加粗 hi(内容必须可信) -->

<button :disabled="isLoading" :id="`btn-${type}`">提交</button>
<input :readonly="readonly" :required="true">
class 绑定

支持对象语法与数组语法,可与静态 class 共存。

<div class="base" :class="{ active: isActive, error: hasErr }"></div>

<div :class="[isActive ? 'active' : '', 'size-' + size]"></div>

<div :class="[{ active: isActive }, cls]"></div>

<!-- 组件上使用会合并到根元素 -->
<MyCard class="rounded" />
style 绑定

对象键名驼峰或短横线均可;浏览器会自动加前缀。

<div :style="{ color: textColor, fontSize: size + 'px' }"></div>

<div :style="{ '--theme': themeColor }">CSS 变量</div>

<div :style="[baseStyle, extraStyle]">数组合并</div>

🔀 条件与列表

v-if / v-else-if / v-else

真正的增删 DOM;可配合 <template> 分组多元素。

<p v-if="type === 'A'">甲</p>
<p v-else-if="type === 'B'">乙</p>
<p v-else>其他</p>

<template v-if="isLogin">
  <h2>欢迎回来</h2>
  <p>退出入口</p>
</template>
v-show 与 v-if 区别

v-show 只切 CSS display,频繁切换用它;v-if 有更高初始切换成本。

<div v-show="visible">始终在 DOM 中,仅 display:none</div>

<!-- 频繁显示隐藏:v-show -->
<Modal v-show="showModal" />

<!-- 很少变化、希望惰性渲染:v-if -->
<HeavyChart v-if="tab === 'chart'" />
v-for 列表渲染

始终绑定唯一 key;可遍历数组、对象、数字、字符串。

<li v-for="(item, index) in list" :key="item.id">
  {{ index }} - {{ item.name }}
</li>

<li v-for="value in 5" :key="value">{{ value }}</li>

<tr v-for="(val, key) in user" :key="key">
  {{ key }}: {{ val }}
</tr>

<!-- v-if 与 v-for 不要同时放同一元素,用 <template> 包裹 -->
<template v-for="u in users" :key="u.id">
  <UserRow v-if="u.active" :user="u" />
</template>

📊 响应式 API

ref 基本类型与模板自动解包

JS 中读写要 .value,模板里自动解包无需 .value。

<script setup>
import { ref } from 'vue'

const count = ref(0)
const user = ref({ name: 'Tom' })

function inc() {
  count.value++
  user.value.name = 'Amy'
}
</script>

<template>
  <button @click="inc">{{ count }} {{ user.name }}</button>
</template>
reactive 对象

Proxy 代理对象,直接改属性即可;不能整体替换丢失响应式。

import { reactive } from 'vue'

const state = reactive({
  list: [],
  loading: false,
  filter: { kw: '' }
})

state.loading = true
state.list.push({ id: 1 })
state.filter.kw = 'vue'

// ❌ 不要整体替换(会断开与原代理的联系)
// state = { list: [], loading: false }
// ✅ 逐属性替换
Object.assign(state, { list: [], loading: false })
toRefs / toRef 解构保持响应式

reactive 直接解构会失去响应式,要用 toRefs 包一层。

import { reactive, toRefs, toRef } from 'vue'

const state = reactive({ name: 'Tom', age: 18 })

const { name, age } = toRefs(state)
name.value = 'Amy'        // state.name 同步变化

const name2 = toRef(state, 'name')

function useUser() {
  const s = reactive({ loading: false, data: null })
  return { ...toRefs(s) }   // return 时保持响应式
}

🧮 computed 与 watch

computed 计算属性

有缓存,依赖不变不重算;可写计算属性需 get/set。

import { ref, computed } from 'vue'

const items = ref([1, 2, 3, 4])
const keyword = ref('')

const evens = computed(() => items.value.filter(x => x % 2 === 0))
const total = computed(() => items.value.reduce((a, b) => a + b, 0))

const fullName = computed({
  get: () => firstName.value + lastName.value,
  set: (v) => { [firstName.value, lastName.value] = v.split(' ') }
})
watch 侦听器

显式指定数据源;immediate 立即执行,deep 深度侦听。

const id = ref(1)
watch(id, (newVal, oldVal) => {
  console.log('变化', oldVal, '->', newVal)
  fetchUser(newVal)
}, { immediate: true })

watch([id, keyword], ([newId, newKw]) => {
  console.log(newId, newKw)
})

watch(() => state.user.id, (n) => {})
watch(state, (n) => {}, { deep: true })
watchEffect 自动收集依赖

立即执行一次,内部用到谁变了就重跑;返回停止函数。

import { watchEffect } from 'vue'

const stop = watchEffect(async (onCleanup) => {
  const controller = new AbortController()
  onCleanup(() => controller.abort())
  const data = await fetchUser(id.value, controller.signal)
  user.value = data
})

// 手动停止
stop()

// watch 是懒执行,watchEffect 立即执行;
// watch 能拿到新旧值,watchEffect 不能

📦 组件通信

defineProps 接收参数

运行时声明可指定类型、默认值、校验;对象默认值必须用工厂函数。

<script setup>
const props = defineProps({
  title: String,
  list: {
    type: Array,
    required: true,
    default: () => []
  },
  size: { type: String, default: 'medium', validator: v => ['small','medium'].includes(v) }
})
</script>

<template>
  <h2>{{ title }}</h2>
  <li v-for="i in list" :key="i">{{ i }}</li>
</template>
类型式声明(TS)

在 <script setup lang="ts"> 中直接用泛型描述 props/emits。

<script setup lang="ts">
interface User { id: number; name: string }

const props = withDefaults(
  defineProps<{
    title?: string
    users: User[]
  }>(),
  { title: '默认标题' }
)

const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'delete', id: number): void
}>()
</script>
defineEmits 抛事件

子组件不直接改 props,通过事件通知父组件。

<script setup>
const emit = defineEmits(['confirm', 'cancel'])

function ok() {
  emit('confirm', { id: 1, ts: Date.now() })
}
</script>

<template>
  <button @click="ok">确定</button>
  <button @click="emit('cancel')">取消</button>
</template>

<!-- 父组件 -->
<Dialog @confirm="onConfirm" @cancel="onCancel" />
组件 v-model(defineModel)

Vue 3.4+ 用 defineModel 实现双向绑定最简洁;老写法是 modelValue/update。

<!-- 子组件 MyInput.vue -->
<script setup>
const model = defineModel()
</script>
<template>
  <input :value="model" @input="model = $event.target.value" />
</template>

<!-- 父组件 -->
<MyInput v-model="keyword" />

<!-- 具名 v-model -->
const first = defineModel('first')
<MyInput v-model:first="fn" v-model:last="ln" />
provide / inject 跨层传值

祖先 provide,任意深层后代 inject;推荐传响应式对象并声明默认值。

// 祖先
import { provide, ref, readonly } from 'vue'
const theme = ref('dark')
provide('theme', readonly(theme))
provide('setTheme', (v) => { theme.value = v })

// 后代
import { inject } from 'vue'
const theme = inject('theme', 'light')
const setTheme = inject('setTheme', () => {})

⏳ 生命周期

组合式生命周期钩子

setup 中用 onXxx 注册;没有 beforeCreate/created(setup 即其时点)。

import {
  onBeforeMount, onMounted,
  onBeforeUpdate, onUpdated,
  onBeforeUnmount, onUnmounted
} from 'vue'

onBeforeMount(() => console.log('挂载前'))
onMounted(() => console.log('已挂载,可访问 DOM'))
onBeforeUpdate(() => console.log('更新前'))
onUpdated(() => console.log('DOM 更新后'))
onBeforeUnmount(() => console.log('卸载前'))
onUnmounted(() => console.log('已卸载,清定时器'))
典型用法:定时器与监听解绑

在 onMounted 启动、onUnmounted 清理,防止内存泄漏。

<script setup>
import { onMounted, onUnmounted } from 'vue'

let timer = null
function onResize() { console.log(window.innerWidth) }

onMounted(() => {
  timer = setInterval(() => console.log('tick'), 1000)
  window.addEventListener('resize', onResize)
})

onUnmounted(() => {
  clearInterval(timer)
  window.removeEventListener('resize', onResize)
})
</script>

🪝 组合式函数与模板 ref

useXxx 组合式函数

把响应式状态与逻辑封装到 use 开头的函数,跨组件复用。

// composables/useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'

export function useMouse() {
  const x = ref(0)
  const y = ref(0)
  const move = (e) => { x.value = e.clientX; y.value = e.clientY }
  onMounted(() => window.addEventListener('mousemove', move))
  onUnmounted(() => window.removeEventListener('mousemove', move))
  return { x, y }
}

// 组件中
const { x, y } = useMouse()
模板 ref 与 defineExpose

同名 ref 挂载 DOM;<script setup> 默认封闭,需 expose 才能被父组件调用。

<!-- 子组件 -->
<script setup>
import { ref } from 'vue'
const inputRef = ref(null)
function focus() { inputRef.value.focus() }
defineExpose({ focus })
</script>
<template><input ref="inputRef" /></template>

<!-- 父组件 -->
<script setup>
const childRef = ref(null)
const onBtn = () => childRef.value.focus()
</script>
<template>
  <MyInput ref="childRef" />
  <button @click="onBtn">让子组件聚焦</button>
</template>

✨ 常用进阶

事件 / 按键 / v-model 修饰符

链式书写,减少手写 e.preventDefault 等样板代码。

<a @click.stop="onClick">阻止冒泡</a>
<form @submit.prevent="onSubmit">阻止默认提交</form>
<button @click.once="init">只触发一次</button>
<div @click.self="onMask">仅点自身才触发</div>

<input @keyup.enter="search" />
<input @keyup.esc="close" />
<input v-model.trim="name" />
<input v-model.number="age" />
<input v-model.lazy="msg" />
nextTick 等 DOM 更新

改完数据后 DOM 是异步刷新的,nextTick 后再测量才准确。

import { nextTick } from 'vue'

async function addAndScroll() {
  list.value.push(newItem)
  await nextTick()          // 等待新 li 渲染完成
  lastRef.value.scrollIntoView({ behavior: 'smooth' })
}

nextTick(() => {
  console.log('DOM 已更新')
})
Teleport 传送与动态组件

Teleport 把 DOM 渲染到 body 等容器(弹窗常用);component 动态切换。

<Teleport to="body" :disabled="false">
  <div class="modal-mask">
    <div class="modal">弹窗内容</div>
  </div>
</Teleport>

<component :is="currentTab === 'a' ? TabA : TabB" />

<KeepAlive>
  <component :is="comp" />
</KeepAlive>

📖 使用说明

全程在浏览器本地运行。

操作步骤:

  1. 搜索或浏览分类条目;
  2. 查看语法与示例代码;
  3. 点击复制代码块。

💬 用户评论 (0)

还没有评论,快来抢沙发!

请添加微信联系我