第一个Vue应用 #

一、创建应用 #

1.1 最简单的Vue应用 #

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>第一个Vue应用</title>
</head>
<body>
  <div id="app">
    <h1>{{ message }}</h1>
    <p>计数器: {{ count }}</p>
    <button @click="increment">+1</button>
  </div>

  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <script>
    const { createApp, ref } = Vue
    
    createApp({
      setup() {
        const message = ref('Hello Vue!')
        const count = ref(0)
        
        function increment() {
          count.value++
        }
        
        return {
          message,
          count,
          increment
        }
      }
    }).mount('#app')
  </script>
</body>
</html>

1.2 应用结构解析 #

text
createApp()        创建Vue应用实例
    │
    ├── setup()    组合式API入口
    │      │
    │      ├── ref()      创建响应式数据
    │      ├── computed() 创建计算属性
    │      ├── watch()    创建侦听器
    │      └── return {}  返回模板需要的数据和方法
    │
    └── mount()     挂载到DOM元素

二、模板语法 #

2.1 文本插值 #

html
<div id="app">
  <!-- 双大括号语法 -->
  <p>{{ message }}</p>
  
  <!-- 支持JavaScript表达式 -->
  <p>{{ count + 1 }}</p>
  <p>{{ message.split('').reverse().join('') }}</p>
  <p>{{ count > 10 ? '大于10' : '小于等于10' }}</p>
</div>

2.2 属性绑定 #

html
<div id="app">
  <!-- v-bind指令 -->
  <img v-bind:src="imageSrc" v-bind:alt="imageAlt">
  
  <!-- 简写形式 -->
  <img :src="imageSrc" :alt="imageAlt">
  
  <!-- 动态属性名 -->
  <button :[attrName]="attrValue">按钮</button>
  
  <!-- 绑定多个属性 -->
  <div v-bind="objectOfAttrs"></div>
</div>

<script>
  const { createApp, ref } = Vue
  
  createApp({
    setup() {
      const imageSrc = ref('image.png')
      const imageAlt = ref('图片描述')
      const attrName = ref('disabled')
      const attrValue = ref(true)
      const objectOfAttrs = ref({
        id: 'container',
        class: 'wrapper'
      })
      
      return { imageSrc, imageAlt, attrName, attrValue, objectOfAttrs }
    }
  }).mount('#app')
</script>

2.3 条件渲染 #

html
<div id="app">
  <!-- v-if -->
  <p v-if="seen">现在你看到我了</p>
  
  <!-- v-else -->
  <p v-if="type === 'A'">A类型</p>
  <p v-else-if="type === 'B'">B类型</p>
  <p v-else>其他类型</p>
  
  <!-- v-show -->
  <p v-show="visible">切换显示</p>
</div>

2.4 列表渲染 #

html
<div id="app">
  <!-- 遍历数组 -->
  <ul>
    <li v-for="(item, index) in items" :key="item.id">
      {{ index }}: {{ item.name }}
    </li>
  </ul>
  
  <!-- 遍历对象 -->
  <ul>
    <li v-for="(value, key, index) in object" :key="key">
      {{ index }}. {{ key }}: {{ value }}
    </li>
  </ul>
</div>

<script>
  const { createApp, ref } = Vue
  
  createApp({
    setup() {
      const items = ref([
        { id: 1, name: '苹果' },
        { id: 2, name: '香蕉' },
        { id: 3, name: '橙子' }
      ])
      
      const object = ref({
        name: '张三',
        age: 25,
        city: '北京'
      })
      
      return { items, object }
    }
  }).mount('#app')
</script>

三、事件处理 #

3.1 基本事件 #

html
<div id="app">
  <!-- 完整写法 -->
  <button v-on:click="count++">点击 {{ count }}</button>
  
  <!-- 简写形式 -->
  <button @click="count++">点击 {{ count }}</button>
  
  <!-- 调用方法 -->
  <button @click="increment">+1</button>
  
  <!-- 传递参数 -->
  <button @click="add(5)">+5</button>
  
  <!-- 访问事件对象 -->
  <button @click="handleClick($event)">点击</button>
</div>

