Skip to content

Vue基础入门:前端框架的新星

欢迎来到Vue的世界!如果你觉得原生JavaScript像手动挡汽车,那么Vue就像一辆带自动驾驶的豪华轿车——让你轻松驾驭前端开发。

1. Vue是什么鬼?

Vue.js(发音为/vjuː/,类似view)是一个渐进式JavaScript框架,由尤雨溪大神在2014年创造。它就像一套乐高积木,你可以根据需要选择使用多少功能。

Vue的核心特点

  • 渐进式:可以只用核心功能,也可以集成全家桶
  • 响应式:数据变化自动更新视图,告别繁琐的DOM操作
  • 组件化:把页面拆成可复用的小组件,像搭积木一样开发
  • 易学易用:API设计简洁,学习曲线平缓

为什么选择Vue 3?

Vue 3就像Vue 2的升级版超级跑车:

  • 更快的渲染速度(性能提升约2倍)
  • 更小的包体积(减少了约40%)
  • 更好的TypeScript支持
  • 更强大的组合式API

2. 环境搭建:让Vue跑起来

安装Node.js和npm

Vue需要Node.js环境,就像汽车需要汽油一样:

bash
# 1. 检查是否已安装Node.js
node --version
npm --version

# 2. 如果没有安装,前往官网下载:
# https://nodejs.org/

# 3. 安装Vue CLI(脚手架工具)
npm install -g @vue/cli

# 4. 验证安装
vue --version

创建第一个Vue项目

bash
# 创建项目
vue create my-first-vue-app

# 进入项目目录
cd my-first-vue-app

# 启动开发服务器
npm run serve

# 访问地址:http://localhost:8080

项目目录结构

my-first-vue-app/
├── public/              # 静态资源目录
│   ├── index.html      # 主页面模板
│   └── favicon.ico     # 网站图标
├── src/                # 源代码目录
│   ├── assets/         # 静态资源(图片、样式等)
│   ├── components/     # 组件目录
│   ├── App.vue         # 根组件
│   └── main.js         # 入口文件
├── package.json        # 项目配置文件
└── README.md           # 项目说明文件

3. Vue基础语法:魔法指令大揭秘

模板语法

Vue的模板语法就像魔法咒语,让数据和视图产生神奇的联系:

vue
<template>
  <!-- 文本插值:最基础的魔法 -->
  <h1>{{ message }}</h1>
  
  <!-- 属性绑定:用v-bind或简写: -->
  <img v-bind:src="imageUrl" :alt="imageDescription">
  
  <!-- 事件绑定:用v-on或简写@ -->
  <button v-on:click="handleClick">点击我</button>
  <button @click="handleClick">点击我(简写)</button>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello Vue!',
      imageUrl: 'https://vuejs.org/images/logo.png',
      imageDescription: 'Vue Logo'
    }
  },
  methods: {
    handleClick() {
      alert('你点击了按钮!')
    }
  }
}
</script>

条件渲染:让元素会"隐身术"

vue
<template>
  <!-- v-if:真正的条件渲染 -->
  <p v-if="isVIP">尊贵的VIP用户,欢迎!</p>
  <p v-else-if="isLoggedIn">普通用户,欢迎回来!</p>
  <p v-else>请先登录</p>
  
  <!-- v-show:CSS显示/隐藏 -->
  <div v-show="isVisible">
    我是用v-show控制的元素
  </div>
</template>

<script>
export default {
  data() {
    return {
      isVIP: false,
      isLoggedIn: true,
      isVisible: true
    }
  }
}
</script>

v-if vs v-show的区别:

  • v-if:真正的条件渲染,条件为false时元素不存在于DOM中
  • v-show:只是CSS的显示/隐藏,元素始终存在于DOM中

列表渲染:批量生产大师

vue
<template>
  <!-- 遍历数组 -->
  <ul>
    <li v-for="item in items" :key="item.id">
      {{ item.name }} - ¥{{ item.price }}
    </li>
  </ul>
  
  <!-- 遍历对象 -->
  <div v-for="(value, key) in userInfo" :key="key">
    {{ key }}: {{ value }}
  </div>
  
  <!-- 带索引的遍历 -->
  <div v-for="(item, index) in items" :key="item.id">
    第{{ index + 1 }}项: {{ item.name }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [
        { id: 1, name: '苹果', price: 5.0 },
        { id: 2, name: '香蕉', price: 3.0 },
        { id: 3, name: '橙子', price: 4.0 }
      ],
      userInfo: {
        name: '张三',
        age: 25,
        city: '北京'
      }
    }
  }
}
</script>

4. 响应式数据与方法:Vue的"心脏"

响应式数据

