理解 Vue.js 内部运行机制(一)响应式原理

关于 Vue 的内部原理其实有很多个重要的部分如变化侦测、模板编译、virtualDOM、整体运行流程等。这篇文章把变化侦测这部分单独拿出来学习。

简述 Vue 的 MVVM

传统的前端页面,在响应用户操作时去服务器拿数据。在拿到数据后更新到前端页面(操作DOM),在 Vue 框架中,View(视图层) 和 Model(数据层) 通过 ViewModel 来进行双向绑定

MVVM(Model–view–viewmodel)是一种用于把数据和 UI 分离的设计模式。Vue 框架的的设计也是受到了它的启发

MVVM 中的 Model 表示应用程序使用的数据,用来保存信息,但通常不处理行为,不对信息进行加工。数据的格式化由 View 处理(模板)。行为一般认为是业务逻辑,封装在 ViewModel 中。

ViewModel 充当数据转换器,将 Model 信息转换为 View 的信息。将命令从 View 传递到 Model

能让我们无需再去关心 DOM 操作上的事情。把更多的精力放在数据和业务逻辑上

实现变化侦测主要下面几个大的部分组件

  1. Object.defineProperty() 实现数据劫持
  2. Dep 类收集和通知依赖
  3. Watch 来监听依赖和通知渲染
  4. Compile 模板编译

Object.defineProperty()

首页我们需要了解Object.defineProperty() ,Vue 就是基于它实现「响应式系统」的。

它的使用方法:

/*
  obj: 目标对象
  prop: 需要操作的目标对象的属性名
  desciptor: 表述符
  
  return value 传入对象
*/
Object.defineProperty(obj, prop, descriptor)

这里的 descriptor是一个对象。我们来看看它的几个属性分别是用来干嘛。 * enumerable,属性是否可枚举,默认 false。 * configurable,属性是否可以被修改或者删除,默认 false。 * get,获取属性的方法。 * set,设置属性的方法。

数据劫持(观察)

在 init 的阶段会进行初始化,对数据进行「响应式化」。我们通过一段代码来模拟实现。

function defineReative(obj, key, val){
  Object.defineProprety(obj, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter(){
      return val
    },
    set: function reactiveSetter(newVal){
      if (newVal === val) return
      cb(newVal)
    }
  })
}
function observe(obj){
  if (typeof obj !== Object) return
  Object.keys(obj).forEach((key)=>{
    defineReative(obj, key, obj[key])
  })
}

发布订阅(观察者)模式

一个典型的观察者模式应用场景是用户在一个网站订阅主题

多个用户(观察者,Observer)都可以订阅某个主题(Subject) 当主题内容更新时订阅该主题的用户都能收到通知 以下是代码实现

Subject是构造函数,new Subject() 创建一个主题对象,该对象内部维护订阅当前主题的观察者数组。主题对象上有一些方法,如添加观察者(addObserver)、删除观察者(removeObserver)、通知观察者更新(notify)。 当notify 时实际上调用全部观察者 observer 自身的 update 方法。

Observer 是构造函数,new Observer() 创建一个观察者对象,该对象有一个 update 方法。

写一个简单的发布订阅模式

先实现单向数据绑定

结合之前讲观察者模式和数据监听,思路:

  1. 主题(subject)是什么?
  2. 观察者(observer)是什么?
  3. 观察者何时订阅主题?
  4. 主题何时通知更新?

上面的例子中,主题应该是 dataname 属性,观察者是视图里的{{name}},当一开始执行 mvvm 初始化(根据 el 解析模板发现{{name}}的时候订阅主题,当data.name发生改变的时候,通知观察者更新内容。 我们可以在一开始监控 data.name通过Object.defineProperty(data, 'name', {…}),当用户修改 data.name 的时候调用主题的subject.notify

双向数据绑定

解析模板字符传中的 v-model 指令,对有该指令的 DOM 节点进行处理。 1. 增加新的观察者 2. 给该 DOM 节点增加事件绑定程序,监听 input 事件,在触发时将输入框中的值复制到 vm 实例数据中

参考链接