目录
模版语法v-bind
模版语法:指的是Vue模板中的VUE语法。笔记1里有记录过插值语法。
还有一类模板语法是指令语法。
对于标签中某个属性的值,如果想把该值设定为data中的数据,使用插值语法是无法生效的(以前是有这种语法,但现在已经被移除了),可以使用v-bind:属性="变量名"的语法,v-bind:可以简写成:。
<div id="root">
<a v-bind:href="url">1</a>
<a :href="url">2</a>
</div>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
new Vue({
el: '#root',
data:{
url: 'xxx',
}
})
</script>
当使用v-bind:或:简写形式时,解析器会把value值当做Js表达式解析(可以是变量,也可以是js语句)。也就是:href="url"的"url"部分。
v-bind为js语句:
<div id="root">
<a v-bind:href="url">1</a>
<a :href="url.toUpperCase()">2</a>
</div>
如果v-bind绑定的数据,在data中没有定义:
会报错误,提示property or method '变量名' is not defined on the instance but referenced during render。
什么时候用插值语法,什么时候用指令语法:对于标签体的内容,也就是两个标签<></>中间夹的内容,应该使用插值语法。指令语法用于标签属性。
插值语法:用于解析标签体的内容。指令语法:用于解析标签(指令语法写在标签属性中,可以给标签添加属性、事件、标签体等)。
数据绑定
对于一个属性,其值和一个数据绑定,这种现象就是数据绑定。
对于v-bind来说,它是实现数据绑定的一种方式。
数据绑定分为单向数据绑定和双向数据绑定。
单向数据绑定:属性和数据绑定,当后台的数据变化时,页面当中的属性值也会发生变化。当页面当中的属性值改变后,后台的数据不会发生改变。
<div id="root">
<input :value="inputData">
</div>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
new Vue({
el: '#root',
data:{
inputData: 'catcat',
}
})
</script>
刚打开页面,input框中的数据是catcat。
v-bind就是单向数据绑定。后台数据的变化会反应到页面上,页面数据的变化不会反应到后台。
当把页面上的内容修改为dogdog之后,后台的值还是catcat。
双向数据绑定:后台数据的改变可以反应在页面上,页面上数据变化时,也可以反应在后台。另一个指令v-model是双向数据绑定。
<div id="root">
<input :value="inputData">
<input v-model:value="modelData">
</div>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#root',
data: {
inputData: 'catcat',
modelData: 'dogdog',
}
})
</script>
当把页面中的值改为dogdogyeah后,后台的值也会发生变化。
但并不是所有标签中v-model都能使用。v-model一般应用在表单类/输入类元素上,这类元素都有一个value值。v-model:value=''可以简写成v-model:'',因为v-model默认绑定的就是value值。
<div id="root">
<input :value="inputData">
<input v-model="modelData">
</div>
MVVM模型
Vue的设计收到了MVVM模型的启发,Vue没有完全遵循MVVM模型,但大体上参考了这个模型。
MVVM的分隔方式是M、V、VM。
其中M是Model,模型,对应Vue实例data中的数据。
V是View,对应Vue模版。
VM是视图模型ViewModel,是对应Vue实例对象。
因为new Vue充当ViewModel,所以其返回的实例值一般命名为vm,但其实这个值也可以自己随便写,就是vm更加合理一点。
data中的数据,最后会体现在vm上。
插值语法可以使用所有在vm、vm原型链上的属性和方法。且实例vm已经内置在{{}}里,因此在{{}}里不需要写this或者vm。直接写属性和方法就可以。实际上,vm、vm原型链上所有的属性和方法都可以在Vue模版中使用
数据代理
数据代理使用Object.defineProperty方法,数据劫持、计算属性等也使用了这个方法。
Object.defineProperty用于给一个对象添加属性。Object.defineProperty接收三个属性,分别是(要添加属性的对象,要添加的属性名称,配置项)
简介Object.defineProperty
let demo = {
name:'catcat',
age:18,
}
Object.defineProperty(demo, 'color', {
value: 'orange',
});
console.log(demo);
Object.defineProperty设置的属性,和用demo{color:'orange'}直接赋值是不一样的,用defineProperty加入的属性默认是不可枚举的,也就是不能够被遍历的。
Object.defineProperty(demo, 'color', {
value: 'orange',
});
console.log(Object.keys(demo));
for(let key in demo){
console.log(demo[key]);
}
只能遍历到name和age,遍历不到color。
如果希望defineProperty新加的属性也能被遍历,需要设置enumerable:true(enumerable设置属性值是否可以被枚举,默认为false)。
Object.defineProperty(demo, 'color', {
value: 'orange',
enumerable:true,
});
用Object.defineProperty新加的属性默认是无法修改的。设置demo.color = 'blue'不会生效。如果希望属性可以被修改,需要设置writable:true。writable设置属性是否可以被修改,默认为false。
Object.defineProperty(demo, 'color', {
value: 'orange',
enumerable:true,
writable:true,
});
用Object.defineProperty新加的属性默认不可删除。写delete demo.color,color不会被删除。需要设置configurable:true,属性才能被删除。configurable控制属性是否可以被删除,默认为false。
Object.defineProperty(demo, 'color', {
value: 'orange',
enumerable:true,
writable:true,
configurable:true,
});
与直接在对象字面量上赋值不同,字面量上的赋值可以删除,可以修改,可以任意进行操作。而defineProperty可以设置配置项,可以对追加的属性进行很多配置。
设置get
可以用defineProperty在新加的属性上设置get。get在读取属性时触发。
考虑下面这种情况:
let color = 'orange';
let demo = {
name:'catcat',
age:18,
color: color,
};
当变量color发生变化时,demo.color不会跟着变化。只有在初始赋值时会把color值赋给demo.color,赋值完两者之间就没有关系了,因此即使过后改变变量color,不会影响demo.color。
但是这种效果可以用defineProperty实现,设置get,当读取demo.color时,返回color值即可。get是函数名,get有一个函数体,一般把get和函数体统称为getter。
let color = 'orange';
let demo = {
name:'catcat',
age:18,
};
Object.defineProperty(demo,'color',{
get(){
return color;
}
});
设置set
可以用defineProperty在新加的属性上设置set。set在给属性赋值时触发。
不管是设置get还是set,要注意不要直接访问当前设置的这个属性,否则会导致无限回调。
可以通过另一个变量辅助进行set和get。
let demo = {
name:'catcat',
age:18,
};
Object.defineProperty(demo,'color',{
get(){
return this._color;
},
set(value){
this._color = value + 'color';
}
});
什么是数据代理
数据代理是通过一个对象,代理另一个对象中属性的操作。
比如我有一个对象Obj,他有属性x,我访问x,使用Obj.x访问就可以了。但如果还有另一个对象Obj2,Obj2也想访问x,也想修改x,则可以使用代理。通过Obj2代理对Obj1中属性的操作。也就是说,虽然Obj2中没有相关的属性和方法,但是可以通过Obj2代理对Obj1的操作。
let obj1 = {
x : 100,
}
let obj2 = {
};
Object.defineProperty(obj2, 'x',{
get(){
return obj1.x;
},
set(value){
obj1.x = value;
},
})
这里给obj2也添加属性x,但当访问obj2的x时,返回的是obj的x。修改obj2的x时,修改的是obj1的x。实现了对obj1的代理。
以上是一个非常简单的数据代理的例子。
Vue中的数据代理
Vue实例身上的data数据就是通过数据代理进行的。
<div id="root">
<h1>姓名:{{name}}</h1>
<h1>年龄:{{age}}</h1>
</div>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#root',
data(){
return{
name:'catcat',
age:14,
}
}
})
</script>
对于上面这段代码,Vue实例vm中,有在data中配置的name和age,对实例vm身上的name和age进行读写操作,实际上就是用的数据代理,name和age有自己的getter和setter。
如果读取vm上的name值,实际上会触发name的getter,这个getter会返回data.name。
如果通过vm去修改name,实际上会触发name的setter,这个setter会修改data.name。
其实是一个数据代理。
data对象只是new Vue里的一个配置,并不是全局变量,vm是如何访问data:
vm把data存在自己身上的某个属性。
这个属性是_data。
let data = {
name: 'catcat',
age: 14,
}
let vm = new Vue({
el: '#root',
data,
})
实际上vm的_data就是在new Vue配置对象里配置的data,这个data值是配置的data值,vm._data=options.data,options.data = data。修改vm._data,配置的data也会改变。
为什么需要用数据代理访问data:通过数据代理,把data上的数据放在vm上一份,在访问data中的数据时,不需要写_data,直接写属性就可以了。更方便开发。
数据代理的原理:通过Object.defineProperty把data对象中的所有属性添加到vm上。
为每一个vm上的属性都设置getter和setter,通过getter和setter去操作data。
_data中的数据并不是单纯复制data,_data为了实现响应式,应用了数据劫持。数据劫持之后会介绍。数据劫持,其实就是当_data中的数据改变时,data中的数据也相应改变,这可能会导致页面中的数据也改变,就需要有一个监听机制,监听data是否发生改变,并更新页面上的值。这个机制就是数据劫持。