vue
<script>
export default {
  data() {
    return {
      // 这些都是响应式数据
      message: 'Hello Vue',
      count: 0,
      user: {
        name: '张三',
        age: 25
      },
      hobbies: ['读书', '游泳', '编程']
    }
  }
}
</script>

计算属性:聪明的"计算器"

vue
<template>
  <div>
    <p>商品价格: ¥{{ price }}</p>
    <p>税率: {{ taxRate * 100 }}%</p>
    <p>含税价格: ¥{{ priceWithTax }}</p>
    <!-- 含税价格会根据price和taxRate自动计算 -->
  </div>
</template>

<script>
export default {
  data() {
    return {
      price: 100,
      taxRate: 0.13
    }
  },
  computed: {
    // 计算属性:会缓存结果,依赖变化时才重新计算
    priceWithTax() {
      return this.price * (1 + this.taxRate)
    }
  }
}
</script>

侦听器:数据的"监护人"

vue
<script>
export default {
  data() {
    return {
      question: '',
      answer: '请输入问题'
    }
  },
  watch: {
    // 侦听question的变化
    question(newVal, oldVal) {
      if (newVal.includes('?')) {
        this.getAnswer()
      }
    }
  },
  methods: {
    getAnswer() {
      // 模拟异步获取答案
      this.answer = '思考中...'
      setTimeout(() => {
        this.answer = '答案是:42'
      }, 1000)
    }
  }
}
</script>

双向绑定:数据与视图的"心灵感应"

vue
<template>
  <div>
    <!-- v-model实现双向绑定 -->
    <input v-model="message" placeholder="请输入内容">
    <p>输入的内容是: {{ message }}</p>
    
    <!-- 文本域双向绑定 -->
    <textarea v-model="description" placeholder="请输入描述"></textarea>
    
    <!-- 复选框双向绑定 -->
    <input type="checkbox" id="agree" v-model="isAgreed">
    <label for="agree">我同意条款</label>
    <p>同意状态: {{ isAgreed }}</p>
    
    <!-- 单选按钮双向绑定 -->
    <div>
      <input type="radio" id="male" value="男" v-model="gender">
      <label for="male">男</label>
      <input type="radio" id="female" value="女" v-model="gender">
      <label for="female">女</label>
      <p>选择的性别: {{ gender }}</p>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: '',
      description: '',
      isAgreed: false,
      gender: ''
    }
  }
}
</script>

5. 组件基础:Vue的积木块

什么是组件?

组件就像乐高积木,可以把页面拆分成可复用的小块:

vue
<!-- Button.vue 组件文件 -->
<template>
  <button 
    :class="['btn', `btn-${type}`, { 'btn-disabled': disabled }]"
    @click="handleClick"
    :disabled="disabled"
  >
    <slot></slot>  <!-- 插槽,可以插入内容 -->
  </button>
</template>

<script>
export default {
  name: 'MyButton',
  props: {
    type: {
      type: String,
      default: 'default'  // default, primary, danger
    },
    disabled: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    handleClick() {
      // 向父组件发送事件
      this.$emit('click')
    }
  }
}
</script>

<style scoped>
.btn {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.btn-default {
  background: #f0f0f0;
  color: #333;
}

.btn-primary {
  background: #409eff;
  color: white;
}

.btn-danger {
  background: #f56c6c;
  color: white;
}

.btn-disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
</style>

组件通信:父子组件的对话

父传子(Props)

vue
<!-- 父组件 -->
<template>
  <div>
    <!-- 向子组件传递数据 -->
    <user-card 
      :user-name="user.name" 
      :user-age="user.age" 
      :user-avatar="user.avatar"
    />
  </div>
</template>

<script>
import UserCard from './UserCard.vue'

export default {
  components: {
    UserCard
  },
  data() {
    return {
      user: {
        name: '张三',
        age: 25,
        avatar: 'https://example.com/avatar.jpg'
      }
    }
  }
}
</script>
vue
<!-- UserCard.vue 子组件 -->
<template>
  <div class="user-card">
    <img :src="userAvatar" :alt="userName">
    <h3>{{ userName }}</h3>
    <p>年龄: {{ userAge }}</p>
  </div>
</template>

<script>
export default {
  name: 'UserCard',
  // 接收父组件传递的数据
  props: {
    userName: String,
    userAge: Number,
    userAvatar: String
  }
}
</script>

子传父(Events)

vue
<!-- 子组件 -->
<template>
  <div>
    <button @click="handleDelete">删除用户</button>
  </div>
</template>

<script>
export default {
  methods: {
    handleDelete() {
      // 向父组件发送事件
      this.$emit('delete-user', { id: 123, name: '张三' })
    }
  }
}
</script>
vue
<!-- 父组件 -->
<template>
  <user-card @delete-user="handleUserDelete" />
</template>

<script>
export default {
  methods: {
    handleUserDelete(userInfo) {
      console.log('要删除的用户:', userInfo)
      // 执行删除逻辑
    }
  }
}
</script>

6. 实践练习:小试牛刀

练习1:创建用户列表组件

vue
<!-- UserList.vue -->
<template>
  <div class="user-list">
    <h2>用户列表</h2>
    
    <!-- 添加用户表单 -->
    <div class="add-user">
      <input v-model="newUser.name" placeholder="姓名">
      <input v-model="newUser.email" placeholder="邮箱">
      <button @click="addUser">添加用户</button>
    </div>
    
    <!-- 用户列表 -->
    <ul>
      <li v-for="user in users" :key="user.id">
        <span>{{ user.name }} - {{ user.email }}</span>
        <button @click="removeUser(user.id)">删除</button>
      </li>
    </ul>
    
    <!-- 统计信息 -->
    <p>总用户数: {{ userCount }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      users: [
        { id: 1, name: '张三', email: 'zhangsan@example.com' },
        { id: 2, name: '李四', email: 'lisi@example.com' }
      ],
      newUser: {
        name: '',
        email: ''
      }
    }
  },
  computed: {
    userCount() {
      return this.users.length
    }
  },
  methods: {
    addUser() {
      if (this.newUser.name && this.newUser.email) {
        this.users.push({
          id: Date.now(),
          name: this.newUser.name,
          email: this.newUser.email
        })
        this.newUser.name = ''
        this.newUser.email = ''
      }
    },
    removeUser(id) {
      this.users = this.users.filter(user => user.id !== id)
    }
  }
}
</script>

