http://pimpmyjs.com/ – Uglify or Beautify your JavaScript, it’s your choice… (minify js online)
На jquery.min.js выдал ошибку, но в общем работает, и причём быстро.
http://pimpmyjs.com/ – Uglify or Beautify your JavaScript, it’s your choice… (minify js online)
На jquery.min.js выдал ошибку, но в общем работает, и причём быстро.
Хороший доклад – Прокрустовы окна. Как вписаться в устройства с минимальными потерями (ссылка на видео – http://mediadl.microsoft.com/mediadl/www/r/rus/html5camp2011/4005_800_2.wmv, слайды на http://pepelsbey.net/pres/procrustes/).
Главное – это понять viewport и dpi.
Для примера:
<meta name="viewport" content="width=device-width, target-densitydpi=device-dp, user-scalable=no, initial-scale=1.0, maximum-scale=1.0i" />
Советуют удобные сервисы для тестирования мобильных сайтов («firebug для мобильных устройств») – Opera Mini Simulator и Opera Mobile Emulator for desktop.
На конференции «.Тостер» попал на доклад Jordi Romero API design and more (слайды).
Говорил про создание API, про кеширование, про масштабирование, клиентскую оптимизацию.
Понемножку, сжато.
Понравилось.
Странно только, что у них три уровня хранения данных:
Использовали мы на проекте одну лебедевскую библиотеку – include.js. Умеет она интересные вещи, вроде последовательной загрузки зависимых библиотек в нужном порядке. Нужно в яваскрипте написать js.include( ‘my/coolwiidget’ );. Работает она, если на пальцах, следующим образом: делается ajax-запрос, которым грузится текст нужного скрипта, потом ищутся в тексте все js.include, делается их подгрузка, после чего делается eval полученных текстов в обратном порядке. При этом ещё и отсекаются попытки по несколько раз грузить один и тот же яваскрипт.
Работал он очень неплохо, хотя и неудобно было в firebug-е искать скрипт по его имени.
Основная проблема встала, когда стало нужно вынести все яваскрипты на отдельный домен. Вот тут-то и проявились проблемы – ajax-ом нельзя грузить код с других доменов. Помогает заголовок Access-Control-Allow-Origin, но его понимают только современные браузеры (к примеру, firefox с версии 3.5), а на проекте критична максимально возможная совместимость (как обычно и бывает). Так что этот способ может быть актуален года через 3-5.
Посмотрели на другие способы, в итоге пришли к динамическому добавлению тега script в head, но особого смысла в этом нет, т.к. зависимости, для которых и городился изначально огород, при этом способе не отслеживаются (JSONP с колбэками не подошёл, т.к. в проекте много legacy-кода, который использует глобальные переменные, причём даже не как константы).
В итоге было принято гениальное в своей простоте решение – отказаться от всех этих изысков нафик и грузить скрипты как раньше – при загрузке страницы.
Сегодня из-за очередных проблем с sol.adbureau.net было решено реализовать ленивую загрузку баннеров.
Начальное решение было использовать iframe, но идея была не очень удачной, т. к. могут быть проблемы с подсчётом кликов, да и модные картинки, увеличивающиеся при наведении, будут вести себя странно. А как этот iframe смотрится в разных браузерах – это вообще сказка)
В итоге родилась идея: при генерации страницы размещать в нужных местах, к примеру, пустые дивы с id с нужным префиксом (<div id=»js-advert-place-{num}»></div>), а сами баннеры загружать в футер вот в такую, к примеру, разметку:
<div id="js-all-advert-block" class="all-advert-block">
<div class="js-advert-block-content" data-number="1">
{баннер}
</div>
<div class="js-advert-block-content" data-number="5">
{баннер}
</div>
<div class="js-advert-block-content" data-number="15">
{баннер}
</div>
</div>
{здесь грузится счётчик показов}
После загрузки страницы размещаем все баннеры на нужных местах:
(function( $ ) {
function moveAdvert() {
$( '#js-all-advert-block' ).find( '.js-advert-block-content' ).each( function() {
var $oldContent = $( this );
// в ие перемещаем
if ( $.browser.msie ) {
$( "#js-advert-place-" + $oldContent.data( "number" ) ).append( $oldContent );
}
// в других браузерах убираем лишнее и вставляем заново
else
{
var cleanedContent = $oldContent.html().replace( /document\.write/gi, 'function a__(){}' );
$( "#js-advert-place-" + $oldContent.data( "number" ) ).html( cleanedContent );
$oldContent.remove();
}
// этот вариант менее хороший - глюки в ие и проблемы с яндекс.директ
// var $newContent = $.browser.msie ? $( $oldContent.html() ) : $oldContent.clone( true );
//// не надо заново создавать скрипты - они заново выполнятся, а там document.write
// $newContent.find( "script, link" ).each( function() {
// $( this ).remove();
// } );
//// вставляем
// $( "#js-advert-place-" + $oldContent.data( "number" ) ).html( $newContent );
//// на старом месте нужно убить всё, кроме скриптов
//// (в скриптах могут быть необходимые для правильного подсчёта кликов переменные)
// $oldContent.find( '*' ).each( function() {
// var $el = $( this );
// if ( ! $( 'script, link', $el ).length && this.tagName.toLowerCase() != 'script' && this.tagName.toLowerCase() != 'link' ) {
// $el.remove();
// }
// } );
} );
}
$( document ).ready( function() {
moveAdvert();
} );
}( jQuery ));
В итоге отвалившаяся внешняя баннерная площадка позволяет корректно отображать сайт без лишних задержек.
За рамками осталась работа сервер-сайда, где нужно собрать данные по всем отображаемым на странице баннерам, а потом вывести их все сразу внизу.
UP:
Всё-таки проблем оказалась куча. К примеру, opera 10.10 (хотя у меня 11.52) некорректно понимает тег script при .html( cleanedContent ), поэтому приходится чистить все теги script, но тогда умирает yandex.direct. К сожалению, на проекте нет нормальной возможности отделить директ от остальной рекламы (нет возможности объяснить тем людям, как что нужно настраивать), поэтому этот способ подходит с большими оговорками.
UP2: Наткнулся на гениальное решение – переопределить document.write, потом немного доработал и вот что получилось:
(function ( $ )
{
// для собирания тега script
var previousValue = '',
// для проверки, собрали ли уже полный тег script
reTestIfScript = new RegExp( '<\/scr' + 'ipt>$', 'i' ),
// чтобы найти id дива, куда вставлять код соли
reGetSolId = new RegExp( "sol\\.adbureau\\.net[^>]+aamsz=(\\d+x\\d+)", 'i' ),
// для проверки, что это относится к яндекс-директу
reYandexDirect = new RegExp( "an\\.yandex\\.ru", 'i' );
// заменяем стандартные методы
document.writeln = document.write = function(value)
{
// если последовательно пишут в документ для получения тега script (document.write( '<SCR' ); document.write( '<IPT src="...' );)
try {
$( value );
}
catch ( e )
{
var joinedValue = previousValue + value;
// сли собрали полный тег скрипт, то вставляем его, куда надо
if ( reTestIfScript.test( joinedValue ) )
{
value = joinedValue;
previousValue = '';
}
// если начали писать тег скрипт, продолжаем до сбора всего тега
else if ( joinedValue.toLowerCase().indexOf( '<sc' ) == 0 )
{
previousValue = joinedValue;
return;
}
// непонятно что это - ничего не делаем
else {
return;
}
}
var parentNodeId,
matches;
// если в строке есть упоминание яндекса - вставляем в яндекс
if ( reYandexDirect.test( value ) ) {
parentNodeId = 'adv_media';
}
// если в строке есть упоминание соли - ищем id и вставляем в нужный div
else if ( matches = value.match( reGetSolId ) ) {
parentNodeId = 'sol' + matches[1];
}
// скрипт вставляем только в нужный элемент
if ( $( value )[0].tagName == "SCRIPT" && parentNodeId )
{
var js = document.createElement( 'SCRIPT' );
var obj = document.getElementById( parentNodeId ).appendChild( js );
// подгрузка внешнего скрипта
if ( $( value ).attr( 'src' ) !== "undefined" ) {
$( obj ).attr( {type: 'text/javascript', 'src': $( value ).attr( "src" )} );
}
// выполнение скрипта
else
{
var reReplaceWrite = new RegExp( '(document\\.write[a-zA-Z]{0,2}\\([^)]+)(\\))', 'ig' );
eval( $( value ).text().replace( reReplaceWrite, "$`$1, '" + parentNodeId + "'$2$'" ) );
}
}
// стили ставим в head
else if ( $( value )[0].tagName == "LINK" )
{
var js = document.createElement( 'LINK' );
var obj = document.getElementsByTagName( 'head' )[0].appendChild( js );
$( obj ).attr( {rel: 'stylesheet', 'href': $( value ).attr( "href" ) , type: 'text/css'} );
}
else if ( $( value )[0].tagName == "STYLE" )
{
if ( $.browser.msie ) {
var css = document.createElement( 'STYLE' );
document.documentElement.firstChild.appendChild( css );
$( obj ).attr( {type: "text/css"} );
css.styleSheet.cssText = $( value ).html();
}
else {
$( value ).appendTo( $( "head" ) );
}
}
// любой другой элемент вставляем в DOM, только если поняли, в какой элемент вставлять
else if ( parentNodeId ) {
$( value ).appendTo( $( "#" + parentNodeId ) );
}
};
}( jQuery ));
Всё бы ничего с этим решением, и даже знает, как собирать тег script из кусочков, но всё-таки мы не можем знать, в какое место DOM должна была производится запись, так что там есть костыли с определением места по контенту, и это решение не универсально.
Для тех, кто не знает, что это такое – ссылка
Вкратце: созздаёт, фактически, «временную таблицу» (более правильно сказать – виртуальную таблицу или результат выборки в виде таблицы), в которой могут храниться данные из других таблиц и результаты применения аггрегирующих функций к ним. При этом если хранятся только данные, то эта таблица обновляется при обновлении входящих в её состав таблиц.
Но оказывается, что, как и многие нововведения (хранимые процедуры должны перекомпилироваться заново при начале сессии, если использовать подзапросы – очень много запросов получается), это тоже имеет очень большие проблемы с производительностью (ссылка).
Вкратце:
неудобно искать проблемы в запросах (вроде как обращаешься к одной таблице, а на деле – нет), создаётся временная таблица без индексов. Также есть проблемы у mySQL при оптимизации запросов при обращении к VIEW.
оригинал; полная цитата:
Сегодня был неожиданно удивлен, какие удобные штуки таит в себе MySQL.
Хочу представить вашему вниманию фичу MySQL — профайлинг.
Появилась она начиная с версии 5.0.37.
Всего парой запросов можно узнать, какими запросами формируется страница (для веб-девелоперов)
и почему она тормозит.
И раньше подобный функционал был доступен, но пользоваться журналом запросов не в пример сложнее.
Итак, как пользоваться:
mysql> set profiling=1;
mysql> select count(*) from comment;
mysql> select count(*) from message;
mysql> show profiles;
+———-+————+——————————+
| Query_ID | Duration | Query |
+———-+————+——————————+
| 1 | 0.00012700 | select count(*) from comment |
| 2 | 0.00014200 | select count(*) from message |
+———-+————+——————————+
2 rows in set (0.00 sec)
Вуаля! Все выполненные запросы за сессию с временем выполнения.
На мой взгляд очень просто и сверх-удобно.
Можно пойти дальше, и узнать подробно на что тратилось время по каждому запросу:
mysql> show profile for query 1;
+——————————–+———-+
| Status | Duration |
+——————————–+———-+
| starting | 0.000015 |
| checking query cache for query | 0.000021 |
| checking permissions | 0.000003 |
| Opening tables | 0.000007 |
| System lock | 0.000004 |
| Table lock | 0.000023 |
| init | 0.000005 |
| optimizing | 0.000005 |
| executing | 0.000025 |
| end | 0.000003 |
| end | 0.000001 |
| query end | 0.000002 |
| storing result in query cache | 0.000003 |
| freeing items | 0.000003 |
| closing tables | 0.000004 |
| logging slow query | 0.000002 |
| cleaning up | 0.000001 |
+——————————–+———-+
17 rows in set (0.00 sec)
Подробнее о профайлинге в статье: Using the New MySQL Query Profiler
Upd: как точно подметил zayceslavshow profiles по умолчанию показывает профили для 15 запросов. Кол-во запрсов можно увеличить с помощью параметра profiling_history_size, но не более чем до 100.
mysql> set profiling=1;
mysql> set profiling_history_size=100;
Установка
Скачать
После помещения библиотеки в папку с расширениями в php.ini надо написать
zend_extension = <путь-к-php>\ext\php_xdebug.dll
Далее ставим плагин для firefox – Xdebug Helper, настройка всего инструментария – в базе знаний Google
Работа с Xdebug под Eclipse
ещё про установку и настройку xdebug,
Профилирование с Xdebug на habrahabr.
Самое основное – параметр в php.ini
xdebug.profiler_enable_trigger=On
В итоге у меня получилось (php5.3)
zend_extension = путь-к-php53\ext\php_xdebug.dll xdebug.remote_enable=1 xdebug.remote_handler=dbgp xdebug.remote_mode=req xdebug.remote_port=9000 xdebug.remote_host=localhost xdebug.idekey=default ;xdebug.dump_once = On ;xdebug.dump_globals = On ;xdebug.dump_undefined = On ;xdebug.dump.REQUEST = * ;xdebug.dump.SERVER = REQUEST_METHOD,REQUEST_URI,HTTP_USER_AGENT xdebug.profiler_enable=Off xdebug.profiler_output_dir="c:\traces" xdebug.profiler_enable_trigger=On xdebug.profiler_output_name = cachegrind.out.%t.%p
Теперь достаточно передать в GET-параметре ?XDEBUG_PROFILE (вот как раз тут полезен XdebugHelper – там это можно включить для определённого сайта).
много ссылок на полезные статьи.
Инструмент для анализа логов Xdebug от создателя.
Дебаг проекта через NetBeans совместно с XdebugHelper:
запустить отладчик в NetBeans и затем нажать кнопку «run» (это NetBeans заставит слушать Xdebug), а затем открыть сайт с активированным на нём XdebugHelper’ом.
При разработке сайта под большую нагрузку нужно постоянно искать слабые места в коде. К примеру, при добавлении нового функционала на частопосещаемой странице появился кусок кода (функция, метод), который заметно замедляет работу проекта в целом. Нет пределов совершенству, и проект можно вылизывать до бесконечности, но на что стоит обратить внимание – подскажет профайлер кода. Лично я использую в работе APD (Advanced PHP Debugger). Во-первых, он хранится в репозитории модулей PHP, во-вторых, мне понравилось им пользоваться, в-третьих, у автора есть достаточно подробное описание в книге «Профессиональное программирование на PHP», в-четвёртых, простота использования. Из минусов – давно не обновлялся и, видимо, уже заброшен разработчиком.
Установка производится с помощью инсталлятора PEAR
#pear install apd
После чего в файл php.ini необходимо добавить установленное расширение и произвести настройку. (Приведу для примера параметры для Windows)
zend_extension = <Путь к установленному php>\ext\php_apd.dll apd.dumpdir = c:\apd_traces\ ; здесь будут храниться промежуточные файлы трассировок apd.statement_trace = 1
Если вызвать phpinfo(), должен появиться раздел с параметрами APD.
Использование.
Активизируется трассировка путём включения в нужный php-файл вызова функции
apd_set_pprof_trace('c:\TEMP\apd_traces');
Протоколируется следующие события:
.
При этом ведутся 3 счётчика:
.
После того, как файлы созданы (т.е. скрипт отработал), можно, к примеру, закомментировать строку вызова
apd_set_pprof_trace('c:\TEMP\apd_traces');
(она больше не нужна).
При установке APD из репозитория в папке
<Путь к php>\PEAR\Console\ появится файл pprofp.
Далее можно создать файл, к примеру, run.bat:
php.exe pprofp -r c:\TEMP\apd_traces\pprof.00288.53 > c:\TEMP\apd_out_rmT.txt
где c:\TEMP\apd_traces\pprof.00288.53 – это промежуточный файл трассировок, созданный при выполнении скрипта, а c:\TEMP\apd_out_rmT.txt будет создан и будет содержать полезную информацию в удобной форме.
Можно, конечно, создать и более «умный» батник, котороый бы принимал имя промежуточного файла параметром, но статья не об этом.
В итоге в файле увидим время выполнения функций и количество вызовов, что в дальнейшем должно помочь писать более «быстрый» код.