一、昨日内容回顾
上一节学习了 Vue 的数据绑定、条件渲染、列表渲染、事件监听和 v-model。本节继续使用 Vue 2 示例,重点补充 JavaScript 的箭头函数、循环方式、事件修饰符、表单控件,以及前端通过 Ajax 请求后端接口的方法。
二、ES6 箭头函数
箭头函数是 ES6 提供的函数简写形式,适合编写短小的回调函数。它可以省略 function,只有一个参数时还可以省略参数括号,只有一个表达式时可以省略大括号和 return。
js
// 普通函数
function add(a, b) {
return a + b
}
// 箭头函数的几种写法
const sayHello = () => {
console.log('hello')
}
const square = n => n * n
const sum = (a, b) => a + b
sayHello()
console.log(square(4))
console.log(sum(4, 6))
箭头函数没有自己的 this、arguments 和 prototype。其中最重要的是 this:箭头函数会捕获定义位置外层作用域的 this,不会因为调用方式改变。Vue 的 methods 建议使用普通方法,让 Vue 正确绑定组件实例;在方法内部作为回调时,箭头函数可以方便地继续使用外层的 this。
js
new Vue({
data: { search: '', words: ['vue', 'react', 'angular'] },
methods: {
filterWords() {
// filter 的回调使用箭头函数,可以直接访问外层方法的 this。
return this.words.filter(word => word.includes(this.search))
}
}
})
三、JavaScript 常见循环
不同循环适合不同场景。for 可以控制下标和步长,for...in 遍历对象属性名,for...of 遍历可迭代对象中的值,forEach 适合对数组的每一项执行操作。
js
const numbers = [3, 4, 5]
// 传统 for:可以使用下标、break 和 continue。
for (let i = 0; i < numbers.length; i++) {
console.log(i, numbers[i])
}
// for...in:数组中得到下标,对象中得到属性名。
for (const index in numbers) {
console.log(index, numbers[index])
}
const user = { name: 'lqz', age: 19 }
for (const key in user) {
console.log(key, user[key])
}
// for...of:得到数组、字符串等可迭代对象的值。
for (const value of numbers) {
console.log(value)
}
// forEach:参数依次是 value、index、array。
numbers.forEach((value, index) => {
console.log(index, value)
})
普通对象不是可迭代对象,不能直接使用 for...of。如果需要以键值对形式遍历,可以使用 Object.entries(user);Vue 模板中的 v-for 则可以直接遍历对象。
js
Object.entries(user).forEach(([key, value]) => {
console.log(key, value)
})
四、事件修饰符
Vue 事件修饰符可以把常见的 DOM 事件处理写在模板中,减少手动调用 event.stopPropagation() 或 event.preventDefault() 的代码。
| 修饰符 | 作用 |
|---|---|
.stop |
阻止事件继续冒泡 |
.self |
只有点击当前元素本身时才处理 |
.prevent |
阻止浏览器默认行为 |
.once |
事件只触发一次 |
.capture |
使用捕获阶段监听事件 |
html
<div id="app">
<div class="panel" @click="parentClick">
<button @click.stop="buttonClick">点击后不触发父元素</button>
</div>
<div class="panel" @click.self="selfClick">
<button @click="childClick">点击按钮不会触发 selfClick</button>
</div>
<a href="https://example.com" @click.prevent="linkClick">暂不跳转</a>
<button @click.once="pay">只能点击一次</button>
</div>
.self 与 .stop 的含义不同:.stop 是阻止事件向上冒泡,.self 是判断事件目标是否就是当前元素。修饰符只应处理交互行为,权限、支付等关键逻辑仍必须在后端再次校验。
五、按键修饰符
键盘事件可以使用 .enter、.esc、.tab、.delete、.up、.down、.left 和 .right 等修饰符,也可以监听系统组合键。
html
<div id="app">
<input
v-model="keyword"
@keyup.enter="search"
placeholder="输入关键词后按回车"
>
<button @click="search">搜索</button>
</div>
<script>
new Vue({
el: '#app',
data: { keyword: '' },
methods: {
search() {
console.log('搜索:', this.keyword)
}
}
})
</script>
相比直接使用数字 keyCode,语义化的 .enter 更容易理解,也不依赖不同浏览器的键码差异。需要读取更多键盘信息时,可以在方法参数中接收原生事件对象。
六、表单控件与 v-model
v-model 会根据控件类型选择合适的属性和事件:文本框通常对应字符串,单个复选框对应布尔值,多个同名复选框对应数组,单选框对应字符串或数字。
html
<div id="app">
<p>用户名:<input v-model.trim="username"></p>
<p>密码:<input type="password" v-model="password"></p>
<label><input type="checkbox" v-model="remember"> 记住登录</label>
<p>爱好:</p>
<label><input type="checkbox" v-model="hobbies" value="篮球"> 篮球</label>
<label><input type="checkbox" v-model="hobbies" value="足球"> 足球</label>
<label><input type="checkbox" v-model="hobbies" value="游泳"> 游泳</label>
<p>性别:</p>
<label><input type="radio" v-model="gender" value="男"> 男</label>
<label><input type="radio" v-model="gender" value="女"> 女</label>
<button @click="submit">提交</button>
</div>
<script>
new Vue({
el: '#app',
data: {
username: '',
password: '',
remember: false,
hobbies: [],
gender: ''
},
methods: {
submit() {
console.log({
username: this.username,
password: this.password,
remember: this.remember,
hobbies: this.hobbies,
gender: this.gender
})
}
}
})
</script>
v-model 只负责在前端同步数据,不代表表单已经安全。登录、注册等数据仍需要后端校验,密码也不能明文保存。
七、购物车综合案例
购物车同时练习了 v-for、v-model、事件、计算总价和全选逻辑。选中商品数组保存的是商品对象,数量变化后总价会根据当前选中项重新计算。
html
<div id="app">
<table>
<thead>
<tr>
<th>商品</th><th>单价</th><th>数量</th><th>选择</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in goods" :key="item.id">
<td>{{ item.name }}</td>
<td>{{ item.price }} 元</td>
<td>
<button @click="decrease(item)">-</button>
{{ item.number }}
<button @click="item.number++">+</button>
</td>
<td>
<input type="checkbox" v-model="selectedGoods" :value="item">
</td>
</tr>
</tbody>
</table>
<label>
<input type="checkbox" v-model="checkAll" @change="toggleAll">
全选
</label>
<p>总价:{{ totalPrice }} 元</p>
</div>
<script>
new Vue({
el: '#app',
data: {
goods: [
{ id: 1, name: '脸盆', price: 9, number: 2 },
{ id: 2, name: '水杯', price: 19, number: 1 },
{ id: 3, name: '电脑', price: 6999, number: 1 }
],
selectedGoods: [],
checkAll: false
},
computed: {
totalPrice() {
return this.selectedGoods.reduce(
(total, item) => total + item.price * item.number,
0
)
}
},
watch: {
selectedGoods: {
deep: true,
handler(value) {
this.checkAll = value.length === this.goods.length
}
}
},
methods: {
toggleAll() {
this.selectedGoods = this.checkAll ? this.goods.slice() : []
},
decrease(item) {
if (item.number > 1) item.number--
}
}
})
</script>
总价属于"由已有数据计算出来的值",因此使用 computed 比每次在模板中调用方法更合适:依赖不变时会缓存结果。全选状态与单个商品选择状态互相影响,使用深度监听可以在选择数组变化后同步更新全选框。
八、v-model 修饰符
v-model 修饰符用于改变同步时机或数据类型:
.lazy:从input事件改为在change后同步。.number:尝试把输入值转换为数字。.trim:去除输入字符串首尾空格。
html
<div id="app">
<input v-model.lazy="text" placeholder="失去焦点后同步">
<input v-model.number="amount" placeholder="尝试转换数字">
<input v-model.trim="name" placeholder="去除首尾空格">
<p>{{ text }} / {{ amount }} / {{ name }}</p>
</div>
.number 不是严格的类型校验,空字符串或无法转换的输入仍可能保留原值。金额、年龄等数据在提交前仍应在后端进行类型和范围校验。
九、Ajax 与接口请求
Ajax 泛指浏览器在页面不刷新时与服务器交换数据的方式。早期可以直接使用 XMLHttpRequest,jQuery 对它做了封装;现代项目更常使用 fetch 或 axios。axios 提供统一的请求配置、拦截器、错误处理和 JSON 转换,适合在 Vue 项目中集中管理接口。
1. axios 请求示例
html
<div id="app">
<button @click="loadUser">加载用户信息</button>
<p>用户名:{{ user.username }}</p>
<p>年龄:{{ user.age }}</p>
<p>性别:{{ user.gender }}</p>
<p v-if="error" class="error">{{ error }}</p>
</div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
user: { username: '', age: '', gender: '' },
error: ''
},
methods: {
async loadUser() {
this.error = ''
try {
const response = await axios.get('http://127.0.0.1:8888/info')
// axios 的响应体位于 response.data。
this.user = response.data
} catch (error) {
console.error(error)
this.error = '用户信息加载失败,请稍后重试'
}
}
}
})
</script>
使用 async/await 可以让异步流程更接近同步代码;try/catch 用于处理网络失败和非预期响应。实际项目还应处理加载中状态、HTTP 状态码和接口业务错误码。
2. Flask 接口示例
python
from flask import Flask, jsonify
app = Flask(__name__)
@app.get('/info')
def info():
response = jsonify({
'username': '张三',
'age': 99,
'gender': '男',
})
# 仅用于演示;生产环境应限制为明确的前端域名。
response.headers['Access-Control-Allow-Origin'] = 'http://localhost:8080'
return response
if __name__ == '__main__':
app.run(port=8888)
当前端和后端的协议、域名或端口不同时,浏览器会执行同源策略检查,服务端需要通过 CORS 响应头明确允许来源。Access-Control-Allow-Origin: * 不适合携带凭证的请求,也不建议在生产环境无限制开放。
十、总结与练习
- 箭头函数适合回调,重点理解它没有自己的
this;Vue 的方法本身不要随意改成箭头函数。 for...in遍历键,for...of遍历值,数组还可以使用forEach、map、filter等方法。- 事件修饰符负责冒泡、默认行为和触发次数;按键修饰符让键盘交互更直观。
- 复选框的
v-model可以绑定布尔值或数组,购物车应使用唯一key、计算属性和清晰的全选逻辑。 - axios 适合 Vue 项目中的接口请求,跨域问题需要由后端 CORS 配置配合解决。