Vue2入门教程 - 从零开始学习Vue.js框架完整指南
Vue2入门教程 - 从零开始学习Vue.js框架
目录
- Vue2简介
- 环境搭建
- 第一个Vue应用
- 模板语法
- 计算属性和侦听器
- Class与Style绑定
- 条件渲染和列表渲染
- 事件处理
- 表单输入绑定
- 组件基础
- 组件通信
- 生命周期钩子
- Vue Router路由
- Vuex状态管理
- HTTP请求
- 实际项目案例
- 总结与进阶
1. Vue2简介
Vue.js(读音 /vjuː/,类似于 view)是一套用于构建用户界面的渐进式框架。Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
核心特点:
- ✅ 渐进式框架:可以逐步采用,不需要重写整个项目
- ✅ 响应式数据绑定:数据变化自动更新视图
- ✅ 组件化开发:可复用的组件系统
- ✅ 虚拟DOM:高效的DOM更新机制
- ✅ 轻量级:体积小,性能优秀
- ✅ 易于学习:学习曲线平缓,文档完善
- ✅ 生态丰富:Vue Router、Vuex等官方插件
| 特性 | Vue2 | Vue3 |
|---|---|---|
| 发布时间 | 2016年 | 2020年 |
| 性能 | 优秀 | 更优秀 |
| 组合式API | 不支持 | 支持 |
| TypeScript支持 | 有限 | 原生支持 |
| 生态成熟度 | 非常成熟 | 逐步完善 |
| 学习资源 | 丰富 | 逐步增加 |
为什么学习Vue2:
大量现有项目使用Vue2
生态成熟,资源丰富
学习成本相对较低
企业应用广泛
单页面应用(SPA)
移动端H5应用
后台管理系统
数据可视化
电商网站
企业级应用
2. 环境搭建
- Node.js:12.x或更高版本
- npm:6.x或更高版本(Node.js自带)
- 代码编辑器:VS Code、WebStorm等
Windows系统
- 访问Node.js官网:https://nodejs.org/
- 下载LTS版本(推荐)
- 运行安装程序,按提示完成安装
- 验证安装:
node -v
npm -vmacOS系统
# 使用Homebrew安装
brew install node
# 验证安装
node -v
npm -vLinux系统
# Ubuntu/Debian
curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt-get install -y nodejs
# 验证安装
node -v
npm -vVue CLI是Vue.js官方提供的脚手架工具,用于快速搭建Vue项目。
# 全局安装Vue CLI
npm install -g @vue/cli
# 验证安装
vue --version方式一:使用Vue CLI创建
# 创建项目
vue create my-vue-app
# 进入项目目录
cd my-vue-app
# 启动开发服务器
npm run serve创建项目时的选项:
- Default (Vue 2):默认Vue2配置
- Default (Vue 3):默认Vue3配置
- Manually select features:手动选择特性
方式二:使用CDN引入(学习用)
创建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>Vue2入门教程</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{ message }}
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
</script>
</body>
</html>2.5 项目目录结构
使用Vue CLI创建的项目结构:
my-vue-app/
├── node_modules/ # 依赖包
├── public/ # 静态资源
│ ├── index.html # HTML模板
│ └── favicon.ico # 网站图标
├── src/ # 源代码目录
│ ├── assets/ # 资源文件(图片、样式等)
│ ├── components/ # 组件目录
│ │ └── HelloWorld.vue
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
├── .gitignore # Git忽略文件
├── package.json # 项目配置和依赖
└── README.md # 项目说明3. 第一个Vue应用
// main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')var vm = new Vue({
// 选项对象
el: '#app', // 挂载元素
data: { // 数据
message: 'Hello Vue!'
},
methods: { // 方法
greet: function() {
return 'Hello!'
}
}
})var data = { a: 1 }
var vm = new Vue({
data: data
})
// 获取实例上的属性
vm.a === data.a // => true
// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2
// 反之亦然
data.a = 3
vm.a // => 3Vue实例在创建时经历一系列初始化过程,这些过程对应不同的生命周期钩子函数。
new Vue({
data: {
a: 1
},
created: function () {
// `this` 指向 vm 实例
console.log('a is: ' + this.a)
}
})4. 模板语法
文本插值
<div id="app">
<p>{{ message }}</p>
</div>new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})原始HTML
<div id="app">
<p>{{ rawHtml }}</p>
<p v-html="rawHtml"></p>
</div>new Vue({
el: '#app',
data: {
rawHtml: '<span style="color: red">这是红色文字</span>'
}
})注意: 使用v-html时要注意XSS攻击风险。
属性绑定
<div id="app">
<div v-bind:id="dynamicId"></div>
<!-- 简写形式 -->
<div :id="dynamicId"></div>
</div>new Vue({
el: '#app',
data: {
dynamicId: 'my-div'
}
})JavaScript表达式
<div id="app">
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
</div>指令是带有v-前缀的特殊属性。
v-if
<div id="app">
<p v-if="seen">现在你看到我了</p>
</div>new Vue({
el: '#app',
data: {
seen: true
}
})v-show
<div id="app">
<p v-show="ok">Hello!</p>
</div>v-if vs v-show:
v-if:条件为false时,元素不会被渲染到DOMv-show:元素始终被渲染,只是切换CSS的display属性
v-for
<div id="app">
<ul>
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</ul>
</div>new Vue({
el: '#app',
data: {
items: [
{ id: 1, text: '学习 JavaScript' },
{ id: 2, text: '学习 Vue' },
{ id: 3, text: '整个牛项目' }
]
}
})v-on
<div id="app">
<button v-on:click="counter += 1">Add 1</button>
<p>按钮被点击了 {{ counter }} 次</p>
<!-- 简写形式 -->
<button @click="counter += 1">Add 1</button>
</div>new Vue({
el: '#app',
data: {
counter: 0
}
})v-model
<div id="app">
<input v-model="message" placeholder="编辑我...">
<p>消息是: {{ message }}</p>
</div>new Vue({
el: '#app',
data: {
message: ''
}
})事件修饰符
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<div v-on:click.self="doThat">...</div>按键修饰符
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
<!-- 可以使用按键码 -->
<input v-on:keyup.13="submit">系统修饰键
<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>5. 计算属性和侦听器
计算属性是基于响应式依赖进行缓存的,只有在相关响应式依赖发生改变时才会重新求值。
<div id="app">
<p>原始消息: "{{ message }}"</p>
<p>计算后反转消息: "{{ reversedMessage }}"</p>
</div>var vm = new Vue({
el: '#app',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})计算属性 vs 方法
<div id="app">
<p>计算属性: {{ reversedMessage }}</p>
<p>方法: {{ reverseMessage() }}</p>
</div>var vm = new Vue({
el: '#app',
data: {
message: 'Hello'
},
computed: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
},
methods: {
reverseMessage: function () {
return this.message.split('').reverse().join('')
}
}
})区别:
- 计算属性有缓存,只有依赖变化时才重新计算
- 方法每次调用都会执行
计算属性的setter
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}当需要在数据变化时执行异步或开销较大的操作时,使用侦听器。
<div id="app">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>var vm = new Vue({
el: '#app',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// `_.debounce` 是一个通过 Lodash 限制操作频率的函数
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})6. Class与Style绑定
对象语法
<div v-bind:class="{ active: isActive }"></div>data: {
isActive: true
}<div v-bind:class="classObject"></div>data: {
classObject: {
active: true,
'text-danger': false
}
}数组语法
<div v-bind:class="[activeClass, errorClass]"></div>data: {
activeClass: 'active',
errorClass: 'text-danger'
}在组件上使用
<my-component v-bind:class="{ active: isActive }"></my-component>对象语法
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>data: {
activeColor: 'red',
fontSize: 30
}<div v-bind:style="styleObject"></div>data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}数组语法
<div v-bind:style="[baseStyles, overridingStyles]"></div>7. 条件渲染和列表渲染
<div id="app">
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
</div>new Vue({
el: '#app',
data: {
type: 'A'
}
})<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template><h1 v-show="ok">Hello!</h1>遍历数组
<ul id="app">
<li v-for="(item, index) in items" :key="item.id">
{{ index }} - {{ item.message }}
</li>
</ul>new Vue({
el: '#app',
data: {
items: [
{ id: 1, message: 'Foo' },
{ id: 2, message: 'Bar' }
]
}
})遍历对象
<div v-for="(value, name, index) in object" :key="name">
{{ index }}. {{ name }}: {{ value }}
</div>data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}维护状态
<div v-for="item in items" v-bind:key="item.id">
<!-- 内容 -->
</div>重要: 使用v-for时必须提供key属性。
8. 事件处理
<div id="app">
<button v-on:click="counter += 1">Add 1</button>
<p>按钮被点击了 {{ counter }} 次</p>
</div>new Vue({
el: '#app',
data: {
counter: 0
}
})<div id="app">
<button v-on:click="greet">Greet</button>
</div>new Vue({
el: '#app',
data: {
name: 'Vue.js'
},
methods: {
greet: function (event) {
alert('Hello ' + this.name + '!')
if (event) {
alert(event.target.tagName)
}
}
}
})<div id="app">
<button v-on:click="say('hi')">Say hi</button>
<button v-on:click="say('what')">Say what</button>
</div>new Vue({
el: '#app',
methods: {
say: function (message) {
alert(message)
}
}
})<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<div v-on:click.self="doThat">...</div><!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
<!-- 可以使用按键码 -->
<input v-on:keyup.13="submit">9. 表单输入绑定
文本
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>多行文本
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>复选框
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>data: {
checked: false
}多个复选框
<div id="app">
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
</div>new Vue({
el: '#app',
data: {
checkedNames: []
}
})单选按钮
<div id="app">
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
</div>选择框
<div id="app">
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div><!-- 复选框 -->
<input
type="checkbox"
v-model="toggle"
true-value="yes"
false-value="no"
>
<!-- 单选按钮 -->
<input type="radio" v-model="pick" v-bind:value="a">
<!-- 选择框的选项 -->
<select v-model="selected">
<option v-bind:value="{ number: 123 }">123</option>
</select>.lazy
<!-- 在 "change" 时而非 "input" 时更新 -->
<input v-model.lazy="msg">.number
<input v-model.number="age" type="number">.trim
<input v-model.trim="msg">10. 组件基础
组件是可复用的Vue实例,带有一个名字。
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div><div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>每个组件都会维护自己独立的count。
创建HelloWorld.vue:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<style scoped>
.hello {
color: #42b983;
}
</style>src/
├── components/
│ ├── HelloWorld.vue
│ ├── UserCard.vue
│ └── ProductList.vue
├── App.vue
└── main.js11. 组件通信
<!-- 子组件 Child.vue -->
<template>
<div>
<h2>{{ title }}</h2>
<p>{{ content }}</p>
</div>
</template>
<script>
export default {
name: 'Child',
props: {
title: {
type: String,
required: true
},
content: {
type: String,
default: '默认内容'
}
}
}
</script><!-- 父组件 Parent.vue -->
<template>
<div>
<Child :title="parentTitle" :content="parentContent" />
</div>
</template>
<script>
import Child from './Child.vue'
export default {
name: 'Parent',
components: {
Child
},
data() {
return {
parentTitle: '子组件标题',
parentContent: '这是从父组件传递的内容'
}
}
}
</script><!-- 子组件 Child.vue -->
<template>
<div>
<button @click="sendMessage">发送消息给父组件</button>
</div>
</template>
<script>
export default {
name: 'Child',
methods: {
sendMessage() {
this.$emit('message', '这是子组件发送的消息')
}
}
}
</script><!-- 父组件 Parent.vue -->
<template>
<div>
<p>收到消息:{{ receivedMessage }}</p>
<Child @message="handleMessage" />
</div>
</template>
<script>
import Child from './Child.vue'
export default {
name: 'Parent',
components: {
Child
},
data() {
return {
receivedMessage: ''
}
},
methods: {
handleMessage(message) {
this.receivedMessage = message
}
}
}
</script>// eventBus.js
import Vue from 'vue'
export default new Vue()<!-- 组件A -->
<script>
import eventBus from './eventBus'
export default {
methods: {
sendData() {
eventBus.$emit('data', '这是组件A发送的数据')
}
}
}
</script><!-- 组件B -->
<script>
import eventBus from './eventBus'
export default {
mounted() {
eventBus.$on('data', (data) => {
console.log('收到数据:', data)
})
},
beforeDestroy() {
eventBus.$off('data')
}
}
</script>(详见第14节)
12. 生命周期钩子
创建阶段:
beforeCreate -> created -> beforeMount -> mounted
更新阶段:
beforeUpdate -> updated
销毁阶段:
beforeDestroy -> destroyed<template>
<div>{{ message }}</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!'
}
},
beforeCreate() {
// 实例初始化之后,数据观测和事件配置之前
console.log('beforeCreate')
},
created() {
// 实例创建完成后立即调用
// 此时可以访问data、methods等
console.log('created')
},
beforeMount() {
// 挂载开始之前被调用
console.log('beforeMount')
},
mounted() {
// 实例挂载完成后调用
// 此时可以访问DOM
console.log('mounted')
},
beforeUpdate() {
// 数据更新时调用,发生在虚拟DOM重新渲染之前
console.log('beforeUpdate')
},
updated() {
// 数据更改导致的虚拟DOM重新渲染后调用
console.log('updated')
},
beforeDestroy() {
// 实例销毁之前调用
console.log('beforeDestroy')
},
destroyed() {
// 实例销毁后调用
console.log('destroyed')
}
}
</script><script>
export default {
data() {
return {
timer: null
}
},
mounted() {
// 组件挂载后启动定时器
this.timer = setInterval(() => {
console.log('定时器执行')
}, 1000)
},
beforeDestroy() {
// 组件销毁前清除定时器
if (this.timer) {
clearInterval(this.timer)
}
}
}
</script>13. Vue Router路由
npm install vue-router// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
router,
render: h => h(App)
}).$mount('#app')<!-- App.vue -->
<template>
<div id="app">
<nav>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
</nav>
<router-view/>
</div>
</template>路径参数
// router/index.js
{
path: '/user/:id',
name: 'User',
component: User
}<!-- 组件中获取参数 -->
<template>
<div>User ID: {{ $route.params.id }}</div>
</template>查询参数
<router-link :to="{ path: '/user', query: { id: 123 } }">User</router-link><!-- 组件中获取查询参数 -->
<template>
<div>User ID: {{ $route.query.id }}</div>
</template>// 字符串路径
this.$router.push('/home')
// 对象
this.$router.push({ path: '/home' })
// 命名的路由
this.$router.push({ name: 'Home' })
// 带查询参数
this.$router.push({ path: '/user', query: { id: 123 } })
// 带路径参数
this.$router.push({ path: '/user/123' })// 全局前置守卫
router.beforeEach((to, from, next) => {
// 检查登录状态
if (to.path === '/admin' && !isLoggedIn) {
next('/login')
} else {
next()
}
})
// 组件内守卫
export default {
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
next()
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
next()
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
next()
}
}14. Vuex状态管理
Vuex是一个专为Vue.js应用程序开发的状态管理模式。
npm install vuex// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
},
decrement(state) {
state.count--
}
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
},
getters: {
doubleCount(state) {
return state.count * 2
}
}
})// main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
store,
render: h => h(App)
}).$mount('#app')<template>
<div>
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementAsync">Async +</button>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {
computed: {
...mapState(['count']),
...mapGetters(['doubleCount'])
},
methods: {
...mapMutations(['increment', 'decrement']),
...mapActions(['incrementAsync'])
}
}
</script>// store/modules/user.js
const user = {
namespaced: true,
state: {
name: '',
age: 0
},
mutations: {
SET_NAME(state, name) {
state.name = name
}
},
actions: {
setName({ commit }, name) {
commit('SET_NAME', name)
}
}
}
export default user// store/index.js
import user from './modules/user'
export default new Vuex.Store({
modules: {
user
}
})15. HTTP请求
npm install axios// api/request.js
import axios from 'axios'
const service = axios.create({
baseURL: 'https://api.example.com',
timeout: 5000
})
// 请求拦截器
service.interceptors.request.use(
config => {
// 在发送请求之前做些什么
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
return response.data
},
error => {
return Promise.reject(error)
}
)
export default service// api/user.js
import request from './request'
export function getUserList() {
return request({
url: '/users',
method: 'get'
})
}
export function getUserById(id) {
return request({
url: `/users/${id}`,
method: 'get'
})
}
export function createUser(data) {
return request({
url: '/users',
method: 'post',
data
})
}<template>
<div>
<ul>
<li v-for="user in users" :key="user.id">
{{ user.name }}
</li>
</ul>
</div>
</template>
<script>
import { getUserList } from '@/api/user'
export default {
data() {
return {
users: []
}
},
async mounted() {
try {
const res = await getUserList()
this.users = res.data
} catch (error) {
console.error('获取用户列表失败', error)
}
}
}
</script>16. 实际项目案例
<!-- TodoList.vue -->
<template>
<div class="todo-list">
<h1>Todo List</h1>
<input
v-model="newTodo"
@keyup.enter="addTodo"
placeholder="添加新任务"
>
<ul>
<li v-for="todo in todos" :key="todo.id">
<input
type="checkbox"
v-model="todo.completed"
>
<span :class="{ completed: todo.completed }">
{{ todo.text }}
</span>
<button @click="deleteTodo(todo.id)">删除</button>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'TodoList',
data() {
return {
newTodo: '',
todos: [
{ id: 1, text: '学习Vue', completed: false },
{ id: 2, text: '学习JavaScript', completed: true }
]
}
},
methods: {
addTodo() {
if (this.newTodo.trim()) {
this.todos.push({
id: Date.now(),
text: this.newTodo,
completed: false
})
this.newTodo = ''
}
},
deleteTodo(id) {
this.todos = this.todos.filter(todo => todo.id !== id)
}
}
}
</script>
<style scoped>
.completed {
text-decoration: line-through;
color: #999;
}
</style>17. 总结与进阶
通过本教程,你已经掌握了:
- ✅ Vue2的基本概念和语法
- ✅ 组件开发
- ✅ 组件通信
- ✅ Vue Router路由
- ✅ Vuex状态管理
- ✅ HTTP请求
- ✅ 实际项目开发
Vue3学习
- Composition API
- 性能优化
- 新特性
工程化
- Webpack配置
- 代码分割
- 性能优化
UI框架
- Element UI
- Vuetify
- Ant Design Vue
测试
- 单元测试
- E2E测试
TypeScript
- Vue + TypeScript
- 类型定义
组件设计:单一职责,可复用
代码规范:遵循Vue风格指南
性能优化:合理使用v-if和v-show
状态管理:简单应用用props/emit,复杂应用用Vuex
路由设计:合理的路由结构
Q: v-if和v-show的区别?
A: v-if是条件渲染,v-show是CSS显示/隐藏。
Q: computed和methods的区别?
A: computed有缓存,methods每次调用都执行。
Q: 什么时候用Vuex?
A: 多个组件需要共享状态时使用Vuex。
Q: 如何优化Vue应用性能?
A: 使用v-show替代v-if、合理使用key、懒加载路由等。
结语
Vue.js是一个功能强大、易于学习的前端框架。通过本教程的学习,相信你已经掌握了Vue2的核心功能和使用方法。
记住:
- 多实践:理论结合实践,多写代码
- 理解原理:理解响应式原理、虚拟DOM等
- 关注性能:注意性能优化
- 持续学习:关注Vue新版本和最佳实践
祝你学习愉快,编程顺利! 🚀
本教程由Java突击队学习社区编写,如有问题欢迎反馈。