<script>
  const { createApp, ref } = Vue
  
  createApp({
    setup() {
      const count = ref(0)
      
      function increment() {
        count.value++
      }
      
      function add(num) {
        count.value += num
      }
      
      function handleClick(event) {
        console.log('事件对象:', event)
        console.log('目标元素:', event.target)
      }
      
      return { count, increment, add, handleClick }
    }
  }).mount('#app')
</script>

3.2 事件修饰符 #

html
<div id="app">
  <!-- 阻止默认行为 -->
  <form @submit.prevent="onSubmit">
    <button type="submit">提交</button>
  </form>
  
  <!-- 阻止事件冒泡 -->
  <div @click="onDivClick">
    <button @click.stop="onBtnClick">按钮</button>
  </div>
  
  <!-- 事件只触发一次 -->
  <button @click.once="doOnce">只触发一次</button>
  
  <!-- 按键修饰符 -->
  <input @keyup.enter="onEnter" @keyup.esc="onEsc">
  
  <!-- 组合修饰符 -->
  <input @keyup.ctrl.enter="onCtrlEnter">
</div>

3.3 常用修饰符 #

修饰符 说明
.stop 阻止事件冒泡
.prevent 阻止默认行为
.capture 使用事件捕获模式
.self 只当事件在该元素本身触发时触发
.once 事件只触发一次
.passive 提升移动端滚动性能

四、双向绑定 #

4.1 v-model基础 #

html
<div id="app">
  <!-- 文本输入 -->
  <input v-model="text" placeholder="输入文本">
  <p>输入内容: {{ text }}</p>
  
  <!-- 多行文本 -->
  <textarea v-model="content" placeholder="输入内容"></textarea>
  
  <!-- 复选框 -->
  <input type="checkbox" v-model="checked">
  <label>{{ checked ? '已选中' : '未选中' }}</label>
  
  <!-- 多个复选框 -->
  <input type="checkbox" v-model="checkedNames" value="张三">
  <input type="checkbox" v-model="checkedNames" value="李四">
  <input type="checkbox" v-model="checkedNames" value="王五">
  <p>选中: {{ checkedNames }}</p>
  
  <!-- 单选按钮 -->
  <input type="radio" v-model="picked" value="one">
  <input type="radio" v-model="picked" value="two">
  <p>选中: {{ picked }}</p>
  
  <!-- 下拉框 -->
  <select v-model="selected">
    <option value="">请选择</option>
    <option value="a">A</option>
    <option value="b">B</option>
  </select>
</div>

<script>
  const { createApp, ref } = Vue
  
  createApp({
    setup() {
      const text = ref('')
      const content = ref('')
      const checked = ref(false)
      const checkedNames = ref([])
      const picked = ref('')
      const selected = ref('')
      
      return { text, content, checked, checkedNames, picked, selected }
    }
  }).mount('#app')
</script>

4.2 v-model修饰符 #

html
<div id="app">
  <!-- .lazy - 在change事件后同步 -->
  <input v-model.lazy="text">
  
  <!-- .number - 转换为数字 -->
  <input v-model.number="age" type="number">
  
  <!-- .trim - 去除首尾空格 -->
  <input v-model.trim="name">
</div>

五、计算属性 #

5.1 基本使用 #

html
<div id="app">
  <p>原价: {{ price }}</p>
  <p>折扣价: {{ discountPrice }}</p>
  <p>最终价格: {{ finalPrice }}</p>
</div>

<script>
  const { createApp, ref, computed } = Vue
  
  createApp({
    setup() {
      const price = ref(100)
      
      // 计算属性
      const discountPrice = computed(() => {
        return price.value * 0.8
      })
      
      // 可写的计算属性
      const finalPrice = computed({
        get() {
          return price.value * 0.8
        },
        set(newValue) {
          price.value = newValue / 0.8
        }
      })
      
      return { price, discountPrice, finalPrice }
    }
  }).mount('#app')
</script>

5.2 计算属性 vs 方法 #

html
<div id="app">
  <!-- 计算属性 - 有缓存 -->
  <p>{{ formattedPrice }}</p>
  <p>{{ formattedPrice }}</p>  <!-- 不会重新计算 -->
  
  <!-- 方法 - 无缓存 -->
  <p>{{ formatPrice() }}</p>
  <p>{{ formatPrice() }}</p>   <!-- 会重新调用 -->
