new Vue 发生了什么
- new Vue 的时候调用会调用
_init
方法 - 合并和初始化配置,初始化一些事件和方法,初始化生命周期
- 进行页面挂载
- 执行 render 生成虚拟 DOM
- 将虚拟 DOM 生成真实 DOM 结构,并且渲染到页面中
https://ustbhuangyi.github.io/vue-analysis/v2/data-driven/new-vue.html
虚拟 DOM
定义:
虚拟 DOM(Virtual DOM)是一层对真实 DOM 的抽象,是由 JavaScript 对象(VNode 节点)构成的一个树,用对象的属性来描述节点,最终映射到真实的 DOM
核心思想:
通过维护一个虚拟的 DOM 树,将复杂的 DOM 操作抽象化。当应用状态发生变化时,虚拟 DOM 会首先在内存中生成一个新的虚拟 DOM 树,然后通过算法比较新旧两棵虚拟 DOM 树的差异,最后仅将需要更新的部分应用到真实 DOM 上,从而避免直接操作真实 DOM 带来的性能开销
优点:
- DOM 操作是浏览器中比较耗性能的部分,虚拟 DOM 可以通过批量处理更新和最小化 DOM 操作次数,提高页面性能
- 虚拟 DOM 是一种抽象的概念,不依赖于浏览器环境,它可以用于跨平台开发
diff 算法
用于虚拟 DOM 渲染成真实 DOM 的新旧 VNode 节点比较
两个特点:
- 比较只会在同层级进行,不会跨层级比较
- 比较的过程中,循环从两边向中间比较
key 的作用
用来对 vnode 进行唯一标识,在进行数据变化时可以不用整个数据重新渲染
v-for 不建议使用 index 作为 key
使用 v-for 更新已渲染的元素列表时,默认是“就地复用”策略,如列表中有表单输入则会导致元素复用产生错位
响应式原理
- vue 初始化 data 时,会使用
Object.defineProperty()
对其中的属性进行劫持,深层次的属性采用递归劫持。同时会对每个属性初始化一个dependency
实例 - 进行 html 模板解析,使用
Compile
函数,解析到其中的插值表达式时,进行 data 中数据的替换。模板每解析到一个属性,就初始化一个watcher
实例(说明有一个地方在用这个属性),watcher 初始化时会在Dependency
类上增加一个属性 target 指向 watcher 实例自己,同时 watcher 初始化时触发属性的get()
,get()
中会把 target (也就是 watcher) 放入dependency
实例的subscribers
数组中 - 当属性值发生变化时,在
set()
中调用dependency
实例的notify()
方法,将subscribers
数组中每个元素(也就是 watcher)执行update()
- data 中的每个属性(任何层级)都有一个
dependency
实例,一个dependency
中对应着多个watcher
- 建立
dependency
和watcher
的联系就是通过Dependency.target
来实现的
为什么要有 Dependency.target
?
dependency
实例必须创建在 Observer 中(因为进行数据劫持时对每个属性都创建一个依赖对象)watcher
实例必须创建在 Compile 中(因为只有哪些地方用到了才创建订阅)
所以需要一个中间桥梁来联系这 2 个实例,所以一个巧妙的办法是通过 watcher 初始化时触发属性的 getter ,并在 getter 中进行依赖的建立
Vue.js 数据双向绑定的原理及实现_哔哩哔哩_bilibili
blog/markdown/vue/vue2原理探索—响应式系统.md at master · LuckyWinty/blog
nextTick
在下次 DOM 更新结束之后执行延迟回调
原理
数据在发生变化的时候,Vue 并不会立刻去更新 Dom,而是将修改数据的操作放在了一个微任务队列中。等待宏任务完成之后,会将队列中的事件拿来进行处理,进行 DOM 的更新
插槽(slot)
插槽本质上是一个对象:
slots = {
default: function(...args) {},
slot1: function(...args) {},
slot2: function(...args) {},
}
每个属性分别对应插槽的名字,属性的值是一个返回 VNode 的函数
v-if v-show
区别:
v-show
隐藏则是为该元素添加display: none
,dom 元素依旧还在。v-if
显示隐藏是将 dom 元素整个添加或删除v-if
切换时会触发组件的生命周期v-if
有更高的切换开销,而v-show
有更高的初始渲染开销- 如果需要非常频繁地切换,则使用
v-show
较好;如果在运行时条件很少改变,则使用v-if
较好
响应式更新
this.$set()
Object.assign()
this.$forceUpdate()
组件之间的通信方式
方式 | 优点 | 缺点 |
---|---|---|
prop ,$emit | 常用 | 只能父子 |
$refs ,$children | 偶尔用 | 父子组件耦合 |
$parent ,$root | 基本不用 | 父子组件严重耦合 |
provide ,inject | 跨层级传数据方便 | 不清楚数据来源与变更 |
$attrs ,$listeners | 数据透传 | 中间组件多余代码 |
vuex | 数据集中管理,可追溯 | 代码比较复杂 |
EventBus | 各种不同组件间通信很方便,代码简洁 | 事件多了后,难以对事件进行维护 |
localStorage 、sessionStorage | 简单,浏览器自带 | 数据和状态比较混乱,不容易维护 |
常用修饰符
.trim
:过滤首尾空格.stop
:阻止事件冒泡.prevent
:阻止事件默认行为.sync
:双向绑定