关于Vue的那点事之②

160 阅读4分钟

Vue 数据双向绑定的原理

Vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调

第一步:

需要 observe 的数据对象进行递归便利,包括子属性对象的属性,都加上,setter 和 getter 这样的话,给这个对象的某个值赋值,就会触发 setter, name就能监听到了数据变化

第二步:

compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图

第三步:

Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,主要做的事情是:

  • 在自身实例化时往属性订阅器(dep)里面添加自己
  • 自身必须有一个 update()方法
  • 待属性变动 dep.notice() 通知时,能调用自身的 update() 方法,并触发 Compile 中绑定的回调,则功成身退

第四步:

MVVM 作为数据绑定的入口,集合 Observer、Compile 和 Watcher 三者,通过 Observer 来监听自己的 model 数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据 model 变更的双向绑定效果

Object.defineProperty 和 Proxy 的区别

(1) Proxy 的优势如下:
  • Proxy 可以直接监听对象而非属性
  • Proxy 可以直接监听数组的变化
  • Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的
  • Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty 只能遍历对象属性直接修改
  • Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利
(2)Object.defineProperty 的优势如下

兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平

Vue 生命周期的各个阶段

Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载 Dom → 渲染、更新 → 渲染、卸载等一系列过程,我们称这是 Vue 的生命周期

beforeCreate

在实例初始化之后,数据观测 (data observer)和 event/watcher 事件配置之前被调用

created

在实例创建完成后被立即调用.在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算,watch/event 事件回调.然而,挂载阶段还没开始,$el 属性目前不可见

beforeMount

在挂载开始之前被调用:相关的 render 函数首次被调用

mounted

el 被创建的 vm.el 替换,并挂载到实例上去之后调用该钩子.如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内.

beforeUpdate

数据更新时调用,发生在虚拟 DOM 打补丁之前.这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器.该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行

updated

由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子

activated

keep-alive 组件激活时调用.该钩子在服务器端渲染期间不被调用

deactivated

keep-alive 组件停用时调用.该钩子在服务器端渲染期间不被调用

deforeDestroy

实例销毁之前调用.在这一步,实例仍然完全可用.该钩子在服务器端渲染期间不被调用

destroyed

Vue 实例销毁后调用.调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁.该钩子在服务器端渲染期间不被调用

errorCaptured(2.5.0+ 新增)

当捕获一个来自子孙组件的错误时被调用.此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回false 以阻止该错误继续向上传播