предисловие
Будучи многоцелевой средой разработки, Taro поддерживает компиляцию до конца H5 с момента запуска проекта. Поскольку мультитерминальные возможности Taro продолжают развиваться, наши требования к приложениям Taro H5-terminal также продолжают расти. Нас больше не устраивает «бег», мы надеемся, что Таро умеет быстро бегать.
Мы часто получаем отзывы от пользователей: почему пустые проекты, созданные с помощью скаффолдинга Taro, имеют размер кода 400 КБ+ после упаковки; некоторые пользователи также упоминали в Issue, что некоторые API Taro занимают много места, но на самом деле их функции не выполняются. идеальный и т.п. Как проект с открытым исходным кодом, мы придаем большое значение мнению разработчиков сообщества. Итак, в последней версии мы оптимизировали производительность Taro H5.
В качестве основы среды выполнения каждое приложение Taro H5 должно импортировать основные зависимости, такие как @tarojs/components и @tarojs/taro-h5. После компиляции и упаковки эти зависимости будут занимать более 400 КБ места на первом экране. Если разработчики также используют библиотеки пользовательского интерфейса, такие как Taro-UI, базовый объем будет больше, что сильно ограничивает возможности оптимизации производительности приложений Taro H5.
На самом деле мы не используем все компоненты Taro и API в приложении H5. Ненужно и неразумно упаковывать все эти зависимости в приложение. Устранение мертвого кода для дальнейшего уменьшения размера кода — одно из наших направлений оптимизации.
Эффект
Прежде чем углубляться в детали, давайте взглянем на эффекты оптимизации, так как это может заинтересовать вас тем, что будет дальше. Словом, эффект замечательный.
Мы создали пустой проект и добавили его в конфигурацию проектаwebpack-bundle-analyzer
плагин для просмотра анализа компиляции. На следующем рисунке показан результат анализа файла пакета перед оптимизацией:
А после оптимизации контраст очень заметен:
Общий размер сгенерированного кода до оптимизации составляет 455 КБ, а после оптимизации осталось всего около 96 КБ, что составляет всего около 1/5 исходного размера.
что вам нужно сделать
Это очень просто, как пользователь, вам не нужно вносить никаких изменений в код, просто обновите Taro до последней версии. Но вне поля зрения Таро большую часть работы делает молча. Далее мы начнем с принципа и подробно познакомим вас с работой Таро.
принцип
Устранение мертвого кода— это метод оптимизации кода, который удаляет код, не влияющий на результат выполнения приложения. В статье Web Fundamentals упоминается, что treeshaking — это форма удаления мёртвого кода, предложенная Rollup.
Tree shaking is a form of dead code elimination. The term was popularized by Rollup, but the concept of dead code elimination has existed for some time.
-- Reduce JavaScript Payloads with Tree Shaking, Jeremy Wagner
По статическому анализу при строительстве инструментов компилятора мы можем проанализировать код в реальных зависимостях. Treeshaking Поставьте наш код, представьте себе дерево, каждая код зависимости рассматривается как узел в дереве. Неиспользованные зависимости удаляются из результатов сборки, что является основной идеей трещины.
Итак, предполагая, что у нас есть фрагмент кода, как мы можем определить его части, которые можно удалить? Ответ, анализируя побочные эффекты:
// add.js
export default function add(a, b){ return a + b; }
// add2.js
console.log('这是一个log')
export default function add2(a, b){ return a + b; }
// index.js
import add from './add.js' // 没有副作用,可以删除
import add2 from './add2.js' // 有副作用,不能直接删除
// EOF
Термин «побочный эффект» определенно знаком студентам, разбирающимся в функциональном программировании. Изменение внешнего состояния или генерация вывода и т. д. — все это побочные эффекты, а код с побочными эффектами нельзя удалить напрямую. Как и в приведенном выше коде, модуль add2 имеет побочные эффекты.
Стоя на плечах гигантов
В дополнение к Rollup существует множество инструментов/плагинов, поддерживающих древовидную структуру, таких как babel-plugin-transform-dead-code-elimination, uglify, terser и т. д. webpack имеет встроенную поддержку treeshaking, начиная с v2, а функция treeshaking была расширена в webpack@4.
Сторона Taro H5 использует веб-пакет в качестве ядра сборки в процессе сборки. Есть несколько моментов, о которых следует помнить при использовании функции treeshaking в webpack:
- Если это модуль npm, он обязателен
package.json
существуют вsideEffects
поля и настроить именно тот исходный код, который имеет побочные эффекты. - Должен использоваться синтаксис модуля ES6. из-за таких вещей, как
babel-preset-env
Предварительно настроенный пакет babel, такой как babel, по умолчанию переписывает модульный механизм кода, а также необходимоmodules
Установить какfalse
, который передает работу по синтаксическому анализу модуля непосредственно webpack. - Нужно работать над вебпаком
production
режим.
Работа webpack по встряске дерева в основном делится на два этапа. Первый шаг — удалить неиспользуемые модули и модули без побочных эффектов на уровне модуля, что делается встроенным плагином webpack; второй шаг — удалить неиспользуемый код на уровне файла, что делается с помощью инструмента минимизации кода Terser. .
Удалить неиспользуемые модули
Как мы упоминали ранее, необходимоpackage.json
Средняя конфигурацияsideEffects
поле.
существуетдокументация веб-пакетаКак уже упоминалось, этот шаг должен позволить веб-пакету правильно идентифицировать модули кода, которые не имеют побочных эффектов.
В webpack анализ зависимостей модулей выполняется встроенными плагинами.SideEffectsFlagPluginнепрерывный.
проходить черезSideEffectsFlagPluginПосле обработки модули, которые не использовались и не имеют побочных эффектов, будут помеченыsideEffectFree
отметка.
существуетModuleConcatenationPluginв, сsideEffectFree
Отмеченные модули не будут упакованы:
Приходя сюда, webpack завершает исключение неиспользуемых модулей на уровне модуля. Затем, полагаясь на Terser, webpack может удалить неиспользуемый код без побочных эффектов на уровне файлов.
Удалить неиспользуемый код
В спецификации CommonJS мы передаемrequire
функция импорта модулей черезmodule.exports
экспортировать. Это означает, что мы можем импортировать и экспортировать модули в любом месте нашего кода любым способом: в функции обратного вызова, которая должна ждать ввода пользователя, или при выполнении определенного условия и т. д. Таким образом, до использования модульной системы ES6 было почти невозможно выполнить анализ зависимостей во время компиляции для Javascript (не совсем невозможно. Prepack может даже выполнять статические вычисления заранее во время компиляции, реализуя интерпретатор JS).
// utils.js
module.exports.add = function (a, b) { return a + b };
module.exports.minus = function (a, b) { return a - b };
// index.js;
var utils = require('./utils.js');
utils.add(1, 2);
Как и код выше, хотя в итоге мы использовали толькоadd
функция, ноminus
Функция также появится в окончательном упакованном коде, потому что нет быстрого способа узнать, используется ли она во время компиляции.minus
функция.
В модульной системе ES6 мы используемimport
/export
синтаксис для импорта и экспорта модулей. В отличие от спецификации CommonJS, эта новая модульная система имеет некоторые ограничения:import
/export
Поведения могут быть только на верхнем уровне кода, по умолчанию использовать строгий режим и т. д. Эти ограничения делают импорт и экспорт модулей кода статическими, зависимости между модулями определяются во время разработки, и компилятору легче анализировать наш код.
// utils.js
export function add (a, b) { return a + b };
export function minus (a, b) { return a - b };
// index.js;
import { add } from './utils.js';
add(1, 2);
После переоснащения модульной системой ES6 мы ясно видим, что:minus
Функция на самом деле не используется, поэтому ее можно безопасно удалить из окончательного упакованного кода.
Конечно, конкретный процесс анализа очень сложен. Продвижение переменных, операция со значением объекта,for(var i in list)
Операторы, самовыполняющиеся функции, параметры функций (onClick(function a () {…})
) и т. д., может привести к непредвиденным ситуациям, что приведет к сбою встряхивания дерева. Если вы хотите понять конкретный процесс обработки Terser, Baidu/Google будет лучшим учителем.
что Таро сделал
Таро требует некоторых изменений в зависимостях.
Модульность компонентов ES
До модуляризации библиотеки компонентов ES, если должен быть выпущен пакет @tarojs/components, Taro выполнит командуyarn build
, используйте webpack для упаковки исходного кода, выводdist/index.js
документ. Поскольку webpack не поддерживает экспорт в виде модулей ES, это модуль UMD.
Этот файл занимает 462 КБ, и древовидная сборка невозможна из-за таких проблем, как спецификация модуля. Таким образом, даже если разработчик введет в проект Таро только два компонента, окончательный пакет будет содержать все встроенные компоненты.
На самом деле исходный код @tarojs/components использует спецификацию ESM:
Так что, пока webpack напрямую анализирует исходный код библиотеки компонентов, мы можем сразу же воспользоваться преимуществами собственного treeshaking webpack.
В то же время мы такжеsideEffects
Файл стиля отмечен в атрибуте, чтобы помочь веб-пакету идентифицировать побочные эффекты кода стиля и сохранить код стиля в коде, скомпилированном проектом.
Модульность ES для API
Кроме того, Таро нужно было выполнять команды до того, как был выпущен @tarojs/taro-h5.yarn build
, используйте Rollup для упаковки исходного кода, выводdist/index.js
документ:
Этот файл занимает 262 КБ. Точно так же, если это приложение Taro H5, сгенерированный код будет полностью импортирован в этот файл.
Мы думали о модульной трансформации @tarojs/taro-h5 и @tarojs/те же компоненты. Мы надеемся, что сам модуль @ tarojs / taro-h5 будет соответствовать спецификациям модуля ESM, его нужно будет только пометить.sideEffects
, а затем измените запись модуля.
На первый взгляд, @tarojs/taro-h5 — это что-то вроде «ESM», но этого недостаточно. Нам также нужно экспортировать эти API в виде namedExports, разработчики используютimport { XXX } from '@tarojs/taro-h5'
Просто импортируйте API.
Итак, возникает вопрос. В проекте Taro мы использовали defaultImport и не будем использовать API.namedExports
форма:
import Taro from '@tarojs/taro-h5'
Taro.navigateTo()
Taro.getSystemInfo()
// Taro.xxx ...
Пока API передаетсяTaro
Переменная Получить атрибут Get,Taro
Переменные должны иметь все API, и о дереве не может быть и речи.
Есть ли способ изменить defaultImport на namedImports? Ответ положительный. Мы написали плагин для Babel babel-plugin-transform-taroapi, который заменяет указанные вызовы Api на namedImports, а неуказанные переменные сохраняют форму значений атрибутов. Конкретный исходный код можно посмотреть __здесь__.
// const apis = new Set(['navigateTo', 'navigateBack', ...])
{
babel: {
preset: ['babel-preset-env'],
plugins: [
// ...,
['babel-plugin-transform-taroapi', {
packageName: '@tarojs/taro-h5',
apis
}]
]
}
}
Этот плагин принимает объект в качестве параметра конфигурации:packageName
атрибут указывает имя модуля, который необходимо заменить,apis
Принимает объект Set, который представляет собой список всех API.
Чтобы избежать ситуации ручного ведения списка Api позже, мы добавили задачу компиляции в модуль @tarojs/taro-h5 через простой плагин Rollup при выполненииyarn build
Создайте список API, когда вы командуете:
Ниже приведено сравнение кода до и после компиляции. Как видите, после компиляцииsetStorage
,getStorage
заменяются на namedImports.
// 编译前
import Taro from '@tarojs/taro-h5';
Taro.initPxTransform({});
Taro.setStorage()
Taro['getStorage']()
// 编译后
import Taro, { setStorage as _setStorage, getStorage as _getStorage } from '@tarojs/taro-h5';
Taro.initPxTransform({});
_setStorage();
_getStorage();
На данный момент, хотя процесс сложный, наша модульная трансформация @tarojs/taro-h5 наконец-то завершена.
наконец
До настоящего времени Taro имеет высокое завершение конца H5, но это не идеально. В будущем при устранении существующих проблем мы будем продолжать приносить больше новых функций TARO H5, например, довольно высокой в сообществе.switchTab
, монитор прокрутки страницыonPageScroll
,Потяните вниз, чтобы обновитьonPullDownRefresh
Дождитесь поддержки Api, более унифицированной анимации переключения страниц, более стабильного многостраничного режима и т.д.
Развитие Таро неотделимо от поддержки сообщества. Большое спасибо разработчикам, которые активно отзываются в группах github и WeChat. Если у вас есть какие-либо мысли или предложения для Таро, Таро приветствует вас, чтобы пожаловаться или посетить:
https://github.com/NervJS/taro