MT — это фреймворк управления модулями JavaScript, созданный и поддерживающийся командой фронтенд-разработчиков мобильной версии сайта Tencent. Он ориентирован на мобильные устройства и имеет уникальную возможность дифференцированного обновления.
Наш официальный сайт расположен по адресам http://mt.tencent.com и https://mtjs.github.io.
Наши проекты доступны на GitHub по адресу https://github.com/mtjs/mt. Если вы считаете, что MT является надёжным проектом, пожалуйста, отметьте его звёздочками. Ваша поддержка очень важна для нас!
В процессе быстрой итерации версий мы иногда меняем всего несколько строк кода внутри js-файла, но всё равно заставляем пользователя скачивать полный js-файл. Это особенно накладно для мобильных устройств, где дорого каждое байт. Уникальный алгоритм дифференциального обновления MT позволяет скачать только те строки кода, которые были изменены.
Например, если нам нужно изменить следующую часть кода, добавив два слова "ok" после вызова mt mthelloworld:
define('mthelloworld', [], function () { console.log('mt helloworld'); });
Модификация приведёт к следующему виду:
define('mthelloworld', [], function () { console.log('mt helloworld ok'); });
Обычно требуется, чтобы пользователи скачали весь этот модуль js-кода, однако при использовании MT достаточно скачать лишь одну строку:
[[0,50],"ok",[52,90]]
Инкрементальное обновление зависит от localstorage, поэтому браузер должен поддерживать localstorage. Android и iOS поддерживают это.
Процесс инкрементального обновления показан ниже:
В localStorage хранится содержание JS предыдущей версии и номер версии. Когда номер текущей версии отличается от номера предыдущей версии, mt собирает URL инкрементного файла и получает его, а затем объединяет с содержанием JS предыдущей версии, чтобы создать новую версию. Основной идеей этого подхода является вычисление и объединение инкрементных файлов. Далее будут рассмотрены два метода инкрементного обновления, поддерживаемых mt.
В mt 1.0 вычисление инкрементного файла и объединение инкрементного файла со старым содержанием основано на алгоритме chunk. Принцип работы этого алгоритма заключается в разделении JS на части и сравнении этих частей для получения двух версий содержания, что позволяет получить инкрементный файл. Подробнее о дизайне алгоритма можно прочитать в следующем PDF:
Технология реализации инкрементного обновления JS в mt 1.0Алгоритм chunk
в mt 1.0
основан на вычислении по частям, точность инкрементного обновления зависит от размера этих частей. В реальных условиях всегда есть часть кода, которую приходится загружать повторно. Для решения этой проблемы в mt 2.0
был внедрен алгоритм инкрементного обновления на основе расчета редакционного расстояния. Подробнее о реализации этого подхода можно найти в следующем PDF:
Расчет редакционного расстояния может быть точным до уровня символов, но требует использования матрицы для хранения символов, что само по себе занимает большое количество памяти и делает использование этого подхода в производственной среде затруднительным. Поэтому наш mixdiff объединяет эти два алгоритма, повышая производительность и позволяя осуществлять инкрементное обновление на уровне символов. Mixdiff представляет собой следующее: для сравнения коротких строк используется LCS для вычисления инкрементного файла, для длинных строк используется ChunkDiff для нахождения максимального общего подстрока двух строк, после чего эта подстрока используется для разделения новых и старых строк на префикс, общую подстроку и суффикс. Затем используются префиксы и суффиксы как параметры для рекурсивного вызова mixdiff для вычисления инкрементного файла. Схема процесса представлена ниже:
Схема программы представлена ниже:
Как модульный менеджмент-фреймворк, основанный на стандартах AMD, mt также предоставляет гибкую поддержку COMBO.
Холодный COMBO — это когда несколько различных модулей собираются вместе в один js во время компиляции и минификации. При загрузке страницы этот единственный js загружается. Например, конфигурация сборки может выглядеть следующим образом:
{ './release/{pv}/base-{fv}.js': { files: ['./js/init.js', './js/util.js'] }, './release/{pv}/page/p1-{fv}.js': { files: ['./js/page/p1.js'] }, './release/{pv}/page/p2-{fv}.js': { files: ['./js/page/p2.js'] }, './release/{pv}/page/p3-{fv}.js': { files: ['./js/page/p3.js'] } }
Как видно, наши модули init и util собраны в base.js, что достигает цели холодного COMBO.
Полугорячий COMBO относится к тому, как холодный COMBO реализуется через конфигурацию на стороне клиента. В дополнение к использованию скриптов сборки для реализации холодного COMBO, мы также поддерживаем использование конфигураций на стороне клиента для реализации полугорячего или горячего COMBO.
combo: { // Активировать ли COMBO cb: true, // Какие модули должны загружаться вместе // Каждый элемент массива представляет собой набор модулей, который будет загружен вместе conf: ['init,util', 'p1,p2,p3'] }
Настройка выше показывает, что мы активируем COMBO, установив cb в true. Конфигурация conf указывает, какие модули должны загружаться вместе, даже если они не были объединены в процессе сборки. Чтобы продемонстрировать эффект, сначала установим cb в false и conf в пустой массив, чтобы отключить COMBO:
combo: { // Активировать ли COMBO cb: false, // Какие модули должны загружаться вместе // Каждый элемент массива представляет собой набор модулей, который будет загружен вместе conf: [] }
Давайте посмотрим на сетевые запросы:
Как можно заметить, base.js, p1.js, p2.js и p3.js загружаются отдельно, что свидетельствует о том, что COMBO не используется.
Затем был установлен cb в true для COMBO, что указывает на использование COMBO. Давайте посмотрим на сетевые запросы:
Можно заметить, что base.js и p1.js были загружены отдельно, а p2.js и p3.js — вместе. Это происходит потому, что mt2.0 самостоятельно анализирует зависимости и загружает модули, общие для нескольких зависимостей, одновременно. В данном примере p1 зависит от p2 и p3, поэтому они загружаются вместе. Это называется горячей комбо!
Теперь мы хотели бы загрузить p1, p2 и p3 за один раз. Как это сделать? Очень просто, нам нужно установить combo.conf следующим образом:
combo:{ // Активировать ли combo cb: true, // Какие модули должны быть загружены как полу-горячий combo // Каждый элемент массива представляет собой набор модулей, которые будут загружены вместе conf: ['init,util', 'p1,p2,p3'] }
Давайте ещё раз посмотрим на сетевые запросы:
Отлично, теперь p1, p2 и p3 загружаются за один раз! Это называется полу-горячей комбо, которая требует конфигурации conf.
Чтобы облегчить сбор статистики и своевременную очистку локального хранилища, в MT предусмотрены два типа обратных вызовов: для статистики и обработки ошибок при работе с локальным хранилищем. Через объект storeInc
конфигурации g_config
можно настроить две функции: statFunc
для статистики и storeExFunc
для обработки ошибок при записи в локальное хранилище. Функция statFunc
вызывается каждый раз при запросе js, что позволяет собирать статистику по каждому запросу. Функция storeExFunc
вызывается при возникновении ошибок при записи содержимого скрипта в локальное хранилище, чтобы обеспечить возможность очистки локального хранилища бизнес-логикой.
storeInc:{ // Обратный вызов для статистики, собирающий данные о запросах js // jsUrl - адрес js // mode - режим запроса, full - полный запрос, inc - частичный запрос, local - чтение из локального хранилища 'statFunc': function(jsUrl, mode) { console.log('Получено ' + jsUrl + ' из ' + mode); }, // Обратный вызов для обработки ошибок при записи в локальное хранилище // Вызывается при возникновении ошибок при записи содержимого скрипта в локальное хранилище // storeKey - ключ записи в хранилище 'storeExFunc': function(storeKey) { console.log('Ошибка записи элемента хранилища ' + storeKey); }, 'store': true, 'inc': true, 'proxy': true, 'debug': false },
На этом этапе мы уже имеем базовое представление о mt. Давайте рассмотрим пример, чтобы быстро начать работу с ним, а также посмотрим, как mt выполняет частичное обновление (в данном примере вы можете найти этот пример в папке quickstart внутри директории demo).
Mt является модульной системой управления, основанной на стандарте AMD, поэтому определение модулей осуществляется следующим образом:
define('p1', ['p2', 'p3'], function(p2, p3) {
var o = {
k: 'v'
};
return o;
});
Используем define для определения модуля, где первый параметр — это id модуля, второй параметр — зависимости, третий параметр — определение метода, а возвращаемое значение — это определение данного модуля.
Как и в других системах управления модулями, mt имеет свою конфигурацию отображения модулей на файлы, конфигурацию частичного обновления, версионирование и конфигурацию обратного вызова. Ниже приведена одна из конфигураций этого примера:
```markdown
var g_config = {
jsmap:{
'init': 'base.js',
'util': 'base.js',
'p1': 'page/p1.js',
'p2': 'page/p2.js',
'p3': 'page/p3.js'
},
storeInc:{
//Статистический обратный вызов, используется для сбора данных о запросах к скриптам, jsUrl - адрес js файла,
//mode - режим запроса, full - полный запрос, inc - частичный запрос, local - чтение из локального хранилища
'statFunc':function(jsUrl,mode){
console.log('get '+jsUrl+' from '+mode);
},
//Обратный вызов при ошибке записи в локальное хранилище, вызывается при возникновении ошибки при записи содержимого скрипта в локальное хранилище, используется для очистки локального хранилища бизнесом, storekey - ключ записи
'storeExFunc':function(storeKey){
console.log('set store item '+storeKey+' exception');
},
'store': true,
'inc': true,
'proxy': true,
'debug': false
},
//Поддерживает объединение нескольких скриптов в один, conf - список скриптов для объединения
combo:{cb:true,conf:["init,util","p1,p2,p3"]},
testEnv: false,
staticPath: '/release',
serverDomain: 'http://localhost:6600',
buildType: 'project',
ver: '2014053000002' //версия
}
Внесены небольшие изменения в пунктуацию и пробелы внутри функций.
В версии 2014053000002 нашего p2 код выглядит следующим образом:
define('p2', [], function () {
console.log('p2 ok!');
document.write('p2 ok!');
});
Упаковка mt осуществляется с помощью собственного файла mtbuild.js. Основные функции включают сжатие, минификацию и объединение js-файлов, а также создание дельты между текущей и предыдущими версиями. Мы запускаем скрипт build.sh в директории demo/quickstart, который фактически выполняет команду mtbuild.js:
node ../../js/mtbuild.js test.html build.conf lcs
Третьим параметром указывается использование алгоритма расстояния Левенштейна для расчета дельты. Вы можете использовать параметр chunk для использования другого алгоритма.
Mt в настоящее время предоставляет сервер для генерации инкрементальных файлов на стороне сервера помимо использования mt build для создания таких файлов. Доступны версии на Java и NodeJS; здесь мы будем использовать версию на NodeJS. Выполните следующую команду в директории js:
node storeincServer.js lcs ../demo/quickstart
Вторым параметром указывается использование алгоритма lcs для инкрементального обновления, а третьим параметром — это корневая директория, которая установлена как `../demo/quickstart`.
Откройте Chrome (обязательна поддержка localStorage), введите адрес: http://localhost:6600/test.html, чтобы видеть запрос полной версии js.
Содержимое в локальном хранилище соответствует версии 2014053000002.
Затем измените код p2.js, добавив "lcs":
define('p2', [], function () {
console.log('p2 ok!');
document.write('p2 ok lcs!');
});
После этого выполните следующую команду:
```bash node ../../js/mtbuild.js test.html build.conf lcs ```На этом этапе будет сгенерирована версия кода 2014053000003. Откройте Chrome (обязательна поддержка localStorage) и введите адрес: http://localhost:6600/test.html. Теперь вы увидите запрос инкрементальной версии, причём до уровня символов:
Рассмотрим то же самое изменение, если бы мы использовали алгоритм chunk. Нам потребуется повторить вышеописанный процесс, заменив параметр lcs в команде build.sh на chunk, а также заменив lcs при запуске storeincServer на chunk. Здесь мы пропустим шаги и сразу рассмотрим сетевые запросы при использовании chunk:
Алгоритм LCS позволяет достичь более высокого уровня точности по сравнению с алгоритмом CHUNK.
С помощью предыдущего примера мы получили общее представление о функциях и принципах работы MT, а также основное понимание эффекта инкрементального обновления. В этом разделе рассмотрим пример мобильного одностраничного веб-приложения, созданного с использованием MT. В данном примере будут применены следующие компоненты:
Этот пример является модификацией приложения Movie Finder, входящего в набор примеров Ratchet. Здесь был упрощён исходный пример и внедрена возможность инкрементальных обновлений с помощью mt.
Для запуска сервера используется Jetty. Директория mtwebapp из папки demo перемещается в директорию webapps Jetty. Приложение mtwebapp уже содержит все необходимые Java классы (пакетированы в mt.jar).
Инкрементальная служба обновления на Java представляет собой servlet, поэтому её следует настроить в файле web.xml:
<display-name>StoreIncServlet</display-name> <servlet-name>StoreIncServlet</servlet-name> <servlet-class>com.storeinc.StoreIncServlet</servlet-class> <init-param> <param-name>jsPath</param-name> <param-value>/Users/waynelu/nginxhtmls/jetty/webapps/mtwebapp/</param-value> </init-param> <init-param> <param-name>chunkSize</param-name> <param-value>12</param-value> </init-param> <init-param> <param-name>diffAlg</param-name> <param-value>lcs</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>StoreIncServlet</servlet-name> <url-pattern>/storeinc/*</url-pattern> </servlet-mapping>
jsPath — каталог хранения JavaScript-файлов.
chunkSize — размер блока для алгоритма chunk.
diffAlg — алгоритм инкрементального обновления, может принимать значения "chunk" или "lcs".
Для запуска используйте команду в директории jetty/bin:
./jetty.sh start
Чтобы увидеть эффект инкрементального обновления, в директории mtwebapp находятся два файла: index.jsp и index1.jsp, представляющие две версии JavaScript-файлов — 2014071600018 и 2014071500017 соответственно.
В адресной строке введите: http://localhost:8080/mtwebapp/index.jsp и http://localhost:8080/mtwebapp/index1.jsp, чтобы увидеть эффект последовательных обновлений этих двух версий.Java-код расположен в директории java
<div class="page-header">
<h1>Кто использует mt</h1>
</div>
<p>
<table>
<tr>
<td>
<span>
<a href="http://infoapp.3g.qq.com/g/s?aid=nbasearch&icfa=home_touch&iarea=98&i_f=235#home">
<div class="img-tit">НБА</div>
<img pl="1" alt="нба" src="http://3glogo.gtimg.com/wap30/info/info5/img/app/NBA.jpg">
</a>
</span>
</td>
</tr>
</table>
</p>```html
</a>
</span>
</td>
<td>
<span>
<a href="http://infoapp.3g.qq.com/g/s?sid=AV-STHlv9Fb_E6Jb0P0_Pdtn&aid=movie&i_f=225">
<div class="img-tit">Люблю кино</div>
<img alt="Люблю кино" pl="1" src="http://3glogo.gtimg.com/wap30/info/info5/img/app/imovie.png">
</a>
</span>
</td>
<td>
<span>
<a href="http://gp.3g.qq.com/g/s?sid=AV-STHlv9Fb_E6Jb0P0_Pdtn&aid=ifinance&i_f=271#fund/0">
<div class="img-tit">Люблю финансы</div>
<img alt="Люблю финансы" pl="1" src="http://3gimg.qq.com/wap30/info/info5/img/app/ifinance.png">
</a>
</span>
</td>
<td>
<span>
<a href="http://infoapp.3g.qq.com/g/s?aid=medialib&iarea=98&i_f=237">
<div class="img-tit">Чтение</div>
<img pl="1" alt="Чтение" src="http://3glogo.gtimg.com/wap30/info/info5/img/app/iread.jpg">
</a>
</a>
</span>
</td>
<td>
<span>
<a href="http://infoapp.cq.com/g/s?aid=touchauto&iarea=98&i_f=238">
<div class="img-tit">Автомобили</div>
<img pl="1" alt="Автомобили" src="http://3glogo.gtimg.com/wap30/info/info5/img/app/autolib.jpg">
</a>
</span>
</td>
</tr>
<tr>
<td>
<span>
<a href="http://infoapp.3g.qq.com/g/s?aid=carshow&iarea=98&i_f=239">
<div class="img-tit">Автошоу</div>
<img pl="1" alt="Автошоу" src="http://3glogo.gtimg.com/wap30/info/info5/img/app/carshow.jpg">
</a>
</span>
</td>
<td>
<span>
<a href="http://live.3g.qq.com/g/touch2/?&iarea=98&i_f=240#home">
<div class="img-tit">Прямой эфир</div>
<img pl="1" alt="Прямой эфир" src="http://3glogo.gtimg.com/wap30/info/info5/img/app/ilive.jpg">
</a>
</span>
</td>
``` <td>
<span>
<a href="http://infoapp.3g.qq.com/g/s?aid=sportpicguess">
<div class="img-tit">Спортивные картинки угадай</div>
<img pl="1" alt="Спортивные картинки угадай" width="100px" src="http://3gimg.qq.com/wap30/info/info5/img/caitu.png">
</a>
</span>
</td>
``` <td>
<span>
<a href="http://infoapp.3g.qq.com/g/s?&aid=common_webapp&webapp=nbaky">
<div class="img-tit">Крейн НБА</div>
<img width="100px" alt="Крейн НБА" src="http://infopic.gtimg.com/info/images/2014/7/140436769816885735.png">
</a>
</span>
</td>
<td>
<span>
<a href="http://infoapp.3g.qq.com/g/s?&aid=common_webapp&webapp=jiecao">
<div class="img-tit">Новости с достоинством</div>
<img width="100px" alt="Новости с достоинством" src="http://infopic.gtimg.com/info/images/2014/3/139597306809381261.png">
</a>
</span>
</td>
</tr>
</table>
</p>
<h1>Наши проекты на GitHub: <a href="https://github.com/mtjs/mt">https://github.com/mtjs/mt</a>. Если вы считаете, что MT является надёжным проектом, пожалуйста, отметьте его звездой. Ваша поддержка — наша самая большая мотивация.</h1>
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )