最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 实现一个简单的vue-响应式模拟(一)

    正文概述 掘金(bluesky108)   2020-12-26   320

    前言

    最近在研究Vue的源码,探索一下Vue的响应式原理。研究它是怎么实现的。Vue2.0是用 Object.defineProperty 去实现的, Vue3.0是用Proxy 去实现的。然后再结合观察者模式去实现数据跟视图的更新。本节先分享一下基础原理,下一节分享一下具体的实现。

    数据响应式的核心原理

    Vue2.x

    • Vue2.x深入响应式原理
    • MDN-Object.defineProperty
    • 兼容IE8以上
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue2.0响应式原理</title>
    </head>
    <body>
    <div id="app">hello</div>
    
    <script>
      // 绑定单个
      let data = {
        msg: 'hello word',
        count: 10
      }
      let vm = {}
      Object.defineProperty(vm, 'msg', {
        configurable: true, // 是否能被改变,是否能被delete删除
        enumerable: true, // 是否可枚举,遍历
        get() {
          console.log('get:' + data.msg)
          return data.msg
        },
        set(newVal) {
          if(data.msg === newVal) return
          data.msg = newVal
          console.log('set:' + data.msg)
          document.getElementById('app').innerText = data.msg
        }
      })
      console.log(vm.msg)
      vm.msg = 'xxx'
    
      console.log('===================')
      // 绑定多个
    
      let data2 = {
        msg: 'hello word',
        count: 10,
        obj: {
          name: '你好啊'
        },
        arr: [1, 2, 3]
      }
    
      let vm2 = {
        data: data2
      }
    
      function defineProperty(obj, key, val) {
        // 如果value是对象,则继续对他下级成员进行响应式监听
        observer(val)
        Object.defineProperty(obj, key, {
          configurable: true, // 是否能被改变,是否能被delete删除
          enumerable: true, // 是否可枚举,遍历
          get() {
            console.log('get:' + val)
            return val
          },
          set(newVal) {
            if(val === newVal) return
            // 如果新设置的值是对象,则继续对他下级成员进行响应式监听
            observer(newVal)
            val = newVal
            console.log('set:' + key + ':' + val)
          }
        })
      }
      function observer(data) {
        // 如果不是对象,则返回
        if(!data || typeof data != 'object' ) return
        Object.keys(data).forEach(key => {
          defineProperty(data, key, data[key])
        })
      }
      observer(vm2.data)
      console.log(vm2)
    </script>
    </body>
    </html>
    

    Vue3.x

    • MDN-Proxy
    • 直接监听对象,而不是属性
    • IE不支持,性能由浏览器优化
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>vue3.0响应式原理</title>
    </head>
    <body>
      <div id="app">hello</div>
      <script>
        let data = {
          msg: 'hello word',
          count: 10,
          obj: {
            name: '你好啊'
          },
          arr: [1, 2, 3]
        }
    
        let vm = new Proxy(data, {
          get(target, key) {
            console.log('get', key, target[key])
            return target[key]
          },
          set(target, key, newVal) {
            if(target[key] == newVal) return
            console.log('set', key, newVal)
            target[key] = newVal
          }
        })
    
        console.log(vm.msg)
      </script>
    </body>
    </html>
    

    发布订阅模式

    发布订阅模式由三者组成:发布者、订阅者、信号中心。我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern)

    • 我们先看下Vue的自定义事件$on和$emit
    let vm = new Vue()
    vm.$on('dataChange', () => {
     console.log('dataChange')
    })
    vm.$on('dataChange', () => {
     console.log('dataChange1')
    })
    vm.$emit('dataChange')
    
    • 自己实现一个发布订阅模式的思路
      • 定义一个类,初始化的时候,定义一个对象subs,用来存储各种事件,以及订阅这个事件的数组对象
      • 创建一个订阅方法,把订阅的事件和对象存储到subs中,如果没有这个事件,则创建一个,有的话,则添加到订阅的数组对象中
      • 创建一个发布方法,把发布事件,所订阅的所有对象,都通知一遍
    class EventEmitter {
      constructor() {
        // 存储所有事件对象
        this.subs = {}
      }
    
      $on(eventType, fn) { // 订阅
        this.subs[eventType] = this.subs[eventType] || []
        this.subs[eventType].push(fn)
      }
    
      $emit(eventType, ...params) { // 发布
        if(this.subs[eventType]) {
          this.subs[eventType].forEach(fn => {
            fn(...params)
          })
        }
      }
    }
    
    let eve = new EventEmitter()
    
    eve.$on('click', function(data, two) {
      console.log('click1', data, two)
    })
    
    eve.$on('click', function(data) {
      console.log('click2', data)
    })
    
    eve.$on('change', function() {
      console.log('change')
    })
    
    eve.$emit('click', 1, 2)
    eve.$emit('change')
    

    观察者模式

    • 观察者 -- Watcher
      • update() 这里处理当事件发生时,所要做的事情
    • 目标 -- Dep
      • subs 存储所有观察者的数组
      • addSub() 添加观察者的方法,参数是观察者对象
      • notify() 循环subs,通知所有观察者,调用观察者的update方法
    class Dep {
      constructor() {
        this.subs = []
      }
    
      addSub(sub) {
        if(sub && sub.update) {
          this.subs.push(sub)
        }
      }
    
      notify() {
        if(!this.subs.length) return
        this.subs.forEach(sub => {
          sub.update()
        })
      }
    }
    
    class Watcher {
      update() {
        console.log('update')
      }
    }
    
    let dep = new Dep()
    let watch = new Watcher()
    dep.addSub(watch)
    dep.notify()
    

    总结

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

    实现一个简单的vue-响应式模拟(一)

    实现一个简单的vue-响应式模拟(二)


    起源地下载网 » 实现一个简单的vue-响应式模拟(一)

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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