Vue:事件 / $event / $nextTick() / $refs /表单校验

更多
2021年04月06日 17点54分 作者:叶飞 修改

v-on

指令v-on:后面直接跟事件名,比如:click/change/focus/blur/mouseover/keyup……

引号("")中指令的值可以是:

JavaScript表达式

比如:
<h1 v-on:click="counter++" id="yz-hello">{{counter}}</h1>
但counter只能是vue data中的值:
data: {
    counter: 1
},
演示:声明一个全局变量n,click中n++仍然报错。

方法名

当handler的逻辑变得复杂,就需要将其封装到一个方法中:

methods: {
    plus: function () {
        this.counter++;
    }
}

模板中调用:(一样不能是vue外部的function)

<h1 v-on:click="plus" id="yz-hello">{{counter}}</h1>
注意:v-on:click的值只是方法名,没有圆括号。

内联(inline)方法

但是,v-on:click的值中,方法名后面加上圆括号也没问题:
<h1 v-on:click="plus()" id="yz-hello">{{counter}}</h1>

这实际上就是一个方法调用表达式,和只写方法名一样一样的!^_^

但是,@试一试@:直接在click后面这样写行不行呢?

<h1 v-on:click="alert('皮一下很开森')" id="yz-hello">{{counter}}</h1>
结果会报错:

Property or method "alert" is not defined on the instance

说明什么?vue里面事件的运行时环境是vue实例!切记切记。


纯粹 vs 包容

习惯于原生JavaScript或者JQuery事件绑定机制的同学可能还在等……

下面呢?下面没了,^_^Vue没有提供在JavaScript中找到某个DOM节点,然后对其绑定事件的机制。

在 HTML 中监听事件不是违背了关注点分离 (separation of concern)原则么?

PS:提这个问题的同学一定不是从头跟着飞哥学的……

软件工程哲学:

  • 要不要纯粹?纯CSS,纯面向对象,纯……
  • 什么是工程:利用一切现有技术,解决当前面临的具体问题
  • 利弊权衡,因地制宜
  • ……

具体到Vue的事件机制(官方解释),飞哥的补充:

  • 关注分离的最终目的是什么?分工明确
  • Vue的写法有没有影响到这个目的?
  • 区分模板和最终生成的HTML:v-on:是加在模板上的

大赞!实际上,有很多问题,100%严格执行并无必要


this和event

在传统的JavaScript事件处理函数中,this指的是触发事件的HTML元素。

但在vue的methods中,this显然指代的是vue的当前实例。这虽然符合JavaScript语法,但要获取事件触发的HTML元素,怎么办呢?

从event中获得,根据内容不同:

方法名

如果模板中v-on:后跟的只是一个方法名,那么methods中方法(回调函数)可以带一个event参数:
plus: function (event) {
    event.preventDefault();
    console.log(event.target);
    this.counter++;
}
这里的event和传统的JavaScript事件回调函数中的event一致,都是指的原生DOM事件。

内联方法

当模板中v-on:后跟的是一个方法调用时,可以将 $eventVue实例属性)作为参数传入:
<h1 v-on:click="plus($event)" id="yz-hello">{{counter}}</h1>

这样,methods中plus也中也能拿到event。

演示:v-on:click="plus()"时,methods中event为undefined


事件修饰符

表单绑定时类似,都是在事件名之后添加以点(.)开头的指令后缀:

  • .prevent:阻止默认事件
    <a href="http://17bang.ren" v-on:click.prevent="plus($event)" id="yz-hello">{{counter}}</a>
  • .stop:阻止单击事件继续传播
  • .self:只当在 event.target 是当前元素自身时触发处理函数
  • .capture:使用捕获机制(默认冒泡)
    演示用代码:
    div {
        border: 1px solid;
    }
    <div v-on:click="outer" style="padding:40px;" id="yz-hello">
        <div v-on:click="inner">inner</div>
    </div>
    outer: function (event) {
        console.log('outer:');
    },
    inner: function (event) {
        console.log('inner');
    }
  • .once:只会触发一次
  • .passive:提升滚动事件移动端的性能

按键修饰符

<!-- 只有在 `key` 是 `Enter` 时调用 `vm.inner()` -->
<input v-on:keyup.enter="inner">

新增系统修饰键

<!-- Alt + C -->
<input v-on:keyup.alt.67="inner">


示例

点击显示/隐藏

绑定click事件:
<button type="button" v-on:click="toggle">提交</button>
关键点在于:如何显示和隐藏其HTML内容?更改其style样式,DOM树上remove,……?

都不是,还记得v-if/v-show么?

<span v-show="show" style="border: 1px dashed; padding: 5px;">源栈欢迎您</span>

显示,将其值设为true;隐藏,设为false即可。

toggle: function () {
    this.show = !this.show;
}

tooltip

模拟bootstrap实现。准备一个样式:

.tooltip {
    background-color: cadetblue;
    position: relative;
    right: 65px;
    bottom: 20px;
}

当然要使用mouseover事件,但也不要忘了mouseout!所以这是在一个DOM元素上绑定了多个事件:

<span v-on:mouseover="show" v-on:mouseout="hide" v-bind:title="showTitle">源栈欢迎您</span>
<span v-if="shown" class="tooltip">{{showTitle}}</span>
show: function () {
    this.shown = true;
},
hide: function () {
    this.shown = false;
}

铃铛闪烁

用bell文本代替铃铛,指定其最初的颜色样式:
<div id="bell" v-on:load="changeColor" v-bind:style="{color:color}">bell</div>

