Vue里的MVVM
MVVM模型:
1.M:模型(Model):对应data的数据
2.V: 视图(View):模板
3.VM: 视图模型(ViewModel): Vue实例对象
代码演示
<body>
<!-- 视图 -->
<div id="app">
<h1>name:{{name}}</h1>
<h1>age:{{age}}</h1>
</div>
<!-- VM: 视图模型 -->
<script type="text/javascript">
Vue.config.productiontip=false
// ViewModel :视图模型
const vm = new Vue({
el:'#app',
// Model 数据
data:{
name:'张三',
age: 12
}
})
</script>
</body>
总结:
MVVM模型:
- M:模型(model):对应data的数据
- V: 视图(View):模板数据
- VM: 视图模型(ViewModel): Vue实例
观察发现:
4. data所有的属性,最后都在vm身上
5. vm身上所有的属性及Vue原型的所有属性,在Vue模板上都可以直接使用。
数据代理
Object_defineProperty
Object_defineProperty:用于给对象添加属性,添加的属性可有控制属性是否可修改,是否可删除,是否支持枚举。
枚举是不支持遍历的。
Object_defineProperty构造方法有4个基本属性,分别是对象,添加属性,添加的配置
基本的配置有:
- 是否能修改属性
- 是否能枚举属性
- 是否能删除属性
Object.defineProperty(person,'age',{
//基本配置项
value:'18', //赋初值
enumrable:true, //控制属性是否可以枚举,默认为false
wirtable:true, //控制属性是否可修改 ,默认为false
configurable: true //控制属性是否可以删除,默认为false
})
默认通过Object_defineProperty的方式的属性是不可枚举,不可修改和不可删除的。
配置的高级项
let number =18
Object.defineProperty(person,'age',{
get(){
console.log('有人读取了age属性')
return number
},
//当有人修改person的age时,set方法就会被调用,保存修改后的数据
set(value){
console.log('有人修改了age属性,且值是'+value)
number = value
}
})
理解数据代理
数据代理:通过一个对象代理对另一个对象属性的操作(读/写)
代码演示
<script type="text/javascript">
let obj1 = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj1.x
},
set(value){
obj1.x= value
}
})
</script>
Vue中的数据代理
通过观察一下代码:
<div id="app">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
<script type="text/javascript">
Vue.config.productiontip = false
const vm = new Vue({
el:'#app',
// data中的数据读取和写入实质上是数据代理
data:{
name:'涉外经济学院',
address: '湖南的'
}
})
</script>
为什么插值语法中输入的表达式可以直接读取到data区域的所有属性?
实际上是数据代理实现的,打开控制台观察:
data对象的属性可以在Vue实例中找到,是通过数据代理Object.defineProperty实现的,通过我们的Vue实例对象去代理了data对象属性的操作。
验证:
读取验证:
验证是否是从data对象取的属性值,修改name的值重新启动即可。
修改验证:
验证是否是从data对象修改属性值。
可以得知通过修改vm.data,data对象的name也发生了变化,验证了Vue实例数据代理。
vm._data和Vue实例的data是相同的,且vue实例以及Vue原型的所有属性和方法可以在模板上直接使用。
Vue实例数据代理实质是将data对象的属性复制了一份,为了方便操作。
总结:
- Vue的数据代理:通过vm对象来代理data对象中属性的操作(读/写)。
- Vue实例的好处: 更加方便操作data中的属性数据
- 基本原理: 通过Object.defineProperty()把data对象所有属性添加到vm上。为每一个添加到vm上的属性,都指定一个getter/setter。在getter/setter内部去操作(读/写)data对应的属性。
vm._data对象的内部:
其底层不是数据代理,而是数据结池,完成对页面数据的响应。
事件
事件代理
我们点击事件使用到一个vue指令:v-on
<div id="app">
<h2>欢迎来到{{name}}</h2>
<button v-on:click="showInfo">点击我</button>
</div>
<script type="text/javascript">
Vue.config.productiontip=false
new Vue({
el:'#app',
data:{
name:'Vue'
},
methods:{
showInfo(){
alert('hello,你好吖')
}
}
})
其中v-on点击的事件要在我们的Vue实例里,放置在methods对象上。
其中方法默认有一个event参数,可以得到event的事件信息。
showInfo(event){
console.log(event.target.innerText)
console.log(this) //this指的是vue实例对象
alert('hello,你好吖')
}
methods里面的方法的this正常情况下是Vue实例对象,但若加上箭头函数,那么找不到自己的this,就将全局的window作为this。
methods:{
showInfo:(event)=>{
console.log(event.target.innerText)
console.log(this)
alert('hello,你好吖')
}
}
v-on的简写方式直接写一个‘@‘即可,不传参事件方法可以不写括号
实现点击事件传入一个参数,完成要求。
<div id="app">
<h2>欢迎来到{{name}}</h2>
<!-- <button v-on:click="showInfo">点击我</button> -->
<button @click="showInfo1">点击我(未传参)</button>
<button @click="showInfo2(1)">点击我(传参)</button>
</div>
<script type="text/javascript">
Vue.config.productiontip=false
// 点击事件的时候传入一个参数,这跟删除和更新操作有关。
new Vue({
el:'#app',
data:{
name:'Vue'
},
methods:{
showInfo1:(event)=>{
alert('hello,你好吖')
},
showInfo2:(value)=>{
console.log(value)
alert('hello,你好吖'+value)
}
事件同时实现event和传参
<button @click="showInfo3($event,1)">点击我(传参和event)</button>
showInfo3:(event,value)=>{
console.log(event,value)
alert('hello,你好吖'+value)
}
输出event的同时也同时实现了传参。
vm的methods底层不是数据代理,数据代理是代理会改变的数据操作。而方法是用来给调用的,不需要改变。是被Vue管理的函数。
总结:
1.使用v-on:xxx或@xxx绑定时间,其中xxx是事件名。
2.事件的回调需要配置在methods对象中,最终会在vm上。
3.methods配置的函数,不要用箭头函数,否则this就不再是vm了。
4.methods配置的函数,都是被Vue管理的函数,this的指向是vm或组件实例对象。
5.@click=“demo” 或@click="demo($event)"效果一致,但后者可以传入参数。
事件修饰符
Vue的事件修饰符有六种
1.prevent:阻止默认事件(常用)
2.stop:阻止事件冒泡(常用)
3,once: 控制事件只触发一次(常用)
4.capture: 使用事件捕获模式
5.self: 只有event,tartget是当前操作的元素才触发事件
6.passive:事件的默认行为立即执行,无需等待时间回调执行完毕。
先准备一个点击事件如下:
<div id="app">
<a :href="url" @click="showInfo">点击我跳转到QQ</a>
</div>
<script type="text/javascript">
Vue.config.productiontip=false
const vm =new Vue({
el:'#app',
data:{
url:'www.qq.com'
},
methods:{
showInfo(){
alert('跳转到QQ')
}
}
})
</script>
使用Vue的事件修饰符
prevent
再点击事件后面添加prevent即可阻止跳转页面。
stop
在之前的代码再添加如下代码
<style>
*{
margin-top: 20px;
}
.demo1{
height: 50px;
background-color: skyblue
}
</style>
<div id="app">
<a :href="url" @click.prevent="showInfo">点击我跳转到QQ</a>
<div id="demo" @click="showInfo">
<button @click="showInfo">点击提示信息</button>
</div>
</div>
打开浏览器运行发现点击按钮弹出两次窗口,发生了事件冒泡。
解决方法
1.在事件方法中使用e.stopPropagation()
methods:{
showInfo(e){
e.stopPropagation()
alert('跳转到QQ')
}
}
2.在vue指令@click后添加stop。
<div id="demo" @click="showInfo">
<button @click.stop="showInfo">点击提示信息</button>
</div>
由内往外阻止冒泡。在里面使用stop。
once
事件只触发一次,之后的点击都不会触发,都不会响应事件了。
<button @click.once="showInfo">点击提示信息(once)</button>
capture
编写一个小案例,两个div边框点击div2,发现输出结果从里往外,由此发生了冒泡。
#box1{
padding: 5px;
background-color: salmon;
}
#box2{
padding: 5px;
background-color: lightcoral;
}
<div id="box1" @click="showMsg(1)">
div1
<div id="box2" @click="showMsg(2)">
(capture:捕获模式)div2
</div>
</div>
分析
事件先进行捕获,然后再进行冒泡。捕获由外向里,而冒泡由里往外。阻止冒泡,实现捕获阶段处理事件。用capture就可以实现了。
<div id="box1" @click.capture="showMsg(1)">
div1
<div id="box2" @click="showMsg(2)">
(capture:捕获模式)div2
</div>
</div>
输出结果:
self
小例子演示,self的效果和stop类似
<div class="demo1" @click="showInfo">
<button @click="showInfo">点我提示信息(self)</button>
</div>
分析
div的事件是冒泡冒上去的,通过在冒泡事件上加上self,冒泡问题得以解决。
passive
记录一个鼠标事件,滚轮和滚动条。分别使用@wheel和@scroll。滚动条是计算滚动的距离(大小有限制),滚轮是计算滚动的次数(大小无限制)。
<!-- passive:无需等待事件回调,立即执行 -->
<!-- 给ul绑定一个滚动事件 @scroll:滚动滚动条
@wheel:滚动滚动轮-->
<ul @scroll="show" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
相应结果:
滚动条结果
滚动轮结果
使用滚动条进行复杂的运算
在事件中添加一个循环。如下:
<ul @wheel="show" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
show(){
for(let i =0 ; i< 100000;i++){
console.log('@')
}
console.log('累坏了')
}
结果分析:滚动滚轮,事件一直在发生,而滚动条没有移动。
使用passive,滚轮事件问题解决。(但滚动条@scroll优先响应滚动)
<ul @wheel.passive="show" class="list">
键盘事件
键盘有常用两个键盘事件,keydown和keyup。
keyup: 键盘按下去抬起来的时候触发
keydown: 键盘按下去触发事件。
使用keyup或keydown后不使用判断条件,按下容易一个键就会触发事件。如下:
部分代码:
<div id="app">
<h1>Hello,{{name}}</h1>
<input type="text" placeholder="请输入" @keyup="showInfo">
</div>
new Vue({
el:'#app',
data:{
name: 'Vue'
},
methods:{
showInfo(event){
console.log(event.target.value)
}
}
})
运行出来会立即触发事件。
要求只有回车才进行回车事件
1.通过js代码来进行判断
methods:{
showInfo(event){
// 使用关键码来控制条件 回车 ===13
if(event.keyCode === 13){ console.log(event.target.value)}
}
}
2.通过vue提供的常用键盘判断按键
<input type="text" placeholder="请输入" @keyup.enter="showInfo">
Vue按键知识
-
常用按键:
enter: 换行
删除: delete(按删除或者退格键)
退出: esc
切换光标/缩进: tab (配合keydown使用,使用keyup会切换到其他的图标,会将焦点切走)
上: up
下: down
左: left
右: right
2.Vue没有提供别名的按键可以使用键盘原始的key去绑定,但要注意以kebab-case(短横线命名)
<input type="text" placeholder="按下capsLock提示输入" @keyup.caps-lock="showInfo">
3.系统特殊修饰键(用法特殊)ctrl,alt,meta,shift
(1).配合keyup使用: 按下修饰键的同时,还需要按下其他键,然后释放其他键,事件才被触发。例如:ctrl+l l
(2).配合keydown使用: 可以正常触发事件。
<input type="text" placeholder="按下tab提示输入" @keydown.tab="showInfo">
<input type="text" placeholder="按下meta提示输入" @keyup.meta="showInfo">
4.使用keycode去指定具体的按键。(不推荐)
<input type="text" placeholder="按下回车提示输入" @keyup.13="showInfo">
5.可以使用Vue.config.keyCodes.自定义键名 = 键码,定制按键别名
<input type="text" placeholder="按下回车提示输入" @keyup.huiche="showInfo">
// 自定义按键别名
Vue.config.keyCodes.huiche = 13
事件总结
阻止冒泡的同时阻止默认行为,可以通过以下代码实现。
<div id="demo1" @click="showMsg">
<a href="www.qqmusic.com" @click.stop.prevent="showInfo">点击我提示信息(stop.prevent)</a>
</div>
要求按下ctrl+y的时候才响应键盘事件
<input type="text" placeholder="按下ctrl+y提示输入" @keyup.ctrl.y="showInfo">