vue响应式原理

深入讲解Vue响应式原理

摘要:采用通俗易懂的方式,讲解Vue响应式原理

Vue响应式原理

​ 什么是Vue的响应式?简单来说就是,当你修改数据时,页面会重新渲染,即时更新数据。要搞懂Vue响应式原理,需要弄懂三个问题:

  • 如何知道哪些数据发生了变化?

    数据劫持

    在生成Vue实例实例时,会对属性data进行遍历,使用 Object.defineProperty 把这些属性转为 getter/setter ,来对数据追踪变化。

  • 当数据发生变化时,如何通知?(发布订阅模式)

    发布订阅模式

    当数据更改的时候,会调用data中数据的setter方法,在数据变更的时候, 通知notity订阅者,通过watcher更新视图。

  • 通知哪些使用该数据的地方进行更新?(收集依赖)

    收集依赖

    当页面渲染,获取数据的时候,会调用data中数据的getter方法,在getter方法中收集依赖(addSub())。所有的依赖信息,存储到Dep对象,即订阅者中。

Vue响应式图解

在这里插入图片描述

Vue响应式——代码解读
class Vue{
    constructor(options){
        // 保存数据
        this.$options = options;
        this.$data = options.data;
        this.$el = options.el;
        
        // 将data添加到响应式系统中
        new Observer(this.$data);
        
        // 代理this.$data的数据
        Object.keys(this.$data).foreach(key => {
            this._proxy(key)
        })
        
        // 处理el
        new Compiler(this.$el, this)
    }
    _proxy(key){
        Object.defineProperty(this, key,{
            configurable:true,
            enumerable:true,
            set(newValue){
                this.$data[key] = newValue
            },
            get(){
                return this.$data[key]
            }
        })
    }
   
    class Observer{
        constructor(data){
            this.data = data;
            // 遍历data中的数据
            Object.keys(data).forEach(key => {
                this.defineReactive(this.data, key, data[key])
            })
        }
        // 定义响应式,添加get和set方法
        defineReactive(data,key,val){
            //一个属性对应一个Dep对象
            const dep = new Dep()
            Object.defineProperty(data, key, {
                configurable:true,
                enumerable:true,
                set(newValue){
                    if(newValue === val)
                        return
                    val = newValue
                    //如何不一样,通知订阅者
                    dep.notify()
                },
                get(){
                    if(Dep.target){
                        //添加订阅者
                        dep.addSub(Dep.target)
                    }
                    return val
                }
            })
        }
    }
	
	// 依赖者类,订阅者
	class Dep{
        constructor(){
            this.subs = []
        }
        
        addSub(sub){
            this.subs.push(sub)
        }
        
        notity(){
            //遍历所有的订阅者,通知更新
            this.subs.forEach(sub => {
                sub.update()
            })
        }
    }
	
	class Watcher{
        constructor(node, name, vm){
            constructor(node, name, vm){
                this.node = node
                this.name = name
                this.vm = vm
                Dep.target = this;
                this.update()
                Dep.target =  null  
                //置空,防止每次调用get的时候重复添加订阅者
            }
        }
        
        update(){
            this.node.nodeValue = this.vm[this.name]
        }
    }
	// 文本节点{{}}匹配正则表达式
	// . 匹配任何内容(除了特殊字符) * 0个或多个  + 1个或多个
	const reg = /\{\{(.+)\}\}/
	class Compiler{
        // el:app    vm:Vue
        constructor(el, vm){
            this.el = document.querySelector(el)
            this.vm = vm
            
            this.frag = this._createFragment()
            this.el.appendChild(this.frag)
        }
        _createFragment(){
            const frag = document.createDocumentFragment()
            let child
            // 遍历 this.el中的每一个子节点
            while(child = this.el.firstChild){
                this._compile(child)  //编译每一个node节点
                frag.appendChild(child)
            }
            return frag
        }
        
        _compile(node){
            //nodeType = 1 标签节点 
            if(node.nodeType === 1){
                const attrs = node.attributes
                if(attrs.hasOwnProperty('v-model')){
                    const name = attrs['v-model'].nodeValue
                    node.addEventListener('input', e => {
                        this.vm[name] = e.target.value
                    })
                }
            }
            //nodeType = 3 文本节点
            if(node.nodeType === 3){
                if(reg.test(node.nodeValue)){
                    const name = RegExp.$1.trim()
                    new Watcher(node,name,this.vm)
                }
            }
        }
    }

	
}
const app = new Vue({
    el:'#app',
    data:{
        message:'你好啊'
    }
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值