Skip to content

Vue 核心概念

本文介绍Vue的核心概念,包括组件、Props、data、生命周期、事件处理、条件渲染、列表渲染、表单、插槽、Provide/Inject、组合式API等基础知识点。

什么是Vue?

Vue(Vue.js)是一个用于构建用户界面的渐进式JavaScript框架。它专注于视图层,易于上手,适合开发单页应用(SPA)和复杂前端项目。

Vue的特点

  • 响应式数据绑定:数据变化会自动更新视图。
  • 组件化开发:将UI拆分为独立、可复用的组件。
  • 声明式渲染:使用模板语法声明UI结构。
  • 轻量高效:体积小,性能优异。
  • 易于集成:可逐步采用,灵活性高。

组件

组件是Vue应用的核心构建块。每个组件本质上是一个拥有自己选项的独立Vue实例。

选项式API组件

vue
<template>
  <h1>Hello, {{ name }}</h1>
</template>

<script>
export default {
  props: {
    name: {
      type: String,
      default: 'Guest'
    }
  }
};
</script>

组合式API组件

vue
<template>
  <h1>Hello, {{ name }}</h1>
</template>

<script setup>
import { defineProps } from 'vue';
const props = defineProps({
  name: {
    type: String,
    default: 'Guest'
  }
});
const name = props.name;
</script>

组件组合

vue
<template>
  <div>
    <Welcome name="Alice" />
    <Welcome name="Bob" />
    <Welcome name="Charlie" />
  </div>
</template>

<script setup>
import Welcome from './Welcome.vue';
</script>

模板语法(类似JSX)

Vue使用模板语法声明UI结构,支持插值、指令等。

vue
<template>
  <h1>Hello, {{ name }}!</h1>
  <img :src="avatarUrl" alt="User avatar" />
  <div>
    <h1>Title</h1>
    <p>Paragraph</p>
  </div>
</template>

<script setup>
const name = 'John';
const avatarUrl = 'https://example.com/avatar.png';
</script>

防注入攻击

Vue会自动对插值内容进行HTML转义,防止XSS攻击。

vue
<template>
  <h1>{{ title }}</h1>
</template>

<script setup>
const title = "<script>alert('XSS');</script>";
</script>

Props

Props用于父组件向子组件传递数据,子组件通过props选项声明接收。

vue
<!-- 父组件 -->
<template>
  <Welcome name="Sara" :age="25" :is-admin="true" />
</template>

<!-- 子组件 Welcome.vue -->
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
  name: String,
  age: Number,
  isAdmin: Boolean
});
</script>

<template>
  <div>
    <h1>Hello, {{ props.name }}</h1>
    <p>Age: {{ props.age }}</p>
    <p v-if="props.isAdmin">Admin user</p>
  </div>
</template>

Props的特点

  • Props是单向数据流,子组件不应直接修改props。
  • 可以设置默认值和类型校验。

data(响应式状态)

组件内部的响应式数据通过data选项(选项式API)或ref/reactive(组合式API)声明。

选项式API

vue
<script>
export default {
  data() {
    return {
      count: 0
    };
  },
  methods: {
    increment() {
      this.count++;
    }
  }
};
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

组合式API

vue
<script setup>
import { ref } from 'vue';
const count = ref(0);
const increment = () => count.value++;
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

生命周期

Vue组件有多个生命周期钩子。

vue
<script setup>
import { onMounted, onUpdated, onUnmounted } from 'vue';
onMounted(() => {
  console.log('组件已挂载');
});
onUpdated(() => {
  console.log('组件已更新');
});
onUnmounted(() => {
  console.log('组件已卸载');
});
</script>

选项式API:

js
export default {
  mounted() {
    console.log('组件已挂载');
  },
  updated() {
    console.log('组件已更新');
  },
  unmounted() {
    console.log('组件已卸载');
  }
};

事件处理

Vue事件使用v-on(简写为@)绑定。

vue
<template>
  <button @click="handleClick">Click me</button>
</template>

