Особенности языка. Инструменты производительности. Более глубокая и широкая интеграция с платформой.
Сегодня на Google I/O мы анонсировали новый Dart SDK версии 2.17. Этот выпуск основан на наших основных принципах повышения производительности и переносимости платформы. Он предлагает новые языковые функции: перечисления с поддержкой элементов, улучшенную пересылку параметров в суперклассы и большую гибкость для именованных параметров. Мы улучшили инструменты с новой основной версией package:lints — наша поддержка инструментов для проверки кода Dart на соответствие нашим передовым методам — и обширное обновление документации по API основной библиотеки с богатыми примерами кода. Для улучшения интеграции с платформой у нас есть новые шаблоны для использования dart:ffi (собственное взаимодействие C) в плагинах Flutter, экспериментальная поддержка процессоров RISC-V и поддержка подписи исполняемых файлов macOS и Windows.

Новые языковые функции для повышения производительности
Мы постоянно совершенствуем язык Dart, чтобы сделать вас более продуктивными — как за счет добавления новых функций, так и за счет улучшения существующих функций. В Dart 2.17 добавлена новая важная поддержка членов в перечислениях, улучшен способ использования именованных аргументов в конструкторах и сделан код для пересылки параметров в суперклассы гораздо менее подробным и повторяющимся.
Расширенные перечисления с членами
Перечисления отлично подходят для представления дискретного набора состояний. Например, мы можем смоделировать воду как enum Water { frozen, lukewarm, boiling }. Но что, если мы хотим добавить некоторые методы к enum — например, для преобразования каждого из состояний в температуру и поддержку преобразования enum в String? Возможно, мы могли бы использовать методы расширения, чтобы добавить метод waterToTemp(), но мы должны быть осторожны, чтобы синхронизировать его с методом enum. Для преобразования String мы бы предпочли переопределить toString(), но это не поддерживается.
В Dart 2.17 теперь есть общая поддержка членов в перечислениях. Это означает, что мы можем добавлять поля, содержащие состояние, конструкторы, которые устанавливают это состояние, методы с функциональностью и даже переопределять существующие члены. Многие из вас просили эту возможность; это была наша третья по количеству голосов проблема в языковом трекере.
Для нашего примера с водой мы можем добавить поле int, содержащее температуру, и конструктор по умолчанию, который принимает int:
enum Water {
…
final int tempInFahrenheit;
const Water(this.tempInFahrenheit);
}
Чтобы убедиться, что конструктор вызывается при создании enum, нам нужно вызывать его для каждого значения enum:
enum Water {
frozen(32),
lukewarm(100),
boiling(212);
…
}
Для поддержки преобразования в String мы просто переопределяем toString, который enums наследует от Object:
@override String toString() => "The $name water is $tempInFahrenheit F.";
И с этим у вас есть полный enum, который можно легко создать, и вы можете вызывать методы для:
void main() {
print(Water.frozen); // Prints “The frozen water is 32 F.”
}
Полный пример этих двух подходов показан ниже; мы считаем, что новую версию Dart 2.17 намного легче читать и поддерживать.
Супер инициализаторы
При наличии иерархии наследования классов распространенным шаблоном является передача некоторых параметров конструктора конструктору суперкласса. Для этого подклассу необходимо 1) перечислить каждый параметр в своем собственном конструкторе и 2) вызвать суперконструктор с этими параметрами. Это приводит к шаблонному коду: много повторений, что делает код трудным для чтения и более утомительным в обслуживании.
В этом помогли несколько членов сообщества Dart. Пользователь GitHub @roy-sianez подал по этому поводу языковую проблему около полугода назад; предлагая что-то похожее на то, что пользователь GitHub @apps-transround предложил ранее: возможно, мы могли бы решить эту проблему, введя новую конструкцию, чтобы выразить, что параметр был указан в суперклассе. Мы подумали, что это отличная идея, поэтому она была добавлена в Dart 2.17. Как видно из следующего примера, это особенно актуально для кода виджета Flutter. На самом деле, когда мы применили новую функцию к платформе Flutter, мы увидели общее сокращение почти двух тысяч строк кода!
Именованные аргументы везде
Наконец, мы усовершенствовали работу именованных аргументов при вызове метода. Раньше они должны были стоять последними в списке аргументов метода. Это раздражало в тех случаях, когда вы предпочитали размещать позиционный аргумент последним, чтобы сделать код более читабельным. Например, см. следующий вызов конструктора List<T>.generate — раньше расширяемый аргумент должен был размещаться последним, что позволяло легко пропустить под большим позиционным аргументом, содержащим сам генератор. Теперь вы можете упорядочить их по своему усмотрению, что позволяет размещать маленькие именованные аргументы первыми, а генератор последним.
Дополнительные примеры этих трех функций в действиях см. в наших обновленных примерах для перечислений, суперинициализаторов и именованных параметров.
Инструменты повышения производительности
Продолжая тему производительности, у нас есть несколько улучшений в наших основных инструментах.
В Dart 2.14 мы представили package:lints, который работает с анализатором Dart, помогая вам писать код Dart, который предотвращает ошибки и использует канонический стиль, что позволяет проводить более эффективную проверку кода. С тех пор в анализаторе стало доступно несколько новых линтов, которые мы тщательно отсортировали и из них выбрали десять новых линтов для всего кода Dart и два новых линта специально для кода Flutter. К ним относятся анализы для обеспечения включения ваших импортов в ваш файл pubspec, предотвращения неправильного использования нулевых проверок для параметров типа и обеспечения согласованного стиля для дочерних свойств. Вы можете перейти на новые линты с помощью простой команды:
- Для пакетов Dart:
dart pub upgrade —-major-versions lints - Для пакетов Flutter:
flutter pub upgrade —-major-versions flutter_lints
SecureSockets обычно используются для включения сокетов TCP, защищенных с помощью TLS и SSL. До Dart 2.17 отладка этих сокетов во время разработки была сложной задачей, поскольку не было возможности проверить защищенный трафик данных. Теперь мы добавили поддержку указания файла keyLog. Если указано, строка текста в формате NSS Key Log Format добавляется к файлу при обмене новыми ключами TLS с сервером. Это позволяет инструментам анализа сетевого трафика (таким как Wireshark) расшифровывать контент, отправляемый через сокет. Дополнительные сведения см. в документации по API для SecureSocket.connect().
Документация по API, созданная с помощью инструмента dart doc, является важным активом для большинства разработчиков Dart, изучающих новые API. В то время как наши API базовой библиотеки уже давно имеют подробные текстовые описания, многие разработчики говорили нам, что предпочитают изучать API, читая примеры кода, использующие API. В Dart 2.17 мы полностью переработали все основные основные библиотеки, добавив примеры кода на 200 самых просматриваемых страниц, так что теперь они содержат полный пример кода. В качестве примера сравните документацию по dart:convert в Dart 2.16 с обновленной страницей для Dart 2.17; надеюсь, это значительно упростит использование документации.
Повышение производительности происходит не только тогда, когда мы добавляем новые функции на нашу платформу, но и когда мы очищаем наш стек и удаляем функции, которые больше не используются. Это помогает сохранить небольшую площадь поверхности, что особенно важно для новых разработчиков. Для этого мы удалили 231 строку устаревшего кода из библиотеки dart:io — если вы все еще используете эти устаревшие API, вы можете перейти на их замену с помощью dart fix. Мы также продолжили работу по удалению устаревших инструментов Dart CLI, на этот раз удалив инструмент dartdoc (вместо него используйте dart doc) и инструмент pub (используйте dart pub или flutter pub).
Расширение интеграции и поддержки нашей платформы
Второй основной темой является интеграция и поддержка платформы. Dart — настоящий мультиплатформенный язык. Хотя мы уже поддерживаем широкий спектр платформ, мы постоянно развиваемся, чтобы обеспечить глубокую интеграцию с каждой поддерживаемой платформой, а также поддерживать новые платформы.
Dart FFI — наш основной механизм взаимодействия с собственным кодом C/Native — это популярный способ интеграции кода Dart с существующим собственным кодом платформы. Во Flutter это может быть отличным способом создания плагинов, использующих собственные API-интерфейсы хост-платформы (например, API-интерфейсы Windows win32). В Dart 2.17 и Flutter 3 мы добавили шаблоны в инструмент flutter, так что теперь вы можете легко создавать подключаемые модули FFI, которые имеют Dart API, поддерживаемый вызовами dart:ffi в собственном коде. Подробности смотрите на обновленной странице Разработка пакетов и плагинов на flutter.dev.
Чтобы разрешить использование FFI на платформах, которые имеют типы, специфичные для их ABI (бинарный интерфейс приложения), FFI теперь поддерживает типы, специфичные для ABI. Например, теперь вы можете использовать Long (long в C) для правильного представления длинного целого числа с размером, зависящим от ABI, который может быть 32-битным или 64-битным в зависимости от архитектуры ЦП. Полный список поддерживаемых типов см. в списке Реализаторы на странице AbiSpecificInteger API.
При глубокой интеграции с нативными платформами с помощью Dart FFI иногда необходимо согласовать очистку памяти или других ресурсов (портов, файлов и т. д.), выделенных Dart и нативным кодом. Исторически это было очень сложно, поскольку Dart — это язык со сборщиком мусора, который автоматически выполняет очистку. Dart 2.17 решает эту проблему, вводя концепцию Finalizer, которая включает в себя интерфейс маркера Finalizable для «пометки» объектов, которые не следует завершать или отбрасывать слишком рано, и класс NativeFinalizer, который можно присоединить к объекту Dart для обеспечения вызова. -back выполняется, когда объект собирается быть собранным мусором. Вместе они позволяют запускать код очистки как в собственном коде, так и в коде Dart. Для получения дополнительной информации см. описание и пример в документации API для NativeFinalizer или документацию для WeakReferences и Finalizer для аналогичной поддержки в обычном коде Dart.
Наша поддержка компиляции Dart в нативный код является основным фактором, позволяющим приложениям Flutter иметь отличную производительность при запуске и быструю визуализацию. Второй вариант использования — возможность компилировать Dart в исполняемые файлы с помощью dart compile. Эти исполняемые файлы могут работать автономно на любом компьютере без необходимости установки Dart SDK. Еще одна новая возможность в Dart 2.17 — поддержка подписания исполняемых файлов, что позволяет развертывать на Windows и macOS, где подпись часто требуется.
Мы также продолжаем расширять набор поддерживаемых платформ, оставаясь в авангарде новых появляющихся платформ. RISC-V — это новый инновационный набор команд для процессоров. RISC-V International, глобальная некоммерческая организация, владеет спецификацией RISC-V, что делает набор инструкций бесплатным и открытым. Это все еще новая платформа, но мы в восторге от ее потенциала, поэтому наш выпуск 2.17.0–266.1.beta Linux (или более поздний из нашего бета-канала) включает экспериментальную поддержку для нее. Мы будем рады услышать ваши отзывы, поэтому, пожалуйста, отправьте вопрос или напишите о своем опыте!
Начните работу с Dart 2.17!
Мы надеемся, что сегодняшний выпуск Dart 2.17 порадует вас, повысит вашу производительность и обеспечит еще большую интеграцию с платформой для ваших приложений. Чтобы начать работу, вы можете напрямую загрузить версию Dart 2.17 или внедрить ее как часть сегодняшнего выпуска SDK Flutter 3.
Мы также приглашаем вас ознакомиться с новым контентом, который мы сделали доступным для Google I/O!