阅读 128

Vue之响应式原理

数据驱动

  • 数据响应式:数据指的是数据模型,数据模型仅仅是普通的JavaScript对象。当我们修改数据的时候,视图会进行更新,避免繁琐的DOM操作,提高开发效率。

  • 双向绑定

    • 数据改变,视图改变,视图改变,数据也随之变化
    • 我们可以使用v-model进行数据的双向绑定
  • 数据驱动是VU独特的特性之一

    • 开发过程中仅仅需要关注数据本身,不需要关心数据是如何渲染到视图的

数据响应式的核心原理

VUE 2.x

当把一个普通的javaScript对象传入Vue实例作为data选项,vue将遍历此对象所有属性,并使用Object.defineProperty把这些属性全部转为getter/setter。 Object.defineProperty是ES5中一个无法被shim(降级)的属性,所以Vue不支持IE8以下的版本。

<body>
    <div id="app">hello</div>
    <script>
        //模拟Vue中的data选项
        let data = {
            msg:'hello',
            count:0

        }
        //模拟vue实例
        let vm = {}

        proxyData(data)

        function proxyData(data){
            //遍历data对象中的所有属性
            Object.keys(data).forEach(key => {
                //把data中的属性,转换成vm的  setter/getter

                //数据劫持:当访问或者设置VM中的成员的时候,做一些干预
                Object.defineProperty(vm,key,{
                    //可枚举(可遍历)
                    enumerable:truem,
                    //可配置(可以使用delete删除,可以通过defineProperty重新定义)
                    configurable:true,
                    //当获取值的时候执行
                    get(){
                        console.log('get:',data[key]);
                        return ddata[key]
                    },
                    //当设置值的时执行
                    set(newValue){
                        console.log('set:',key,newValue);
                        if(newValue === data[key]){
                            return
                        }
                        data[key] = newValue
                        //数据更改,更新DOM的值
                        document.querySelector('#app').textContent = data[key]
                    }
                })
            })
        }
        
        //测试
        vm.msg = 'hello world'
        console.log(vm.msg);
    </script>
</body>
复制代码

Vue 3.x

vue 3中使用Proxy进行对象监听,直接监听对象本身,而非属性。是ES6中新增的是属性,性能由浏览器优化。

 //模拟vue实例
        
        let vm = new Proxy(data,{
            //执行代理行为的函数
            //当访问vm的成员时会执行
            get(target,key){
                console.log('get,key:',key,target[key]);
                return target[key]
            },
            //当设置vm成员的时候会执行
            set(target,key,newValue){
                console.log('set,key:',key,newValue);
                if(target[key] === newValue){
                    return
                }
                target[key] = newValue
                document.querySelector('#app').textContent = target[key]
            }
        })
复制代码

发布订阅模式

我们假定,存在一个信号中心,某个任务执行完成,就像信号中心“发布”一个信号,其他任务可以向中心“订阅”这个信号,从而知道什么时候自己可以开始执行。这就叫“发布订阅模式

//发布者,订阅者和消息中心都是vm
let vm = new Vue()
vm.$on('dataChange',()=>{
    console.log('data---change---')
})
vm.$emit('dataChange')
复制代码
  • 兄弟组件之间通信
//eventBus.js
//事件中心
let eventHub = new Vue()

//ComponentA.vue
//发布者
addTodo:function(){
    //发布消息(事件)
    eventHub.$emit('add-todo',{text:this.newText})
}

//ComponentB.vue
//订阅者
created:function(){
    eventHub.$on('add-todo',this.addTodo)
}
复制代码
  • 自定义发布订阅模式
//html
<script>
        //发布订阅模式
        class EventEmitter {
            constructor () {
                //{'click':[fn1,fn2],'change':[fn1,fn2]}
                this.subs = Object.create(null)
            }

            //注册事件
            $on (eventType,handler) {
                this.subs[eventType] = this.subs[eventType] || []
                this.subs[eventType].push(handler)
            }

            //触发事件
            $emit (eventType) {
                if(this.subs[eventType]){
                    this.subs[eventType].forEach(handler => handler()
                }
                
            }
        }

        //测试
        let em = new EventEmitter
        em.$on('click',()=>{
            console.log('click1');
        })
        em.$on('click',()=>{
            console.log('click2');
        })
        em.$emit('click')
    </script>
复制代码

观察者模式

  • 观察者模式中没有事件中心,只有发布者和订阅者
<script>
        //发布者
        class Dep {
            constructor () {
                //记录所有的订阅者
                this.subs = []
            }
            //把传递的订阅者添加到数组中
            addSub (sub) {
                if(sub && sub.update){
                    this.subs.push(sub)
                }
            }
            notify () {
                this.subs.forEach(sub => {
                    sub.update()
                })
            }
        }
        //订阅者
        class Watcher {
            update () {
                console.log('update');
            }
        }

        //测试
        let dep = new Dep()
        let watcher = new Watcher()
        dep.addSub(watcher)
        dep.notify()
    </script>
复制代码

观察者模式和发布订阅模式的区别

  • 观察者模式是由具体的目标调度,比如当事件触发,Dep就会去调用观察者的方法,所以,观察者模式的订阅者和发布者之间是存在依赖的。
  • 发布/订阅模式:由统一调度中心调度,因此发布者和订阅者不需要知道对方的存在。

image.png

文章分类
前端
文章标签