7、事件处理
7.1、事件的基本处理
- 事件的使用
- 使用v-on:xxx或者用@xxx绑定事件,其中XXX是事件名
- 事件的回调需要配置在methods对象中,最终出现在VM上
- methods配置的函数,不需要箭头函数
vue
<div id="root">
<h1>{{hi}}</h1>
<!-- 绑定事件 v-on:click 当被点击的时候 -->
<button v-on:click="click">点我</button>
<!-- 两种写法都可以,下面是上面的缩写 -->
<button @click="click">点我</button>
</div>
<script>
new Vue({
el:'#root',
data:{
hi:'hi'
},
//方法体
methods:{
click(){
alert("hellword")
}
}
})
</script>
7.2、事件修饰符
在js中提供的事件修饰符,vue也给我们提供了
常见的有 .prevent、.stop、.once
- .prevent 阻止默认事件
- .stop 阻止事件冒泡
- .once 事件只触发一次
- .capture 使用事件的捕获模式
- .self 只有event.target是当前元素时才触发
- .passive 事件的默认行为立即执行,无需等回调
- 修饰符key连续写 如 @click.prevent.stop
vue
<div id="root">
<!-- 阻止默认事件 -->
<a href="http://www.baidu.com" @click.prevent="show">跳转</a>
<!-- 阻止冒泡 -->
<div @click="show">
<button @click.stop="show">阻止冒泡</button>
</div>
<!-- 只触发一次事件 -->
<button @click.once="show">Once</button>
</div>
<script>
new Vue({
el:'#root',
methods:{
show(e){
alert('hello')
}
}
})
</script>
7.3、键盘事件
- vue中的键盘事件 @keydown.xxx 或者@keyup.xxx
- vue中提供的常用的键盘别名:
- enter(回车键)
- delete(删除键指删除和退格俩个键)
- esc(退出键)
- space(空格)
- tab(换行 必须配合keydown去使用)
- up (上)
- down(下)
- left(左)
- right(右)
- 未提供的别名按键,也可以通过原始的key值去绑定,但要注意像CapsLock(转换大小写键)要写成 caps-lock(全小写加短横线)
- 系统特殊的键位 ctrl、alt、shift、win键等
- 如果配合keyup使用 必须按下修饰键的同时加上一个键位 如 同时按下ctrl+A键位才会触发
- 也可以 @keyup.crtl.y 这样 只有按下ctrl+y 键位才会触发
- 配合keydown键可正常使用
- 自定义按键:Vue.config.keyCodes.自定义的键位 = 键码
vue
C:\Users\Administrator\Desktop\vueB\vue2\08_计算属性\1、插值语法实现.htmlx <div id="root"> <!-- 通过enter键触发事件拿值 --> <input type="text" placeholder="提示" @keyup.enter = 'show'> </div> <script> new Vue({ el:'#root', methods:{ show(e){ //拿当前触发元素的值 console.log(e.target.value); } } }) </script>
8、计算属性
效果:
8.1、使用插值语法实现
vue
<div id="root">
姓:<input type="text" v-model="lastName"><br>
名:<input type="text" v-model="firstName"><br>
<!-- 通过拼接的方式 -->
全名:<span>{{lastName}}{{firstName}}</span>
</div>
<script>
new Vue({
el:'#root',
data:{
lastName:'z',
firstName:'y',
}
})
</script>
只能进行简单的计算,不能太复杂
8.2、methods方法实现
vue
<div id="root">
姓:<input type="text" v-model="lastName"><br>
名:<input type="text" v-model="firstName"><br>
<!-- 通过methods方法的方式 -->
<!-- 这里必须加() 否则会不会有值,只会打印整个方法 -->
全名:<span>{{fullName()}}</span><br>
全名:<span>{{fullName()}}</span><br>
全名:<span>{{fullName()}}</span><br>
全名:<span>{{fullName()}}</span>
</div>
<script>
new Vue({
el:'#root',
data:{
lastName:'z',
firstName:'y',
},
methods:{
fullName(){
console.log("调用了");
return this.lastName+this.firstName
}
}
})
</script>
8.3、computed计算属性实现
- 计算属性
- 定义:要用的属性都是通过
已经有的属性
计算得来。 - 原理:底层借助了Object.defineProperty方法提供getter和setter
- 优势:与methods实现相比,内部有缓存机制,效率更高
- 注意事项
- 如果计算属性要被修改,那必须用set去响应修改,并要引起被计算属性的值发生变化
- 定义:要用的属性都是通过
vue
<div id="root">
姓:<input type="text" v-model="lastName"><br>
名:<input type="text" v-model="firstName"><br>
全名:<span>{{fullName}}</span><br>
全名:<span>{{fullName}}</span><br>
全名:<span>{{fullName}}</span><br>
全名:<span>{{fullName}}</span><br>
</div>
<script>
new Vue({
el:'#root',
data:{
lastName:'z',
firstName:'y',
},
computed:{
fullName:{
get(){
// 这里为了set方法方便使用空格连接
console.log("被调用");
return this.lastName+' '+this.firstName
},
set(){
const arr = value.split(' ')
this.lastName = arr[0]
this.firstName = arr[1]
}
}
}
})
</script>
9、监视属性
9.1、基础监视
- 监视属性watch:
- 用法:当监视的属性发生变化时,自动调用进行相关参数
- 可以监听 计算属性
- 注意事项
- 监视属性必须存在,才能监视。不存在控制台不会提示
- 两种写法
- 通过new Vue时传入watch配置,用于已经知道需要监听的属性
- 通过vm.$watch监视,用于开始并不知道需要监视时
vue
<div id="root">
<h1>数字为{{info}}</h1>
<button @click="change">改变数字</button>
</div>
<script>
const vm = new Vue({
el:'#root',
data:{
value:true
},
computed:{
info(){
return this.value ? '1':'2'
}
},
methods:{
//取相反
change(){
this.value = !this.value
}
},
// 第一种写法
// watch:{
// value:{
// //初始化时handler被调用一次
// immediate:true,
// //当监听到value值被改变时 handler被调用
// handler(newValue,oldValue){
// console.log("value被修改了"+newValue+" "+oldValue);
// }
// }
// }
})
//第二种写法
vm.$watch('value',{
immediate:true,
handler(newValue,oldValue){
console.log("value被修改了"+newValue+" "+oldValue);
}
})
</script>
9.2、深度监视
在vue中watch方法默认不会监视多层级的值
- vue中给我们提供了方法deep
- vue自身可以监控到内部值的改变,但是vue提供的watch默认不可以
- 需要根据数据具体结构来决定是否开启deep
vue
<div id="root">
<h1>数字为{{value.a}}</h1>
<button @click="value.a++">add</button>
</div>
<script>
const vm = new Vue({
el:'#root',
data:{
value:{
a:1,
b:2,
}
},
watch:{
value:{
//开启深度监测
deep:true,
immediate:true,
handler(){
console.log("value被修改了");
}
}
}
})
</script>
9.3、watch与computed区别
我们拿一个小案例来举例
computed版本
watch版本
- 从代码层面来看两者对比,conputed的代码更精简
- 但如果涉及到异步操作时,只有watch能够实现
- 如:让它延迟一秒后再显示修改的全名
- 这里涉及到两个小原则
- 只要是被Vue管理的函数,最好都写成普通函数,这样this指向的才是组件实例对象
- 不被vue所管理的函数(定时器回调函数、ajax回调函数),最好写成箭头函数这样this才能指向组件实例对象
10、绑定样式
10.1、绑定class样式
- 绑定class样式
- 字符串写法
- 用于样式类名不确定,需要动态指定
- 数组写法
- 用于要绑定的样式个数不确定、名字也不确定
- 对象写法
- 用于要绑定的样式个数确定、名字也确定,但要动态决定用不用
- 字符串写法
- 字符串写法
通过改变mood的值来实现切换
- 数组的写法
方便以后管理样式,如果不需要有些样式,可通过数组形式删除
- 对象写法
动态的决定样式是否开启
10.2、内联style样式写法
- 内联样式写法
- :style="{fontSize:xxx}"其中xxx是动态值
- 也可以写成对象形式
以对象的形式存储,方便修改
11、条件渲染
-
条件渲染
-
v-if( 与JavaScript中的if作用相同)
-
写法:v-if="表达式"
-
v-else-if="表达式"
-
v-else="表达式"
-
适用于:用于切换频率较低的场景
-
特点:不展示的Dom元素直接被删除
-
三个表达式可以连用,但是结构必须在一起,不能拆分
-
-
v-show
- 写法:v-show="表达式"
- 适用于:切换频率较高的 场景
- 特点:不展示的Dom元素只会被display隐藏
- 写法:v-show="表达式"
-
注意事项
- v-if被触发时,会导致元素直接被删除,获取不到元素
11.1、v-if的使用
vue
<div id="root">
<!-- v-if条件渲染 -->
<h2 v-if="n===1">1</h2>
<h2 v-else-if="n===2">2</h2>
<h2 v-else="n===3">3</h2>
<button @click="n++">v-if测试</button>
</div>
<script>
new Vue({
el:'#root',
data:{
n:0
}
})
</script>
可以看到以前的Dom元素随着切换会被移除
11.2、v-show的使用
vue
<div id="root">
<!-- v-if条件渲染 -->
<h1 v-show="false">1</h1>
<h2 v-show="1===1">2</h2>
</div>
<script>
new Vue({
el:'#root'
})
</script>
这里可以看到v-show等于false时元素没有被移除
12、列表渲染
12.1、v-for指令
- v-for指令
- 用于展示列表数据
- 语法:v-for="(p,index) of persons" :key="index"
- 可遍历:数组、对象、字符串、指定次数
- :key的作用
- 虚拟Dom中的key作用
- 作为虚拟Dom中的对象标识,当数据发生变化,会生成一个新的虚拟Dom,随后新旧Dom进行一个比对
- 新旧Dom比对规则
- 若旧Dom找到与新Dom相同的Key
- 虚拟Dom中内容没变,直接使用前面的真实Dom
- 若发生变化则生成新的真实Dom,替换旧的
- 虚拟Dom中的key作用
vue
<div id="root">
<!-- 遍历数组 -->
<h2>数组</h2>
<ul>
<!-- 这里必须直接列表的Key 否则vue会把整个Li当成一个 会报错 -->
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
<!-- 遍历数组 -->
<h2>数组</h2>
<ul>
<li v-for="(p,index) in student" :key="index">
{{p}}--{{index}}
</li>
</ul>
</div>
<script>
new Vue({
el:'#root',
data:{
persons:[
{id:'01',name:'zy',age:'18'},
{id:'02',name:'zy2',age:'19'},
{id:'03',name:'zy3',age:'20'},
],
student:{name:'zy',age:'18'}
}
})
</script>
这里如果用index(索引)作为key,我们在最前面追加一个数据的时候,会导致vue 复用前面的input 会出现下面的结果
推荐使用 p.id作为key
12.2、列表模糊查找过滤案例
先看效果
- watch监听实现
vue
<div id="root">
<h2>模糊查找案例</h2>
<!-- 建立一个输入框 取值用于模糊查找 -->
<input type="text" placeholder="输入要查找的名字" v-model="likeName">
<ul>
<li v-for="(p,index) in newPersons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
persons: [
{ id: '01', name: '周冬雨', age: '18', sex: 'woman' },
{ id: '02', name: '马冬梅', age: '19', sex: 'woman' },
{ id: '03', name: '周杰伦', age: '21', sex: 'man' },
{ id: '04', name: '马东伦', age: '20', sex: 'man' },
],
likeName:'',
//不对原数组进行操作
newPersons: []
},
//监听事件
watch: {
likeName:{
//初始化调用一次 使indexOf比较为空刷新所有数据
immediate:true,
handler(val){
//过滤数据
this.newPersons = this.persons.filter((p)=>{
//通过indexOf判断搜索的值是否与列表匹配,如果不匹配等于-1
return p.name.indexOf(val) !== -1
})
}
}
}
})
</script>
2、computed计算实现
vue
<div id="root">
<h2>模糊查找案例</h2>
<!-- 建立一个输入框 取值用于模糊查找 -->
<input type="text" placeholder="输入要查找的名字" v-model="likeName">
<ul>
<li v-for="(p,index) in newPersons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
persons: [
{ id: '01', name: '周冬雨', age: '18', sex: 'woman' },
{ id: '02', name: '马冬梅', age: '19', sex: 'woman' },
{ id: '03', name: '周杰伦', age: '21', sex: 'man' },
{ id: '04', name: '马东伦', age: '20', sex: 'man' },
],
likeName:'',
},
computed:{
newPersons(){
return this.persons.filter((p)=>{
//通过indexOf判断搜索的值是否与列表匹配,如果不匹配等于-1
return p.name.indexOf(this.likeName) !== -1
})
}
}
})
</script>
12.3、排序列表案例
效果:
代码实现
vue
<div id="root">
<h2>模糊查找案例+升降序</h2>
<!-- 建立一个输入框 取值用于模糊查找 -->
<input type="text" placeholder="输入要查找的名字" v-model="likeName">
<button @click="isup=1">升序</button>
<button @click="isup=2">降序</button>
<button @click="isup=0">原顺序</button>
<ul>
<li v-for="(p,index) in newPersons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
persons: [
{ id: '01', name: '周冬雨', age: '20', sex: 'woman' },
{ id: '02', name: '马冬梅', age: '30', sex: 'woman' },
{ id: '03', name: '周杰伦', age: '21', sex: 'man' },
{ id: '04', name: '马东伦', age: '40', sex: 'man' },
],
likeName: '',
//判断为升序还是降序 1为升 2为降 0为与原来的顺序
isup: 0
},
computed: {
newPersons() {
const arr = this.persons.filter((p) => {
//通过indexOf判断搜索的值是否与列表匹配,如果不匹配等于-1
return p.name.indexOf(this.likeName) !== -1
})
//判断升降序
if (this.isup ==0) {
return arr
}else{
//如果要升降序利用三元表达式进行操作
arr.sort((a, b) => {
return this.isup === 1 ? a.age-b.age : b.age-a.age
})
}
return arr
}
}
})
</script>
13、vue监测数据的原理
-
原理
-
vue会监视data中所有层次的数据
-
如何监测对象中的数据?
-
通过setter事件监视,且要在newVue时就传入要监测的数据
-
在对象后追加的属性,Vue默认不做响应式处理
-
如需给后添加的属性做响应式,需要使用以下API:
Vue.set(target,propertyName/index,value)
vm.$ser(target,propertyName/index,value)
-
-
如何监测数组中的数据?
- 通过包裹数组更新元素的方法实现,本质就是做了两件事:
- 调用原生对应的方法对数组进行更新
- 重新解析模板,进而更新页面
-
在Vue修改数组中的某个元素需要用到以下方法:
- 使用这些API:push(),pop(),shift(),unshift(),splice(),sort(),erverse()
- Vue.set()或vm.$set
-
注意
Vue.set
:- 不允许修改vue的根数据或者vue实例
- 只能给data里面的对象追加属性
-
13.1、例子 更新一个人的信息
html
<!-- html -->
<div id="root">
<h1>更新信息的一个问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
<!-- 只能更新第一条的信息 -->
<button @click="upData">点我更新一条信息</button>
</div>
vue
<!-- vue -->
<script>
const vm = new Vue({
el:'#root',
data:{
persons: [
{ id: '01', name: '周冬雨', age: '20', sex: 'woman' },
{ id: '02', name: '马冬梅', age: '30', sex: 'woman' },
{ id: '03', name: '周杰伦', age: '21', sex: 'man' },
{ id: '04', name: '马东伦', age: '40', sex: 'man' },
],
},
methods:{
//更新第一条信息
upData(){
this.persons[0]= { id: '01', name: 'zy', age: '30', sex: 'man' }
}
}
})
</script>
当我触发点击事件时,可以发现前后的对比
虽然我们修改了数据,但是vue并没有监测到,就不会渲染到页面上。同时我们发现,通过我们直接赋值的这种方式,将第一个的对象属性的get和set方法没有了。
那我们通过下面这种方式操作对象的属性在进行一次数据的修改
我们可以看到数据成功的渲染到了页面上,同时我们可以看到对象的方法并没有被删除,其背后的原理就是Vue底层利用了Object.defineProperty 做数据代理
,通过get和set函数进行修改和解析,封装成Observer方法遍历data的属性,并对属性添加getter和setter。
而我们用的persons[0].name是对象中的属性,所以会触发监听重新渲染页面。
13.2、简单实现一个vue监测数据的原理
js
<script>
let data = {
name:'zy',
age:18
}
const obs = new Observer(data)
console.log(obs);
//new 一个vm实例对象
let vm ={}
vm._data = data = obs
function Observer(obj) {
//汇总对象中的所有属性,形成一个数组
const keys = Object.keys(obj)
console.log(keys);
//遍历
keys.forEach((item)=>{
//这里this指向的是Observer的实例对象
Object.defineProperty(this,item,{
get(){
//返回对应属性身上的值
return obj[item]
},
set(val){
//修改对应属性身上的值
obj[item]=val
}
})
})
}
</script>
13.3、vue.set方法
当我们的data已经写好,但后续需要添加一个对象时,vue给我们提供了一个set方法,给我们使用。达到重新渲染的效果。
- 例子:Vue.set(this.student,'sex','男') 或者 this.$Vue(this.student,'sex','男')
- 注意事项
- 不允许修改vue的根数据或者vue实例
- 只能给data里面的对象追加属性
- 注意事项
如图所示,我们需要向红色框添加一个性别属性
vue
<div id="root">
<button @click="addSex">添加一个性别:默认值为男</button>
<h2>姓名:{{student.name}}</h2>
<h2>年龄:{{student.age}}</h2>
<!-- 这里要注意 如果值为undefind vue不会渲染,也就是开始data.student中没有sex这个属性 -->
<h2>性别:{{student.sex}}</h2>
</div>
<script>
new Vue({
el:'#root',
data:{
student:{
name:'zy',
age:'1'
}
},
methods:{
addSex(){
Vue.set(this.student,'sex','男')
}
}
})
</script>
13.4、利用vue提供的方法做例1 -更新一个人的信息
在开头我们直接利用
vue
this.persons[0]= { id: '01', name: 'zy', age: '30', sex: 'man' }
修改数组不会奏效,同时页面也不会渲染。在vue中给我们提供了方法
也就是说我们需要变更数组的属性值时,需要用到上面的七个方法或者利用Vue.set去修改
vue
this.persons.splice(0,1,{ id: '01', name: 'zy', age: '30', sex: 'man' })
14、收集表单数据
- 收集表单数据
- 若:,则v-model收集的是value值为用户输入的value值
- 若:,则v-model收集的是value值并且要给
标签配置value值
- 若:
- 没有给input配置value,则收集起来的就是checked(布尔值)
- 配置了input的value值
- v-model的初始值不是为非数组形式,那么还是为checked
- 如果为数组,那么就收集数组组成的值
- v-model的三个修饰符
- 用法:v-model.lazy
- lazy:失去焦点在收集
- number:输入的字符串转为有效的数字
- trim:去掉首尾空格
14.1、案例---收集表单数据
vue
<div id="root">
<form>
<!-- 限制值为数字,传入的值也是数字-->
账号:<input type="number" v-model.number.trim="account"><br>
密码:<input type="password" v-model="password"><br>
<!-- 分组归为一类,辨别性别 -->
性别:男<input type="radio" value="1" name="sex" v-model="sex">
女<input type="radio" value="0" name="sex" v-model="sex"> <br>
<!-- 用数组形式存储 -->
爱好: 吃饭<input type="checkbox" value="吃饭"v-model="hobby">
睡觉<input type="checkbox" value="睡觉"v-model="hobby">
打游戏<input type="checkbox" value="打游戏"v-model="hobby"><br>
所属校区:<select v-model="school">
<option value="">请输入你的校区</option>
<option value="第一区">第一区</option>
<option value="第二区">第二区</option>
</select><br>
其他信息:<textarea v-model.lazy="other"></textarea> <br>
<input type="checkbox" v-model="agree">阅读并接收<a href="www.baidu.com">《用户协议》</a><br>
<button>提交</button>
</form>
</div>
<script>
new Vue({
el:'#root',
data:{
account:'',
password:'',
sex:'',
hobby:[],
school:'',
other:'',
agree:''
}
})
</script>