在前几篇文章中,我们学习了Vue的核心概念和常用指令。今天我们将深入探讨Vue的表单数据收集 、完整的指令系统 以及自定义指令的创建与使用。这些内容是日常开发中最常用的技能。
一、表单数据收集
1.1 基础表单绑定
Vue提供了 v-model 指令来实现表单元素的双向数据绑定。结合修饰符,我们可以轻松处理各种表单场景。
<form @submit.prevent="sendMsg">
<!-- 自动去除首尾空格 -->
<input v-model.trim="userInfo.name">
<!-- 自动转换为数字类型 -->
<input v-model.number="userInfo.age">
<!-- 失焦后更新数据(懒加载) -->
<textarea v-model.lazy="userInfo.area"></textarea>
</form>
v-model 常用修饰符:
| 修饰符 | 功能 | 说明 |
|---|---|---|
.trim |
自动去除首尾空格 | 适用于文本输入 |
.number |
自动将输入转为数字类型 | 适用于数字输入 |
.lazy |
在 change 事件后更新(失焦时) |
减少实时更新频率 |
1.2 不同表单元素的数据收集
不同类型的表单元素,其数据格式和绑定方式有所不同:
| 元素类型 | 数据格式 | 示例 |
|---|---|---|
| 文本框 | 字符串 | <input v-model="name"> |
| 单选框 | 字符串 | <input type="radio" v-model="gender" value="男"> |
| 复选框 | 数组 | <input type="checkbox" v-model="hobbies" value="篮球"> |
| 下拉框 | 字符串 | <select v-model="city"><option value="北京">北京</option></select> |
| 协议框 | 布尔值 | <input type="checkbox" v-model="agreed"> |
完整示例:
<form @submit.prevent="sendMsg">
<!-- 文本框 -->
姓名:<input v-model.trim="userInfo.name"><br>
<!-- 数字输入 -->
年龄:<input v-model.number="userInfo.age" type="number"><br>
<!-- 单选框 -->
性别:
<input type="radio" v-model="userInfo.gender" value="男">男
<input type="radio" v-model="userInfo.gender" value="女">女<br>
<!-- 复选框(数组) -->
爱好:
<input type="checkbox" v-model="userInfo.hobbies" value="篮球">篮球
<input type="checkbox" v-model="userInfo.hobbies" value="足球">足球
<input type="checkbox" v-model="userInfo.hobbies" value="乒乓球">乒乓球<br>
<!-- 下拉框 -->
城市:
<select v-model="userInfo.city">
<option value="">请选择城市</option>
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
</select><br>
<!-- 协议框(布尔值) -->
<input type="checkbox" v-model="userInfo.agreed">同意用户协议<br>
<button type="submit">提交</button>
</form>
1.3 数据提交处理
methods: {
sendMsg() {
// 将JS对象转为JSON格式,便于后端处理
console.log(JSON.stringify(this.userInfo));
// 发送到后端
// axios.post('/api/submit', this.userInfo).then(...)
}
}
二、Vue指令系统
2.1 内容渲染指令
Vue提供了三种内容渲染方式,各有特点和适用场景。
v-text 指令
将内容填充到标签体中,以覆盖的形式填充。即使内容中包含HTML标签,也只会当作普通字符串处理,不会解析。
<!-- 功能等同于 innerText -->
<div v-text="name"></div>
<!-- 等价于 -->
<div>{{ name }}</div>
v-html 指令
将内容填充到标签体中,以覆盖的形式填充,并且会将内容中的HTML标签进行解析。
<!-- 功能等同于 innerHTML -->
<div v-html="htmlContent"></div>
⚠️ 安全警告 :v-html 不要用在用户提交的内容上,可能导致XSS攻击。
XSS攻击示例:
<!-- 用户留言中恶意植入以下代码 -->
<script>alert('恶意代码')</script>
指令对比:
| 指令 | 功能 | 特点 |
|---|---|---|
v-text |
文本渲染 | 覆盖内容,不解析HTML |
v-html |
HTML渲染 | 解析并执行HTML代码 |
{``{ }} |
插值表达式 | 灵活的内容绑定,不覆盖 |
2.2 特殊指令
v-pre 指令
跳过该元素及其子元素的编译过程,提高编译效率,避免不必要的编译。
<!-- 不会编译,直接显示原始内容 -->
<div v-pre>{{ 这里的内容不会被编译 }}</div>
v-once 指令
只渲染元素和组件一次,后续视为静态内容,跳过更新。
<!-- 只渲染一次,后续num变化不再更新 -->
<div v-once>{{ num }}</div>
v-cloak 指令
解决页面闪烁问题(未编译模板显示问题),需配合CSS使用。
/* CSS中定义 */
[v-cloak] {
display: none;
}
<!-- HTML中使用 -->
<div v-cloak>{{ message }}</div>
三、XSS安全防护
3.1 潜在风险示例
<!-- 恶意代码可能通过用户输入注入 -->
<li v-for="item in myList" v-html="item"></li>
3.2 防护措施
-
避免直接使用
v-html渲染用户输入 -
对用户输入进行过滤和转义
-
使用
v-text替代v-html显示纯文本// 对用户输入进行转义
function escapeHtml(text) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(text));
return div.innerHTML;
}
四、自定义指令
当Vue内置指令无法满足需求时,我们可以创建自定义指令。
4.1 局部自定义指令
在组件的 directives 选项中定义,仅在该组件中可用。
directives: {
// 简写形式(在bind和update时触发)
'text-danger': function(element, binding) {
element.innerHTML = binding.value;
element.style.color = 'red';
},
// 完整写法
focus: {
// 指令第一次绑定到元素时调用
bind(el, binding) {
// 初始化工作
},
// 元素插入父节点时调用
inserted(el) {
el.focus();
},
// 所在组件更新时调用
update(el, binding) {
// 更新处理
}
}
}
4.2 全局自定义指令
通过 Vue.directive() 注册,在整个应用中都可使用。
// 全局注册
Vue.directive('bind-blue', {
// 指令绑定到元素时
bind: function(element, binding) {
element.value = binding.value;
},
// 元素插入父节点时
inserted: function(element, binding) {
element.parentNode.style.backgroundColor = 'blue';
}
});
4.3 指令钩子函数
| 钩子函数 | 调用时机 |
|---|---|
bind |
指令第一次绑定到元素时 |
inserted |
元素插入父节点时 |
update |
所在组件更新时 |
componentUpdated |
组件及其子组件全部更新后 |
unbind |
指令与元素解绑时 |
4.4 钩子函数参数
指令钩子函数接收以下参数:
el:指令所绑定的元素,可直接操作DOMbinding:一个对象,包含以下属性:name:指令名,不含v-前缀value:指令的绑定值oldValue:指令绑定的前一个值(仅在update和componentUpdated中可用)expression:绑定值的字符串形式arg:传给指令的参数modifiers:包含修饰符的对象
示例:
<div v-demo:foo.a.b="message"></div>
Vue.directive('demo', {
bind(el, binding) {
console.log(binding.name); // 'demo'
console.log(binding.value); // message的值
console.log(binding.expression); // 'message'
console.log(binding.arg); // 'foo'
console.log(binding.modifiers); // { a: true, b: true }
}
});
总结
今天我们学习了以下内容:
表单处理
- 合理使用
.trim、.number、.lazy修饰符 - 根据表单元素类型选择合适的数据格式
- 提交时使用
JSON.stringify转换为JSON格式
内容渲染
- 纯文本显示使用
v-text或{``{ }} - 需要解析HTML时使用
v-html,但要警惕XSS攻击 - 优先使用
v-text替代v-html显示用户输入
性能优化
- 使用
v-pre跳过不需要编译的区域 - 使用
v-once标记静态内容,减少更新开销 - 使用
v-cloak解决页面闪烁问题
安全防护
- 警惕XSS攻击,谨慎使用
v-html - 对用户输入进行过滤和转义
- 避免在
v-html中渲染用户提交的内容
代码复用
- 通过自定义指令封装常用功能
- 全局指令适用于通用功能,局部指令适用于组件特定功能
- 合理使用指令钩子函数,在合适的时机操作DOM
掌握了表单数据收集、指令系统和自定义指令,你的Vue开发能力已经相当全面了。