练习2:条件渲染和列表展示

vue
<!-- ProductList.vue -->
<template>
  <div class="product-list">
    <h2>商品列表</h2>
    
    <!-- 筛选 -->
    <div class="filters">
      <input v-model="searchKeyword" placeholder="搜索商品">
      <select v-model="selectedCategory">
        <option value="">所有分类</option>
        <option value="electronics">电子产品</option>
        <option value="clothing">服装</option>
        <option value="books">图书</option>
      </select>
    </div>
    
    <!-- 商品列表 -->
    <div v-if="filteredProducts.length > 0" class="products">
      <div 
        v-for="product in filteredProducts" 
        :key="product.id" 
        class="product-card"
      >
        <img :src="product.image" :alt="product.name">
        <h3>{{ product.name }}</h3>
        <p class="price">¥{{ product.price }}</p>
        <p class="category">{{ product.category }}</p>
        <button 
          :class="{ 'in-cart': product.inCart }"
          @click="toggleCart(product)"
        >
          {{ product.inCart ? '已加入购物车' : '加入购物车' }}
        </button>
      </div>
    </div>
    
    <!-- 空状态 -->
    <div v-else class="empty-state">
      <p>没有找到相关商品</p>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      searchKeyword: '',
      selectedCategory: '',
      products: [
        { id: 1, name: 'iPhone 15', price: 5999, category: 'electronics', image: 'iphone.jpg', inCart: false },
        { id: 2, name: 'MacBook Pro', price: 12999, category: 'electronics', image: 'macbook.jpg', inCart: false },
        { id: 3, name: 'T恤', price: 99, category: 'clothing', image: 'tshirt.jpg', inCart: false },
        { id: 4, name: 'Vue.js实战', price: 89, category: 'books', image: 'vue-book.jpg', inCart: false }
      ]
    }
  },
  computed: {
    filteredProducts() {
      return this.products.filter(product => {
        // 搜索关键词过滤
        const matchesKeyword = product.name.toLowerCase().includes(this.searchKeyword.toLowerCase())
        
        // 分类过滤
        const matchesCategory = !this.selectedCategory || product.category === this.selectedCategory
        
        return matchesKeyword && matchesCategory
      })
    }
  },
  methods: {
    toggleCart(product) {
      product.inCart = !product.inCart
    }
  }
}
</script>

练习3:表单组件

