Анимированный баннер, посвященный выходу Dart 3.13, с логотипом Dart.
Анонс Dart 3.13

Мы рады объявить о выходе Dart 3.13!

В этом релизе мы отмечаем тему, которую любят все разработчики: чистый и легковесный код. Dart 3.13 фокусируется на этом аспекте на каждом уровне работы разработчика. Что касается языка, то первичные конструкторы устраняют повторяющийся шаблонный код, позволяя определять классы в одной строке кода. В инструментах разработчика внесены улучшения, позволяющие dart formatподдерживать аккуратность кода в соответствии с правилами стиля Effective Dart , а также dart pubвведены новые команды рабочей области для поддержания порядка в монорепозиториях. На pub.dev расширенные функции документации, такие как <callout-box>теги и {@example}директивы, делают документацию пакетов более понятной. Внутри системы, оптимизация кода нативных библиотек (tree-shaking @RecordUse) и отложенная загрузка WebAssembly помогают сделать скомпилированные бинарные файлы приложений максимально компактными.

Так что обновите Dart или запустите программу flutter upgrade и следите за обновлениями, пока мы будем изучать новинки Dart 3.13!

Обновления языка

Основные конструкторы

После экспериментальной предварительной версии в Dart 3.12, основные конструкторы теперь официально стабильны в Dart 3.13!

Первичные конструкторы представляют собой значительный шаг вперед в плане лаконичности классов. Мы слышали от разработчиков, что повторение имен полей и типов в телах классов и списках параметров может показаться утомительным. Первичные конструкторы устраняют это повторение, позволяя, например, определить класс Pointс двумя полями и конструктором в одной строке кода:

class Point(final int x, final int y);

Эта функция также включает в себя лаконичный синтаксис для конструкторов, использующих ключевые слова  new or factory, позволяющий пустым телам объявлений заканчиваться символом . ;.

Чтобы помочь вам внедрить основные конструкторы и поддерживать единообразный стиль во всем коде, мы ввели несколько новых проверок кода с автоматическим исправлением ошибок:

  • empty_container_bodiesСпособствует краткости, используя ;вместо {}в объявлениях, подобных классам.

  • initialize_in_field_declaration: Способствует большей ясности, перенося инициализацию поля из основного конструктора в объявление поля.

  • unnecessary_const_in_enum_constructorСпособствует краткости, удаляя этот параметр constиз конструкторов перечислений.

  • unnecessary_primary_constructor_bodyСпособствует краткости, удаляя ненужные тела основных конструкторов.

  • unnecessary_type_name_in_constructorСпособствует краткости, заменяя имя типа newв объявлениях конструкторов.

  • use_declaring_parametersСпособствует краткости, используя объявление параметров там, где это возможно.

Помимо этих проверок кода, мы также добавили новые функции рефакторинга IDE для автоматизации миграции кода:

  • Преобразовать в основной конструктор : Преобразует подходящий конструктор внутри объекта в основной конструктор.

  • Преобразовать в конструктор внутри тела кода : Преобразует основной конструктор в конструктор внутри тела кода.

  • Преобразовать в объявляемый параметр : Преобразует необъявляемый параметр в основном конструкторе в объявляемый параметр.

  • Переместить инициализацию в объявление поля : Перемещает инициализацию поля из списка инициализаторов конструктора в объявление поля.

Подробнее: Документация по основным конструкторам

Обновления веб-платформы

Предварительный просмотр отложенной загрузки WebAssembly

Мы усердно работали над расширением возможностей WebAssembly для Dart в веб-разработке. В Dart 3.13 мы рады представить предварительную версию поддержки отложенной загрузки при компиляции с использованием WebAssembly dart2wasm. Вы можете включить её уже сегодня, используя экспериментальный флаг компилятора:

dart compile wasm -O2 --enable-deferred-loading

Примечание

Для отложенной загрузки ваш встраиваемый модуль приложения должен предоставить функцию обратного вызова, отвечающую за загрузку байтов модуля Wasm. Подробности см. CompiledApp.instantiateв документации в сгенерированном файле вашего приложения <app>.mjs.

В крупных приложениях, использующих отложенную загрузку, dart2wasmобеспечивается значительное сокращение времени первоначальной загрузки страницы (IPL) по сравнению с dart2js, что выгодно как для веб-приложений Flutter, так и для веб-приложений на основе DOM.

Напоминаем, что устаревшие веб-библиотеки, такие как <b> dart:html и <b>package:js не поддерживаются в dart2wasmDart 3.7 и были объявлены устаревшими в Dart 3.7 (и перестанут работать dart2jsв будущем). Многие пакеты уже перенесены, поэтому обновление зависимостей пакетов часто автоматически решает проблемы. Однако, если ваш код напрямую использует эти устаревшие библиотеки, ознакомьтесь с нашими руководствами по миграции, чтобы уже сегодня перейти на поддерживаемые библиотеки <b> dart:js_interop и  package:web <b>.

Подробнее: документация WebAssembly

Обновления основного движка и среды выполнения

Основные инженерные решения