</div>

<script>
  const { createApp, ref, computed } = Vue
  
  createApp({
    setup() {
      const price = ref(100)
      
      // 计算属性 - 基于依赖缓存
      const formattedPrice = computed(() => {
        console.log('计算属性执行')
        return `¥${price.value.toFixed(2)}`
      })
      
      // 方法 - 每次都执行
      function formatPrice() {
        console.log('方法执行')
        return `¥${price.value.toFixed(2)}`
      }
      
      return { price, formattedPrice, formatPrice }
    }
  }).mount('#app')
</script>

六、完整示例:待办事项 #

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>待办事项</title>
  <style>
    .completed { text-decoration: line-through; color: #999; }
    .todo-item { padding: 8px; border-bottom: 1px solid #eee; }
    .todo-item:hover { background: #f5f5f5; }
  </style>
</head>
<body>
  <div id="app">
    <h2>待办事项</h2>
    
    <!-- 输入框 -->
    <input 
      v-model="newTodo" 
      @keyup.enter="addTodo"
      placeholder="添加新任务"
    >
    <button @click="addTodo">添加</button>
    
    <!-- 过滤按钮 -->
    <div>
      <button @click="filter = 'all'" :class="{ active: filter === 'all' }">全部</button>
      <button @click="filter = 'active'" :class="{ active: filter === 'active' }">未完成</button>
      <button @click="filter = 'completed'" :class="{ active: filter === 'completed' }">已完成</button>
    </div>
    
    <!-- 列表 -->
    <ul>
      <li v-for="todo in filteredTodos" :key="todo.id" class="todo-item">
        <input type="checkbox" v-model="todo.completed">
        <span :class="{ completed: todo.completed }">{{ todo.text }}</span>
        <button @click="removeTodo(todo.id)">删除</button>
      </li>
    </ul>
    
    <!-- 统计 -->
    <p>共 {{ totalTodos }} 项,已完成 {{ completedTodos }} 项</p>
  </div>

  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <script>
    const { createApp, ref, computed } = Vue
    
    createApp({
      setup() {
        const newTodo = ref('')
        const filter = ref('all')
        const todos = ref([
          { id: 1, text: '学习Vue基础', completed: false },
          { id: 2, text: '学习Vue组件', completed: false },
          { id: 3, text: '学习Vue Router', completed: false }
        ])
        
        // 添加待办
        function addTodo() {
          const text = newTodo.value.trim()
          if (text) {
            todos.value.push({
              id: Date.now(),
              text,
              completed: false
            })
            newTodo.value = ''
          }
        }
        
        // 删除待办
        function removeTodo(id) {
          const index = todos.value.findIndex(t => t.id === id)
          if (index > -1) {
            todos.value.splice(index, 1)
          }
        }
        
        // 过滤后的列表
        const filteredTodos = computed(() => {
          switch (filter.value) {
            case 'active':
              return todos.value.filter(t => !t.completed)
            case 'completed':
              return todos.value.filter(t => t.completed)
            default:
              return todos.value
          }
        })
        
        // 统计
        const totalTodos = computed(() => todos.value.length)
        const completedTodos = computed(() => 
          todos.value.filter(t => t.completed).length
        )
        
        return {
          newTodo,
          filter,
          todos,
          addTodo,
          removeTodo,
          filteredTodos,
          totalTodos,
          completedTodos
        }
      }
    }).mount('#app')
  </script>
</body>
</html>

七、总结 #

概念 说明
createApp() 创建Vue应用实例
mount() 挂载应用到DOM
ref() 创建响应式数据
{{ }} 模板插值语法
v-bind/: 属性绑定
v-on/@ 事件绑定
v-model 双向数据绑定
v-if/v-show 条件渲染
v-for 列表渲染
computed() 计算属性

学习要点:

  • 理解Vue应用的创建和挂载过程
  • 掌握模板语法和指令的使用
  • 学会响应式数据绑定
  • 熟悉事件处理和修饰符
  • 理解计算属性的缓存特性
最后更新:2026-03-26