Значительный недостаток CoffeeScript состоит в том, что он добавляет ещё один слой абстракции между вами и JavaScript-кодом, и приходится каждый раз компилировать CoffeeScript-код, чтобы получить актуальный JavaScript. К счастью, CoffeeScript имеет и другие альтернативные способы компиляции, которые могут несколько ускорить процесс разработки.
Как было сказано в первой главе, мы можем компилировать CoffeeScript-файлы с помощью команды coffee
:
$ coffee --compile --output lib src
В примере выше все .coffee
файлы в директории src
будут скомпилированы, и результат компиляции будет помещён в директорию lib
. Постоянный ручной вызов этой команды быстро надоест, так что давайте посмотрим, как мы можем автоматизировать процесс компиляции.
Cake — это простая система сборки, основанная на Make и Rake. Она упакована в npm-пакет coffee-script, а также доступна в виде исполняемого файла под названием cake
.
Вы можете определять задачи для Cake с помощью CoffeeScript, описывая их в файле под названием Cakefile. Cake прочтёт этот файл, и вы сможете выполнять задачи с помощью команды cake [task] [option]
, выполненной в директории с Cakefile. Чтобы вывести список всех задач просто напечатайте cake
.
Задачи описываются на CoffeeScript. Чтобы определить задачу используйте функцию task()
с аргументами в виде названия задачи, описания (опционально) и коллбэк-функции. Например, создайте Cakefile и две директории, lib и src. Добавьте в Cakefile следующий код:
fs = require 'fs'
{print} = require 'sys'
{spawn} = require 'child_process'
build = (callback) ->
coffee = spawn 'coffee', ['-c', '-o', 'lib', 'src']
coffee.stderr.on 'data', (data) ->
process.stderr.write data.toString()
coffee.stdout.on 'data', (data) ->
print data.toString()
coffee.on 'exit', (code) ->
callback?() if code is 0
task 'build', 'Build lib/ from src/', ->
build()
В этом примере мы определяем задачу «build», которая может быть вызвана командой cake build
. Эта команда выполнит те же действия, что и предыдущий пример — скомпилирует CoffeeScript-файлы из директории src и поместит JavaScript-файлы в директорию lib. Теперь вы можете ссылаться на JavaScript-файлы прямо из разметки, как это делается обычно:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<script src="lib/app.js"></script>
</head>
<body>
</body>
</html>
Однако нам всё ещё приходится вручную запускать cake build
, когда наш CoffeeScript-код меняется. К счастью, команда coffee
имеет аргумент --watch
, который указывает компилятору следить за изменениями исходного кода и компилировать его сразу после внесения изменений. Давайте доработаем нашу задачу для Cake в соответствии с этим:
task 'watch', 'Watch src/ for changes', ->
coffee = spawn 'coffee', ['-w', '-c', '-o', 'lib', 'src']
coffee.stderr.on 'data', (data) ->
process.stderr.write data.toString()
coffee.stdout.on 'data', (data) ->
print data.toString()
Если одна задача зависит от другой, то вы можете выполнить другую задачу с помощью invoke(name)
. Давайте добавим полезную задачу, которая открывает файл index.html
и начинает следить за изменениями:
task 'open', 'Open index.html', ->
# First open, then watch
spawn 'open', 'index.html'
invoke 'watch'
Также вы можете определять аргументы для задачи с помощью функции option()
, которая принимает короткое название (алиас) аргумента, длинное название и описание.
option '-o', '--output [DIR]', 'output dir'
task 'build', 'Build lib/ from src/', ->
# Now we have access to a `options` object
coffee = spawn 'coffee', ['-c', '-o', options.output or 'lib', 'src']
coffee.stderr.on 'data', (data) ->
process.stderr.write data.toString()
coffee.stdout.on 'data', (data) ->
print data.toString()
Как вы можете видеть, теперь в контексте задачи мы имеем доступ к объекту options
, содержащему данные, указанные пользователем. Если вызвать команду cake
, то будет выведен список всех доступных задач и аргументов.
Cake является отличным инструментом для автоматизации простых задач, таких как компиляция CoffeeScript. Также было бы неплохо взглянуть на исходники Cake, представляющие собой хороший пример выразительности CoffeeScript. Следует отметить, что исходники очень хорошо документированы.
Использование Cake для компиляции оправданно для статичных сайтов, однако на динамических сайтах вам может потребоваться интегрировать компиляцию в цикл «запрос/ответ». Различные готовые решения уже существуют для популярных бэкэнд языков и фреймворков, таких как Rails или Django.
В Rails 3.1 поддержка CoffeeScript добавляется с помощью Sprockets и the asset pipeline. Поместите свои CoffeeScript-файлы в директорию app/assets/javascripts
, и Rails автоматически скомпилирует их при запросе. JavaScript и CoffeeScript файлы склеиваются и упаковываются с помощью специальных комментариев. Это значит, что вы можете поместить весь ваш JavaScript-код в один файл. Когда дело дойдёт до production'а, Rails запишет скомпилированные файлы на диск и обеспечит кэширование.
Другие варианты использования CoffeeScript в Ruby — это Rack-сервера Pow или Nack. Они рекомендуются к использованию в том случае, когда вам не требуется Rails и его избыточная функциональность.
Django тоже поддерживает CoffeeScript с помощью специальных тегов шаблонов. CoffeeScript-код работает отлично как прямо на странице, так и в отдельных файлах.
Как Ruby, так и Python при компиляции передают код в Node и CoffeeScript-библиотеку, так что вам потребуется установить оба этих инструмента для успешной компиляции. Если же вы используете Node в качестве бэкэнда вашего сайта, то интеграция CoffeeScript будет намного проще, и вы сможете использовать CoffeeScript также в качестве языка как для фронтэнда, так и для бэкэнда. Мы поговорим об этом подробнее в следующей главе.