最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • vue2和vue3的数据响应式原理剖析, Proxy 真香?

    正文概述 掘金(duffy39262)   2021-01-07   644

    写在前面

    相信大家都知道,vue实现数据双向绑定无非就是采用数据劫持的方式,结合发布订阅模式,通过Object.defineProperty()来劫持各个属性的setter,getter以监听属性的变动,在数据变动时发布消息给订阅者,以上是vue2.x的实现原理,3.0的话就是proxy替换Object.defineProperty(),其他流程没有改变。总结一下数据相应原理就发布订阅模式(观察者模式)+ 数据劫持(defineProperty or proxy)

    什么是观察者模式?

    通常又被称为 发布-订阅者模式 或 消息机制,它定义了对象间的一种一对多的依赖关系,只要当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,解决了主体对象与观察者之间功能的耦合,即一个对象状态改变给其他对象通知的问题。

    文字繁琐,看名字高大尚,我们举个生活中栗子 平时想了解某个服装品牌的信息,但是又不想老是去官网查看,于是偷偷关注了公众号(订阅者),公众号有最新产品产品上市就推送消息给你(发布者),没错这个就是了

    我们工作中也有经常看到,比如:

    document.querySelector('#btn').addEventListener('click',function () {
            alert('You click this btn');
        },false)
    

    简单代码实现

    const EventHub = {
      _messager: {},
      on (type, fn) {
        if (this._messager[type] === undefined) {
          this._messager[type] = [fn]
        } else {
          this._messager[type].push(fn)
        }
      },
      emit (type, arv) {
        const params = {
          type,
          arv: arv || {}
        }
        const len = this._messager[type]
        for (let i = 0; i < len.length; i++) {
          const fn = len[i];
          fn.call(this, params)
        }
      }
    }
    
    EventHub.on('say', function (data) {
      console.log(data.arv.text)
    })
    EventHub.on('say', function (data) {
      console.log('hhhhh')
    })
    EventHub.on('hi', function () {
      console.log('hi,小田田')
    })
    setTimeout(() => {
      // 发布
      EventHub.emit('say', {text: 'ys大佬们,我要发布信息了'})
      EventHub.emit('hi')
    }, 3000)
    

    观察者模式使用

    最熟悉的莫过于我们每天使用vue框架了,其中数据的双向绑定就是利用 Object.defineProperty() 和观察者模式实现的。

    看看简单实现代码:

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <div id="demo"></div>
        <input type="text" id="inp">
        <script>
            var obj  = {};
            var demo = document.querySelector('#demo')
            var inp = document.querySelector('#inp')
            Object.defineProperty(obj, 'name', {
                get: function() {
                    return val;
                },
                set: function (newVal) {//当该属性被赋值的时候触发
                    inp.value = newVal;
                    demo.innerHTML = newVal;
                }
            })
            inp.addEventListener('input', function(e) {
                // 给obj的name属性赋值,进而触发该属性的set方法
                obj.name = e.target.value;
            });
            obj.name = 'fei';//在给obj设置name属性的时候,触发了set这个方法
        </script>
    </body>
    </html
    
    

    当然真正的实现过程是复杂的,是这样子的 vue2和vue3的数据响应式原理剖析, Proxy 真香?

    vue2.x vs vue3 响应式原理对比

    vue2.x

    Vue.prototype.observe= function(obj){
        var value;
        var that = this;
        for(var key in obj){
            value = obj[key];
            if(typeof value === 'object'){
                this.observe(value);
            }else{
                Object.defineProperty(this.$data,key,{
                    get:function(){
                        return value
                    },
                    set:function(newValue){
                        value = newValue;
                        self.render();
                    }
                })
            }
        }
    }
    

    vue3.0

    Vue.prototype.observe= function(obj){
        var value;
        var that = this;
        // 用proxy就不需要for in了,即监听的颗粒度减小了,拿到的信息更多了。
        this.$data = new Proxy(obj,{
            get:function(target,key,reveive){
                return target[key];// 不需要中间变量了
            },
            set: function (target, key, newValue,reveive){
                target[key] = newValue;
            }
        })
    }
    

    Vue 3.0与Vue 2.0的区别仅是数据劫持的方式由Object.defineProperty更改为Proxy代理,其他代码不变。vue2和vue3的数据响应式原理剖析, Proxy 真香?

    Proxy 可以做哪些有意思的事情?

    等不及可选链:深层取值(get)

    平时取数据的时候,经常会遇到深层数据结构,如果不做任何处理,很容易造成 JS 报错。 为了避免这个问题,也许你会用多个 && 进行处理:

    const country = {
        name: 'china',
        province: {
            name: 'guangdong',
            city: {
                name: 'shenzhen'
            }
        }
    }
    const cityName = country.province
        && country.province.city
        && country.province.city.name;
    

    最新的 ES 提案中提供了可选链的语法糖,支持我们用下面的语法来深层取值。

    country?.province?.city?.name
    

    proxy的实现

    function noop() {}
    function get (obj) {
        // 注意这里拦截的是 noop 函数
        return new Proxy(noop, {
            // 这里支持返回执行的时候传入的参数
            apply(target, context, [arg]) {
                return obj;
            },
            get(target, prop) {
                return get(obj[prop]);
            }
        })
    }
    const obj = {
        person: {}
    }
    
    get(obj)() === obj; // true
    get(obj).person.name(); // undefined
    

    管道

    在最新的 ECMA 提案中,出现了原生的管道操作符 |>,在 RxJS 和 NodeJS 中都有类似的 pipe 概念。vue2和vue3的数据响应式原理剖析, Proxy 真香? 使用 Proxy 也可以实现 pipe 功能,只要使用 get 对属性访问进行拦截就能轻易实现,将访问的方法都放到 stack 数组里面,一旦最后访问了 execute 就返回结果。

    const pipe = (value) => {
        const stack = [];
        const proxy = new Proxy({}, {
            get(target, prop) {
                if (prop === 'execute') {
                    return stack.reduce(function (val, fn) {
                        return fn(val);
                    }, value);
                }
                stack.push(window[prop]);
                return proxy;
            }
        })
        return proxy;
    }
    var double = n => n * 2;
    var pow = n => n * n;
    pipe(3).double.pow.execute
    

    起源地下载网 » vue2和vue3的数据响应式原理剖析, Proxy 真香?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元