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之旅吧!