第一个 Vue 应用

理论知识准备得差不多了,现在让我们动手创建第一个 Vue 应用。我们将从最基础的例子开始,逐步深入。

最简单的例子

创建一个 index.html 文件:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>我的第一个 Vue 应用</title>
</head>
<body>
  <div id="app">
    <h1>{{ message }}</h1>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
  <script>
    var app = new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue!'
      }
    })
  </script>
</body>
</html>

用浏览器打开这个文件,你会看到 "Hello Vue!"。

代码解析

var app = new Vue({
  el: '#app',        // 挂载点:Vue 管理的 DOM 范围
  data: {            // 数据:页面显示的内容
    message: 'Hello Vue!'
  }
})
  • el:指定 Vue 实例挂载的 DOM 元素
  • data:定义响应式数据
  • {{ message }}:插值表达式,显示 data 中的 message

双向绑定示例

让我们添加一个输入框,体验 Vue 的双向绑定:

<div id="app">
  <input type="text" v-model="message">
  <p>你输入的是:{{ message }}</p>
</div>

<script>
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
</script>

当你在输入框中输入内容时,下面的文字会实时更新。这就是 Vue 的双向数据绑定

计数器应用

一个稍微复杂点的例子——计数器:

<div id="app">
  <h2>计数器</h2>
  <p>当前计数:{{ count }}</p>
  <button @click="decrement">-1</button>
  <button @click="increment">+1</button>
  <button @click="reset">重置</button>
</div>

<script>
var app = new Vue({
  el: '#app',
  data: {
    count: 0
  },
  methods: {
    increment: function() {
      this.count++
    },
    decrement: function() {
      this.count--
    },
    reset: function() {
      this.count = 0
    }
  }
})
</script>

新知识点

  • methods:定义方法
  • @click:事件绑定,@v-on: 的简写
  • this.count:在方法中通过 this 访问 data

待办事项列表

让我们做一个更实用的例子——待办事项:

<div id="app">
  <h2>待办事项</h2>
  
  <!-- 输入区域 -->
  <input 
    type="text" 
    v-model="newItem" 
    @keyup.enter="addItem"
    placeholder="输入待办事项,按回车添加"
  >
  <button @click="addItem">添加</button>
  
  <!-- 列表区域 -->
  <ul>
    <li v-for="(item, index) in items" :key="index">
      <input type="checkbox" v-model="item.done">
      <span :style="{ textDecoration: item.done ? 'line-through' : 'none' }">
        {{ item.text }}
      </span>
      <button @click="removeItem(index)">删除</button>
    </li>
  </ul>
  
  <!-- 统计区域 -->
  <p>共 {{ items.length }} 项,已完成 {{ doneCount }} 项</p>
</div>

<script>
var app = new Vue({
  el: '#app',
  data: {
    newItem: '',
    items: [
      { text: '学习 Vue 基础', done: true },
      { text: '学习 Vue 组件', done: false },
      { text: '学习 Vue Router', done: false }
    ]
  },
  computed: {
    doneCount: function() {
      return this.items.filter(function(item) {
        return item.done
      }).length
    }
  },
  methods: {
    addItem: function() {
      var text = this.newItem.trim()
      if (text) {
        this.items.push({
          text: text,
          done: false
        })
        this.newItem = ''
      }
    },
    removeItem: function(index) {
      this.items.splice(index, 1)
    }
  }
})
</script>

新知识点

  • v-for:列表渲染
  • v-model:表单双向绑定
  • @keyup.enter:按键修饰符
  • computed:计算属性
  • :style:动态样式绑定
  • :key:列表渲染的 key

完整的单文件组件示例

如果你使用 Vue CLI 或 Vite 创建项目,会使用 .vue 单文件组件:

<template>
  <div class="todo-app">
    <h2>待办事项</h2>
    
    <div class="input-area">
      <input
        v-model="newItem"
        @keyup.enter="addItem"
        placeholder="输入待办事项"
      >
      <button @click="addItem">添加</button>
    </div>
    
    <ul class="todo-list">
      <li
        v-for="(item, index) in items"
        :key="item.id"
        :class="{ done: item.done }"
      >
        <input type="checkbox" v-model="item.done">
        <span>{{ item.text }}</span>
        <button class="delete-btn" @click="removeItem(index)">×</button>
      </li>
    </ul>
    
    <div class="stats">
      <span>共 {{ items.length }} 项</span>
      <span>已完成 {{ doneCount }} 项</span>
    </div>
  </div>
</template>

<script>
export default {
  name: 'TodoApp',
  data() {
    return {
      newItem: '',
      items: [],
      nextId: 1
    }
  },
  computed: {
    doneCount() {
      return this.items.filter(item => item.done).length
    }
  },
  methods: {
    addItem() {
      const text = this.newItem.trim()
      if (text) {
        this.items.push({
          id: this.nextId++,
          text: text,
          done: false
        })
        this.newItem = ''
      }
    },
    removeItem(index) {
      this.items.splice(index, 1)
    }
  }
}
</script>

<style scoped>
.todo-app {
  max-width: 500px;
  margin: 20px auto;
  padding: 20px;
}

.input-area {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}

.input-area input {
  flex: 1;
  padding: 8px 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.todo-list {
  list-style: none;
  padding: 0;
}

.todo-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border-bottom: 1px solid #eee;
}

.todo-list li.done span {
  text-decoration: line-through;
  color: #999;
}

.delete-btn {
  margin-left: auto;
  background: none;
  border: none;
  color: #f44336;
  cursor: pointer;
  font-size: 18px;
}

.stats {
  margin-top: 20px;
  color: #666;
  display: flex;
  gap: 20px;
}
</style>

调试技巧

使用 Vue DevTools

  1. 安装 Vue DevTools 浏览器扩展
  2. 打开开发者工具(F12)
  3. 切换到 Vue 面板
  4. 可以查看组件树、数据、事件等

控制台调试

// 在控制台直接访问 Vue 实例
app.message = 'New Message'
app.items.push({ text: 'New Item', done: false })

响应式数据变化追踪

// 在 Vue 实例中添加 watch
var app = new Vue({
  data: { count: 0 },
  watch: {
    count: function(newVal, oldVal) {
      console.log('count changed:', oldVal, '->', newVal)
    }
  }
})

常见错误

1. 挂载点不存在

// ❌ 错误:找不到 #app 元素
new Vue({
  el: '#app'
})

// ✅ 正确:确保 DOM 已加载
document.addEventListener('DOMContentLoaded', function() {
  new Vue({
    el: '#app'
  })
})

2. data 不是函数(组件中)

// ❌ 错误:组件中 data 不能是对象
Vue.component('my-component', {
  data: {
    message: 'Hello'
  }
})

// ✅ 正确:组件中 data 必须是函数
Vue.component('my-component', {
  data: function() {
    return {
      message: 'Hello'
    }
  }
})

3. 直接修改数组索引

// ❌ 错误:Vue 无法检测
this.items[0] = newValue

// ✅ 正确:使用 $set
this.$set(this.items, 0, newValue)

小结

通过这几个例子,你已经学会了:

  • 创建 Vue 实例
  • 使用插值表达式显示数据
  • 使用 v-model 实现双向绑定
  • 使用 v-on 绑定事件
  • 使用 v-for 渲染列表
  • 使用 computed 计算属性
  • 编写单文件组件

接下来,我们将深入学习 Vue 的每个特性。