关于 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 操作上的事情。把更多的精力放在数据和业务逻辑上
实现变化侦测主要下面几个大的部分组件
Object.defineProperty()
实现数据劫持- Dep 类收集和通知依赖
- Watch 来监听依赖和通知渲染
- 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 方法。
先实现单向数据绑定
结合之前讲观察者模式和数据监听,思路:
- 主题(subject)是什么?
- 观察者(observer)是什么?
- 观察者何时订阅主题?
- 主题何时通知更新?
上面的例子中,主题应该是 data
的 name
属性,观察者是视图里的{{name}}
,当一开始执行 mvvm 初始化(根据 el 解析模板发现{{name}}
的时候订阅主题,当data.name
发生改变的时候,通知观察者更新内容。 我们可以在一开始监控 data.name
通过Object.defineProperty(data, 'name', {…})
,当用户修改 data.name
的时候调用主题的subject.notify
双向数据绑定
解析模板字符传中的 v-model
指令,对有该指令的 DOM 节点进行处理。
1. 增加新的观察者
2. 给该 DOM 节点增加事件绑定程序,监听 input
事件,在触发时将输入框中的值复制到 vm 实例数据中
参考链接
- 轻项目-实现一个 MVVM-数据的监听 - 写代码啦!
- Vue.js是如何做到数据响应的? - 众成翻译
- 剖析 Vue.js 内部运行机制 — 掘金小册
- GitHub - huangchucai/MVVM-: 从0开始手写MVVM
- 深入浅出 - vue变化侦测原理 · Issue #17 · berwin/Blog · GitHub
- vue响应式原理-依赖收集 · Issue #115 · frontend9/fe9-library · GitHub
- 从源码解释VUE响应式原理-响应式的整体流程 · Issue #116 · frontend9/fe9-library · GitHub
- 剖析Vue原理&实现双向绑定MVVM - 前端足迹 - SegmentFault 思否
- 实现数据的双向绑定mvvm-剖析Vue的原理 | Poetry’s Blog
- 彻底搞懂Vue针对数组和双向绑定(MVVM)的处理方式