Vue2 构造函数、生命周期与数据双向绑定
Vue是一个响应式的、渐进式的JavaScript框架,它在设计上采用MVVM模式,将视图与数据两部分分离。下面就是一个简单的Vue实例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{ message }}
</div>
<script>
var app=new Vue({
el:"#app",
data:{
message:"Hello Vue!"
}
})
</script>
</body>
</html>
可以打开浏览器的控制台,改变app.message的值,可以看到页面上的视图显示部分也会相应的更新。
(1)通过Vue构造函数函数来创建Vue实例
var app = new Vue({
})
变量app就是指这个Vue实例。
在Vue实例中,el选项是必须的,el用于指定一个页面上已存在的DOM元素来挂载Vue实例,例如:
<div id="app"></div>
<script>
var app=new Vue({
el:"#app",
})
</script>
之后可以通过app.$el来访问这个被挂载的元素。
console.log(app.$el)
//<div id="app"></div>
(2)实例的生命周期钩子函数
每个Vue实例创建时,都会经历一系列初始化的过程,同时也会调用相应的生命周期钩子函数。比较常用的有:
1.created
实例创建完成后调用,在这个阶段完成了数据的观测等,但尚未挂载,$el还不可用。
2.mounted
el挂载到实例上之后调用。
3.beforeDestroyed
实例销毁之前调用。主要解绑一些使用addEventListener监听的事件等。
这些生命周期钩子函数与data类似,也可以写入Vue实例内,并且生命周期钩子函数内的this指向的是调用它的Vue实例。
var app=new Vue({
el:"#app",
data:{
a:"Hello Vue!"
},
created:function(){
console.log(this.a);
//Hello Vue!
},
mounted:function () {
console.log(this.$el);
//<div id="app">
// Hello Vue!
// </div>
}
});
不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a)。
ES6中提到:在箭头函数中没有自己的this对象,导致内部的this就是外层代码的this。
所以,个人理解在选项属性或回调上使用箭头函数,此时的this指向的是全局属性:
var app=new Vue({
el:"#app",
data:{
a:"Hello Vue!"
},
created:()=>{
console.log(this.a);
//undefined
},
mounted:()=> {
console.log(this.$el);
//undefined
}
});
var a=123;
var $el=456;
var app=new Vue({
el:"#app",
data:{
a:"Hello Vue!"
},
created:()=>{
console.log(this.a);
//123
},
mounted:()=> {
console.log(this.$el);
//456
}
});
(3)数据的双向绑定
Vue实现了数据的双向绑定,通过Vue内的data选项,声名需要绑定的数据。
var app=new Vue({
el:"#app",
data:{
a:"Hello Vue!"
}
});
Vue本身也代理了data对象里面的所有属性,所以对于以上代码,data中的a属性,可以通过app.a进行访问:
console.log(app.a)
//Hello Vue!
除了显式的声名数据,也可以指向一个已有的变量,并且它们之间默认建立了双向绑定,当修改其中一个时,另一个也会变化。
let test={
a:"Hello Vue!"
};
var app=new Vue({
el:"#app",
data:test,
});
console.log(app.a);
//Hello Vue!
app.a="Hello World!";
console.log(test.a);
//Hello World!
test.a="Hello simple!";
console.log(app.a);
//Hello simple!
需要注意只有当实例被创建时存在的属性才是响应式的:
let test = {
a: "Hello Vue!"
};
var app = new Vue({
el: "#app",
data: test,
});
test.b = 1;
console.log(test.b);//1
console.log(app.b);//undefined
上面代码中,在Vue实例被创建时,对象中只有属性a,之后在Vue实例创建完成后,给obj添加属性b,这时属性b不是响应式的。
1.双大括号的文本插值
<div id="app">
{{ message }}
</div>
<script>
var app=new Vue({
el:"#app",
data:{
message:"Hello Vue!"
}
})
</script>
双大括号会被替换成Vue实例上的data对象上的message属性的值。无论何时,只要绑定的数据对象上的message属性的值发生了改变,插值处的内容都会被更新。
比如下面的例子,实时显示当前时间,每秒更新:
<div id="app">
{{ a }}
</div>
<script>
var app = new Vue({
el: "#app",
data: {
a: new Date()
},
mounted: function () {
var _this = this;
this.timer = setInterval(function () {
_this.a = new Date();
}, 1000);
},
beforeDestroy: function () {
if (this.timer) {
clearInterval(this.timer);
}
}
});
</script>
setTimeout()与setInterval()调用的代码运行在与所在函数完全分离的执行环境上。也就是说,setTimeout()与setInterval()代码中包含的 this 关键字会指向 window (或全局)对象。所以需要声明一个_this变量,用来保证setInterval()可以改变Vue实例中的a的值。
显示{{}},不进行替换
如果想显示{{}}标签,而不进行替换,可以使用v-pre,就可以跳过这个元素和它的子元素的编译过程。
<div id="app">
<div>{{a}}
<sapn>{{b}}</sapn>
</div>
<div v-pre>{{a}}
<sapn>{{b}}</sapn>
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
a: "你好!",
b: "Hello!"
},
});
</script>
渲染结果为:
<div id="app">
<div>你好!
<sapn>Hello!</sapn>
</div>
<div>{{a}}
<sapn>{{b}}</sapn>
</div>
</div>
2.HTML代码插值
如果有时候想输出HTML代码,可以使用v-html:
<div id="app">
<div v-html="a"></div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
a: "<span>你好!</span>"
},
});
</script>
渲染后的结果为:
<div id="app">
<div><span>你好!</span></div>
</div>
3.双大括号中表达式插值
在{{}}中,除了绑定属性值外,还可以使用Javascript表达式进行简单的运算、三元运算等:
<div id="app">
<div>{{message+1}}</div>
<div>{{right ? "yes":"no"}}</div>
<div>{{string+" world!"}}</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
message: 1,
right: true,
string:"hello"
},
})
</script>
渲染结果为:
<div id="app">
<div>2</div>
<div>yes</div>
<div>hello world!</div>
</div>
4.过滤器
可以在{{}}插值的尾部添加一个管道符号"|",对数据进行过滤。过滤的规则通过给Vue实例添加filters选项来设置:
<div id="app">
<div>{{a|toUp}}</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
a: "aBcdeFg"
},
filters: {
toUp: function (value) {//这里的value是a数据本身
return value.toUpperCase();
}
}
});
</script>
上面的代码中,通过过滤器,将字符串中的字母都变为大写。
渲染结果为:
<div id="app">
<div>ABCDEFG</div>
</div>
过滤器可以串联,也可以接收除了数据本身以外的参数:
<div>{{a|filter1|filter2}}</div>
<div>{{a|filter("arg1","arg2")}}</div>
传入"arg1"和"arg2"将作为传递给过滤器的第二和第三个参数,因为过滤器的第一个参数是数据本身。
要注意过滤器属于简单的转换,要实现更复杂的数据处理,应该使用下一篇文章中介绍的计算属性。
5.Object.freeze()阻止修改现有的属性
Object.freeze()使冻结的对象不能添加、修改属性,也不能删除某个属性:
var obj={
a: 1
};
Object.freeze(obj);
obj.a=11;
console.log(obj.a);//1
obj.b=2;
console.log(obj.b);//undefined
delete obj.a;
console.log(obj.a);//1
在Vue中使用Object.freeze()
没有冻结对象时:
var app = new Vue({
el: "#app",
data: {
a: 1
}
});
console.log(app.a);//1
app.a=11;
console.log(app.a);//11
使用Object.freeze()冻结对象之后,修改app.a的值会报错:
var app = new Vue({
el: "#app",
data: Object.freeze({
a: 1
}),
});
console.log(app.a);//1
app.a=11;
//Uncaught TypeError: Cannot assign to read only property 'a' of object '#<Object>'
冻结已有变量
var obj={
a: 1
};
Object.freeze(obj);
var app = new Vue({
el: "#app",
data:obj,
});
console.log(app.a);//1
obj.a=2;
console.log(app.a);//1
在这里,我发现了一个比较好玩的事情,把 Object.freeze(obj)放在构造Vue实例之后,就不会起作用了:
var obj={
a: 1
};
var app = new Vue({
el: "#app",
data:obj,
});
Object.freeze(obj);
console.log(app.a);//1
obj.a=2;
console.log(app.a);//2
这个的原因是什么,我还没有搞清楚,如果有哪位大神可以指导一下,非常感谢!
参考:
1.Vue.js官方文档
2.《Vue.js实战》