然后你可能会想到onload事件,但是,只有document、frame、image之类的才能绑定load事件啊!难道要把id加在body上面?试一下:

咋整?还记得我们学过的生命周期钩子么?那些钩子函数是不是和事件处理函数类似的?我们可以选择mounted:

mounted: function () {
    setInterval(function () {
        if (this.color == 'red') {
            this.color = 'blue';
        } else {
            this.color = 'red';
        }
    }, 1000)    //每隔一秒变换一次颜色
},
行不行?为什么?(F12断点调试:此时的this不再是vue实例)

setInterval(function () {
    //...
}.bind(this), 1000)

复习:bind()


获取DOM元素

从中我们可以看出,vue和传统的JavaScript编程根本性的差别:
  • JQuery:找到DOM元素,从DOM元素中获得数据,操作DOM元素 —— DOM查找驱动
  • Vue:直接操作数据(vue data),用数据来影响DOM元素的呈现 —— vue data变更驱动

迫不得已的DOM操作

而且,Vue是不建议直接操作DOM的。但有的时候,因为各种各样的原因,我们还是不得不找到DOM元素……比如:

上述tooltip的例子中,DOM原有的title提示,如何消掉呢?

  • event.preventDefault()吗?
  • this.showTitle += '';吗?

演示说明:都不行的。只有通过DOM操作将其属性title值设置为空才行(HTML中已添加id="slagon"):

document.getElementById('slagon').title = '';

陷阱:$NextTick()

修改一下上述代码,每次mouseover我们都更新一下title

this.times++;    //初始值为1,每次递增
this.showTitle += ('-' + this.times);

这是因为Vue的渲染是异步的,所以当我们执行的getElementById()的时候,拿到的是渲染前的数据:这是一个大坑!

所以Vue提供了内置的$nextTick()方法,传入的回调函数会在渲染完成之后再执行:

this.$nextTick(function () {
    document.getElementById('slagon').title = '';
})

$refs

Vue还提供了一个快捷的定位某DOM元素的方式:
  • 首先在HTML中添加一个ref属性,值由开发人员指定:
    <span ref="slagon" 
  • 然后就可以在用Vue实例调用:
    this.$refs.slagon.title = ''

这样可以从Vue的template开始查起,性能可以有些微提升。


输入验证

因为有ViewData绑定,在vue里面进入输入验证也是已经非常容易的事情:只需要在相应事件(比如:submit/blur/change)中绑定验证方法即可。

<form novalidate="true" id="yz-hello"><!-- 注意novalidate --> 
    <input v-on:change="require" v-model="sname" >
    <p v-if="error">{{error}}</p>
</form>
        require:function(){
            if (!this.sname) {//this.能省略么?
                this.error = '* 用户名必须填写';
            }//一眼看去,这代码有问题不? }

牢记:有if就一定要想到else

@想一想@:上述代码能阻止form表单的提交么?

所以我们还需要给form添加submit事件:(@submit是vue3推出的简写方式)

<form @submit="checkAll" novalidate="true" id="yz-hello">
checkAll: function (event) {
    if (this.error) {
        event.preventDefault();
    }
}

@想一想@:能看出上述代码的问题么?

不能用this.error来判断用户的输入是否正确哟!因为this.error是因为change(或其他)事件而触发,form表单提交的时候,这些事件完全有可能没有触发,所以this.error仍然为空。还是要使用this.sname进行判断。

多个表单元素

当有多个表单元素时,我们通常将error设置为一个对象

error: {}
这样可以用:
  • error.sname代替errorOfSname
  • error.password代替errorOfPassword
  • ……
让代码更整洁更有条理性。

而且还可以重用require方法,

<input v-on:blur="require" v-model="sname">
<input v-on:blur="require" v-model="password">
这就需要把错误信息直接写在HTML template中:
<span v-if="error.sname">* 用户名必须填写</span>
<span v-if="error.password">* 密码必须填写</span>

require方法非常简单:

this.error.sname = !this.sname;
this.error.password = !this.password;

演示:未能显示错误信息!

注意:error一定不能是空对象,要把用到的属性都列出来:

error: {
    sname: false,
    password: false,
    confirmPassword: false
}
复习:set()方法


其他

同学们可能想到能不能使用JQuery?可以的。Vue和JQuery以及JQuery的各种插件(如validation)并不冲突,直接引入稍加调整就可以使用。

但是,一般来说,Vue推荐的validation插件是:VeeValidatevuelidate,我们后面再讲。


作业

将之前的JavaScript事件,用vue重写一遍:

  1. JavaScript:事件:绑定/this/event
  2. JavaScript:事件:常用类型详述
Vue.js 事件
赞: 0 踩: 0

打赏
已收到打赏的 帮帮币

你的 打赏 非常重要!
为了保证文章的质量,每一篇文章的发布,都已经消耗了作者 1 枚 帮帮币
没有“帮帮币”,作者无法发布新的文章。

全系列阅读
评论 / 0

Web前端


HTML和CSS

HTML最常用的标签和属性,含HTML5的语义标签和新属性,但不包含需要JavaScript操作的HTML5 API,以及CSS基础(简述CSS 3动画相关),以及bootstrap.js以外的Bootstrap的内容。

Javascript入门

Javascript的基本语法:为能使用JQuery和Bootstrap.js的使用打下基础

ES进阶

借助于ES6,讲解JavaScript中一些更复杂的语法特性,如作用域、闭包、面向对象、原型链、this变化、module等

Bootstrap和JQuery

CSS框架Bootstrap,和JavaScript的JQuery类库(含Ajax)

Vue.js

全部
关键字



帮助

反馈