2.1 Vue中的数据代理
2.1.1 数据代理原理
vue中的数据代理
-
通过
vm
对象来代理data
对象中属性的操作(读、写)
vue中数据代理的好处
-
更加方便的操作
data
中的数据
基本原理
-
通过
Object.defineproperty()
把data
对象中所有属性添加到vm
上 -
为每一个添加到
vm
的属性,都指定一个getter
、setter
-
在
getter
、setter
内部去操作(读、写)data中对应的属性
2.1.2 数据代理图示
2.1.3 Object.defineproperty()方法
参数 (obj, prop, descriptor)
-
obj 需要定义属性的对象
-
prop 需被定义或修改的属性名
-
descriptor 需被定义或修改的属性的描述符
方法描述
-
该方法允许精确添加或修改对象的属性
-
一般情况下,我们为对象添加属性是通过赋值来创建并显示在属性枚举中(for…in 或 Object.keys 方法),但这种方式添加的属性值可以被改变,也可以被删除。
-
而使用 Object.defineProperty() 则允许改变这些额外细节的默认设置。例如,默认情况下,使用 Object.defineProperty() 增加的属性值是不可改变的。
可选键值
-
configurable: 仅当该属性的 configurable 为 true 时,该属性才能够被改变,也能够被删除。默认为 false
-
enumerable: 仅当该属性的 enumerable 为 true 时,该属性才能够出现在对象的枚举属性中。默认为 false
-
value: 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined
-
writable: 仅当仅当该属性的writable为 true 时,该属性才能被赋值运算符改变。默认为 false
-
get: 一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。该方法返回值被用作属性值。undefined
-
set: 一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为undefined
获取、设置属性
var o = {}; // 创建一个新对象
// Example of an object property added with defineProperty with a data property descriptor
Object.defineProperty(o, "a", {
value : 37,
writable : true,
enumerable : true,
configurable : true
});
// 对象o拥有了属性a,值为37
var bValue;
Object.defineProperty(o, "b", {
get : function(){ return bValue; },
set : function(newValue){ bValue = newValue; },
enumerable : true,
configurable : true
});
o.b = 38;
// 对象o拥有了属性b,值为38
// 数据描述符和存取描述符不能混合使用
Object.defineProperty(o, "conflict", {
value: 0x9f91102,
get: function() { return 0xdeadbeef; }
});
2.2 Vue中的事件
2.2.1 事件的基本使用
-
使用v-on:xxxx 或 @xxxx 绑定事件,其中xxxx为事件名
-
事件的回调需要配置在methods对象中,最终会在vm上
-
methods中配置的函数,不需要使用箭头函数!否则this就不是vm了
-
methods中配置的函数,都是被vue所管理的函数,this的指向是vm或组件对象
-
@click=”demo“ 与 @click=”demo($event)“ 的效果是一样的,但后者可以传参
2.2.2 事件修饰符
-
prevent:阻止默认事件(常用)
-
stop:阻止事件冒泡(常用)【如:点击按钮执行事件,冒泡到div上】
-
once:事件只触发一次(常用)
-
capture:使用事件的捕获模式
-
self: 只有 event.target 是当前操作元素时,触发处理函数
-
passive:事件默认行为立即执行,无需等待事件回调执行完毕
<!-- 点击shout只输出1,不会触发shout(2) -->
<div @click="shout(2)">
<button @click.stop="shout(1)">ok</button>
</div>
2.3 Vue中的计算、侦听属性
2.3.1 使用插值计算
-
插值语法:data中的值变换,插值重新计算
2.3.2 methods计算
-
调用函数:data中的值变化,Vue中模板重新加载,函数肯定是要重新调用的
2.3.3 计算属性 computed
-
定义
-
要用的属性不存在,需要通过已有的属性计算得来
-
-
原理
-
底层借助于
Object.defineProperty()
方法提供的getter、setter -
get方法什么时候执行
-
初次读取会执行一次,内部有缓存机制,依赖数据不变读取缓存
-
所依赖的数据变化时,会被再次调用
-
-
优势
-
与methods相比,内部有缓存机制(复用),效率更高,调试更方便
-
-
备注
-
计算属性最终会出现在vm上,直接调用即可
-
如果计算属性要被修改,那么必须写set函数去响应修改,且set中要引起计算时依赖的数据发生变化
-
computed:{
// 函数中不能使用箭头函数
fullName:{
// 使用fullName时调用:由vm调用get
get(){
return this.firstName + "-" + lastName;
},
// 修改fullName时调用
set(value){
}
}
}
2.3.4 侦听属性 watch
-
当被监视的属性变化时,回调函数自动调用,执行相关操作
-
监视属性必须存在,才能进行监视操作
-
监视属性的两种写法:
-
new Vue() 时候传入watch配置
-
通过vm.$watch监视
-
// 写法1
watch:{
info:{
immediate:true, // 初始化时候调用handler
handler(newValue,oldValue){
// 当info发生变化时执行
console.log(oldValue,newValue)
}
}
}
// 写法2
vm.$watch('info',{
immediate:true, // 初始化时候调用handler
handler(newValue,oldValue){
// 当info发生变化时执行
console.log(oldValue,newValue)
}
});
深度侦听
-
vue中的watch默认不监视对象内部的改变
-
配置
deep:true
可以监视内部多层级属性的变化 -
vue自身可以对象内部变化,但是对于watch默认不提供监视内部变化,可以通过开始深度监视实现
// 监视多级结构中的属性变化
watch:{
info:{
deep:true, // 开启深度监视
handler(){
// 当info、内部值变化时执行
console.log("info发生了变化...")
}
}
}
2.3.5 计算、侦听属性的区别
-
computed能完成的功能,watch都能完成
-
watch能完成的功能,computed不一定能完成,比如:watch可以进行异步操作
-
两个重要原则:
-
所有被vue管理的函数,最好写成普通函数,这样this的指向才是vm,或者指向组件实例对象
-
所有不被vue管理的函数(定时器函数,ajax回调函数),最好写成箭头函数,这样this才能指向vm、组件实例
-
可参照 【Vue中this指向问题】
-
2.4 Vue中的样式
2.4.1 class样式
-
写法: :class ="xxx" ,xxx可以是字符串、数组、对象
-
字符串写法适用于:类名不确定,要动态获取
-
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定
-
数组写法适用于:要绑定多个样式,个数、名字确定,就是不知道用不用
2.4.2 style样式
-
:style=“{fontSize:xxx}”,其中xxx是动态值
-
:style="[a,b]",其中a,b不带双引号是样式对象,带双引号是样式名称
2.5 条件渲染
2.5.1 v-if
-
写法:
-
v-if =“条件”
-
v-else-if=“条件”
-
v-else
-
-
适用于:切换频率比较低的场景
-
特点:不展示的dom元素直接被移除
-
注意:中间必须相连,结构不能被打断
-
组合使用<template>可将条件包裹,不改变原有结构
2.5.2 v-show
-
写法:v-show=“表达式”
-
适用于:切换频率比较高的场景
-
特点:不展示的dom未被移除,只是使用样式隐藏
2.5.3 备注
-
使用v-if时,元素可能无法获取,使用v-show一定可以获取到【v-if元素会移除】
2.6 v-for循环key的原理
2.6.1 虚拟DOM中key的作用
-
key是虚拟DOM对象的标识
-
当数据发生变化时,Vue会根据 新数据 生成 新的虚拟DOM
-
随后Vue会进行 新虚拟DOM 与 旧的虚拟DOM 的差异比较
2.6.2 虚拟DOM对比规则
-
旧虚拟DOM 中找到了与 新虚拟DOM 中相同的key
-
若虚拟DOM内容没有变,直接使用之前的真是DOM
-
若虚拟DOM中内容变了,则生成新的真是DOM,随后替换页面中之前的真是DOM
-
-
旧虚拟DOM 中未找到与 新虚拟DOM 相同的key
-
创建新的真是DOM,随后渲染到页面中
-
2.6.3 index作为key可能会引发的问题
-
若对数组进行逆序添加、逆序删除等破坏顺序的操作:
-
会产生没有必要的真是DOM更新
-
界面显示没有问题,但是效率低下
-
-
若数组结构中还包含输入类的DOM:
-
会产生错误的DOM更新
-
界面显示顺序有问题
-
2.6.4 index作为key执行原理
-
默认值作为key,会默认使用index作为key
2.6.5 元素id作为key执行原理
-
使用元素中唯一标识作为key【推荐做法】
-
数组随意插入,效率高、数据不会错乱
2.7 Vue监测数据改变的原理
2.7.1 数据监测流程
1.加工data
-
data与_data中的数据是不一样的,vue对数据进行加工
-
_data中做了数据劫持,增加getter、setter
2.赋值vm._data
-
将data值赋值给vm._data
3.简单的处理流程
2.7.2 数据监视原理
如何检测对象中的数据
-
通过setter实现监视,且要在new vue() 时候就要传入要监视的数据
-
对象中后追加的数据,vue不做响应式处理
-
如需给后追加的数据做响应式处理,可以使用API:
-
Vue.set(target, propertyName/index, value)
-
vm.$set(target, propertyName/index, value)
-
如何监视数组中的数据
-
通过包装数组中更新数据的方法实现,本质上就做了两件事情:
-
调用数组原生更新方法,对数组进行更新
-
重新解析模板,进而更新页面
-
-
在vue中修改数组中某个元素,一定要使用如下方法:
-
使用包装API:
push、pop、shift、unshift、splice、sort、reverse
-
使用
Vue.set()
或者vm.$set()
-
特别注意
-
Vue会监视data中所有层次的数据
-
Vue.Set()
、vm.$set()
不能给 vm 或者 vm 根对象添加数据。
2.8 Vue中的过滤器
2.8.1 定义
-
对于显示的数据进行特定格式化后再显示
-
适用于简单的逻辑处理,负责逻辑使用函数处理
2.8.2 语法
-
注册过滤器:Vue.filter(name, callback) 或 new Vue(filters: {})
-
使用过滤器:{{ xxx | 过滤器名 }} 或 v-bind:属性 = " xxx | 过滤器名"
2.8.3 备注
-
过滤器也可以接收额外参数、多个过滤器也可以串联使用
-
并没有改变原来的数据,只是产生新的对应的数据
<h3>现在的时间:{{time | timeFormatFilter}}</h3>
2.9 Vue中的内置指令
2.9.1 v-text 指令
-
作用:向所在节点中渲染文本内容
-
与插值语法的区别:v-text会替换节点中所有内容,插值语法不会
2.9.2 v-html 指令
-
作用:向指定节点中渲染包含html结构的内容
-
与插值语法的区别:
-
v-html会替换节点中的所有内容,插值语法不会
-
v-html会识别html结构
-
-
严重注意:
-
在网站上动态渲染任意的html是非常危险的,会导致XSS攻击
-
一定要在可信的内容上使用v-html,永远不要在用户提交内容上使用
-
2.9.3 v-cloak 指令
-
没有值的
-
本质上是一种特殊属性,vue实例创建完毕接管容器后,会删除v-cloak属性
-
使用v-cloak配合css可以解决:网速慢时,页面加载出现 {{xxxxx}} 问题
2.9.4 v-once 指令
-
v-once所在的节点在初次动态渲染后,就视为静态内容了
-
以后数据变化,不会影响v-once所在结构的更新,可以用于优化性能
2.9.5 v-pre 指令
-
可以让vue跳过所在节点的编译过程
-
可利用其跳过:没有使用指令语法、没有插值语法的节点,会加快编译
2.10 自定义指令
2.10.1 定义
-
使用js底层操作DOM,实现指令操作
-
在Vue中
direxctives:{}
里面是定义的指令
2.10.2 自定义方式
-
函数模式:
-
使用函数模式
big(element, binding)
-
element为当前使用指令的节点元素,binding为指令绑定的值
-
-
钩子函数模式:
-
内置三个生命周期函数:不能改变名称,只能在函数中实现功能
-
bind(element, binding)
:指令与元素成功绑定就执行 -
inserted(element, binding)
:指令所在元素被插入页面时执行 -
update(element, binding)
:指令所在的模板被重新解析时执行
-
2.10.3 自定义语法
-
局部指令:
-
new Vue({ directives: { 指令名:配置对象 } })
-
new Vue({ directives{ 指令名:配置对象 } })
-
-
全局指令:
-
Vue.directives( 指令名,配置对象 )
-
Vue.directives( 指令名,回调函数)
-
-
注意:
-
指令定义时不加 v- ,但是使用时需要加 v-
-
指令名如果是多个单词,使用
kebab-case
命名方式,不要使用camelcase
命名
-
2.11 Vue生命周期
2.11.1 生命周期的定义
-
别名:生命周期函数、生命周期回调函数、生命周期钩子
-
是什么:Vue在关键时候帮我们调用的一些特殊名称的函数
-
生命周期函数名称不能更改,但是函数内部可以由程序员根据需求进行编写
-
生命周期函数中 this 指向VM 或者 组件实例对象
2.11.2 VM的生命周期
-
将要创建 ===> 调用beforeCreate函数
-
创建完毕 ===> 调用created函数
-
将要挂载 ===> 调用beforeMount函数
-
挂载完毕 ===> 调用mounted函数 【重要的钩子函数】
-
将要更新 ===> 调用beforeUpdate函数
-
更新完毕 ===> 调用updated函数
-
将要销毁 ===> 调用beforeDestroy函数 【重要的钩子函数】
-
销毁完毕 ===> 调用destroyed函数
2.11.3 生命周期函数使用注意事项
-
常用的生命周期钩子函数:
-
mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等
-
beforeDestroy:清除定时器、解绑事件、取消订阅消息等
-
-
关于销毁Vue实例:
-
销毁后借助Vue实例看不到任何信息
-
销毁后自定义事件会失效,但是原生DOM事件依然有效
-
一般不会在beforeDestroy中操作数据,因为即便是操作数据,也不会再触发更新流程了
-