Vue - это магия Javascript. Vue предоставляет инструменты для быстрого создания интерактивных пользовательских интерфейсов. Это делается с помощью концепции многократно используемых компонентов, которые визуализируют и обновляют html на основе данных этого компонента. В кругах разработчиков это называется двусторонней привязкой данных. Внесите изменения в данные и следите за обновлением HTML автоматически. У вас есть элемент ввода HTML? Скажите Vue привязать форму к данным и наблюдать за изменением данных при вводе текста. Видеть? Магия.

Компоненты Vue также имеют методы, которые вы можете вызывать. Эти методы чаще используются в качестве обработчиков событий. Хотите запускать код при нажатии кнопки? Назначьте функцию в вашем компоненте Vue в качестве обработчика. Внутри вашего метода вы можете манипулировать данными компонента, а затем снова наблюдать, как изменяется HTML. Это круговорот жизни.

Вот фрагмент HTML-кода, который может быть в HTML-коде вашего компонента.

И это может быть ваше простое приложение Vue

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

Итак, как мы можем вызвать этот метод, не ссылаясь на свойство метода? Как Vue это делает?

Если бы мы проверили экземпляр Vue ‘app’ непосредственно в Chrome DevTools, мы увидим очень богатый объект, как показано ниже.

В этот объект встроено множество функций. Вы можете видеть свойства и функции, относящиеся к наблюдателям, событиям жизненного цикла и т. Д. В этом объекте вы также можете увидеть функцию increment. Эта функция increment имеет дополнительные свойства, которые включают TargetFunction, BoundThis и BoundArgs. Это говорит нам кое-что интересное. Секрет в том, что функция increment не наша функция increment. Это оболочка, которая указывает на нашу функцию increment. Как Vue.JS это сделал?

Внутреннее строение вашего экземпляра Vue

Когда мы создаем новый экземпляр Vue с помощью new Vue ({…}), мы передаем объект options, который содержит состояние и поведение нашего приложения. В результате мы получаем состав Vue нового объекта, который обертывает все динамические привязки и поведение вокруг наших намерений. Давайте посмотрим на файл конструктора Vue в исходном коде Vue.

Когда мы вызываем конструктор Vue, он внутренне вызывает функцию _init для нового объекта Vue, передавая наш объект параметров. Эта функция _init определена в initMixin. Внутри функции _init Vue начинает создавать обернутое поведение. Внутри этого набора вызовов находится вызов функции initMethods, который происходит, когда у нас есть свойство method в нашем объекте параметров. Давайте посмотрим на функцию initMethods.

Эта функция вызывается с аргументом vm, который указывает на новый создаваемый экземпляр Vue. Аргумент methods содержит все определения наших методов, готовые к присвоению нашему экземпляру.

Если все проверки на ошибки выглядят хорошо, включая проверку того, что у нас нет имени функции, такого же, как у свойства данных, то мы вызываем функцию bind, передавая наше собственное объявление функции и ссылку на новый экземпляр Vue как vm. Давайте посмотрим на объявление функции Vue bind.

В служебном файле мы видим, что функция bind будет использовать настраиваемую реализацию bind для сред Javascript, в которых нет собственной Function.prototype.bind функция. В противном случае он будет использовать встроенную функцию Function.prototype.bind, гарантируя, что контекст this внутри функции будет указывать на наш новый экземпляр Vue.

Возвращаясь к нашей функции initMethods, код в контексте нашего примера можно увидеть, как показано ниже.

Функция bind, принадлежащая Function.prototype, возвращает новую функцию, которая при вызове будет иметь ключевое слово this, установленное для переданного аргумента. в привязку. Затем результат вызова bind присваивается нашему новому экземпляру Vue с использованием обозначения скобок. Более подробную информацию о функции Function.prototype.bind можно найти в документации для веб-разработчиков Mozilla. .



После того, как наш объект Vue создан и возвращен конструктором Vue, последний объект имеет свойство приращения, которое ссылается на новую функцию, возвращаемую вызовом bind. Вот как ваша функция может использовать ключевое слово this, которое теперь правильно ссылается на компонент Vue, которому он принадлежит.

А как насчет производительности?

Когда вы вызываете метод increment в новом экземпляре Vue, который мы создали, вы фактически вызываете функцию, которая была подготовлена ​​с помощью вызова bind. Каждый механизм Javascript по-разному выполняет внутреннюю реализацию привязки, и между браузерами есть различия в производительности. В последних версиях Chrome нет заметной разницы, когда движок v8 оптимизирует ваш код. Однако в других браузерах, таких как Microsoft Edge и Firefox, разница в производительности связанных функций и несвязанных функций может быть значительной. Вы можете просмотреть результаты этих тестов и запустить их самостоятельно, используя следующий тест jsPerf.



Последняя версия Microsoft Edge идентифицируется как Chrome 64 в диаграммах jsPerf. Вы можете увидеть значительные различия в производительности вызывающих функций, связанных с помощью Function.prototype.bind. Последние версии Chrome могут оптимизировать это решение, если понимают, что вы делаете.

Почему это важно?

Возможно, для вас это не имеет значения. Vue.js - это волшебство, когда вам не нужно думать о внутреннем устройстве. Просто вставай и пользуйся этим. Потенциальные накладные расходы на производительность, связанные с вызовом метода, также, вероятно, не имеют для вас значения, поскольку взаимодействия внутри Vue.js не должны быть высокопроизводительными, чтобы обеспечить богатый интерактивный пользовательский интерфейс.

Почему это важно для меня

В настоящее время я создаю эмулятор Nintendo, написанный на Javascript с Vue.js. Это требует, чтобы все было как можно быстрее. Каждое обернутое поведение в Vue.js связано со снижением производительности, и мне приходится переносить большую часть поведения эмулятора за пределы Vue.js для достижения необходимой скорости.

Теперь мы знаем, что за магию приходится расплачиваться.

Следите за моей разработкой моего 8-битного эмулятора Nintendo, написанного на Javascript с Vue.js, следуя моему репозиторию кода.