手写vue3.0双向绑定-es6 Proxy

前言

vue3.0中使用Proxy,为什么放弃了原来vue2.0的Object.defineProperty,是因为监听、拦截的更加全面,功能更加强大,下文我就详细介绍vue3.0中是如何使用Proxy实现数据的双向绑定的

目录

      • 前言
      • 1、什么是Proxy
      • 2、vue.js中使用双向绑定
      • 3、手写,实现vue3.0双向绑定
      • 4、Proxy对比Object.defineProperty
      • 5、总结

1、什么是Proxy

Proxy取其英文意思即“代理”。所谓代理,是你要取得某样东西或对其进行某些操作的中间媒介,而不是直接作用在这个对象上。

Proxy可以理解成在目标对象前架设一层拦截层,外界访问该对象都必须先通过这层拦截,因此提供一种机制可以对外界的访问进行拦截或过滤。

2、vue.js中使用双向绑定

<div id="app">
 <h2>hello {
     {
     msg}}</h2>
   <input type="text" v-model="msg" />
 </div>

 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
 <script>
   let vm = new Vue({
     
     el: '#app',
     data: {
     
       msg: '阿良',
     },
   })
 </script>

3、手写,实现vue3.0双向绑定

这里就不一一讲解了,不懂得看注释哦

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
    <script src="./index.js"></script>
  </head>
  <body>
    <div id="app">
      <p>姓名:{
     {
     name}}</p>
      <p>年龄:{
     {
     age}}</p>
      <input type="text" v-model="name" />
    </div>
    <script>
      let vm = new Reactive({
     
        // 挂载元素
        el: '#app',
        data: {
     
          name: '阿良',
          age: 24,
        },
      })
    </script>
  </body>
</html>

index.js

// EventTarget [6]
class Reactive extends EventTarget {
     
  // 接收参数
  constructor(options) {
     
    super()
    this.options = options
    // data 赋值
    this.$data = this.options.data
    // 挂载元素
    this.el = document.querySelector(this.options.el)
    // 调用 compile 函数
    this.compile(this.el)
    // 调用双向绑定
    this.observe(this.$data)
  }
  // 双向绑定
  observe(data) {
     
    // 备份this
    let _this = this
    // 接收目标对象进行代理
    this.$data = new Proxy(data, {
     
      set(target, prop, value) {
     
        // 创建一个自定义事件 CustomEvent [5]
        // 事件名称使用的是 prop
        let event = new CustomEvent(prop, {
     
          // 传入新的值
          detail: value,
        })
        // 派发 event 事件
        _this.dispatchEvent(event)
        return Reflect.set(...arguments)
      },
    })
  }
  // 渲染数据
  compile(el) {
     
    // 获取el的子元素
    let child = el.childNodes
    // 遍历判断是否存在文本
    ;[...child].forEach((node) => {
     
      // 如果node的类型是TEXT_NODE
      if (node.nodeType === 3) {
     
        // 拿到文本内容
        let txt = node.textContent
        // 正则匹配
        let reg = /\{\{\s*([^\s\{\}]+)\s*\}\}/g
        if (reg.test(txt)) {
     
          let $1 = RegExp.$1
          this.$data[$1] &&
            (node.textContent = txt.replace(reg, this.$data[$1]))
          // 绑定自定义事件
          this.addEventListener($1, (e) => {
     
            // 替换成传进来的 detail
            node.textContent = txt.replace(reg, e.detail)
          })
        }
        // 如果node的类型是ELEMENT_NODE
      } else if (node.nodeType === 1) {
     
        // 获取attr
        let attr = node.attributes
        // 判断是否存在v-model属性
        if (attr.hasOwnProperty('v-model')) {
     
          // 获取v-model中绑定的值
          let keyName = attr['v-model'].nodeValue
          // 赋值给元素的value
          node.value = this.$data[keyName]
          // 绑定事件
          node.addEventListener('input', (e) => {
     
            // 当事件触发的时候我们进行赋值
            this.$data[keyName] = node.value
          })
        }
        // 递归执行
        this.compile(node)
      }
    })
  }
}

4、Proxy对比Object.defineProperty

Vue2.0中的双向绑定,使用Object.defineProperty()进行双向绑定

缺点:
1、无法对数组进行监听,采用的是对数组的方法进行重写(push, pop,shift,unshift等等)。对此进行双向绑定和数据监听的操作
2、效率差,这主要是因为对多层数据进行一次性的递归操作,如果数据很多或者是很深层次,这样性能非常的差
3、因为局限性,无法对新加/删除的数据进行监听,所以使用在vue2.0中使用$set进行手动添加

Vue3.0中双向绑定,使用Proxy和Reflect进行双向绑定

优点:
1、Proxy可以对数组和对象进行拦截和监听

缺点:
1、Proxy会出发多次set/get响应
解决办法:
 ①使用类似于debounce的操作,对其进行优化,使其值响应一次
 ②(vue3.0中的解决方式),判断key是否是target的自身属性,以及value是否和target[key]相等,可以避免多余的set/get操作
2、Proxy只能代理一层,无法深度监听
 ①使用深度递归,对每一层进行监听。巧妙的使用的Reflect.get()会返回对象内层结构的特性(下一层),判断下一层是否还是对象,并且使用深度递归操作。但是在性能上又很大的影响
 ②使用weakMap,使用两个weakMap来保存原始数据和可响应数据。访问数据时会从保存的数据中查找,如果没有再对其进行Proxy操作

5、总结

总的来说使用es6 Proxy实现数据的双向绑定,解决了很多vue2.0使用Object.defineProperty解决双向绑定隐藏的一些bug,性能也有所提升

希望本文的内容可以帮助到大家!

你可能感兴趣的:(js深入解析,javascript,es6,vue.js,html)