предисловие
Если вы прочитали исходный код 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
добавить соответствующий通知
а также监控
-
уведомлятьто есть
this._pushWatcher(...)
, эквивалентно установке прослушивателя, поэтому, пока в this.$data происходит операция установки, она будет выполнятьсяthis._pushWatcher
Функция, переданная в круглых скобках, чтобы уведомить узел о необходимости обновления данных. -
мониторто есть
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На сайте есть и другие туториалы и компоненты про передок.Заинтересованные детские туфли могут посмотреть.Ваша поддержка-моя самая большая мотивация.