1、数据的双向绑定
双向绑定:当数据发生变化的时候,页面中的内容会随之变化,页面中的内容发生变化的时候,数据也会随之变化 双向绑定一般只会出现在表单元素中!
<!-- 双向绑定最实用的场景,就是获取用户在表单中输入的数据 -->
<input type="text" v-model="name">
双向绑定原理
Object.defineProperty方法
let obj = {name: ""}
obj.age = 18;
obj["gender"] = "male";
// Object.defineProperty可以用来给对象添加属性,也可以用来修改现有的属性
// 在添加属性的时候,可以设置属性以下的特性:
// 1. 是否为只读属性
// 2. 是否可以删除
// 3. 是否可以遍历
// 4. 可以为属性注册像改变事件一样的函数
// Object.defineProperty(要添加或者修改属性的对象, 要添加或者修改的属性名, 特征对象)
Object.defineProperty(obj, "name", {
// 1. 默认的,该属性是只读的,不能被赋值 默认是false
writable: true,
// 2. 默认的,该属性不能被for in循环遍历 默认是false
enumerable: true,
// 3. 默认的,该属性不能被delete删除 默认是false
configurable: true,
// value属性用来设置属性的默认值
value: "123"
})
let nameValue = "";
Object.defineProperty(obj, "name", {
// set和get不能和上面的额writable,value同时出现
set(value){
// 这个函数就像是属性的改变事件,我们在给属性赋值的时候
// JS内部会自动调用这个函数
console.log("属性被赋值了");
// 在set函数中,需要接收用户所赋值的内容,也就是等号右边的内容 可以通过value形参接收
// 将其存储起来
nameValue = value;
},
get(){
// 这个函数就像是属性的被获取的事件,只要有人用到这个属性了,那这个函数就会被调用
console.log("属性被获取值了")
// 这个函数会在用户获取值得时候调用,用户获取到的值,就是当前函数的返回值
return nameValue;
}
})
通过Object.defineProperty 方法我们就可以像监听事件一样来监听绑定数据是何时修改的,然后把这个修改后的数据重新帮到表单控件上
let nameValue = "";
Object.defineProperty(obj, "name", {
set(value){
input.value = nameValue = value;
},
get(){
return nameValue;
}
})
2、生命周期
beforeCreate( 创建前 )
在实例初始化之后,数据观测和事件配置之前被调用,此时组件的选项对象还未创建,el
和data
并未初始化,因此无法访问methods
,data
,computed
等上的方法和数据。created ( 创建后 )
实例已经创建完成之后被调用,在这一步,实例已完成以下配置:数据观测、属性和方法的运算,watch/event
事件回调,完成了data
数据的初始化,el
没有。 然而,挂载阶段还没有开始,$el
属性目前不可见,这是一个常用的生命周期,因为你可以调用methods
中的方法,改变data
中的数据,并且修改可以通过vue的响应式绑定体现在页面上,,获取computed
中的计算属性等等,通常我们可以在这里对实例进行预处理,也有一些童鞋喜欢在这里发ajax
请求,值得注意的是,这个周期中是没有什么方法来对实例化过程进行拦截的,因此假如有某些数据必须获取才允许进入页面的话,并不适合在这个方法发请求,建议在组件路由钩子beforeRouteEnter
中完成beforeMount
挂载开始之前被调用,相关的render
函数首次被调用(虚拟DOM),实例已完成以下的配置: 编译模板,把data
里面的数据和模板生成html
,完成了el
和data
初始化,注意此时还没有挂在html到页面上。mounted
挂载完成,也就是模板中的HTML渲染到HTML页面中,此时一般可以做一些ajax
操作,mounted
只会执行一次。beforeUpdate
在数据更新之前被调用,发生在虚拟DOM重新渲染和打补丁之前,可以在该钩子中进一步地更改状态,不会触发附加地重渲染过程updated(更新后)
在由于数据更改导致地虚拟DOM重新渲染和打补丁只会调用,调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作,然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环,该钩子在服务器端渲染期间不被调用beforeDestroy(销毁前)
在实例销毁之前调用,实例仍然完全可用,这一步还可以用this来获取实例,
一般在这一步做一些重置的操作,比如清除掉组件中的定时器 和 监听的dom事件destroyed(销毁后)
在实例销毁之后调用,调用后,所以的事件监听器会被移出,所有的子实例也会被销毁,该钩子在服务器端渲染期间不被调用
3、vue实例
const vm = new Vue({
el:'#app',
data:{
msg:'hello world!'
},
methods:{
// 定义函数、事件处理函数
fn(){}, // 简写方式
fn1:function(){}
},
computed:{
//如果页面中需要某个数据,而这个数据是根据当前data中的数据计算出来的,那么我们就使用计算属性
cmp(){return 数据},
cmp1:function(){return 数据},
// 完整形式,一般有双向绑定的时候会用到这完整形式
cmp:{
get(){
return 数据
},
set(value){
this.xxx = value
}
}
},
watch:{
// watch属性是一个对象,对象中的属性名就是要监视的数据(data中的)的名字
// 当这个数据发生变化的时候,这个函数会自动被执行
// 并且Vue提供了两个参数,newValue 是修改后的值, oldValue 是修改前的值
msg(newValue, oldValue){
// msg数据改变的时候自动执行
},
// 完整形式
// 针对数组、对象这样的复杂类型需要如下这么写
msg:{
handler(newValue, oldValue){
// msg数据改变的时候自动执行
},
// 进行深度监视,只要对象的任意属性发生变化,都会触发这个监视函数
// 进行深度监视的时候,newValue和oldValue值一样,因为监视的是引用类型的值,newValue和oldValue指向的是同一个对象
deep:true, // 默认是false
// 这个属性设置的是,代码一开始运行,就执行一次handler函数!
immediate:true // 默认是false
}
},
filters:{
// 局部过滤器
// 参数一定死了是 管道符 "|" 前面的值
dateFormat(data,arg1,arg2){},
dateFormat:function(data,arg1,arg2){}
},
directives:{
// 局部自定义指令
mytext:{
// 指令钩子函数
// el: 表示的是当前的元素
// binding:一个对象
// -name 指令名,不包括 v- 前缀。
// -rawName 带v的指令名
// -arg 传给指令的参数
// -modifiers 一个包含修饰符的对象 ,修饰符对象为 { foo: true, bar: true }
// -expression =后面的表达式 这是一个字符串
// -value 表达式对应的值
bind(el,binding){ // 比较常用
// 在vue开始解析这个指令的时候执行的
// 在这里一般可以进行一些样式的设置,比如设置字体颜色、字体粗细等
el.style.backgroundColor = binding.value
},
inserted(el,binding){
// 在vue将当前指令所在的元素渲染到页面上的时候执行的
// 比如执行自动获取焦点的操作,需要在inserted中执行
},
update(el,binding){ // 比较常用
// 当当前指令绑定的数据发生变化的时候,页面元素还没有更新的时候调用
// 一般update中执行的和bind中执行的差不多,以此就有了后面的简写形式
},
componentUpdated(el,binding){
// 当当前指令绑定的数据发生变化的时候,页面元素更新全部完成时候调用
},
unbind(el,binding){
// 指令被卸载的时候调用
}
}
},
//在数据初始化前后调用的事件
beforeCreate(){
// beforeCreate 数据初始化之前执行的,这时候还没数据
},
created(){
// 能够访问数据最早的钩子函数,其实就是created
// 重点: 工作中最常用的就是这个钩子函数
// 页面一家在就要请求数据的ajax请求一般都在这里发送
},
//在将有数据的元素挂载到页面前后调用的事件
beforeMount(){},
mounted(){
// 如果在实际开发当中,我们要访问有数据的DOM,那么就需要在mounted钩子函数里去做
},
// 数据发生改变,页面更新前后调用的事件
beforeUpdate(){
// 这里可以访问更新前的DOM
},
updated(){
// 这里可以访问更新后的DOM
},
//当vm.$destroy被调用,资源释放前后调用的事件//
beforeDestroy(){},
destroyed(){}
})
计算属性(computed)和方法(methods)的区别:
- 1.methods 可以传参,计算属性不能
- 2.methods不一定有返回值,而计算属性必须有返回值
- 3.计算属性有缓存,如果页面中多次使用,只会调用一次计算属性的函数
- 4.如果用methods来实现计算属性的功能,他是没有缓存的,只要有响应式属性改变,视图刷新,函数就执行,使用多次的时候,用几次调几次性能不好