Vue
为什么要用Vue?
- Vue 是一个MVVM(Model-View-ViewModel)框架,双向数据绑定把view和model层连接起来,通过对数据的操作自动更新DOM的状态,完成对页面视图的渲染,而不是进行繁琐的DOM操作。
- MVVM模型是在前端页面中,把Model用纯JavaScript对象表示(对应data中的数据),View负责显示(对应DOM模板),ViewModel把Model和View关联起来(对应Vue对象)。
data为什么是函数
- 在Vue中,组件的data必须被定义为一个函数,这样每个实例可以维护一份被返回对象的独立的拷贝。 如果data是一个对象,则所有的组件实例将共享这个对象,导致一个实例的状态变化可能会影响到所有其他实例。
computed和watch的区别
- computed:计算属性,当依赖的数据变化时,会计算新的值。计算属性是基于依赖进行缓存的,只要依赖属性还没变,多次访问计算属性会立即返回之前的计算结果。watch属性也可以做到相同的事,但是实现更加复杂。计算属性不支持异步。
- watch:监听某一个值,当被监听的值发生变化时,执行相关操作,更适合做一些数据变化后的异步操作或者较大开销操作。
- 所有不被Vue管理的函数(定时器、ajax的回调函数等),最好写成箭头函数,this才能指向vue对象
双向绑定
- Vue 2.0:数据代理,通过Object.defineProperty()将data中的所有属性转换为 getter/setter。当渲染函数(或计算属性等)被首次执行时,会访问与其相关的数据属性,这时数据属性的 getter收集依赖,使每个数据属性知道被那些组件依赖。
- 当数据发生变化时,调用数据属性的setter,通知所有依赖于该数据属性的 watcher 对象数据已经改变。watcher 对象收到变化通知后,会触发其回调函数,更新视图。
- Object.defineProperty(对象,属性,配置项): 为对象添加属性,通过这种方法添加的属性默认不可枚举、不可修改、不可删除(基本配置项:value,enumerable,writable,configurable)
- get/set为defineProperty的配置项,属性值被读取时,会调用get函数返回属性值,属性值被修改时,set函数会被调用,收到修改的值(然后在函数体中修改被代理属性的值)。
obj2代理obj.x属性的读写:
let obj = {x:100}
let obj2 = {y:100}
Object.defineProperty(obj2,'x',{
get(){return obj.x},
set(value){obj.x = value}
})
- watcher:每个组件实例都对应一个 watcher 实例,可以订阅并收到每个属性的变动通知,执行指令绑定的相应回调函数,从而更新视图。渲染 watcher :组件渲染时观察该组件所依赖的所有数据;计算属性 watcher:计算属性被访问时响应数据变化;侦听器 watcher:使用组件的 watch 对数据进行侦听时,为每个侦听器创建watcher
- 发布-订阅模式:消息的发送者不会直接将消息发送给接收者。发布的消息被分类后放入一个共享的消息队列或通道中,订阅者可以订阅这些通道来接收感兴趣的消息。
- 缺点:当创建一个Vue实例时,Vue将遍历所有DOM对象,并为每个数据属性添加getter 和 setter,但如果在 Vue 实例创建之后,为对象添加或删除一个新的属性,Vue 将无法追踪这个新属性的变化。所以在 Vue 实例创建之后,应该使用Vue.set 和 Vue.delete向响应式对象添加或删除属性。
- Vue3通过Proxy实现数据双向绑定, Proxy 可以对外界对对象的访问进行过滤和改写,可以拦截对象的任何操作,包括属性的添加和删除。
生命周期
- Vue提供了许多生命周期钩子,可以在生命周期的不同阶段调用指定函数
- 生命周期函数中的this指向vm或组件实例对象
- 生命周期分为8个阶段:创建前/后、挂载前/后、更新前/后、销毁前/后
钩子函数 | 状态 | 用处 |
---|
beforeCreate | 实例初始化完成,数据代理未开始 | |
| 初始化数据监测、数据代理 | |
created | 实例创建完成,属性已绑定,可以访问数据和方法 | |
| 解析模板,生成虚拟DOM | |
beforeMount | 页面呈现未经编译的DOM结构,但随后会被虚拟DOM覆盖,无法进行对DOM的操作 | |
| 将虚拟DOM转为真实DOM | |
mounted | vue实例挂载完成,对DOM的操作有效 | 可以进行开启定时器,发送请求等初始化操作 |
beforeUpdate | 页面尚未和数据保持同步 | |
| 根据新数据生成虚拟DOM,与真实DOM比较最终完成页面更新 | |
updated | 页面已经和数据保持同步 | |
| 调用vm.$destoy()销毁组件或组件在 v-if 指令的条件下从真变为假 | |
beforeDestory | 组件销毁之前,可以访问数据和方法,但是数据修改不会触发更新 | 清除定时器,解绑自定义事件等 |
| 调用vm.$destoy()销毁组件,解绑全部指令和自定义事件,但是会保留原生事件回调 | |
destroyed | 组件销毁之后,DOM结构依然存在 | |
虚拟DOM
- 虚拟DOM是以js 对象形式存在的DOM树结构。当状态变更时,渲染器得到新的虚拟DOM,对比新旧虚拟DOM的差异(Diff算法),只重新渲染变化的部分,减少多次重排和重绘,提高渲染效率。
- 虚拟DOM对象中有三个属性:
tagName:元素的标签名
props:元素包含的属性
children:元素的子节点
v-for中的key
- 需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。主要是为了高效的更新虚拟DOM。key属性不会放入真实DOM。
- 为什么不能用index作为key:如果在列表中间插入新节点,索引会改变。
Diff算法
- 对新旧两棵树进行深度优先遍历,标记每个节点;每遍历到一个节点就把该节点和新的树进行对比
- 同层比较:diff 算法只会在同一层级的节点之间进行比较,如果一个节点在旧树中存在,而在新树中不存在,会直接移除该节点及其子节点
- 节点比较:如果新旧节点的tagName不同,新节点替换旧节点;如果节点相同,会递归比较子节点;如果节点属性、文本内容发生变化,使用patch对象记录。
- 子节点比较:Vue建议对列表元素使用key属性唯一标识,识别相同的列表元素,防止遍历时子节点的顺序发生变化。
- 对于子节点列表,采用双端对比算法,从列表两端开始向中间对比元素的key值,如果相同则复用旧节点,如果不相同,尝试对比对边节点元素的key值是否相同,相同则将节点移动到对边。如果都不相同,尝试通过 key 值查找对应节点进行对比。直到新列表或旧列表长度为0,插入新元素或删除旧元素。双端对比算法