vue
<!-- ContactForm.vue -->
<template>
  <div class="contact-form">
    <h2>联系我们</h2>
    
    <form @submit.prevent="submitForm">
      <!-- 姓名 -->
      <div class="form-group">
        <label for="name">姓名 *</label>
        <input 
          id="name" 
          v-model="form.name" 
          type="text" 
          required 
          :class="{ 'error': errors.name }"
        >
        <span v-if="errors.name" class="error-msg">{{ errors.name }}</span>
      </div>
      
      <!-- 邮箱 -->
      <div class="form-group">
        <label for="email">邮箱 *</label>
        <input 
          id="email" 
          v-model="form.email" 
          type="email" 
          required 
          :class="{ 'error': errors.email }"
        >
        <span v-if="errors.email" class="error-msg">{{ errors.email }}</span>
      </div>
      
      <!-- 电话 -->
      <div class="form-group">
        <label for="phone">电话</label>
        <input 
          id="phone" 
          v-model="form.phone" 
          type="tel"
        >
      </div>
      
      <!-- 主题 -->
      <div class="form-group">
        <label for="subject">主题 *</label>
        <select 
          id="subject" 
          v-model="form.subject" 
          required
          :class="{ 'error': errors.subject }"
        >
          <option value="">请选择主题</option>
          <option value="support">技术支持</option>
          <option value="sales">销售咨询</option>
          <option value="feedback">意见反馈</option>
        </select>
        <span v-if="errors.subject" class="error-msg">{{ errors.subject }}</span>
      </div>
      
      <!-- 消息 -->
      <div class="form-group">
        <label for="message">消息 *</label>
        <textarea 
          id="message" 
          v-model="form.message" 
          rows="5" 
          required
          :class="{ 'error': errors.message }"
        ></textarea>
        <span v-if="errors.message" class="error-msg">{{ errors.message }}</span>
      </div>
      
      <!-- 同意条款 -->
      <div class="form-group checkbox-group">
        <input 
          id="agree" 
          v-model="form.agree" 
          type="checkbox" 
          required
        >
        <label for="agree">我同意隐私条款 *</label>
        <span v-if="errors.agree" class="error-msg">{{ errors.agree }}</span>
      </div>
      
      <!-- 提交按钮 -->
      <button 
        type="submit" 
        :disabled="isSubmitting"
        class="submit-btn"
      >
        {{ isSubmitting ? '提交中...' : '提交' }}
      </button>
    </form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      form: {
        name: '',
        email: '',
        phone: '',
        subject: '',
        message: '',
        agree: false
      },
      errors: {},
      isSubmitting: false
    }
  },
  methods: {
    validateForm() {
      this.errors = {}
      
      if (!this.form.name) {
        this.errors.name = '请输入姓名'
      }
      
      if (!this.form.email) {
        this.errors.email = '请输入邮箱'
      } else if (!/\S+@\S+\.\S+/.test(this.form.email)) {
        this.errors.email = '邮箱格式不正确'
      }
      
      if (!this.form.subject) {
        this.errors.subject = '请选择主题'
      }
      
      if (!this.form.message) {
        this.errors.message = '请输入消息内容'
      }
      
      if (!this.form.agree) {
        this.errors.agree = '请同意隐私条款'
      }
      
      return Object.keys(this.errors).length === 0
    },
    
    async submitForm() {
      if (!this.validateForm()) {
        return
      }
      
      this.isSubmitting = true
      
      try {
        // 模拟提交到服务器
        await new Promise(resolve => setTimeout(resolve, 2000))
        
        alert('提交成功!我们会尽快联系您。')
        
        // 重置表单
        this.form = {
          name: '',
          email: '',
          phone: '',
          subject: '',
          message: '',
          agree: false
        }
      } catch (error) {
        alert('提交失败,请稍后重试')
      } finally {
        this.isSubmitting = false
      }
    }
  }
}
</script>

<style scoped>
.contact-form {
  max-width: 600px;
  margin: 0 auto;
  padding: 20px;
}

.form-group {
  margin-bottom: 20px;
}

label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}

input, select, textarea {
  width: 100%;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 16px;
}

input.error, select.error, textarea.error {
  border-color: #f56c6c;
}

.error-msg {
  color: #f56c6c;
  font-size: 14px;
  margin-top: 5px;
  display: block;
}

.checkbox-group {
  display: flex;
  align-items: center;
}

.checkbox-group input {
  width: auto;
  margin-right: 10px;
}

.submit-btn {
  width: 100%;
  padding: 12px;
  background: #409eff;
  color: white;
  border: none;
  border-radius: 4px;
  font-size: 16px;
  cursor: pointer;
}

.submit-btn:disabled {
  background: #a0cfff;
  cursor: not-allowed;
}

.empty-state {
  text-align: center;
  padding: 40px;
  color: #999;
}
</style>

总结

恭喜你完成了Vue基础入门!现在你已经掌握了:

  • Vue的核心概念和特点
  • 环境搭建和项目创建
  • 基础模板语法(插值、指令、条件渲染、列表渲染)
  • 响应式数据、计算属性、侦听器
  • 组件基础和组件通信
  • 简单的表单处理

Vue就像一个贴心的助手,帮你自动处理数据和视图的同步,让你可以专注于业务逻辑的实现。在下一章节中,我们将学习Vue的核心特性和进阶用法,包括组件生命周期、状态管理、路由等更高级的功能。

记住,学习Vue最重要的是多动手实践,尝试修改示例代码,创建自己的组件,这样才能真正掌握这门强大的前端框架。赶紧打开你的开发环境,开始你的Vue之旅吧!