- LiveReload — обновление javascript без полной перезагрузки страницы (на примере mithril)
- Постановка
- Извлечение view-функций
- Плагин к LiveReload
- Встраивание в процесс сборки
- Неявная подгрузка нового скрипта
- Итог
- Где посмотреть:
- Три способа легко обновить JavaScript (2021 г.)
- Как обновить JavaScript
- Изменить в настройках
- Установить вручную
- Обновите свой браузер.
LiveReload — обновление javascript без полной перезагрузки страницы (на примере mithril)
Не так давно я начал пользоваться такой полезной штукой как livereload (для тех, кто не знает, что это — есть статья на хабре). Livereload отслеживает изменения в коде веб-приложения и перегружает страницу в браузере при необходимости. При этом со стилями Livereload поступает умнее и заменяет их на лету без перезагрузки, что выглядит волшебно.
К сожалению с javascript такой номер не прокатывает — Livereload не знает как гуманно заменить только изменившиеся скрипты и перегружает всю страницу. Это особенно печалит если использовать инструмент вроде mithril, в котором представление (читай — html) задается так же в javascript. Если я меняю модель или контроллер, то тут все понятно, но если я меняю класс у дива в представлении (скажем, подбирая правильное сочетание bootstrap классов), то перезагрузка страницы кажется излишней — ну вот же, я поменял одну функцию, просто перерисуй view с ее помощью!
В целом не страшно, конечно (работали же как-то без этого раньше), но почему бы не сделать работу еще немного удобнее?
Для тех, кто торопится
Постановка
У нас есть чистые функции, отображающие модель на представление (читай — html). Нам нужно сделать так, что если меняется такая функция, то мы должны в браузере подгрузить новую версию, подменить исходную и, в случае с Mithril, вызвать m.redraw().
Самый простой способ подмены — запихать все такие функции в глобальный объект, а в местах их прежнего обитания расставить обращения к этому объекту. Что-то типа такого:
//file1.js window.MyObj.MyFn = function (c) < return m("h1", c.text) >//file2.js var Page = < controller: function () < this.text = "Hello"; >, view: function (c) < return window.MyObj.MyFn(c); >>
Теперь мы можем перезагружать один только file1.js и дергать m.redraw(), после чего представление будет перерисовано. При этом текущее состояние системы (в нашем случае хранящееся в контроллере Page) сохранится.
Однако вручную делать такое разделение не хочется — во-первых нарушается целостность компонента (довольно удобно при разработке менять только один файл, настраивая и поведение и внешний вид компонента), во-вторых есть уже написанный код, в-третьих пришлось бы больше объяснять новым разработчикам. Значит нужно парсить исходный код и извлекать нужные нам функции.
- как найти нужные нам функции в существующем коде и извлечь их в отдельный скрипт
- как научить LiveReload обновлять этот скрипт без перезагрузки страницы
Извлечение view-функций
- в теле функции могут использоваться идентификаторы (переменные, функции, и пр.) доступные через замыкание, а не только те что передаются в аргументах функции
- некоторые функции не надо выносить даже если они носят имя view
- некоторые функции надо выносить даже если они не носят имя view
var model = new Model(); //какой-то метод модели, по совпадению называется view model.view = function () < this.viewed = true; >//метод представления, хотя и не называется view function formatDate(date) < return m("span.date", [ m("span.year", date.getFullYear()), formatMonth(date), (date.getDate()+1) ]) >var Page1 = < //каноничный mithril-овский метод view //formatDate и model доступны через замыкание, и еще m - глобально //date, в свою очередь, объявляется внутри самой функции view: function() < var date = new Date(); return m("h1", [model.title(), " today is ", formatDate(date)]); >>
Для решения проблемы 1 парсинга кода регекспами будет явно недостаточно, нужен синтаксический разбор. К счастью, есть библиотека esprima, которая разбирает переданный ей js-код и выдает синтаксическое дерево (можете поиграться здесь) как обычный json. Обход такого дерева не должен составить труда, надо только разобраться со всеми возможными типами узлов дерева, чтобы не пропустить какие-то случаи. Например запись
(соответственно результаты парсинга вот и вот)
Теперь несложно и функцию найти, и все ее зависимости. Тела функций вынимаем и складируем отдельно, в исходном коде помещаем вызов глобальной функции, и собираем код обратно с помощью библиотеки escodegen.
Для решения задач 2 и 3 пригодилось бы что-то вроде аттрибутов C# или аннотаций Java — какой-то способ пометить нужные функции. Т.к. в javascript такого способа не предусмотрено, пришлось придумать — пусть аттрибутом будет строка, являющаяся первым выражением в теле функции. И если значение аттрибуты «__stateless», то функцию надо извлекать, а если «__ignore» — то не надо.
var model = new Model(); //какой-то метод модели, по совпадению называется view model.view = function () < "__ignore"; this.viewed = true; >//метод представления, хотя и не называется view function formatDate(date)
Все описанное выше я собрал в виде отдельной библиотеки st8less, никак не заточенной под mithril. В теории ее можно использовать для других похожих задач.
Плагин к LiveReload
Теперь, когда мы умеем доставать нужные нам функции и сохранять их отдельным js-файлом, надо научить LiveReload обновлять его без перезагрузки всей страницы.
Плагин к LiveReload написать несложно, причем он подхватится независимо от того как вы используете Livereload — вставляете сниппет на страницу или используете расширение браузера. Плагины имеют приоритет над стандартным поведением, и если мы напишем вот так:
function Plugin() < this.reload = function(path) < console.log("reloaded", path); return true; >> window.LiveReload.addPlugin(Plugin)
то при любом изменении вызовется наш метод reload, и если мы возвращаем true, то стандартная обработка не происходит. Так что можем отслеживать обновление только нашего файла с вынесенными функциями (зная имя этого файла, конечно) и перезагружать только его.
Для перезагрузки мы будем удалять существующий элемент script и добавлять в DOM новый, причем к URL скрипта каждый раз будет добавляться текущее время — чтобы предотвратить кэширование.
doReloadScript: function (scriptNode) < var oldSrcBase = scriptNode.src.split("?")[0], parent = scriptNode.parentNode, newNode = this.window.document.createElement("script"); parent.removeChild(scriptNode); newNode.src = [oldSrcBase, new Date().getTime()].join('?'); parent.appendChild(newNode); >,
Встраивание в процесс сборки
Если проект собирается с помощью gulp (у меня как раз так) или другой системы сборки, то логично было бы встроить извлечение view-функций в процесс сборки. В случае с gulp нужно было написать плагин, который обработает все проходящие через него js-скрипты, повыдергивает из них view-функции и запишет отдельным файлом, а потом оповестит LiveReload об изменениях.
Я не буду описывать создание плагина для gulp, все делалось строго по туториалам и примерам других плагинов (типа gulp-coffee и gulp-concat), ничего необычного. В итоге gulpfile.js выглядит примерно так:
. other requires var changed = require('gulp-changed'); var extract = require('gulp-livereload-mithril'); var server; //используется двумя gulp-задачами, поэтому вынесено // собирает проект и оповещает livereload-сервер (если запущен) gulp.task('compile', function () < // Начинаем обработку coffee-файлов. // Допустим у нас их два - main.coffee и dashboard.coffee gulp.src("src/**/*.coffee") // ->main.coffee, dashboard.coffee // компилируем в js .pipe(coffee()) // -> main.js, dashboard.js // наш плагин - извлекаем view-функции .pipe(extract()) // -> main'.js, dashboard'.js, st8less.js // проверяем какие файлы действительно поменялись по сравнению со старыми версиями. // допустим поменялся только st8less.js .pipe(changed("public", < hasChanged: changed.compareSha1Digest >)) // -> st8less.js // копируем его в public .pipe(gulp.dest("public")) // -> st8less.js // оповещаем LiveReload сервер об изменениях .pipe(server ? server.notify() : gutil.noop()); >); // запускает наш сервер, LiveReload-сервер и следит за изменениями // чуть что - вызывает задачу compile gulp.task('serve', ['compile'], function () < server = gls.new('./server.js'); server.start(); gulp.watch(SRC, ['compile'] /* no need to notify here*/); >);
Обратите внимание на использование gulp-changed. Если мы меняем только main.coffee, то на выходе получаем обновленный main.js и st8less.js, причем если мы меняли view-функцию, то main.js по факту будет точно таким же. Но время изменения у main.js все равно поменяется, и в результате LiveReload перезагрузит всю страницу. Чтобы этого не случилось, необходимо сравнить фактическое содержимое, что и делает плагин gulp-changed.
Плагины для gulp и LiveReload лежат в отдельном репозитории — gulp-livereload-mithril. Он, в свою очередь, ссылается на библиотеку st8less описанную выше.
Неявная подгрузка нового скрипта
Наш плагин создает новый js-файл (st8less.js), и надо сослаться на него из html-страницы. Можно было попросить пользователя сделать это самостоятельно, но я подумал: все равно я меняю пользовательские js-файлы, почему бы не добавить в один из них простой document.write?
Так и было сделано, но этого оказалось недостаточно. Если, скажем, мы добавим document.write в начало main.js, а main.js где-то в середине уже использует вынесенные в функции, то мы получим ошибку, т.к. свежедобавленный элемент script еще не начал подгружать наш скрипт.
Надо как-то подгрузить указанный скрипт здесь и сейчас, и другого способа, кроме как послать синхронный ajax-запрос, я не нашел. Добавляемая в начало одного из скриптов конструкция выглядит так:
(function loadScriptSynchronously() '); var req = new XMLHttpRequest(); req.open('GET', path, false); req.send(); var src = req.responseText eval(src) >.call());
При желании можно этот ужас отключить, передав плагину . Тогда придется добавлять тэг script вручную.
Итог
Задача оказалась вполне разрешимой, причем приведенное решение можно применить не только для mithril, но и для других подобных случаев — на ум приходят react и angular.js 1.x (зачастую html-верстка для директив помещается прямо в js-код директивы).
- esprima не поддерживает полностью ES 6, т.е. если в вашем коде встречаются, скажем, генераторы, плагин использоваться не удастся.
Решение: предварительно превращать ES6-код в ES5
m "pre", JSON.stringify(someValueIWantToWatch)
Где посмотреть:
Спасибо за внимание, надеюсь было полезно.
Три способа легко обновить JavaScript (2021 г.)
JavaScript — это язык программирования, который позволяет вам реализовывать на веб-странице разные вещи. Он позволяет вам создавать 2D и 3D анимированные изображения, интерактивные карты и т. Д. Таким образом, вы должны знать, как Обновить JavaScript прямо сейчас.
Его нужно время от времени обновлять; старая версия может содержать дыры, позволяющие вредоносным программам проникать на ваш компьютер без каких-либо действий.
Как обновить JavaScript
Вот несколько простых шагов, которые вы можете выполнить для обновления JavaScript:
Изменить в настройках
Лучший способ поддерживать Java в актуальном состоянии — это изменить настройки обновления и установить для него автоматическое обновление.
- Перейдите в панель управления и запустите Панель управления Java.
- Нажмите «Обновить», чтобы получить доступ к настройкам.
- Нажмите «Проверять обновления автоматически», чтобы установить автоматическое обновление.
- Щелкните ОК, чтобы применить изменения.
Установить вручную
Если у вас возникнут проблемы с описанным выше методом, вы можете выполнить обновление javascript, загрузив последнюю версию JavaScript и установив ее вручную.
- После того, как вы нажмете кнопку «Загрузить», появится всплывающее окно с условиями лицензионного соглашения с конечным пользователем. Нажмите «Принять и начать бесплатную загрузку» внизу, чтобы загрузить программное обеспечение бесплатно.
- Выберите файл, в который вы хотите сохранить установочный файл.
- Теперь перейдите в открытое окно, дважды щелкните по нему и следуйте инструкциям по загрузке.
- После завершения установки перезагрузите компьютер.
Обновите свой браузер.
JavaScript работает в вашем браузере, и ваш браузер предоставляет JavaScript с интерпретатором для интерпретации Java на веб-сайте. Поэтому, когда вы обновляете свой браузер, JavaScript автоматически обновляется.
Перечисленные выше методы являются наиболее простыми способами обновления JavaScript. Надеюсь, эта статья была вам полезна.
См. Также: 6 лучших приложений для студентов, которые упростят вашу студенческую жизнь
Предыдущая статьяКак заставить эмулятор Dolphin работать быстрее [Configuration]Следующая статьяКак пикселизировать изображение с помощью Microsoft Paint [And Other Methods]
Когда я не смотрю сериалы с пиццей, я всегда играю на пианино или в ФИФА. Источник TechWhoop. Жизненный девиз: Успех приходит только тогда, когда работа находится в словаре.