<script setup>
function handleClick(e) {
  e.preventDefault();
  console.log('Button clicked');
}
</script>

传递参数

vue
<template>
  <button @click="() => handleClick(id)">Delete</button>
</template>

<script setup>
const id = 1;
function handleClick(id) {
  console.log('Delete', id);
}
</script>

条件渲染

使用v-ifv-else-ifv-elsev-show实现条件渲染。

vue
<template>
  <h1 v-if="isLoggedIn">Welcome back!</h1>
  <h1 v-else>Please sign up.</h1>
</template>

<script setup>
const isLoggedIn = true;
</script>

列表渲染

使用v-for渲染列表。

vue
<template>
  <ul>
    <li v-for="number in numbers" :key="number">{{ number }}</li>
  </ul>
</template>

<script setup>
const numbers = [1, 2, 3, 4, 5];
</script>

表单

Vue通过v-model实现表单的双向绑定。

vue
<template>
  <form @submit.prevent="handleSubmit">
    <label>
      Name:
      <input v-model="name" type="text" />
    </label>
    <input type="submit" value="Submit" />
  </form>
</template>

<script setup>
import { ref } from 'vue';
const name = ref('');
function handleSubmit() {
  alert('A name was submitted: ' + name.value);
}
</script>

处理多个输入

vue
<template>
  <form>
    <label>
      Is going:
      <input type="checkbox" v-model="isGoing" />
    </label>
    <br />
    <label>
      Number of guests:
      <input type="number" v-model="numberOfGuests" />
    </label>
  </form>
</template>

<script setup>
import { ref } from 'vue';
const isGoing = ref(true);
const numberOfGuests = ref(2);
</script>

非受控组件(ref获取DOM)

vue
<template>
  <form @submit.prevent="handleSubmit">
    <input type="file" ref="fileInput" />
    <button type="submit">Submit</button>
  </form>
</template>

<script setup>
import { ref } from 'vue';
const fileInput = ref(null);
function handleSubmit() {
  if (fileInput.value && fileInput.value.files.length > 0) {
    alert(`Selected file - ${fileInput.value.files[0].name}`);
  }
}
</script>

插槽(Slots)

插槽用于组件内容分发,实现灵活的组件组合。

vue
<!-- 父组件 -->
<FancyBorder color="blue">
  <h1 class="Dialog-title">Welcome</h1>
  <p class="Dialog-message">Thank you for visiting our spacecraft!</p>
</FancyBorder>

<!-- 子组件 FancyBorder.vue -->
<template>
  <div :class="'FancyBorder FancyBorder-' + color">
    <slot></slot>
  </div>
</template>
<script setup>
const props = defineProps({ color: String });
</script>

具名插槽

vue
<template>
  <BaseLayout>
    <template #header>
      <h1>Header</h1>
    </template>
    <template #default>
      <p>Main content</p>
    </template>
    <template #footer>
      <p>Footer</p>
    </template>
  </BaseLayout>
</template>

Provide/Inject

用于祖先组件向后代组件传递数据,无需逐层props传递。

vue
<!-- 祖先组件 -->
<script setup>
import { provide } from 'vue';
provide('theme', 'dark');
</script>

<!-- 后代组件 -->
<script setup>
import { inject } from 'vue';
const theme = inject('theme', 'light');
</script>

组合式API(Composition API)

组合式API通过setup函数、refreactivecomputedwatch等实现更灵活的逻辑复用。

vue
<script setup>
import { ref, computed, watch } from 'vue';
const count = ref(0);
const double = computed(() => count.value * 2);
watch(count, (newVal, oldVal) => {
  console.log('count changed:', oldVal, '->', newVal);
});
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double: {{ double }}</p>
    <button @click="count++">Increment</button>
  </div>
</template>

片段(Fragment)

Vue 3支持组件返回多个根节点,无需额外包裹元素。

vue
<template>
  <td>Hello</td>
  <td>World</td>
</template>

严格模式

Vue本身没有React的StrictMode,但开发环境下会有警告和提示,推荐开启严格类型检查和lint工具。