[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3wM6g5Bk-1652351202807)(模拟Vue.js响应式原理/image-20220505104638376.png)]
数据响应式的核心原理
vue 2.x
shim降级处理
vue 2.x 基于 Object.defineProperty()实现的 所以不支持IE8以及更低的版本
- 如果有一个对象中多个属性需要转换 getter/setter 如何处理?
vue 3.x
基于proxy实现响应式原理
proxy直接监听对象,而非属性,因此不需要直接遍历对象
发布订阅模式
学生家长,学生每次考完试都要获取孩子的成绩,考完试催要成绩,到班级订阅孩子的信息,成绩出来由老师以短信的形式发送给孩子的家长,不需要再催要成绩
家长 订阅者
老师 发布者
班级 事件中心
事件中心将发布者和订阅者隔离开来,使用更灵活,
观察者模式
没有事件中心,只有发布者和订阅者,发布者需要知道订阅者的存在
订阅者(又叫观察者)
发布者和订阅者之间存在相互依赖的关系
Vue响应式原理模拟
class Vue {
constructor (options) {
// 1. 通过属性保存选项的数据
this.$options = options || {}
this.$data = options.data || {}
this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el
// 2. 把data中的成员转换成getter和setter,注入到vue实例中
this._proxyData(this.$data)
// 3. 调用Observer对象,监听数据的变化
// 4. 调用compiler对象,解析指令和插值表达式
}
_proxyData (data) {
// 遍历data中的所有属性
Object.keys(data).forEach(key => {
// 把data的属性注入到vue实例中
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get () {
return data[key]
},
set (newValue) {
if (newValue === data[key]) {
return
}
data[key] = newValue
}
})
})
}
}
Observer
数据劫持,监听data中的数据变化,将data中的所有属性转换为geter和seter
class Observer {
constructor (data) {
this.walk(data)
}
walk (data) {
// 1. 判断data是否是对象
if (!data || typeof data !== 'object') {
return
}
// 2. 遍历data对象的所有属性
Object.keys(data).forEach(key => {
this.defineReactive(data, key, data[key])
})
}
defineReactive (obj, key, val) { // 转换data的所有属性为geterheseter
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get () {
return val
},
set (newValue) {
if (newValue === val) {
return
}
val = newValue
// 发送通知
}
})
}
}
vue
class Vue {
constructor (options) {
...
// 3. 调用Observer对象,监听数据的变化
new Observer(this.$data)
// 4. 调用compiler对象,解析指令和插值表达式
}
...
}
Observer-defineReactive1
为什么defineReactive1中要传第三个值,再get方法中调用obj[key]时会触发get方法,发生死递归,
defineReactive中的obj为vue. d a t a , data, data,data对象引用leget方法,所以get方法是一个闭包扩展了(引用着)val的作用域
Observer-defineReactive2
data对象属性的响应式实现
class Observer {
...
defineReactive (obj, key, val) { // 转换data的所有属性为geterheseter
// 如果val是对象,把val内部的属性转换成响应式数据
this.walk(val)
...
}
}
新赋值的属性是一个对象,将其转换为响应式的
class Observer {
...
defineReactive (obj, key, val) { // 转换data的所有属性为geterheseter
...
let that = this
Object.defineProperty(obj, key, {
...
set (newValue) {
...
that.walk(val)
}
})
}
}
Compiler
class Compiler {
constructor (vm) {
this.el = vm.$el
this.vm = vm
this.compile(this.el)
}
// 编译模板,处理文本节点和元素节点
compile (el) {
}
// 编译元素节点,处理指令
compileElement (node) {
}
// 编译文本节点,处理差值表达式
compileText (node) {
}
// 判断元素属性是否是指令
isDirective (attrName) {
return attrName.startsWith('v-')
}
// 判断节点是否是文本节点
isTextNode (node) {
return node.nodeType === 3
}
// 判断节点是否是元素节点
isElementNode (node) {
return node.nodeType === 1
}
}
Compiler-compile方法的实现
compile(el){
let childNodes = el.childNodes
Array.from(childNodes).forEach(node=>{
// 处理文本节点
if(this.isTextNode(node)){
this.compileText(node)
// 处理元素节点
}else if(this.isElementNode(node)){
this.compileElement(node)
}
// 递归调用compile,处理子节点
if(node.childNodes&&node.childNodes.length){
this.compile(node)
}
})
}
Compiler-compileText的方法实现
处理文本节点,差值表达式
class Compiler{
constructor(vm){
...
this.compiler(this.el)
}
...
compileText(node){
// console.dir(node)// 将变量以对象的形式打印
let reg = /\{\{(.+?)\}\}/
let value = node.textContent // 获取文本节点的内容
if(reg.test(value)){
let key = RegExp.$1.trim() // 获取到正则表达式第一个分组的内容
node.textContent = value.replace(reg,this.vm[key]) // 替换文本节点的值
}
}
...
}
vue
class Vue{
constructor(options){
...
// 4调用compiler,解析指令和差值表达式
new Compiler(this)
}
}
Compiler-compileElement
处理属性节点,处理指令
class Compiler{
...
compilerElement(node){
// 遍历所有的属性节点
Array.from(node.attributes).forEach(attr=>{
let attrName = attr.name
if(this.isDirective(attrName)){
// v-test --> text
attrName = atttrName.substr(-2)
let key = attr.value
this.update(node,key,attrName)
}
})
}
update(node,key,attrName){ // 执行指令的方法
let updateFn = this[attrName+'updater']
updateFn && updateFn(node,this.vm[key])// thi
}
// 处理v-text 指令
textUpdater(node,value){
node.textContent = value
}
// v-model
modelUpdater(node,value){
node.value = value
}
}
Dep
class Dep {
constructop(){
// 存储所有的观察者
this.subs = []
}
// 添加观察者
addSub(sub){
if(sub && sub.update){
this.subs.push(sub)
}
}
// 发送通知
notify(){
this.subs.forEach(sub=>{
sub.update()
})
}
}
observer.js
class observe {
...
defineReactive(obj.key.val){
let that = this
let dep = new Dep()
...
get(){
// 收集依赖
Dep.target && dep.addSub(Dep.target)
return val
}
set(newValue){
...
// 发送通知
dep.notify()
}
}
}
Watcher
class Watcher {
constructor(vm,key,cb){
this.vm = vm
this.key = key
this.cb = cb
// 把wtcher对象记录到Dep类的静态属性target
Dep.target = this
//触发get方法,在get方法中会调用addSub
this.oldValue = vm[key]
Dep.target = null
}
// 当数据发生改变的时更新视图
update(){
let newValue = this.vm[this.key]
if(this.oldValue === newValue){
return
}
this.cb(newValue)
}
}
创建watcher对象
compiler.js
class compiler{
...
update(node,key,attrName){
...
updateFn && updateFn.call(this,node,this.vm[key],key) // call修正this指向
}
textUpdater(node,value,key){
...
new Watcher(this.vm,key,(newValue)={ // 调用时需要修正this指向
node.textContent = newValue
})
}
compileText(node){
...
// 创建Watcher对象,当数据改变更新视图
new Watcher(this.vm,key,(newValue)=>{
node.textContent = newValue
})
}
modelUpdater(node,value,key){
...
new Watcher(this.vm,key,(newValue)=>{
node.value = newValue
})
// 双向绑定
node.addEventListener('input',()=>{
this.vm[key]=node.value
})
}
}
// 创建Watcher对象,当数据改变更新视图
new Watcher(this.vm,key,(newValue)=>{
node.textContent = newValue
})
}
modelUpdater(node,value,key){
…
new Watcher(this.vm,key,(newValue)=>{
node.value = newValue
})
// 双向绑定
node.addEventListener(‘input’,()=>{
this.vm[key]=node.value
})
}
}