Реализовать более элегантный vue с прокси

внешний интерфейс GitHub Язык программирования Vue.js

предисловие

Если вы прочитали исходный код Vue или поняли принцип отклика Vue, то вы должны знать Object.defineProperty(), Тогда вы также должны знать, что в Vue 2.x через递归 + 遍历 dataобъект для мониторинга данных, Вы также можете знать, что когда мы его используем, мы устанавливаем значение массива напрямую через нижний индекс массива и не можем ответить в реальном времени, потому что Object.defineProperty() Невозможно отслеживать изменение индекса массива и метод массива, который мы обычно используемpush, pop, shift, unshift, splice, sort, reverse, На самом деле это не настоящий метод массива, а видоизмененный, потому что ограниченные возможности, предоставляемые Object.defineProperty(), не могут быть идеальными.

Я видел много руководств по интерпретации исходного кода Vue или реализации упрощенной версии Vue в Интернете, и все они используют Object.defineProperty (вероятно, из соображений совместимости), Object.defineProperty() имеет много ограничений.Говорят, что версия Vue 3.x вместо этого будет использовать Proxy.Так что сегодня мы попробуем сначала и используем Proxy для реализации простой версии Vue.

введение прокси

Прокси используется для изменения поведения определенных операций по умолчанию, что эквивалентно внесению изменений на уровне языка, поэтому он относится к разновидности «метапрограммирования», то есть к программированию на языке программирования.

Прокси можно понимать как настройку уровня «перехвата» перед целевым объектом, и доступ к объекту из внешнего мира должен сначала пройти этот уровень перехвата, поэтому он обеспечивает механизм фильтрации и перезаписи доступа к внешнему миру. . Первоначальное значение слова Proxy — прокси, и здесь оно используется для обозначения того, что оно используется для «прокси» некоторых операций, что можно перевести как «прокси».

Приведенный выше контент взят из главы «Прокси» учебника Ruan Yifeng по es6.Пожалуйста, нажмите здесь.

Давайте посмотрим, как использовать Proxy для определения функции, которая прослушивает данные.

определить наблюдать

 _observe (data){
    var that = this
    
    // 把代理器返回的对象存到 this.$data 里面
    this.$data = new Proxy(data, {
    set(target,key,value){
      // 利用 Reflect 还原默认的赋值操作
      let res =  Reflect.set(target,key,value)
      // 这行就是监控代码了
      that.handles[key].map(item => {item.update()})
      return res
    }
    })
}

Когда набор срабатывает, он будет выполненthat.handles[key].map(item => {item.update()}), функция этого кода заключается в выполнении该属性名下的所有 "监视器"

Так как же появился монитор? Пожалуйста, продолжайте видеть следующий код

определить компиляцию

  _compile (root){
       const nodes = Array.prototype.slice.call(root.children)
       let data = this.$data
       nodes.map(node => {
         // 如果不是末尾节点,就递归
         if(node.children.length > 0) this._complie(node)
         //  处理 v-bind 指令
         if(node.hasAttribute('v-bind')) {
           let key = node.getAttribute('v-bind')
           this._pushWatcher(new Watcher(node,'innerHTML',data,key))
         }
         //  处理 v-model 指令
         if(node.hasAttribute('v-model')) {
           let key = node.getAttribute('v-model')
           this._pushWatcher(new Watcher(node,'value',data,key))
           node.addEventListener('input',() => {data[key] = node.value})
         }
         //  处理 v-click 指令
         if(node.hasAttribute('v-click')) {
           let methodName = node.getAttribute('v-click')
           let mothod = this.$methods[methodName].bind(data)
           node.addEventListener('click',mothod)
         }
       })
     }

Приведенный выше код выглядит очень длинным, но на самом деле он работает только с очень простыми мнениями. то есть"编译" html 模板, положить тудаv-bind,v-model,v-clickдобавить соответствующий通知а также监控

  1. уведомлятьто естьthis._pushWatcher(...), эквивалентно установке прослушивателя, поэтому, пока в this.$data происходит операция установки, она будет выполнятьсяthis._pushWatcherФункция, переданная в круглых скобках, чтобы уведомить узел о необходимости обновления данных.

  2. мониторто естьnode.addEventListener(...)Прослушайте соответствующее событие, а затем выполните соответствующееwatcherилиmethods

this._pushWatcherЧто ты опять сделал?

 _pushWatcher (watcher) {
      if (!this._binding[watcher.key]) this._binding[watcher.key] = []
      this._binding[watcher.key].push(watcher)
    }

Это еще проще, еслиthis._binding[watcher.key]Если он пуст, инициализируйте его, а затем добавьте к нему слушателя.

Наконец, давайте посмотрим, как реализован слушатель.

Определить наблюдателя

 class Watcher {
     constructor (node,attr,data,key) {
       this.node = node
       this.attr = attr
       this.data = data
       this.key = key
     }
     update () {
       this.node[this.attr] = this.data[this.key]
     }
   }

WatcherЭто класс только с одним методом, который заключается в обновлении данных.Как узнать, какой узел обновить и какие данные обновить? При создании экземпляра (новый) передаются параметры, которые определяют эти

Таким образом мы добились предварительной двусторонней привязки, а всего кода всего около 50 строк. На самом деле могло быть и меньше. А вот если меньше, то это продолжение функции кастрации (хотя текущая реализация тоже серьезная версия кастрации), Но я думаю, что осознание этого может просто помочь мне понять суть Vue.

наконец

Окончательный код реализации этой статьи помещен вgithubВверх, студенты, которые хотят увидеть эффект напрямую, могут скопировать и запустить его напрямую.

Если вы считаете, что эта статья полезна для вас, пожалуйста, дайтеgithubДобавь звезду, спасибо большое

Кроме того,githubНа сайте есть и другие туториалы и компоненты про передок.Заинтересованные детские туфли могут посмотреть.Ваша поддержка-моя самая большая мотивация.