WebJars + RequireJS

в 20:16, , рубрики: coffeescript, java, javascript, play framework, requirejs, scala

День добрый, читатели !
В этой статье спешу вам рассказать (хоть и с опозданием) о том что такое WebJars на примере приложения в Play Framework

image В практически любом веб-приложении нельзя обойтись без сторонних javascript-библиотек. Самый простой способ добавить их: скачать и добавить в проект, а также добавить файл в git-репозиторий. Решение годное, но лично для меня наличие в проекте какой-либо статики малость раздражает. Есть другой метод: указывать ссылку на внешний хостинг js-библиотек такой как google, yandex. В принципе вариант, но в моей практике были случаи, когда необходимо было продолжить разработку а доступ в интернет оставлял желать лучшего либо его вообще не было, в итоге клиентская часть не функционировала. Наиболее годным решением мне видится добавления js библиотеки в качестве зависимости в проект, с подобным подходом вы могли сталкивать в Ruby on Rails

Ближе к делу

У нас есть веб-приложение, написанное на java-based языке и нам нужно добавить в него пару javascript библиотек, именно для такого случая нам идеально подойдут WebJars

WebJars — набор библиотек, каждая из которых содержит в себе JS библиотеку и/или CSS модули

Полный список библиотек можно посмотреть здесь. У каждой из них есть несколько версий которые соответствуют версии js-библиотеки. Все WebJars доступны на Maven репозитории

В данном примере я опишу, как WebJars в приложение Play Framework. Описания добавления WebJars в другие фреймворки можно найти здесь


Приступаем

Для того, чтобы добавить WebJar достаточно добавить новую зависимость в проект

К примеру

libraryDependencies ++= Seq(
  cache,
  ws,
  "org.webjars" %% "webjars-play" % "2.3.0-2",
  "org.webjars" % "requirejs" % "2.1.14-3",
  "org.webjars" % "requirejs-domready" % "2.0.1-2",
  "org.webjars" % "jquery" % "2.1.1",
  "org.webjars" % "bootstrap" % "3.2.0-2"
)

Если вы скомпилируете проект, вы можете обнаружить новую папку web-modules в директории target/web. В ней как раз и содержаться добавленные в проект js и css файлы.

Обращаться к ним из view можно по старинке @routes.Assets.at(«lib/requirejs/require.js»), но для этого нужно знать полный путь. Другой более кошерный способ использовать готовые контроллеры WebJars. Для этого добавим в conf/routes новый маршрут специально для WebJars

GET        /lib/*file              controllers.WebJarAssets.at(file)

А для обращения к ним из view мы можем использовать еще метод от WebJars WebJarAssets.locate. Указанный метод сам ищет необходимый файл в каталоге с WebJars. В итоге получаем:

<link rel="stylesheet" href="@routes.WebJarAssets.at(WebJarAssets.locate("bootstrap.min.css"))">

Что на html странице будет выглядеть как

<link rel="stylesheet" href="/lib/bootstrap/3.2.0-2/css/bootstrap.min.css">

Удобно не правда ли? Но это еще не все

RequireJS

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

WebJars содержит в себе механизм для более удобной интеграции с RequireJS. Если вы зайдете в каталог target/web/web-modules/main/webjars/lib/jquery, то обнаружите файл webjars-requirejs.js c содержимым

/*global requirejs */

// Ensure any request for this webjar brings in jQuery.
requirejs.config({
    paths: { "jquery": webjars.path("jquery", "jquery") },
    shim: { "jquery": { "exports": "$" } }
});

В каждом WebJar есть файл с конфигом для RequireJS. Чтобы получить общий конфиг создадим новый view с именем requireJsConfig

@Html(org.webjars.RequireJS.getSetupJavaScript(routes.WebJarAssets.at("").url))

Добавим новый экшн в контроллер Application

import play.api.cache.Cached
import views._
import play.api.Play.current

  def requireJsConfig = Cached("require_js_config") {
    Action {
      Ok(html.requireJsConfig()).as("application/javascript")
    }
  }

Результат выполнения мы кэшируем, поскольку его содержимое не будет меняться только при добавлении/удалении используемых в проекте WebJars. Также результат мы помечаем как application/javascript, чтобы явно указать в содержимое является javascript'ом

Добавляем новый маршрут

GET        /files/config.js        controllers.Application.requireJsConfig

Забавы ради url прописываем как у статического js-файла

Проверяем что получилось, в браузере переходим по url localhost:9000/files/config.js

var webjars = {
    versions: {"requirejs-domready":"2.0.1","requirejs":"2.1.14-3","bootstrap":"3.2.0-2","jquery":"2.1.1"},
    path: function(webJarId, path) {
        console.error('The webjars.path() method of getting a WebJar path has been deprecated.  The RequireJS config in the ' + webJarId + ' WebJar may need to be updated.  Please file an issue: http://github.com/webjars/' + webJarId + '/issues/new');
        return ['/lib/' + webJarId + '/' + webjars.versions[webJarId] + '/' + path];
    }
};

var require = {
    callback: function() {
        // Deprecated WebJars RequireJS plugin loader
        define('webjars', function() {
            return {
                load: function(name, req, onload, config) {
                    if (name.indexOf('.js') >= 0) {
                        console.warn('Detected a legacy file name (' + name + ') as the thing to load.  Loading via file name is no longer supported so the .js will be dropped in an effort to resolve the module name instead.');
                        name = name.replace('.js', '');
                    }
                    console.error('The webjars plugin loader (e.g. webjars!' + name + ') has been deprecated.  The RequireJS config in the ' + name + ' WebJar may need to be updated.  Please file an issue: http://github.com/webjars/webjars/issues/new');
                    req([name], function() {;
                        onload();
                    });
                }
            }
        });

        // All of the WebJar configs


requirejs.config({"paths":{"requirejs-domready":["/lib/requirejs-domready/2.0.1/domReady","domReady"]}})
requirejs.config({"paths":{}})
requirejs.config({"paths":{"bootstrap":["/lib/bootstrap/3.2.0-2/js/bootstrap","js/bootstrap"],"bootstrap-css":["/lib/bootstrap/3.2.0-2/css/bootstrap","css/bootstrap"]},"shim":{"bootstrap":["jquery"]}})
requirejs.config({"paths":{"jquery":["/lib/jquery/2.1.1/jquery","jquery"]},"shim":{"jquery":{"exports":"$"}}})    }
}

Данный скрипт настраивает RequireJS необходимым нам образом и должен предшествовать самому RequireJS

Финал

Создадим небольшой coffescript файл (простите, если кого-то этим огорчил, но от чистого javascript я подустал) в каталоге app/assets/js/main.coffee

require ['jquery','requirejs-domready!'],->
  $('body').text 'Success'

И в интересующей нас view, добавим

<script type="application/javascript" src="@routes.Application.requireJsConfig()"></script>
<script data-main="@routes.Assets.at("js/main.js")" src="@routes.WebJarAssets.at(WebJarAssets.locate("require.min.js"))"></script>

Запускаем и видим, что все необходимые javascript-файлы загрузились и код выполнился

Итог

В данном примере мы познакомились с возможностями WebJars. От себя добавлю, что ничего кардинально-инновационного я не обнаружил, но все же они облегчают frontend разработку.

Автор: lucky_libora

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js