Vue.js 基础知识(一)

基于 https://cn.vuejs.org/ 官方文档整理,以 组合式 API + <script setup> 为主要语法。


一、Vue 概述

Vue 是一款用于构建用户界面的渐进式 JavaScript 框架。核心特性:

  • 声明式渲染:基于标准 HTML 拓展模板语法
  • 响应性:自动跟踪 JS 状态并响应式更新 DOM

单文件组件 (SFC)

将逻辑、模板、样式封装在同一个 .vue 文件中:

<script setup>
import { ref, onMounted } from 'vue'
const count = ref(0)
function increment() { count.value++ }
onMounted(() => { console.log(`初始值: ${count.value}`) })
</script>

<template>
  <button @click="increment">Count is: {{ count }}</button>
</template>

<style scoped>
button { font-weight: bold; }
</style>

API 风格选择

风格 适用场景
选项式 API 低复杂度、无构建工具
组合式 API + <script setup> 完整 SPA 项目(推荐)

二、创建一个应用

import { createApp } from 'vue'
import App from './App.vue'       // 从单文件组件导入根组件
const app = createApp(App)       // 创建应用实例
app.mount('#app')                 // 挂载到 DOM

要点:

  • .mount() 必须在所有应用配置和资源注册之后调用,返回根组件实例
  • 容器元素本身不属于应用,根组件内容渲染在其内部
  • 根组件无 template 时,Vue 自动使用容器的 innerHTML 作为模板
// 应用级配置
app.config.errorHandler = (err) => { /* 处理错误 */ }

// 全局注册组件
app.component('TodoDeleteButton', TodoDeleteButton)

// 同一页面可创建多个独立应用实例
const app1 = createApp({ /* ... */ })
app1.mount('#container-1')

三、模板语法

文本插值

<span>Message: {{ msg }}</span>

原始 HTML

<p v-html="rawHtml"></p>

安全警告:动态渲染任意 HTML 有 XSS 风险,永远不要使用用户提供的 HTML 内容。v-html 不能拼接组合模板。

Attribute 绑定

<div v-bind:id="dynamicId"></div>
<div :id="dynamicId"></div>          <!-- 简写 -->
<div :id></div>                       <!-- 同名简写 (Vue 3.4+),等同于 :id="id" -->
<button :disabled="isButtonDisabled"></button>  <!-- 布尔型 attribute -->

<!-- 动态绑定多个值 -->
<div v-bind="objectOfAttrs"></div>
const objectOfAttrs = {
  id: 'container',
  class: 'wrapper',
  style: 'background-color:green'
}

v-bind 值为 nullundefined 时,该 attribute 从渲染元素上移除。

使用 JavaScript 表达式

{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
  • 仅支持单一表达式,不支持语句和条件控制
  • 表达式中的方法每次更新都会被重新调用,不应有副作用
  • 表达式被沙盒化,仅能访问 MathDate 等有限全局对象

指令 (Directives)

指令是带 v- 前缀的特殊 attribute:

<a v-bind:href="url">...</a>         <!-- :href 简写 -->
<a v-on:click="doSomething">...</a>  <!-- @click 简写 -->
<a @[eventName]="doSomething">...</a> <!-- 动态参数 -->
<form @submit.prevent="onSubmit">...</form> <!-- 修饰符 -->
  • 动态参数:值应为字符串或 null,避免空格和引号
  • 修饰符:以 . 指明的特殊后缀,如 .prevent.stop

四、响应式基础

ref()

ref() 是声明响应式状态的主要 API(推荐)

import { ref } from 'vue'
const count = ref(0)
console.log(count.value)  // 0 — JS 中需要 .value
count.value++              // 触发响应式更新
  • 模板中 ref 自动解包,无需 .value
  • <script setup> 中的顶层变量和函数可直接在模板中使用

reactive()

import { reactive } from 'vue'
const state = reactive({ count: 0 })
state.count++  // 无需 .value

reactive() 的三大局限性:

  1. 只能用于对象类型,不能持有原始值
  2. 不能替换整个对象(会丢失响应性)
  3. 对解构操作不友好(解构后丢失响应性)

推荐使用 ref() 作为主要 API。

深层响应性

无论 ref() 还是 reactive(),嵌套对象/数组都自动具有深层响应性:

const obj = ref({ nested: { count: 0 }, arr: ['foo'] })
obj.value.nested.count++   // 深层修改也能被追踪
obj.value.arr.push('bar')

DOM 更新时机

DOM 更新不是同步的,Vue 在 “next tick” 中批量更新:

import { nextTick } from 'vue'
async function increment() {
  count.value++
  await nextTick()  // DOM 已更新
}

ref 解包规则

场景 是否自动解包
模板中顶级属性
模板中嵌套属性 object.id 仅作为插值最终值时解包
reactive 对象的属性
数组 / 集合中的元素 否,需要 .value

五、计算属性

基本用法

import { reactive, computed } from 'vue'
const author = reactive({ books: ['Vue 3 Guide', 'Vue 4 Mystery'] })

const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})

计算属性 vs 方法: 计算属性基于响应式依赖缓存,仅依赖变化时重新计算;方法每次重渲染都重新执行。

可写计算属性

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
  get() { return firstName.value + ' ' + lastName.value },
  set(newValue) { [firstName.value, lastName.value] = newValue.split(' ') }
})

获取上一个值 (Vue 3.4+)

const count = ref(2)
const alwaysSmall = computed((previous) => {
  return count.value <= 3 ? count.value : previous
})

最佳实践:

  • Getter 不应有副作用(不做异步请求、不修改其他状态)
  • 计算属性应视为只读的派生状态

更多推荐