Наряду с развитием нашей веб-платформы, наша команда постоянно совершенствует основу языка Dart. Вот несколько основных моментов из того, над чем мы работали за кулисами в течение последних трех месяцев:

  • Исправлена ​​ошибка корректности преобразования типов : Исправлена ​​редкая ошибка корректности преобразования типов, связанная с вложенными функциями.

  • Общая рефакторизация фронтенда : Внутренние общие компоненты фронтенда были переработаны для обеспечения более эффективного взаимодействия кода с анализатором Dart.

  • Унификация модульной системы DDC : Продолжение удаления устаревших модульных систем из компилятора разработки Dart (DDC) во всех инструментах для создания единой, унифицированной модульной системы.

  • Безопасность памяти во время выполнения : Повышена безопасность памяти во время выполнения за счет введения «корзины памяти» вокруг кучи Dart.

  • Динамическая модульная компоновка : начаты эксперименты с динамическими модулями для обеспечения динамической компоновки кода, что открывает путь к улучшению рабочих процессов разработки, таких как быстрая передача прототипов внутри команды.

Обновления инструментов

Форматировщик Dart

Поддержание чистоты и читаемости кодовой базы является для нас первостепенной задачей dart format. В Dart 3.13 мы внесли ряд изменений в стиль, чтобы сделать отформатированный код еще чище. Хотя большинство изменений незначительны, несколько ключевых обновлений заметно упростят чтение вашего повседневного кода:

  • Исправлена ​​ошибка форматирования при вызове метода : мы исправили ошибку, из-за которой оптимизация иногда ошибочно срабатывала и форматировала код. Чаще всего это происходило при вызовах методов, содержащих большие литералы коллекций:

    // Before:
    await MethodChannelContainer()
        .onMethodChannelInvoke('reportCrash', <String, Object?>{
          'time': nowTime,
          'errorValue': errorName,
          'reason': reason,
          'stacktrace': stacktrace,
        });
    
    // After:
    await MethodChannelContainer().onMethodChannelInvoke(
      'reportCrash',
      <String, Object?>{
        'time': nowTime,
        'errorValue': errorName,
        'reason': reason,
        'stacktrace': stacktrace,
      },
    );
    
  • Эвристика разделения цепочки методов : Мы изменили эвристику, определяющую, следует ли форматировщику разделять цепочку вызовов методов в самом конце .или внутри одного из списков аргументов. Если целью цепочки методов является литерал коллекции или вызов функции с одним элементом или аргументом, теперь он предпочитает разделять цепочку вызовов, а не цель:

    // Before, split the target:
    function(
      argument,
    ).method().another();
    
    // After, split the chain:
    function(argument)
        .method()
        .another();
    
  • Разделение разделов импорта : Теперь форматтер вставляет пустую строку между различными «разделами» внутри серии импортов. Форматтер не сортирует импорты, но теперь разделяет их в соответствии с правилами, описанными в Effective Dart :

    // Before:
    import 'dart:io';
    import 'dart:math';
    import 'package:args/args.dart';
    import 'package:test/test.dart';
    import 'my_library.dart';
    
    // After:
    import 'dart:io';
    import 'dart:math';
    
    import 'package:args/args.dart';
    import 'package:test/test.dart';
    
    import 'my_library.dart';
    

dart_style В списке изменений описаны и другие корректировки стиля , но эти — наиболее заметные. Мы знаем, что изменения форматирования могут вызывать досадные проблемы, поэтому стараемся быть осторожными при изменении стиля. В то же время, улучшение стиля может сделать код более читабельным, что является ценным преимуществом в эпоху, когда мы все проверяем больше кода, чем когда-либо, благодаря генеративному ИИ. Помимо исправления ошибки оптимизации, эти изменения стиля версионируются в зависимости от языка . Вы увидите их только после обновления вашего кода до Dart 3.13.

Подробнее: Документация по форматтеру Dart

Обновления паба

Обновления документации пакета

Четкая и хорошо структурированная документация пакетов необходима для здоровой экосистемы. Чтобы помочь авторам пакетов создавать более чистую и поддерживаемую документацию, pub.dev теперь поддерживает <callout-box>теги и {@example}директивы от начала до конца. Вы можете извлекать фрагменты кода непосредственно из примеров файлов и вносить их в документацию API, скрывая при этом ненужный шаблонный код.

Например, определите область фрагмента текста в example/foo.dart:

// example/foo.dart
void main() {
  // #region abc
  // Included in documentation
  foo();
  assert(false); // #hide
  // #endregion
}

Затем укажите ссылку на этот регион в комментариях к документации Dart:

/// This is a great function.
///
/// Example usage:
/// {@example /example/foo.dart#abc}
void foo() {}

pub.dev отображает указанный фрагмент кода непосредственно в сгенерированной документации.

В документации cupertino_ui на pub.dev отображается элемент 'callout-box' с синей рамкой, а также встроенный блок кода '{@example}'.
В документации cupertino_ui на pub.dev отображается элемент <callout-box>с синей рамкой рядом с встроенным {@example}блоком кода.

В фоновом режиме pub.dev также внедрил двухуровневый хэш-индекс для поиска файлов dartdoc. Это обновление значительно ускоряет рендеринг документации для больших пакетов, содержащих более 100 000 сгенерированных файлов.

