new Vue 发生了什么

  1. new Vue 的时候调用会调用 _init 方法
  2. 合并和初始化配置,初始化一些事件和方法,初始化生命周期
  3. 进行页面挂载
  4. 执行 render 生成虚拟 DOM
  5. 将虚拟 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 更新已渲染的元素列表时,默认是“就地复用”策略,如列表中有表单输入则会导致元素复用产生错位

响应式原理

  1. vue 初始化 data 时,会使用 Object.defineProperty() 对其中的属性进行劫持,深层次的属性采用递归劫持。同时会对每个属性初始化一个 dependency 实例
  2. 进行 html 模板解析,使用 Compile 函数,解析到其中的插值表达式时,进行 data 中数据的替换。模板每解析到一个属性,就初始化一个 watcher 实例(说明有一个地方在用这个属性),watcher 初始化时会在 Dependency 类上增加一个属性 target 指向 watcher 实例自己,同时 watcher 初始化时触发属性的 get()get() 中会把 target (也就是 watcher) 放入 dependency 实例的 subscribers 数组中
  3. 当属性值发生变化时,在 set() 中调用 dependency 实例的 notify() 方法,将 subscribers 数组中每个元素(也就是 watcher)执行 update()
  • data 中的每个属性(任何层级)都有一个 dependency 实例,一个 dependency 中对应着多个 watcher
  • 建立 dependencywatcher 的联系就是通过 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基本不用父子组件严重耦合
provideinject跨层级传数据方便不清楚数据来源与变更
$attrs$listeners数据透传中间组件多余代码
vuex数据集中管理,可追溯代码比较复杂
EventBus各种不同组件间通信很方便,代码简洁事件多了后,难以对事件进行维护
localStoragesessionStorage简单,浏览器自带数据和状态比较混乱,不容易维护

常用修饰符

  • .trim:过滤首尾空格
  • .stop:阻止事件冒泡
  • .prevent:阻止事件默认行为
  • .sync:双向绑定

v-model 和 .sync

vue2.0中.sync与v-model - 掘金