Vue2 实战进阶:表单数据收集、指令系统与自定义指令全解析

在前几篇文章中,我们学习了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 防护措施

  1. 避免直接使用 v-html 渲染用户输入

  2. 对用户输入进行过滤和转义

  3. 使用 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:指令所绑定的元素,可直接操作DOM
  • binding:一个对象,包含以下属性:
    • name:指令名,不含 v- 前缀
    • value:指令的绑定值
    • oldValue:指令绑定的前一个值(仅在 updatecomponentUpdated 中可用)
    • 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开发能力已经相当全面了。

相关推荐
计算机魔术师1 小时前
实测GPT-6 Astra后,我发现过去十年的专业门槛正在失效
前端
虎头金猫1 小时前
Pic Smaller 本地部署:压缩图片、转换格式,再搭建自己的 Web 工具
运维·前端·开源·aigc·ai编程·ai写作
hunterandroid1 小时前
Android ANR 排查实战:从线上告警到主线程卡点定位
android·前端
图扑软件2 小时前
下篇・换墨|主题/多语言/移动端,一套组件全覆盖
前端·javascript·ui·性能优化·数据可视化
程序员老赵2 小时前
Docker 部署 TeX Live:轻松搭建 LaTeX 论文排版编译平台
前端·docker·latex
计算机毕设定制辅导-无忧学长2 小时前
《基于Spring Boot传承之光非遗陶瓷烧造产品交易平台的设计与实现》
java·vue.js·spring boot·后端·毕业设计
Zhu7582 小时前
部署安全的pg数据库,搭配pgadmin实现web界面管理
前端·数据库·安全
杉氧2 小时前
打破边界(二):在 React Native 中嵌入原生 UI 组件 (Native UI Components)
android·前端·react native
计算机学姐2 小时前
基于SpringBoot的旅游系统的设计与实现
java·vue.js·spring boot·后端·spring·java-ee·旅游