Диаграмма, демонстрирующая снижение задержки запросов к документации после развертывания нового индекса BLOB-объектов.
В документации указано, что задержка снижается после развертывания нового индекса BLOB-объектов.

Подробнее: CupertinoActivityIndicatorдокументация класса

Обновления совместимости с нативными системами

Библиотеки с @RecordUse функцией удаления неиспользуемого программного обеспечения (tree-shaking) package: record_use

Наша приверженность эффективности распространяется на все аспекты компиляции ваших приложений. При создании приложений Dart и Flutter, взаимодействующих с нативным кодом C, C++ или Rust с использованием dart:ffiCode Assets, компилятор Dart всегда удалял неиспользуемые функции-обертки Dart, удаляя лишние фрагменты кода. Однако базовые нативные бинарные файлы традиционно оставались полностью включенными в пакет.

Благодаря @RecordUse(в package:meta) и package:record_use, Dart и Flutter теперь могут выполнять оптимизацию кода нативных библиотек вместе с вашим кодом Dart. Это гарантирует, что итоговый пакет вашего приложения будет содержать только тот нативный код, который ваше приложение фактически вызывает.

Как это работает

  1. Аннотируйте собственные привязки FFI с помощью@RecordUse() : Отметьте ваши собственные привязки FFI (например, сгенерированные ffigen) аннотацией @RecordUse():

    import 'dart:ffi';
    import 'package:meta/meta.dart';
    
    @RecordUse()
    @Native<Int32 Function(Int32, Int32)>()
    external int sqlite3_open(
      Pointer<Utf8> filename,
      Pointer<Pointer<sqlite3>> ppDb,
    );
    
    @RecordUse()
    @Native<Int32 Function(Pointer<sqlite3>)>()
    external int sqlite3_close(Pointer<sqlite3> db);
    
  2. Отслеживание доступных вызовов во время компиляции : Во время компиляции всей программы и очистки кода (tree-shaking) в сборках Dart AOT компилятор отслеживает, какие привязки, аннотированные с помощью  @RecordUse() tree-shaking, доступны в исполняемом коде. Вызовы, находящиеся в неиспользуемом коде Dart, который очищается в процессе очистки кода, автоматически исключаются.

  3. Удалите неиспользуемые нативные символы в вашем хуке компоновки : в хуке компоновки вашего пакета ( hook/link.dart) получите input.recordedUses. Используя package:record_use, запросите записанные вызовы и дайте указание вашему нативному инструментарию (например package:native_toolchain_c, или скрипту сборки Rust) сохранять только те символы, которые фактически вызывает Dart:

    import 'package:hooks/hooks.dart';
    import 'package:native_toolchain_c/native_toolchain_c.dart';
    import 'package:record_use/record_use.dart';
    import 'package:my_package/src/c_library.dart';
    import 'package:my_package/src/record_use_mapping.dart';
    
    void main(List<String> arguments) async {
      await link(arguments, (input, output) async {
        // Extract symbols for functions called in reachable Dart code:
        final symbolsToKeep = input.recordedUses?.calls.keys
            .cast<Method>()
            .map((method) => recordUseMapping[method.name]!);
    
        await cLibrary.link(
          input: input,
          output: output,
          linkerOptions: LinkerOptions.treeshake(
            symbolsToKeep: symbolsToKeep,
          ),
        );
      });
    }
    

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

  • Значительно меньший размер исполняемых файлов : Мощные нативные зависимости, такие как SQLite, криптография, декодеры изображений или аудиодвижки, предоставляют доступ к сотням функций. Во время сборки релизной версии компоновщик удаляет неиспользуемый нативный код, неработающие объектные файлы и неиспользуемые экспортируемые функции.

  • Полное исключение неиспользуемых нативных библиотек : если ваше приложение никогда не вызывает нативные привязки пакета, обработчик компоновки полностью удаляет нативный исполняемый файл из конечного пакета приложения.

  • Бесшовная интеграция инструментов : такие инструменты, как ffigenавтоматическое аннотирование сгенерированных привязок, package:native_toolchain_cнапрямую интегрируются с хуками ссылок для сквозного анализа исходного кода нативных приложений.

Подробнее: Документация по встроенным перехватчикам ссылок и документация по использованию записей.

Заключение

Dart 3.13 воплощает в жизнь наше видение более простого и удобного интерфейса для разработчиков. Сочетая выразительные языковые возможности, такие как первичные конструкторы, с более интеллектуальными инструментами, более подробной документацией pub.dev и компактным оптимизацией кода нативных приложений, этот релиз помогает вам сосредоточиться на самом важном: создании отличных приложений.

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

Мы с нетерпением ждём, что вы создадите с Dart 3.13! Найдите минутку, чтобы обновить Dart или запустить его flutter upgrade сегодня, и присоединяйтесь к обсуждению в нашем трекере задач на GitHub . Спасибо вам за то, что вы такое увлечённое сообщество и строите будущее Dart вместе с нами!

Подробнее: Список изменений в Dart SDK