[{"data":1,"prerenderedAt":9611},["ShallowReactive",2],{"pt-BR-vuejs-category-articles":3},[4,668,986,2466,4208,4892,7647,8659],{"id":5,"title":6,"articleCover":7,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":12,"categoriesIds":649,"description":652,"enAlternateSlug":653,"extension":654,"meta":655,"navigation":656,"path":657,"ptBrAlternateSlug":658,"publishedAt":659,"seo":660,"sitemap":661,"stem":665,"updatedAt":664,"wordCount":666,"__hash__":667},"blog\u002Fblog\u002Fcomo-adicionar-caracteres-especiais-no-i18n.md","Como Adicionar Caracteres Especiais em Traduções do i18N","\u002Fimages\u002Fblog\u002Fi18n\u002Fi18n-caracteres-especiais-no-nuxtjs-thumb.webp",null,720,1280,"thiago-nunes-batista",{"type":13,"value":14,"toc":643},"minimark",[15,24,43,48,74,101,104,111,114,124,134,207,425,429,442,445,611,624,629,639],[16,17,18,19,23],"p",{},"O ",[20,21,22],"code",{},"i18n"," é uma das ferramentas mais populares para a internacionalização de sites e aplicativos, e em alguns cenários pode ser desafiador resolver problemas específicos de se utilizar o i18n com a ferramenta que você está utilizando.",[16,25,26,27,30,31,34,35,38,39,42],{},"Se você tentou utilizar algum caractere como ",[20,28,29],{},"@"," ou ",[20,32,33],{},"{"," nos arquivos de tradução do i18n no ",[20,36,37],{},"Vue.js"," e se deparou com um ",[20,40,41],{},"erro 500",", neste artigo você vai entender o porquê isso ocorreu e também vai aprender como resolver esse problema.",[44,45,47],"h2",{"id":46},"o-problema-de-alguns-caracteres-no-i18n","O problema de alguns caracteres no I18N",[16,49,50,51,54,55,58,59,62,63,66,67,62,70,73],{},"Assim como o ",[20,52,53],{},"React.JS"," que reserva palavras-chave do ",[20,56,57],{},"HTML"," como ",[20,60,61],{},"class"," e ",[20,64,65],{},"for",", substituindo esses atributos por ",[20,68,69],{},"className",[20,71,72],{},"htmlFor",", o mesmo ocorre com o i18n que trata alguns caracteres de forma especial:",[75,76,77,82,87,91,96],"ul",{},[78,79,80],"li",{},[20,81,33],{},[78,83,84],{},[20,85,86],{},"}",[78,88,89],{},[20,90,29],{},[78,92,93],{},[20,94,95],{},"$",[78,97,98],{},[20,99,100],{},"|",[16,102,103],{},"Se não trabalharmos da forma correta com esses caracteres, você poderá se deparar com o mesmo erro que eu tive:",[105,106],"img",{"alt":107,"height":108,"src":109,"width":110},"exemplo de erro ao adicionar caracteres especiais nos arquivos de tradução do i18n",241,"\u002Fimages\u002Fblog\u002Fi18n\u002Fspecial-character-error.webp",680,[16,112,113],{},"Para entendermos o problema, vamos levar em consideração um projeto que segue a seguinte estrutura:",[115,116,121],"pre",{"className":117,"code":119,"language":120},[118],"language-text",".\n├── i18n \u003C-- Pasta contendo configurações do i18n\n│   └── locales \u003C--- Pasta contendo os arquivos de traduções do i18n\n│       ├── en.json \u003C--- Arquivo de tradução do I18n para o idioma Inglês\n│       └── pt-BR.json \u003C--- Arquivo de tradução do I18n para o idioma Português do Brasil\n","text",[20,122,119],{"__ignoreMap":123},"",[16,125,126,127,130,131,133],{},"Dentro do arquivo ",[20,128,129],{},"pt-BR.json"," estão as chaves e valores referentes aos textos traduzidos para o idioma Português do Brasil, e o problema começou quando eu fui adicionar em um dos links do Footer o caractere ",[20,132,29],{},":",[135,136,137,153],"table",{},[138,139,140],"thead",{},[141,142,143,147,150],"tr",{},[144,145,146],"th",{},"Caractere Especial",[144,148,149],{},"Descrição",[144,151,152],{},"Solução",[154,155,156,167,177,187,197],"tbody",{},[141,157,158,161,164],{},[159,160,29],"td",{},[159,162,163],{},"Utilizado em URLs, e-mails, etc.",[159,165,166],{},"{'@'}",[141,168,169,171,174],{},[159,170,33],{},[159,172,173],{},"Início de um bloco em JSON",[159,175,176],{},"{'{'}",[141,178,179,181,184],{},[159,180,86],{},[159,182,183],{},"Fim de um bloco em JSON",[159,185,186],{},"{'}'}",[141,188,189,191,194],{},[159,190,95],{},[159,192,193],{},"Utilizado em interpolação de strings",[159,195,196],{},"{'$'}",[141,198,199,201,204],{},[159,200,100],{},[159,202,203],{},"Caractere Pipe",[159,205,206],{},"{'|'}",[115,208,212],{"className":209,"code":210,"language":211,"meta":123,"style":123},"language-json shiki shiki-themes dracula","{\n  \"footer\": {\n    \"socialMedia\": [\n      {\n        \"url\": \"https:\u002F\u002Fwww.youtube.com\u002F@CodigoAoPonto\",\n        \"ariaLabel\": \"Ir para o canal do Código ao Ponto no Youtube\",\n        \"icon\": \"mdi:youtube\"\n      },\n      {\n        \"url\": \"https:\u002F\u002Fwww.instagram.com\u002Fcodigo_ponto\",\n        \"ariaLabel\": \"Ir para o Instagram do Código ao Ponto\",\n        \"icon\": \"mdi:instagram\"\n      }\n    ]\n  }\n}\n","json",[20,213,214,223,243,259,265,291,312,332,338,343,363,383,401,407,413,419],{"__ignoreMap":123},[215,216,219],"span",{"class":217,"line":218},"line",1,[215,220,222],{"class":221},"sCdxs","{\n",[215,224,226,230,234,237,240],{"class":217,"line":225},2,[215,227,229],{"class":228},"sY8FZ","  \"",[215,231,233],{"class":232},"sLL85","footer",[215,235,236],{"class":228},"\"",[215,238,133],{"class":239},"s0Tla",[215,241,242],{"class":221}," {\n",[215,244,246,249,252,254,256],{"class":217,"line":245},3,[215,247,248],{"class":228},"    \"",[215,250,251],{"class":232},"socialMedia",[215,253,236],{"class":228},[215,255,133],{"class":239},[215,257,258],{"class":221}," [\n",[215,260,262],{"class":217,"line":261},4,[215,263,264],{"class":221},"      {\n",[215,266,268,271,274,276,278,282,286,288],{"class":217,"line":267},5,[215,269,270],{"class":228},"        \"",[215,272,273],{"class":232},"url",[215,275,236],{"class":228},[215,277,133],{"class":239},[215,279,281],{"class":280},"seVfx"," \"",[215,283,285],{"class":284},"s-mGx","https:\u002F\u002Fwww.youtube.com\u002F@CodigoAoPonto",[215,287,236],{"class":280},[215,289,290],{"class":221},",\n",[215,292,294,296,299,301,303,305,308,310],{"class":217,"line":293},6,[215,295,270],{"class":228},[215,297,298],{"class":232},"ariaLabel",[215,300,236],{"class":228},[215,302,133],{"class":239},[215,304,281],{"class":280},[215,306,307],{"class":284},"Ir para o canal do Código ao Ponto no Youtube",[215,309,236],{"class":280},[215,311,290],{"class":221},[215,313,315,317,320,322,324,326,329],{"class":217,"line":314},7,[215,316,270],{"class":228},[215,318,319],{"class":232},"icon",[215,321,236],{"class":228},[215,323,133],{"class":239},[215,325,281],{"class":280},[215,327,328],{"class":284},"mdi:youtube",[215,330,331],{"class":280},"\"\n",[215,333,335],{"class":217,"line":334},8,[215,336,337],{"class":221},"      },\n",[215,339,341],{"class":217,"line":340},9,[215,342,264],{"class":221},[215,344,346,348,350,352,354,356,359,361],{"class":217,"line":345},10,[215,347,270],{"class":228},[215,349,273],{"class":232},[215,351,236],{"class":228},[215,353,133],{"class":239},[215,355,281],{"class":280},[215,357,358],{"class":284},"https:\u002F\u002Fwww.instagram.com\u002Fcodigo_ponto",[215,360,236],{"class":280},[215,362,290],{"class":221},[215,364,366,368,370,372,374,376,379,381],{"class":217,"line":365},11,[215,367,270],{"class":228},[215,369,298],{"class":232},[215,371,236],{"class":228},[215,373,133],{"class":239},[215,375,281],{"class":280},[215,377,378],{"class":284},"Ir para o Instagram do Código ao Ponto",[215,380,236],{"class":280},[215,382,290],{"class":221},[215,384,386,388,390,392,394,396,399],{"class":217,"line":385},12,[215,387,270],{"class":228},[215,389,319],{"class":232},[215,391,236],{"class":228},[215,393,133],{"class":239},[215,395,281],{"class":280},[215,397,398],{"class":284},"mdi:instagram",[215,400,331],{"class":280},[215,402,404],{"class":217,"line":403},13,[215,405,406],{"class":221},"      }\n",[215,408,410],{"class":217,"line":409},14,[215,411,412],{"class":221},"    ]\n",[215,414,416],{"class":217,"line":415},15,[215,417,418],{"class":221},"  }\n",[215,420,422],{"class":217,"line":421},16,[215,423,424],{"class":221},"}\n",[44,426,428],{"id":427},"a-solução","A solução",[16,430,431,432,435,436,438,439,441],{},"Para resolver esse problema nós teremos que fazer a ",[20,433,434],{},"Literal Interpolation"," no i18n, onde para usar o caractere ",[20,437,29],{}," no arquivo de traduções do i18n nós usaremos o seguinte texto: ",[20,440,166],{},", onde basicamente adicionamos as chaves em volta do caractere especial e também aspas simples.",[16,443,444],{},"Com isso feito, o arquivo com o problema resolvido terá o seguinte formato:",[115,446,448],{"className":209,"code":447,"language":211,"meta":123,"style":123},"{\n  \"footer\": {\n    \"socialMedia\": [\n      {\n        \"url\": \"https:\u002F\u002Fwww.youtube.com\u002F{'@'}CodigoAoPonto\",\n        \"ariaLabel\": \"Ir para o canal do Código ao Ponto no Youtube\",\n        \"icon\": \"mdi:youtube\"\n      },\n      {\n        \"url\": \"https:\u002F\u002Fwww.instagram.com\u002Fcodigo_ponto\",\n        \"ariaLabel\": \"Ir para o Instagram do Código ao Ponto\",\n        \"icon\": \"mdi:instagram\"\n      }\n    ]\n  }\n}\n",[20,449,450,454,466,478,482,501,519,535,539,543,561,579,595,599,603,607],{"__ignoreMap":123},[215,451,452],{"class":217,"line":218},[215,453,222],{"class":221},[215,455,456,458,460,462,464],{"class":217,"line":225},[215,457,229],{"class":228},[215,459,233],{"class":232},[215,461,236],{"class":228},[215,463,133],{"class":239},[215,465,242],{"class":221},[215,467,468,470,472,474,476],{"class":217,"line":245},[215,469,248],{"class":228},[215,471,251],{"class":232},[215,473,236],{"class":228},[215,475,133],{"class":239},[215,477,258],{"class":221},[215,479,480],{"class":217,"line":261},[215,481,264],{"class":221},[215,483,484,486,488,490,492,494,497,499],{"class":217,"line":267},[215,485,270],{"class":228},[215,487,273],{"class":232},[215,489,236],{"class":228},[215,491,133],{"class":239},[215,493,281],{"class":280},[215,495,496],{"class":284},"https:\u002F\u002Fwww.youtube.com\u002F{'@'}CodigoAoPonto",[215,498,236],{"class":280},[215,500,290],{"class":221},[215,502,503,505,507,509,511,513,515,517],{"class":217,"line":293},[215,504,270],{"class":228},[215,506,298],{"class":232},[215,508,236],{"class":228},[215,510,133],{"class":239},[215,512,281],{"class":280},[215,514,307],{"class":284},[215,516,236],{"class":280},[215,518,290],{"class":221},[215,520,521,523,525,527,529,531,533],{"class":217,"line":314},[215,522,270],{"class":228},[215,524,319],{"class":232},[215,526,236],{"class":228},[215,528,133],{"class":239},[215,530,281],{"class":280},[215,532,328],{"class":284},[215,534,331],{"class":280},[215,536,537],{"class":217,"line":334},[215,538,337],{"class":221},[215,540,541],{"class":217,"line":340},[215,542,264],{"class":221},[215,544,545,547,549,551,553,555,557,559],{"class":217,"line":345},[215,546,270],{"class":228},[215,548,273],{"class":232},[215,550,236],{"class":228},[215,552,133],{"class":239},[215,554,281],{"class":280},[215,556,358],{"class":284},[215,558,236],{"class":280},[215,560,290],{"class":221},[215,562,563,565,567,569,571,573,575,577],{"class":217,"line":365},[215,564,270],{"class":228},[215,566,298],{"class":232},[215,568,236],{"class":228},[215,570,133],{"class":239},[215,572,281],{"class":280},[215,574,378],{"class":284},[215,576,236],{"class":280},[215,578,290],{"class":221},[215,580,581,583,585,587,589,591,593],{"class":217,"line":385},[215,582,270],{"class":228},[215,584,319],{"class":232},[215,586,236],{"class":228},[215,588,133],{"class":239},[215,590,281],{"class":280},[215,592,398],{"class":284},[215,594,331],{"class":280},[215,596,597],{"class":217,"line":403},[215,598,406],{"class":221},[215,600,601],{"class":217,"line":409},[215,602,412],{"class":221},[215,604,605],{"class":217,"line":415},[215,606,418],{"class":221},[215,608,609],{"class":217,"line":421},[215,610,424],{"class":221},[16,612,613,614,617,618,623],{},"Depois de resolver esse problema, se você precisar conferir o tamanho de uma string traduzida, por exemplo para não estourar o ",[20,615,616],{},"maxlength"," de um campo ou o espaço de um botão, o ",[619,620,622],"a",{"href":621},"\u002Fferramentas\u002Fcontador-de-caracteres-e-palavras","Contador de Caracteres e Palavras"," do Código ao Ponto conta caracteres, palavras e linhas em tempo real, direto no navegador.",[625,626,628],"h3",{"id":627},"conclusão","Conclusão",[16,630,631,632,638],{},"Espero que tenha conseguido te ajudar a resolver esse desafio de utilizar caracteres especiais no i18n com Vue.js, se você ainda tiver dúvidas pode consultar a ",[619,633,637],{"href":634,"rel":635},"https:\u002F\u002Fvue-i18n.intlify.dev\u002Fguide\u002Fessentials\u002Fsyntax#literal-interpolation",[636],"nofollow","Documentação Oficial do VueI18n",".",[640,641,642],"style",{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .sY8FZ, html code.shiki .sY8FZ{--shiki-default:#8BE9FE}html pre.shiki code .sLL85, html code.shiki .sLL85{--shiki-default:#8BE9FD}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}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);}",{"title":123,"searchDepth":225,"depth":225,"links":644},[645,646],{"id":46,"depth":225,"text":47},{"id":427,"depth":225,"text":428,"children":647},[648],{"id":627,"depth":245,"text":628},[650,651],"nuxt","vuejs","Aprenda a adicionar caracteres especiais como @ e {} nas traduções do i18n no Vue.js sem causar erros de compilação, com a solução passo a passo e exemplos.","how-to-add-special-characters-on-i18n","md",{},true,"\u002Fblog\u002Fcomo-adicionar-caracteres-especiais-no-i18n","como-adicionar-caracteres-especiais-no-i18n","2025-01-29",{"title":6,"description":652},{"loc":657,"images":662,"lastmod":664},[663],{"loc":109},"2026-10-06","blog\u002Fcomo-adicionar-caracteres-especiais-no-i18n",413,"XjGVS2z9OY3rNm3e_KgGZbUatXwuUWaxaeI-BZtH-HA",{"id":669,"title":670,"articleCover":671,"articleCoverEmbed":672,"articleCoverEmbedUploadDate":673,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":674,"categoriesIds":976,"description":977,"enAlternateSlug":8,"extension":654,"meta":978,"navigation":656,"path":979,"ptBrAlternateSlug":8,"publishedAt":980,"seo":981,"sitemap":982,"stem":983,"updatedAt":664,"wordCount":984,"__hash__":985},"blog\u002Fblog\u002Fcomo-adicionar-o-google-tag-manager-no-seu-website-com-nuxt-3.md","Como adicionar o Google Tag Manager no seu website com Nuxt 3","\u002Fimages\u002Fblog\u002Fcomo-adicionar-google-tag-manager-em-projeto-com-nuxt-3.webp","https:\u002F\u002Fwww.youtube.com\u002Fembed\u002FeFEHKZhwCcU?si=vEaEl61cM3gAOyao","2024-08-13T09:00:35-07:00",{"type":13,"value":675,"toc":970},[676,679,693,696,700,709,712,737,751,810,814,830,880,884,887,912,959,961,964,967],[16,677,678],{},"O Google Tag Manager é uma plataforma do Google que permite adicionar e gerenciar a integração do seu site ou aplicativo com diversos outros serviços como por exemplo:",[75,680,681,684,687,690],{},[78,682,683],{},"Google Analytics",[78,685,686],{},"Google Ads",[78,688,689],{},"Crazy Egg",[78,691,692],{},"Hotjar Tracking Code",[16,694,695],{},"Neste artigo você vai aprender através de um passo a passo como adicionar o Google Tag Manager no seu website desenvolvido com Nuxt 3.",[44,697,699],{"id":698},"_1-instalar-o-módulo-nuxt-gtag","1. Instalar o módulo nuxt-gtag",[16,701,702,703,708],{},"O Nuxt possui um módulo chamado ",[619,704,707],{"href":705,"rel":706},"https:\u002F\u002Fnuxt.com\u002Fmodules\u002Fgtag",[636],"\"nuxt-gtag\" que possui mais de 100 mil downloads"," mensais e que vai nos ajudar a realizar a instalação do Google Tag Manager no Nuxt 3 de forma simplificada.",[16,710,711],{},"O primeiro passo é executar o comando abaixo:",[115,713,717],{"className":714,"code":715,"language":716,"meta":123,"style":123},"language-bash shiki shiki-themes dracula","npx nuxi@latest module add gtag\n","bash",[20,718,719],{"__ignoreMap":123},[215,720,721,725,728,731,734],{"class":217,"line":218},[215,722,724],{"class":723},"sAOxA","npx",[215,726,727],{"class":284}," nuxi@latest",[215,729,730],{"class":284}," module",[215,732,733],{"class":284}," add",[215,735,736],{"class":284}," gtag\n",[16,738,739,740,744,745,747,748,638],{},"Esse comando será responsável por instalar o pacote no NPM do módulo ",[741,742,743],"strong",{},"nuxt-gtag"," no projeto e também irá adicionar o ",[741,746,743],{}," na seção de módulos do arquivo ",[741,749,750],{},"nuxt.config.ts",[115,752,756],{"className":753,"code":754,"language":755,"meta":123,"style":123},"language-javascript shiki shiki-themes dracula","export default defineNuxtConfig({\n  \u002F\u002F Other Nuxt Settings\n  modules: [\n    \u002F\u002F Other Nuxt Modules\n    \"nuxt-gtag\"\n  ]\n});\n","javascript",[20,757,758,772,778,787,792,800,805],{"__ignoreMap":123},[215,759,760,763,766,769],{"class":217,"line":218},[215,761,762],{"class":239},"export",[215,764,765],{"class":239}," default",[215,767,768],{"class":723}," defineNuxtConfig",[215,770,771],{"class":221},"({\n",[215,773,774],{"class":217,"line":225},[215,775,777],{"class":776},"shSDL","  \u002F\u002F Other Nuxt Settings\n",[215,779,780,783,785],{"class":217,"line":245},[215,781,782],{"class":221},"  modules",[215,784,133],{"class":239},[215,786,258],{"class":221},[215,788,789],{"class":217,"line":261},[215,790,791],{"class":776},"    \u002F\u002F Other Nuxt Modules\n",[215,793,794,796,798],{"class":217,"line":267},[215,795,248],{"class":280},[215,797,743],{"class":284},[215,799,331],{"class":280},[215,801,802],{"class":217,"line":293},[215,803,804],{"class":221},"  ]\n",[215,806,807],{"class":217,"line":314},[215,808,809],{"class":221},"});\n",[44,811,813],{"id":812},"_2-configurar-o-google-tag-manager-no-projeto","2. Configurar o Google Tag Manager no projeto.",[16,815,126,816,818,819,822,823,826,827,829],{},[741,817,750],{},", você deverá adicionar um novo campo chamado ",[741,820,821],{},"gtag"," dentro do objeto de configuração do Nuxt e que possuirá um outro campo de ",[741,824,825],{},"id"," que é valor referente ao ",[741,828,825],{}," da sua conta no Google Tag Manager:",[115,831,833],{"className":753,"code":832,"language":755,"meta":123,"style":123},"export default defineNuxtConfig({\n  \u002F\u002F Other Nuxt Settings\n  gtag: {\n    id: \"GTAG-ID\"\n  }\n});\n",[20,834,835,845,849,858,872,876],{"__ignoreMap":123},[215,836,837,839,841,843],{"class":217,"line":218},[215,838,762],{"class":239},[215,840,765],{"class":239},[215,842,768],{"class":723},[215,844,771],{"class":221},[215,846,847],{"class":217,"line":225},[215,848,777],{"class":776},[215,850,851,854,856],{"class":217,"line":245},[215,852,853],{"class":221},"  gtag",[215,855,133],{"class":239},[215,857,242],{"class":221},[215,859,860,863,865,867,870],{"class":217,"line":261},[215,861,862],{"class":221},"    id",[215,864,133],{"class":239},[215,866,281],{"class":280},[215,868,869],{"class":284},"GTAG-ID",[215,871,331],{"class":280},[215,873,874],{"class":217,"line":267},[215,875,418],{"class":221},[215,877,878],{"class":217,"line":293},[215,879,809],{"class":221},[44,881,883],{"id":882},"_3-adicionar-o-id-do-google-tag-manager-nas-variáveis-de-ambiente","3. Adicionar o id do Google Tag Manager nas variáveis de ambiente",[16,885,886],{},"Eu recomendo você não colocar o id do Google Tag Manager dessa forma no seu código, e sim colocar o valor no arquivo .env do projeto e utilizar essa variável de ambiente para fornecer o valor do id do Google Tag Manager na configuração do projeto:",[115,888,890],{"className":714,"code":889,"language":716,"meta":123,"style":123},"# .env file\nGTAG_ID=\"GTAG-ID\"\n",[20,891,892,897],{"__ignoreMap":123},[215,893,894],{"class":217,"line":218},[215,895,896],{"class":776},"# .env file\n",[215,898,899,903,906,908,910],{"class":217,"line":225},[215,900,902],{"class":901},"sIQBb","GTAG_ID",[215,904,905],{"class":239},"=",[215,907,236],{"class":280},[215,909,869],{"class":284},[215,911,331],{"class":280},[115,913,915],{"className":753,"code":914,"language":755,"meta":123,"style":123},"export default defineNuxtConfig({\n  \u002F\u002F Other Nuxt Settings\n  gtag: {\n    id: process.env.GTAG_ID\n  }\n});\n",[20,916,917,927,931,939,951,955],{"__ignoreMap":123},[215,918,919,921,923,925],{"class":217,"line":218},[215,920,762],{"class":239},[215,922,765],{"class":239},[215,924,768],{"class":723},[215,926,771],{"class":221},[215,928,929],{"class":217,"line":225},[215,930,777],{"class":776},[215,932,933,935,937],{"class":217,"line":245},[215,934,853],{"class":221},[215,936,133],{"class":239},[215,938,242],{"class":221},[215,940,941,943,945,948],{"class":217,"line":261},[215,942,862],{"class":221},[215,944,133],{"class":239},[215,946,947],{"class":221}," process.env.",[215,949,950],{"class":901},"GTAG_ID\n",[215,952,953],{"class":217,"line":267},[215,954,418],{"class":221},[215,956,957],{"class":217,"line":293},[215,958,809],{"class":221},[44,960,628],{"id":627},[16,962,963],{},"Após realizar as etapas acima, o Google Tag Manager estará configurado no seu projeto e você já vai poder começar a adicionar tags no seu website como por exemplo as tags do Google Analytics e Google Ads.",[16,965,966],{},"Espero ter conseguido te ajudar nessa missão de configurar com sucesso o Google Tag Manager no seu projeto com Nuxt 3. 🙋🏻‍♂️",[640,968,969],{},"html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}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 pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}",{"title":123,"searchDepth":225,"depth":225,"links":971},[972,973,974,975],{"id":698,"depth":225,"text":699},{"id":812,"depth":225,"text":813},{"id":882,"depth":225,"text":883},{"id":627,"depth":225,"text":628},[650,651],"Aprenda a adicionar o Google Tag Manager (GTM) em um site Nuxt com o módulo nuxt-gtag e facilite o gerenciamento de tags e do analytics do seu projeto.",{},"\u002Fblog\u002Fcomo-adicionar-o-google-tag-manager-no-seu-website-com-nuxt-3","2024-08-15",{"title":670,"description":977},{"loc":979,"lastmod":664},"blog\u002Fcomo-adicionar-o-google-tag-manager-no-seu-website-com-nuxt-3",309,"Vt1i2an6nu4k0qYmpce20WyUJk4H4e-BBq1WsI5x2N4",{"id":987,"title":988,"articleCover":989,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":990,"categoriesIds":2450,"description":2452,"enAlternateSlug":2453,"extension":654,"meta":2454,"navigation":656,"path":2455,"ptBrAlternateSlug":2456,"publishedAt":2457,"seo":2458,"sitemap":2459,"stem":2463,"updatedAt":664,"wordCount":2464,"__hash__":2465},"blog\u002Fblog\u002Fcomo-criar-dados-estruturados-com-nuxtjs.md","Como Adicionar Dados Estruturados no Nuxt.JS","\u002Fimages\u002Fblog\u002Fseo\u002Fstructured-data\u002Fstructured-data-thumb-1.webp",{"type":13,"value":991,"toc":2422},[992,999,1003,1006,1013,1017,1026,1033,1039,1043,1046,1056,1174,1178,1181,1192,1195,1199,1202,1216,1220,1232,1235,1244,1251,1270,1276,1291,1300,1356,1359,1363,1373,1418,1432,1747,1760,1764,1772,1895,1914,1918,1961,1965,1971,2116,2119,2125,2129,2138,2142,2145,2166,2169,2197,2201,2204,2223,2226,2229,2233,2236,2244,2248,2251,2266,2276,2279,2327,2330,2334,2346,2350,2354,2360,2364,2367,2371,2374,2378,2381,2385,2388,2390,2399,2419],[16,993,994,995,998],{},"Aqui você vê o que são Dados Estruturados, quando usar, quando não usar e como marcar os artigos do seu blog no Nuxt.JS com o ",[20,996,997],{},"nuxt-schema-org",". Depois vem a validação da marcação e o que fazer para o Google descobrir e indexar o conteúdo e, quando possível, recomendá-lo no Discover.",[44,1000,1002],{"id":1001},"o-que-são-dados-estruturados","O que são Dados Estruturados?",[16,1004,1005],{},"Dados estruturados são informações de um site que estão organizadas de forma padronizada em formatos como JSON-LD, Microdata ou RDFa. O Google recomenda o JSON-LD, que fica separado do HTML visível e é o mais simples de manter, e é o formato que vamos usar aqui.",[16,1007,1008,1009,1012],{},"Através dos dados estruturados os mecanismos de busca como o Google recebem mais informações sobre o que se trata a sua página e, com base nessas informações, juntamente com outros fatores, os mecanismos de busca podem fazer a exibição de ",[20,1010,1011],{},"Resultados Ricos"," de páginas do seu site nos resultados de busca.",[44,1014,1016],{"id":1015},"o-que-são-rich-snippets","O que são Rich Snippets ?",[16,1018,1019,1020,30,1023,1025],{},"Os ",[20,1021,1022],{},"Rich Snippets",[20,1024,1011],{}," são melhorias visuais nos resultados de busca como imagens, preços, breadcrumbs, estrelas de avaliação, etc.",[16,1027,1028,1029,1032],{},"Abaixo você pode conferir um exemplo de Resultado Rico no Google do site ",[20,1030,1031],{},"Yahoo Finances",", onde uma imagem é exibida, título e data de publicação. Essas informações e recursos visuais aumentam a chance de chamar a atenção do usuário e fazer ele acessar o seu site.",[105,1034],{"alt":1035,"height":1036,"src":1037,"width":1038},"exemplo de resultado rico de busca no Google do site Yahoo Finances",358,"\u002Fimages\u002Fblog\u002Fseo\u002Fstructured-data\u002Fyahoo-finances-schema-org-rich-result.webp",688,[44,1040,1042],{"id":1041},"exemplo-do-código-de-dado-estruturado","Exemplo do Código de Dado Estruturado",[16,1044,1045],{},"Abaixo você pode ver um exemplo de dados estruturados em JSON-LD. Não se preocupe, você não vai precisar escrever isso manualmente, vamos usar uma ferramenta do Nuxt para gerar o código.",[1047,1048,1049],"blockquote",{},[16,1050,1051,1052,1055],{},"Este exemplo usa ",[20,1053,1054],{},"FAQPage",", que foi o tipo mais comum de resultado rico durante anos. O Google parou de exibir o resultado rico de FAQ em 7 de maio de 2026, para qualquer tipo de site, e removeu a documentação do recurso em junho. A marcação continua válida no schema.org, mas hoje ela só descreve o conteúdo e não muda a aparência do seu resultado na busca.",[115,1057,1061],{"className":1058,"code":1059,"language":1060,"meta":123,"style":123},"language-html shiki shiki-themes dracula","\u003Cscript type=\"application\u002Fld+json\">\n  {\n    \"@type\": \"FAQPage\",\n    \"@context\": \"https:\u002F\u002Fschema.org\",\n    \"@graph\": [\n      {\n        \"@id\": \"https:\u002F\u002Fcodigoaoponto.com\u002Ftools\u002Fcpf-generator\u002F#\u002Fschema\u002Fquestion\u002Fc6e1aab\",\n        \"@type\": \"Question\",\n        \"inLanguage\": \"pt-BR\",\n        \"name\": \"O que é um Gerador de CPF?\",\n        \"acceptedAnswer\": {\n          \"@type\": \"Answer\",\n          \"text\": \"Um Gerador de CPF é uma ferramenta que gera números de CPF válidos, sendo útil para testes e desenvolvimento de sistemas. Ele aplica o algoritmo oficial de validação do CPF para assegurar que os números criados estejam formatados corretamente.\"\n        }\n      }\n    ]\n  }\n\u003C\u002Fscript>\n","html",[20,1062,1063,1087,1092,1097,1102,1107,1111,1116,1121,1126,1131,1136,1141,1146,1151,1155,1159,1164],{"__ignoreMap":123},[215,1064,1065,1068,1071,1075,1077,1079,1082,1084],{"class":217,"line":218},[215,1066,1067],{"class":221},"\u003C",[215,1069,1070],{"class":239},"script",[215,1072,1074],{"class":1073},"sY_PY"," type",[215,1076,905],{"class":239},[215,1078,236],{"class":280},[215,1080,1081],{"class":284},"application\u002Fld+json",[215,1083,236],{"class":280},[215,1085,1086],{"class":221},">\n",[215,1088,1089],{"class":217,"line":225},[215,1090,1091],{"class":221},"  {\n",[215,1093,1094],{"class":217,"line":245},[215,1095,1096],{"class":221},"    \"@type\": \"FAQPage\",\n",[215,1098,1099],{"class":217,"line":261},[215,1100,1101],{"class":221},"    \"@context\": \"https:\u002F\u002Fschema.org\",\n",[215,1103,1104],{"class":217,"line":267},[215,1105,1106],{"class":221},"    \"@graph\": [\n",[215,1108,1109],{"class":217,"line":293},[215,1110,264],{"class":221},[215,1112,1113],{"class":217,"line":314},[215,1114,1115],{"class":221},"        \"@id\": \"https:\u002F\u002Fcodigoaoponto.com\u002Ftools\u002Fcpf-generator\u002F#\u002Fschema\u002Fquestion\u002Fc6e1aab\",\n",[215,1117,1118],{"class":217,"line":334},[215,1119,1120],{"class":221},"        \"@type\": \"Question\",\n",[215,1122,1123],{"class":217,"line":340},[215,1124,1125],{"class":221},"        \"inLanguage\": \"pt-BR\",\n",[215,1127,1128],{"class":217,"line":345},[215,1129,1130],{"class":221},"        \"name\": \"O que é um Gerador de CPF?\",\n",[215,1132,1133],{"class":217,"line":365},[215,1134,1135],{"class":221},"        \"acceptedAnswer\": {\n",[215,1137,1138],{"class":217,"line":385},[215,1139,1140],{"class":221},"          \"@type\": \"Answer\",\n",[215,1142,1143],{"class":217,"line":403},[215,1144,1145],{"class":221},"          \"text\": \"Um Gerador de CPF é uma ferramenta que gera números de CPF válidos, sendo útil para testes e desenvolvimento de sistemas. Ele aplica o algoritmo oficial de validação do CPF para assegurar que os números criados estejam formatados corretamente.\"\n",[215,1147,1148],{"class":217,"line":409},[215,1149,1150],{"class":221},"        }\n",[215,1152,1153],{"class":217,"line":415},[215,1154,406],{"class":221},[215,1156,1157],{"class":217,"line":421},[215,1158,412],{"class":221},[215,1160,1162],{"class":217,"line":1161},17,[215,1163,418],{"class":221},[215,1165,1167,1170,1172],{"class":217,"line":1166},18,[215,1168,1169],{"class":221},"\u003C\u002F",[215,1171,1070],{"class":239},[215,1173,1086],{"class":221},[44,1175,1177],{"id":1176},"quando-usar-dados-estruturados","Quando usar dados estruturados?",[16,1179,1180],{},"Use Dados Estruturados quando:",[75,1182,1183,1186,1189],{},[78,1184,1185],{},"O conteúdo da página for de alguma das categorias suportadas, como exemplo: artigos, produtos, receitas, eventos, vagas de emprego, etc.",[78,1187,1188],{},"Desejar aumentar a visibilidade das suas páginas e a taxa de cliques (CTR).",[78,1190,1191],{},"Querer ajudar mecanismos de busca a entender melhor o conteúdo do seu site.",[16,1193,1194],{},"Em qualquer caso, o que você marca precisa estar visível para o usuário na página, e as propriedades precisam ser mantidas atualizadas.",[44,1196,1198],{"id":1197},"quando-não-usar-dados-estruturados","Quando não usar dados estruturados ?",[16,1200,1201],{},"Evite usar Dados Estruturados quando:",[75,1203,1204,1207,1210,1213],{},[78,1205,1206],{},"Conteúdo da página não se encaixa nas categorias suportadas.",[78,1208,1209],{},"Conteúdo de baixa qualidade, os dados estruturados não auxiliam na melhoria do conteúdo.",[78,1211,1212],{},"Conteúdo mudar com frequência, pois se tornará custoso realizar atualizações tão frequentes dessas informações.",[78,1214,1215],{},"O conteúdo marcado não aparece na página. O Google trata isso como uso indevido de dados estruturados e pode aplicar uma ação manual no seu site.",[44,1217,1219],{"id":1218},"instalação-do-pacote-schemaorg-no-nuxt","Instalação do pacote Schema.org no Nuxt",[16,1221,18,1222,1227,1228,1231],{},[619,1223,1226],{"href":1224,"rel":1225},"https:\u002F\u002Fnuxtseo.com",[636],"Nuxt SEO"," é uma coleção de módulos de SEO que facilitam o desenvolvimento de ",[20,1229,1230],{},"SEO técnico"," para o seu site. Para este tutorial nós vamos utilizar o módulo que facilitará a implementação dos estruturados.",[16,1233,1234],{},"Você pode realizar a instalação desse módulo de várias formas, aqui vou exemplificar as duas principais:",[1236,1237,1238,1241],"ol",{},[78,1239,1240],{},"nuxi (Interface de linha de comando oficial do Nuxt)",[78,1242,1243],{},"npm (Gerenciador de pacotes)",[16,1245,1246,1247,1250],{},"Se você for utilizar o ",[20,1248,1249],{},"nuxi",", basta executar o seguinte comando:",[115,1252,1254],{"className":714,"code":1253,"language":716,"meta":123,"style":123},"npx nuxi module add schema-org\n",[20,1255,1256],{"__ignoreMap":123},[215,1257,1258,1260,1263,1265,1267],{"class":217,"line":218},[215,1259,724],{"class":723},[215,1261,1262],{"class":284}," nuxi",[215,1264,730],{"class":284},[215,1266,733],{"class":284},[215,1268,1269],{"class":284}," schema-org\n",[16,1271,1246,1272,1275],{},[20,1273,1274],{},"npm",", terá que fazer uma etapa extra, comece instalando o pacote:",[115,1277,1279],{"className":714,"code":1278,"language":716,"meta":123,"style":123},"npm i nuxt-schema-org\n",[20,1280,1281],{"__ignoreMap":123},[215,1282,1283,1285,1288],{"class":217,"line":218},[215,1284,1274],{"class":723},[215,1286,1287],{"class":284}," i",[215,1289,1290],{"class":284}," nuxt-schema-org\n",[16,1292,1293,1294,1297,1298,133],{},"E depois adicione o pacote instalado na opção de ",[20,1295,1296],{},"modules"," dentro do arquivo ",[20,1299,750],{},[115,1301,1305],{"className":1302,"code":1303,"language":1304,"meta":123,"style":123},"language-js shiki shiki-themes dracula","export default defineNuxtConfig({\n  \u002F\u002F outras configurações....\n  modules: [\n    \u002F\u002F outros módulos\n    \"nuxt-schema-org\"\n  ]\n  \u002F\u002F outras configurações...s\n});\n","js",[20,1306,1307,1317,1322,1330,1335,1343,1347,1352],{"__ignoreMap":123},[215,1308,1309,1311,1313,1315],{"class":217,"line":218},[215,1310,762],{"class":239},[215,1312,765],{"class":239},[215,1314,768],{"class":723},[215,1316,771],{"class":221},[215,1318,1319],{"class":217,"line":225},[215,1320,1321],{"class":776},"  \u002F\u002F outras configurações....\n",[215,1323,1324,1326,1328],{"class":217,"line":245},[215,1325,782],{"class":221},[215,1327,133],{"class":239},[215,1329,258],{"class":221},[215,1331,1332],{"class":217,"line":261},[215,1333,1334],{"class":776},"    \u002F\u002F outros módulos\n",[215,1336,1337,1339,1341],{"class":217,"line":267},[215,1338,248],{"class":280},[215,1340,997],{"class":284},[215,1342,331],{"class":280},[215,1344,1345],{"class":217,"line":293},[215,1346,804],{"class":221},[215,1348,1349],{"class":217,"line":314},[215,1350,1351],{"class":776},"  \u002F\u002F outras configurações...s\n",[215,1353,1354],{"class":217,"line":334},[215,1355,809],{"class":221},[16,1357,1358],{},"Feito isso, já podemos começar a utilizar a ferramenta.",[44,1360,1362],{"id":1361},"dados-estruturados-para-artigos-no-nuxtjs","Dados Estruturados para Artigos no Nuxt.JS",[16,1364,1365,1366,1369,1370,1372],{},"Para um blog, o tipo mais útil é o ",[20,1367,1368],{},"Article",". Ele diz ao Google que a página é um artigo, quem escreveu, quando foi publicado e atualizado e qual imagem o representa. O Google não exige nenhuma propriedade para ",[20,1371,1368],{},", mas recomenda estas:",[75,1374,1375,1381,1387,1396,1412],{},[78,1376,1377,1380],{},[20,1378,1379],{},"headline",": o título do artigo.",[78,1382,1383,1386],{},[20,1384,1385],{},"image",": uma imagem representativa. O Google recomenda várias imagens em alta resolução, nas proporções 16x9, 4x3 e 1x1.",[78,1388,1389,62,1392,1395],{},[20,1390,1391],{},"datePublished",[20,1393,1394],{},"dateModified",": data da primeira publicação e da última modificação, em formato ISO 8601.",[78,1397,1398,1401,1402,1405,1406,62,1409,1411],{},[20,1399,1400],{},"author",": de preferência um ",[20,1403,1404],{},"Person"," com ",[20,1407,1408],{},"name",[20,1410,273],{},", apontando para uma página que identifique o autor.",[78,1413,1414,1417],{},[20,1415,1416],{},"publisher",": a organização por trás do site, com nome e logo.",[16,1419,1420,1421,1424,1425,1428,1429,1431],{},"No Nuxt estará disponível de forma global o ",[20,1422,1423],{},"composable"," chamado ",[20,1426,1427],{},"useSchemaOrg",", que você pode usar dentro da tag ",[20,1430,1070],{}," da página. Em uma página de blog que carrega o post com o Nuxt Content, ele fica assim:",[115,1433,1437],{"className":1434,"code":1435,"language":1436,"meta":123,"style":123},"language-vue shiki shiki-themes dracula","\u003Cscript setup lang=\"ts\">\nconst route = useRoute();\n\nconst { data: post } = await useAsyncData(`post-${route.path}`, () =>\n  queryCollection(\"blog\").path(route.path).first()\n);\n\nuseSchemaOrg([\n  defineArticle({\n    headline: post.value?.title,\n    description: post.value?.description,\n    image: post.value?.articleCover,\n    datePublished: post.value?.publishedAt,\n    dateModified: post.value?.updatedAt ?? post.value?.publishedAt\n  }),\n  defineBreadcrumb({\n    itemListElement: [\n      { name: \"Home\", item: \"\u002F\" },\n      { name: \"Blog\", item: \"\u002Fblog\" },\n      { name: post.value?.title }\n    ]\n  })\n]);\n\u003C\u002Fscript>\n","vue",[20,1438,1439,1462,1478,1483,1526,1555,1560,1564,1571,1578,1588,1598,1608,1618,1634,1639,1646,1655,1684,1711,1721,1726,1732,1738],{"__ignoreMap":123},[215,1440,1441,1443,1445,1448,1451,1453,1455,1458,1460],{"class":217,"line":218},[215,1442,1067],{"class":221},[215,1444,1070],{"class":239},[215,1446,1447],{"class":1073}," setup",[215,1449,1450],{"class":1073}," lang",[215,1452,905],{"class":239},[215,1454,236],{"class":280},[215,1456,1457],{"class":284},"ts",[215,1459,236],{"class":280},[215,1461,1086],{"class":221},[215,1463,1464,1467,1470,1472,1475],{"class":217,"line":225},[215,1465,1466],{"class":239},"const",[215,1468,1469],{"class":221}," route ",[215,1471,905],{"class":239},[215,1473,1474],{"class":723}," useRoute",[215,1476,1477],{"class":221},"();\n",[215,1479,1480],{"class":217,"line":245},[215,1481,1482],{"emptyLinePlaceholder":656},"\n",[215,1484,1485,1487,1490,1492,1495,1497,1500,1503,1506,1509,1512,1515,1517,1520,1523],{"class":217,"line":261},[215,1486,1466],{"class":239},[215,1488,1489],{"class":221}," { data",[215,1491,133],{"class":239},[215,1493,1494],{"class":221}," post } ",[215,1496,905],{"class":239},[215,1498,1499],{"class":239}," await",[215,1501,1502],{"class":723}," useAsyncData",[215,1504,1505],{"class":221},"(",[215,1507,1508],{"class":284},"`post-",[215,1510,1511],{"class":239},"${",[215,1513,1514],{"class":221},"route.path",[215,1516,86],{"class":239},[215,1518,1519],{"class":284},"`",[215,1521,1522],{"class":221},", () ",[215,1524,1525],{"class":239},"=>\n",[215,1527,1528,1531,1533,1535,1538,1540,1543,1546,1549,1552],{"class":217,"line":267},[215,1529,1530],{"class":723},"  queryCollection",[215,1532,1505],{"class":221},[215,1534,236],{"class":280},[215,1536,1537],{"class":284},"blog",[215,1539,236],{"class":280},[215,1541,1542],{"class":221},").",[215,1544,1545],{"class":723},"path",[215,1547,1548],{"class":221},"(route.path).",[215,1550,1551],{"class":723},"first",[215,1553,1554],{"class":221},"()\n",[215,1556,1557],{"class":217,"line":293},[215,1558,1559],{"class":221},");\n",[215,1561,1562],{"class":217,"line":314},[215,1563,1482],{"emptyLinePlaceholder":656},[215,1565,1566,1568],{"class":217,"line":334},[215,1567,1427],{"class":723},[215,1569,1570],{"class":221},"([\n",[215,1572,1573,1576],{"class":217,"line":340},[215,1574,1575],{"class":723},"  defineArticle",[215,1577,771],{"class":221},[215,1579,1580,1583,1585],{"class":217,"line":345},[215,1581,1582],{"class":221},"    headline",[215,1584,133],{"class":239},[215,1586,1587],{"class":221}," post.value?.title,\n",[215,1589,1590,1593,1595],{"class":217,"line":365},[215,1591,1592],{"class":221},"    description",[215,1594,133],{"class":239},[215,1596,1597],{"class":221}," post.value?.description,\n",[215,1599,1600,1603,1605],{"class":217,"line":385},[215,1601,1602],{"class":221},"    image",[215,1604,133],{"class":239},[215,1606,1607],{"class":221}," post.value?.articleCover,\n",[215,1609,1610,1613,1615],{"class":217,"line":403},[215,1611,1612],{"class":221},"    datePublished",[215,1614,133],{"class":239},[215,1616,1617],{"class":221}," post.value?.publishedAt,\n",[215,1619,1620,1623,1625,1628,1631],{"class":217,"line":409},[215,1621,1622],{"class":221},"    dateModified",[215,1624,133],{"class":239},[215,1626,1627],{"class":221}," post.value?.updatedAt ",[215,1629,1630],{"class":239},"??",[215,1632,1633],{"class":221}," post.value?.publishedAt\n",[215,1635,1636],{"class":217,"line":415},[215,1637,1638],{"class":221},"  }),\n",[215,1640,1641,1644],{"class":217,"line":421},[215,1642,1643],{"class":723},"  defineBreadcrumb",[215,1645,771],{"class":221},[215,1647,1648,1651,1653],{"class":217,"line":1161},[215,1649,1650],{"class":221},"    itemListElement",[215,1652,133],{"class":239},[215,1654,258],{"class":221},[215,1656,1657,1660,1662,1664,1667,1669,1672,1674,1676,1679,1681],{"class":217,"line":1166},[215,1658,1659],{"class":221},"      { name",[215,1661,133],{"class":239},[215,1663,281],{"class":280},[215,1665,1666],{"class":284},"Home",[215,1668,236],{"class":280},[215,1670,1671],{"class":221},", item",[215,1673,133],{"class":239},[215,1675,281],{"class":280},[215,1677,1678],{"class":284},"\u002F",[215,1680,236],{"class":280},[215,1682,1683],{"class":221}," },\n",[215,1685,1687,1689,1691,1693,1696,1698,1700,1702,1704,1707,1709],{"class":217,"line":1686},19,[215,1688,1659],{"class":221},[215,1690,133],{"class":239},[215,1692,281],{"class":280},[215,1694,1695],{"class":284},"Blog",[215,1697,236],{"class":280},[215,1699,1671],{"class":221},[215,1701,133],{"class":239},[215,1703,281],{"class":280},[215,1705,1706],{"class":284},"\u002Fblog",[215,1708,236],{"class":280},[215,1710,1683],{"class":221},[215,1712,1714,1716,1718],{"class":217,"line":1713},20,[215,1715,1659],{"class":221},[215,1717,133],{"class":239},[215,1719,1720],{"class":221}," post.value?.title }\n",[215,1722,1724],{"class":217,"line":1723},21,[215,1725,412],{"class":221},[215,1727,1729],{"class":217,"line":1728},22,[215,1730,1731],{"class":221},"  })\n",[215,1733,1735],{"class":217,"line":1734},23,[215,1736,1737],{"class":221},"]);\n",[215,1739,1741,1743,1745],{"class":217,"line":1740},24,[215,1742,1169],{"class":221},[215,1744,1070],{"class":239},[215,1746,1086],{"class":221},[16,1748,1749,1750,1752,1753,1756,1757,1759],{},"Os campos vêm do front matter do artigo, então o ",[20,1751,1394],{}," só reflete a realidade se você atualizar o ",[20,1754,1755],{},"updatedAt"," quando mudar o conteúdo de verdade. O módulo converte as datas para ISO 8601 e transforma o caminho da imagem em URL absoluta a partir da ",[20,1758,273],{}," do site.",[625,1761,1763],{"id":1762},"configuração-global-do-site","Configuração global do site",[16,1765,1766,1767,1769,1770,133],{},"Autor e editora não precisam ser repetidos em cada página. O ",[20,1768,997],{}," usa a configuração do site e a identidade da organização, que você define uma vez no ",[20,1771,750],{},[115,1773,1776],{"className":1774,"code":1775,"language":1457,"meta":123,"style":123},"language-ts shiki shiki-themes dracula","export default defineNuxtConfig({\n  site: {\n    url: \"https:\u002F\u002Fseusite.com\",\n    name: \"Nome do Site\"\n  },\n  schemaOrg: {\n    identity: defineOrganization({\n      name: \"Nome do Site\",\n      logo: \"https:\u002F\u002Fseusite.com\u002Flogo-512.png\"\n    })\n  }\n});\n",[20,1777,1778,1788,1797,1813,1827,1832,1841,1853,1868,1882,1887,1891],{"__ignoreMap":123},[215,1779,1780,1782,1784,1786],{"class":217,"line":218},[215,1781,762],{"class":239},[215,1783,765],{"class":239},[215,1785,768],{"class":723},[215,1787,771],{"class":221},[215,1789,1790,1793,1795],{"class":217,"line":225},[215,1791,1792],{"class":221},"  site",[215,1794,133],{"class":239},[215,1796,242],{"class":221},[215,1798,1799,1802,1804,1806,1809,1811],{"class":217,"line":245},[215,1800,1801],{"class":221},"    url",[215,1803,133],{"class":239},[215,1805,281],{"class":280},[215,1807,1808],{"class":284},"https:\u002F\u002Fseusite.com",[215,1810,236],{"class":280},[215,1812,290],{"class":221},[215,1814,1815,1818,1820,1822,1825],{"class":217,"line":261},[215,1816,1817],{"class":221},"    name",[215,1819,133],{"class":239},[215,1821,281],{"class":280},[215,1823,1824],{"class":284},"Nome do Site",[215,1826,331],{"class":280},[215,1828,1829],{"class":217,"line":267},[215,1830,1831],{"class":221},"  },\n",[215,1833,1834,1837,1839],{"class":217,"line":293},[215,1835,1836],{"class":221},"  schemaOrg",[215,1838,133],{"class":239},[215,1840,242],{"class":221},[215,1842,1843,1846,1848,1851],{"class":217,"line":314},[215,1844,1845],{"class":221},"    identity",[215,1847,133],{"class":239},[215,1849,1850],{"class":723}," defineOrganization",[215,1852,771],{"class":221},[215,1854,1855,1858,1860,1862,1864,1866],{"class":217,"line":334},[215,1856,1857],{"class":221},"      name",[215,1859,133],{"class":239},[215,1861,281],{"class":280},[215,1863,1824],{"class":284},[215,1865,236],{"class":280},[215,1867,290],{"class":221},[215,1869,1870,1873,1875,1877,1880],{"class":217,"line":340},[215,1871,1872],{"class":221},"      logo",[215,1874,133],{"class":239},[215,1876,281],{"class":280},[215,1878,1879],{"class":284},"https:\u002F\u002Fseusite.com\u002Flogo-512.png",[215,1881,331],{"class":280},[215,1883,1884],{"class":217,"line":345},[215,1885,1886],{"class":221},"    })\n",[215,1888,1889],{"class":217,"line":365},[215,1890,418],{"class":221},[215,1892,1893],{"class":217,"line":385},[215,1894,809],{"class":221},[16,1896,1897,1898,1900,1901,1903,1904,1405,1906,62,1908,1910,1911,638],{},"Se o ",[20,1899,1368],{}," não tiver ",[20,1902,1416],{},", o módulo usa essa identidade. Para o autor, passe um ",[20,1905,1404],{},[20,1907,1408],{},[20,1909,273],{}," no ",[20,1912,1913],{},"defineArticle",[625,1915,1917],{"id":1916},"como-as-entidades-se-ligam","Como as entidades se ligam",[16,1919,1920,1921,1924,1925,1928,1929,1928,1932,1928,1934,62,1936,1939,1940,1943,1944,1946,1947,1949,1950,1952,1953,1955,1956,62,1958,1960],{},"O módulo monta um único ",[20,1922,1923],{},"@graph"," em que ",[20,1926,1927],{},"WebSite",", ",[20,1930,1931],{},"WebPage",[20,1933,1368],{},[20,1935,1404],{},[20,1937,1938],{},"Organization"," se referenciam por ",[20,1941,1942],{},"@id",". O ",[20,1945,1368],{}," aponta para a ",[20,1948,1931],{}," atual, e o ",[20,1951,1400],{}," e o ",[20,1954,1416],{}," apontam para os nós de ",[20,1957,1404],{},[20,1959,1938],{},". Essa ligação diz ao Google que o artigo pertence àquele autor e àquela marca.",[625,1962,1964],{"id":1963},"e-o-faqpage","E o FAQPage?",[16,1966,1967,1968,1970],{},"Se a página tem uma seção de perguntas e respostas visível, ainda vale marcar com ",[20,1969,1054],{},". Só não espere o dropdown na busca. O exemplo abaixo gera o mesmo JSON-LD que vimos antes:",[115,1972,1974],{"className":1434,"code":1973,"language":1436,"meta":123,"style":123},"\u003Cscript setup>\nuseSchemaOrg([\n  defineWebPage({\n    \"@type\": \"FAQPage\"\n  }),\n  defineQuestion({\n    name: \"O que é um Gerador de CPF?\",\n    acceptedAnswer:\n      \"Um Gerador de CPF é uma ferramenta que gera números de CPF válidos, sendo útil para testes e desenvolvimento de sistemas. Ele aplica o algoritmo oficial de validação do CPF para assegurar que os números criados estejam formatados corretamente.\"\n  }),\n  defineQuestion({\n    name: \"Como funciona o Gerador de CPF?\",\n    acceptedAnswer:\n      \"O Gerador de CPF cria aleatoriamente os nove primeiros dígitos do CPF e, em seguida, calcula os dois dígitos verificadores usando o algoritmo de validação oficial, assegurando que o número gerado seja válido.\"\n  })\n]);\n\u003C\u002Fscript>\n",[20,1975,1976,1986,1992,1999,2016,2020,2027,2042,2050,2060,2064,2070,2085,2091,2100,2104,2108],{"__ignoreMap":123},[215,1977,1978,1980,1982,1984],{"class":217,"line":218},[215,1979,1067],{"class":221},[215,1981,1070],{"class":239},[215,1983,1447],{"class":1073},[215,1985,1086],{"class":221},[215,1987,1988,1990],{"class":217,"line":225},[215,1989,1427],{"class":723},[215,1991,1570],{"class":221},[215,1993,1994,1997],{"class":217,"line":245},[215,1995,1996],{"class":723},"  defineWebPage",[215,1998,771],{"class":221},[215,2000,2001,2003,2006,2008,2010,2012,2014],{"class":217,"line":261},[215,2002,248],{"class":280},[215,2004,2005],{"class":284},"@type",[215,2007,236],{"class":280},[215,2009,133],{"class":239},[215,2011,281],{"class":280},[215,2013,1054],{"class":284},[215,2015,331],{"class":280},[215,2017,2018],{"class":217,"line":267},[215,2019,1638],{"class":221},[215,2021,2022,2025],{"class":217,"line":293},[215,2023,2024],{"class":723},"  defineQuestion",[215,2026,771],{"class":221},[215,2028,2029,2031,2033,2035,2038,2040],{"class":217,"line":314},[215,2030,1817],{"class":221},[215,2032,133],{"class":239},[215,2034,281],{"class":280},[215,2036,2037],{"class":284},"O que é um Gerador de CPF?",[215,2039,236],{"class":280},[215,2041,290],{"class":221},[215,2043,2044,2047],{"class":217,"line":334},[215,2045,2046],{"class":221},"    acceptedAnswer",[215,2048,2049],{"class":239},":\n",[215,2051,2052,2055,2058],{"class":217,"line":340},[215,2053,2054],{"class":280},"      \"",[215,2056,2057],{"class":284},"Um Gerador de CPF é uma ferramenta que gera números de CPF válidos, sendo útil para testes e desenvolvimento de sistemas. Ele aplica o algoritmo oficial de validação do CPF para assegurar que os números criados estejam formatados corretamente.",[215,2059,331],{"class":280},[215,2061,2062],{"class":217,"line":345},[215,2063,1638],{"class":221},[215,2065,2066,2068],{"class":217,"line":365},[215,2067,2024],{"class":723},[215,2069,771],{"class":221},[215,2071,2072,2074,2076,2078,2081,2083],{"class":217,"line":385},[215,2073,1817],{"class":221},[215,2075,133],{"class":239},[215,2077,281],{"class":280},[215,2079,2080],{"class":284},"Como funciona o Gerador de CPF?",[215,2082,236],{"class":280},[215,2084,290],{"class":221},[215,2086,2087,2089],{"class":217,"line":403},[215,2088,2046],{"class":221},[215,2090,2049],{"class":239},[215,2092,2093,2095,2098],{"class":217,"line":409},[215,2094,2054],{"class":280},[215,2096,2097],{"class":284},"O Gerador de CPF cria aleatoriamente os nove primeiros dígitos do CPF e, em seguida, calcula os dois dígitos verificadores usando o algoritmo de validação oficial, assegurando que o número gerado seja válido.",[215,2099,331],{"class":280},[215,2101,2102],{"class":217,"line":415},[215,2103,1731],{"class":221},[215,2105,2106],{"class":217,"line":421},[215,2107,1737],{"class":221},[215,2109,2110,2112,2114],{"class":217,"line":1161},[215,2111,1169],{"class":221},[215,2113,1070],{"class":239},[215,2115,1086],{"class":221},[16,2117,2118],{},"Até maio de 2026, essa marcação podia render um resultado rico como o da imagem abaixo:",[105,2120],{"alt":2121,"height":2122,"src":2123,"width":2124},"exemplo de resultado rico de FAQ no Google para a pergunta sobre o que é Nuxt.JS, exibido antes de o Google encerrar o recurso",351,"\u002Fimages\u002Fblog\u002Fseo\u002Fstructured-data\u002Ffaq-rich-result-example-1.webp",681,[44,2126,2128],{"id":2127},"como-conferir-se-o-json-ld-está-no-html","Como conferir se o JSON-LD está no HTML",[16,2130,2131,2132,1943,2135,2137],{},"Abra a página no navegador e use \"Ver código-fonte da página\", não \"Inspecionar elemento\". Procure por um bloco ",[20,2133,2134],{},"\u003Cscript type=\"application\u002Fld+json\">",[20,2136,997],{}," injeta o JSON-LD durante a renderização no servidor, então ele precisa estar no HTML bruto. Se ele só aparecer depois da hidratação, revise a configuração de renderização da página.",[44,2139,2141],{"id":2140},"como-validar-os-dados-estruturados","Como validar os dados estruturados",[16,2143,2144],{},"Depois de publicar, valide a página com estas ferramentas:",[1236,2146,2147,2155,2163],{},[78,2148,2149,2154],{},[619,2150,2153],{"href":2151,"rel":2152},"https:\u002F\u002Fsearch.google.com\u002Ftest\u002Frich-results",[636],"Teste de Resultados Ricos"," do Google: mostra os itens detectados na URL e lista erros e avisos.",[78,2156,2157,2162],{},[619,2158,2161],{"href":2159,"rel":2160},"https:\u002F\u002Fvalidator.schema.org",[636],"Validador do Schema Markup",": confere a sintaxe do JSON-LD segundo o schema.org, independente de o Google exibir um resultado rico.",[78,2164,2165],{},"Relatório de dados estruturados no Search Console: agrupa os erros e avisos do site inteiro.",[16,2167,2168],{},"Os problemas mais comuns em blogs são estes:",[75,2170,2171,2182,2185,2194],{},[78,2172,2173,2174,30,2176,2178,2179,2181],{},"Propriedade recomendada ausente, como ",[20,2175,1385],{},[20,2177,1400],{},". Não é erro crítico em ",[20,2180,1368],{},", mas limita o que o Google pode usar.",[78,2183,2184],{},"Imagem pequena ou inacessível. Para o Discover, a imagem precisa ter pelo menos 1200 px de largura e a URL não pode estar bloqueada para o rastreador.",[78,2186,2187,2188,30,2191,638],{},"Data em formato inválido. Use ISO 8601, como ",[20,2189,2190],{},"2026-10-03",[20,2192,2193],{},"2026-10-03T14:30:00-03:00",[78,2195,2196],{},"Marcação diferente do conteúdo visível. Título, autor, data e imagem do schema devem ser os mesmos que o usuário vê na página.",[44,2198,2200],{"id":2199},"como-fazer-o-google-indexar-o-seu-artigo","Como fazer o Google indexar o seu artigo",[16,2202,2203],{},"Dados estruturados corretos não adiantam se o Google não indexar a página. O fluxo para um artigo novo é este:",[1236,2205,2206,2217,2220],{},[78,2207,2208,2209,2212,2213,2216],{},"Confirme que a URL está no ",[20,2210,2211],{},"sitemap.xml"," e que o sitemap está enviado no Search Console. No Nuxt, o módulo ",[20,2214,2215],{},"@nuxtjs\u002Fsitemap"," gera esse arquivo.",[78,2218,2219],{},"Cole a URL no campo \"Inspecionar qualquer URL\" do Search Console.",[78,2221,2222],{},"Clique em \"Solicitar indexação\".",[16,2224,2225],{},"O pedido de indexação coloca a URL numa fila de rastreamento, mas não garante a indexação, e o Google não informa o prazo. O Google também não publica o limite diário de pedidos. Quando você o atinge, o botão fica indisponível até o dia seguinte, então use só para URLs importantes e não reenvie a mesma página várias vezes.",[16,2227,2228],{},"Para as demais páginas, o que ajuda o Google a descobri-las são o sitemap e os links internos. Uma página sem nenhum link vindo de outras páginas do site tende a ser descoberta mais devagar. Crie links para os artigos novos a partir da listagem do blog e de posts relacionados.",[625,2230,2232],{"id":2231},"descoberta-mas-não-indexada","Descoberta, mas não indexada",[16,2234,2235],{},"No relatório de indexação de páginas, dois status confundem muita gente:",[75,2237,2238,2241],{},[78,2239,2240],{},"Descoberta, mas não indexada no momento: o Google conhece a URL, mas ainda não a rastreou. Reforce os links internos, confira se a URL está no sitemap e verifique se o servidor não está devolvendo erros 5xx ou respondendo devagar.",[78,2242,2243],{},"Rastreada, mas não indexada no momento: o Google já leu a página e decidiu não indexá-la por enquanto. Costuma ser uma questão de qualidade: conteúdo muito parecido com outro já indexado, pouco aprofundado ou fora da intenção de busca. Melhore o conteúdo e só depois peça a indexação de novo.",[44,2245,2247],{"id":2246},"como-aparecer-no-google-discover","Como aparecer no Google Discover",[16,2249,2250],{},"O Discover é o feed de conteúdo do app do Google e de navegadores móveis. Ele mostra conteúdo conforme os interesses de cada usuário, sem depender de uma pesquisa. Segundo a documentação do Google, para ser elegível o conteúdo precisa:",[75,2252,2253,2256,2263],{},[78,2254,2255],{},"Estar indexado e seguir as políticas de conteúdo do Discover, sem clickbait nem sensacionalismo.",[78,2257,2258,2259,2262],{},"Ter uma imagem com pelo menos 1200 px de largura e mais de 300.000 pixels no total, na proporção 16x9, e a meta robots ",[20,2260,2261],{},"max-image-preview:large"," habilitada.",[78,2264,2265],{},"Ser um conteúdo original, útil e feito para pessoas.",[16,2267,2268,2269,2271,2272,2275],{},"Nenhum dado estruturado é obrigatório para o Discover. Mas o Google usa o ",[20,2270,1385],{}," do schema.org e a meta tag ",[20,2273,2274],{},"og:image"," para escolher a imagem do card, então vale manter as duas apontando para uma imagem boa e não usar o logo do site no lugar.",[16,2277,2278],{},"No Nuxt, a meta robots pode ser definida na própria página:",[115,2280,2282],{"className":1434,"code":2281,"language":1436,"meta":123,"style":123},"\u003Cscript setup>\nuseSeoMeta({\n  robots: \"index, follow, max-image-preview:large\"\n});\n\u003C\u002Fscript>\n",[20,2283,2284,2294,2301,2315,2319],{"__ignoreMap":123},[215,2285,2286,2288,2290,2292],{"class":217,"line":218},[215,2287,1067],{"class":221},[215,2289,1070],{"class":239},[215,2291,1447],{"class":1073},[215,2293,1086],{"class":221},[215,2295,2296,2299],{"class":217,"line":225},[215,2297,2298],{"class":723},"useSeoMeta",[215,2300,771],{"class":221},[215,2302,2303,2306,2308,2310,2313],{"class":217,"line":245},[215,2304,2305],{"class":221},"  robots",[215,2307,133],{"class":239},[215,2309,281],{"class":280},[215,2311,2312],{"class":284},"index, follow, max-image-preview:large",[215,2314,331],{"class":280},[215,2316,2317],{"class":217,"line":261},[215,2318,809],{"class":221},[215,2320,2321,2323,2325],{"class":217,"line":267},[215,2322,1169],{"class":221},[215,2324,1070],{"class":239},[215,2326,1086],{"class":221},[16,2328,2329],{},"O desempenho no Discover aparece no relatório próprio do Search Console. Se o seu site não tiver dados lá, o conteúdo ainda não está sendo exibido no feed.",[44,2331,2333],{"id":2332},"avisando-o-bing-com-indexnow","Avisando o Bing com IndexNow",[16,2335,2336,2337,2340,2341,638],{},"O IndexNow é um protocolo adotado pelo Bing e por outros buscadores para avisar quando uma URL é criada, atualizada ou removida. Você gera uma chave, publica um arquivo ",[20,2338,2339],{},".txt"," com ela na raiz do domínio e faz uma requisição ao endpoint do IndexNow sempre que publicar ou atualizar um artigo. Isso avisa o buscador de que existe algo novo, mas não garante indexação imediata. O guia oficial está em ",[619,2342,2345],{"href":2343,"rel":2344},"https:\u002F\u002Fwww.bing.com\u002Findexnow\u002Fgetstarted",[636],"bing.com\u002Findexnow\u002Fgetstarted",[44,2347,2349],{"id":2348},"perguntas-frequentes","Perguntas frequentes",[625,2351,2353],{"id":2352},"preciso-de-dados-estruturados-para-o-meu-blog-rankear","Preciso de dados estruturados para o meu blog rankear?",[16,2355,2356,2357,2359],{},"Não. Eles não são obrigatórios para rankear, mas ajudam o Google a entender melhor os seus artigos. Para um blog, o ponto de partida é ",[20,2358,1368],{}," com autor e imagem, mais o breadcrumb.",[625,2361,2363],{"id":2362},"vale-a-pena-manter-faqpage-se-ele-não-gera-mais-resultado-rico","Vale a pena manter FAQPage se ele não gera mais resultado rico?",[16,2365,2366],{},"Vale se a página tem uma seção de perguntas e respostas visível para o usuário. Nesse caso a marcação serve para descrever o conteúdo.",[625,2368,2370],{"id":2369},"dados-estruturados-garantem-que-meu-post-apareça-no-discover","Dados estruturados garantem que meu post apareça no Discover?",[16,2372,2373],{},"Não. O Discover não exige dados estruturados. O que pesa é o conteúdo estar indexado, ter uma imagem grande e seguir as políticas de conteúdo.",[625,2375,2377],{"id":2376},"meu-artigo-aparece-como-rastreada-mas-não-indexada-o-que-fazer","Meu artigo aparece como \"Rastreada, mas não indexada\". O que fazer?",[16,2379,2380],{},"Esse status costuma apontar para a qualidade ou a originalidade da página. Melhore o conteúdo, reforce os links internos e peça a indexação de novo depois.",[625,2382,2384],{"id":2383},"como-sei-que-o-google-está-lendo-o-meu-json-ld","Como sei que o Google está lendo o meu JSON-LD?",[16,2386,2387],{},"Confira o HTML bruto em \"Ver código-fonte da página\", teste a URL no Teste de Resultados Ricos e acompanhe o relatório de dados estruturados no Search Console.",[44,2389,628],{"id":627},[16,2391,2392,2393,2395,2396,2398],{},"Em cada artigo novo, marque o ",[20,2394,1368],{}," com autor, imagem e datas, confira o JSON-LD no código-fonte, valide no Teste de Resultados Ricos e acompanhe o relatório de indexação no Search Console. Atualize o ",[20,2397,1755],{}," só quando o conteúdo mudar de verdade e, se o Bing importa para você, configure o IndexNow.",[16,2400,2401,2402,2407,2408,2413,2414,638],{},"Se você quiser se aprofundar, acesse a ",[619,2403,2406],{"href":2404,"rel":2405},"https:\u002F\u002Fnuxtseo.com\u002Fdocs\u002Fschema-org\u002Fgetting-started\u002Fintroduction",[636],"documentação completa do módulo",", a ",[619,2409,2412],{"href":2410,"rel":2411},"https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Farticle",[636],"documentação do Google sobre dados estruturados de Article"," e a ",[619,2415,2418],{"href":2416,"rel":2417},"https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fgoogle-discover",[636],"documentação do Google Discover",[640,2420,2421],{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sY_PY, html code.shiki .sY_PY{--shiki-default:#50FA7B;--shiki-default-font-style:italic}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}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 pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}",{"title":123,"searchDepth":225,"depth":225,"links":2423},[2424,2425,2426,2427,2428,2429,2430,2435,2436,2437,2440,2441,2442,2449],{"id":1001,"depth":225,"text":1002},{"id":1015,"depth":225,"text":1016},{"id":1041,"depth":225,"text":1042},{"id":1176,"depth":225,"text":1177},{"id":1197,"depth":225,"text":1198},{"id":1218,"depth":225,"text":1219},{"id":1361,"depth":225,"text":1362,"children":2431},[2432,2433,2434],{"id":1762,"depth":245,"text":1763},{"id":1916,"depth":245,"text":1917},{"id":1963,"depth":245,"text":1964},{"id":2127,"depth":225,"text":2128},{"id":2140,"depth":225,"text":2141},{"id":2199,"depth":225,"text":2200,"children":2438},[2439],{"id":2231,"depth":245,"text":2232},{"id":2246,"depth":225,"text":2247},{"id":2332,"depth":225,"text":2333},{"id":2348,"depth":225,"text":2349,"children":2443},[2444,2445,2446,2447,2448],{"id":2352,"depth":245,"text":2353},{"id":2362,"depth":245,"text":2363},{"id":2369,"depth":245,"text":2370},{"id":2376,"depth":245,"text":2377},{"id":2383,"depth":245,"text":2384},{"id":627,"depth":225,"text":628},[650,651,2451],"seo","Como adicionar dados estruturados no Nuxt.js com nuxt-schema-org: Article, breadcrumb, validação, indexação no Search Console e Google Discover, com exemplos.","how-to-create-structured-data-with-nuxtjs",{},"\u002Fblog\u002Fcomo-criar-dados-estruturados-com-nuxtjs","como-criar-dados-estruturados-com-nuxtjs","2025-01-22",{"title":988,"description":2452},{"loc":2455,"images":2460,"lastmod":664},[2461,2462],{"loc":1037},{"loc":2123},"blog\u002Fcomo-criar-dados-estruturados-com-nuxtjs",2117,"BGjviT8apRyvkm1BK6McXSTpscdgbzA3H9QMGzuI_fw",{"id":2467,"title":2468,"articleCover":2469,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":2470,"categoriesIds":4198,"description":4199,"enAlternateSlug":4200,"extension":654,"meta":4201,"navigation":656,"path":3972,"ptBrAlternateSlug":2637,"publishedAt":4202,"seo":4203,"sitemap":4204,"stem":4205,"updatedAt":2190,"wordCount":4206,"__hash__":4207},"blog\u002Fblog\u002Fcomo-funciona-o-sistema-de-rotas-no-nuxt.md","Rotas no Nuxt: dinâmicas, aninhadas e catch-all","\u002Fimages\u002Fblog\u002Fnuxt\u002Fsistema-de-rotas-no-nuxtjs-thumb.webp",{"type":13,"value":2471,"toc":4173},[2472,2479,2483,2493,2530,2555,2558,2562,2565,2571,2585,2589,2592,2628,2639,2642,2945,2972,2979,2983,2997,3095,3100,3104,3107,3110,3116,3123,3264,3270,3274,3285,3306,3310,3317,3350,3353,3357,3370,3449,3452,3456,3466,3472,3495,3613,3716,3723,3727,3730,3808,3819,3823,3832,4024,4044,4048,4074,4076,4080,4093,4097,4106,4110,4123,4127,4139,4143,4160,4162,4170],[16,2473,2474,2475,2478],{},"O Nuxt transforma os arquivos da pasta ",[20,2476,2477],{},"pages"," em rotas. Este artigo mostra como esse mapeamento funciona no Nuxt 4, com slugs dinâmicos, rotas aninhadas, opcionais e catch-all, parâmetros de consulta e as escolhas de roteamento que afetam o SEO de um site real.",[44,2480,2482],{"id":2481},"como-funciona-o-roteamento-baseado-em-arquivos-do-nuxt","Como funciona o roteamento baseado em arquivos do Nuxt",[16,2484,2485,2486,2489,2490,2492],{},"Todo arquivo ",[20,2487,2488],{},".vue"," dentro da pasta ",[20,2491,2477],{}," vira uma página, e a URL vem do caminho do arquivo:",[135,2494,2495,2505],{},[138,2496,2497],{},[141,2498,2499,2502],{},[144,2500,2501],{},"Arquivo",[144,2503,2504],{},"Rota",[154,2506,2507,2514,2522],{},[141,2508,2509,2512],{},[159,2510,2511],{},"app\u002Fpages\u002Findex.vue",[159,2513,1678],{},[141,2515,2516,2519],{},[159,2517,2518],{},"app\u002Fpages\u002Flinks.vue",[159,2520,2521],{},"\u002Flinks",[141,2523,2524,2527],{},[159,2525,2526],{},"app\u002Fpages\u002Ftools\u002Fcpf-generator.vue",[159,2528,2529],{},"\u002Ftools\u002Fcpf-generator",[16,2531,2532,2533,2536,2537,2540,2541,2544,2545,2548,2549,2552,2553,638],{},"No Nuxt 4, projetos novos guardam o código da aplicação dentro de um diretório ",[20,2534,2535],{},"app\u002F",". As páginas ficam em ",[20,2538,2539],{},"app\u002Fpages\u002F",", os layouts em ",[20,2542,2543],{},"app\u002Flayouts\u002F"," e os middlewares de rota em ",[20,2546,2547],{},"app\u002Fmiddleware\u002F",". Se você migrou do Nuxt 3 e ainda tem uma pasta ",[20,2550,2551],{},"pages\u002F"," na raiz, ela continua funcionando: o Nuxt detecta a estrutura antiga e não força a nova. Os exemplos abaixo usam os caminhos com ",[20,2554,2535],{},[16,2556,2557],{},"O sistema usa o Vue Router por baixo. O Nuxt gera a lista de rotas a partir dos seus arquivos, então raramente você precisa escrever uma configuração de roteador à mão.",[44,2559,2561],{"id":2560},"rotas-estáticas","Rotas estáticas",[16,2563,2564],{},"Uma rota estática é uma página cuja URL nunca muda, como uma página institucional ou uma ferramenta:",[115,2566,2569],{"className":2567,"code":2568,"language":120,"meta":123},[118],"app\u002Fpages\u002F\n├── index.vue              -> \u002F\n├── about.vue              -> \u002Fabout\n├── privacy-policy.vue     -> \u002Fprivacy-policy\n└── tools\u002F\n    └── cpf-generator.vue  -> \u002Ftools\u002Fcpf-generator\n",[20,2570,2568],{"__ignoreMap":123},[16,2572,2573,2574,2577,2578,62,2581,2584],{},"Se a URL base for ",[20,2575,2576],{},"https:\u002F\u002Flojaonline.com",", o Nuxt serve ",[20,2579,2580],{},"https:\u002F\u002Flojaonline.com\u002Fabout",[20,2582,2583],{},"https:\u002F\u002Flojaonline.com\u002Fprivacy-policy"," a partir desses arquivos.",[44,2586,2588],{"id":2587},"rotas-dinâmicas","Rotas dinâmicas",[16,2590,2591],{},"Quando muitas URLs usam o mesmo modelo de página, como os posts de um blog, você cria um único arquivo com o nome do parâmetro entre colchetes. As duas estruturas abaixo geram a mesma rota:",[135,2593,2594,2605],{},[138,2595,2596],{},[141,2597,2598,2601,2603],{},[144,2599,2600],{},"Local do parâmetro",[144,2602,2501],{},[144,2604,2504],{},[154,2606,2607,2618],{},[141,2608,2609,2612,2615],{},[159,2610,2611],{},"Nome do arquivo",[159,2613,2614],{},"app\u002Fpages\u002Fblog\u002F[slug].vue",[159,2616,2617],{},"\u002Fblog\u002Fmeu-post",[141,2619,2620,2623,2626],{},[159,2621,2622],{},"Nome do diretório",[159,2624,2625],{},"app\u002Fpages\u002Fblog\u002F[slug]\u002Findex.vue",[159,2627,2617],{},[16,2629,2630,2631,2634,2635,2638],{},"O nome entre colchetes é a chave que você lê em ",[20,2632,2633],{},"route.params",". O slug é a parte legível da URL que identifica um recurso, geralmente derivada do título, como ",[20,2636,2637],{},"como-funciona-o-sistema-de-rotas-no-nuxt",". Ele não é um id de banco de dados: nada garante que seja único nem que nunca mude, então quem decide como resolvê-lo é a sua aplicação.",[16,2640,2641],{},"Aqui está uma página de post que carrega o conteúdo do Nuxt Content usando o slug:",[115,2643,2645],{"className":1434,"code":2644,"language":1436,"meta":123,"style":123},"\u003Cscript setup lang=\"ts\">\nconst route = useRoute();\n\nconst slug = computed(() => String(route.params.slug));\n\nconst { data: post } = await useAsyncData(\n  () => `post-${slug.value}`,\n  () => queryCollection(\"blog\").path(`\u002Fblog\u002F${slug.value}`).first()\n);\n\nif (!post.value) {\n  throw createError({\n    status: 404,\n    statusText: \"Post não encontrado\",\n    fatal: true\n  });\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Carticle v-if=\"post\">\n    \u003Ch1>{{ post.title }}\u003C\u002Fh1>\n  \u003C\u002Farticle>\n\u003C\u002Ftemplate>\n",[20,2646,2647,2667,2679,2683,2707,2711,2730,2751,2791,2795,2799,2813,2823,2835,2851,2861,2866,2870,2878,2882,2891,2913,2928,2937],{"__ignoreMap":123},[215,2648,2649,2651,2653,2655,2657,2659,2661,2663,2665],{"class":217,"line":218},[215,2650,1067],{"class":221},[215,2652,1070],{"class":239},[215,2654,1447],{"class":1073},[215,2656,1450],{"class":1073},[215,2658,905],{"class":239},[215,2660,236],{"class":280},[215,2662,1457],{"class":284},[215,2664,236],{"class":280},[215,2666,1086],{"class":221},[215,2668,2669,2671,2673,2675,2677],{"class":217,"line":225},[215,2670,1466],{"class":239},[215,2672,1469],{"class":221},[215,2674,905],{"class":239},[215,2676,1474],{"class":723},[215,2678,1477],{"class":221},[215,2680,2681],{"class":217,"line":245},[215,2682,1482],{"emptyLinePlaceholder":656},[215,2684,2685,2687,2690,2692,2695,2698,2701,2704],{"class":217,"line":261},[215,2686,1466],{"class":239},[215,2688,2689],{"class":221}," slug ",[215,2691,905],{"class":239},[215,2693,2694],{"class":723}," computed",[215,2696,2697],{"class":221},"(() ",[215,2699,2700],{"class":239},"=>",[215,2702,2703],{"class":723}," String",[215,2705,2706],{"class":221},"(route.params.slug));\n",[215,2708,2709],{"class":217,"line":267},[215,2710,1482],{"emptyLinePlaceholder":656},[215,2712,2713,2715,2717,2719,2721,2723,2725,2727],{"class":217,"line":293},[215,2714,1466],{"class":239},[215,2716,1489],{"class":221},[215,2718,133],{"class":239},[215,2720,1494],{"class":221},[215,2722,905],{"class":239},[215,2724,1499],{"class":239},[215,2726,1502],{"class":723},[215,2728,2729],{"class":221},"(\n",[215,2731,2732,2735,2737,2740,2742,2745,2747,2749],{"class":217,"line":314},[215,2733,2734],{"class":221},"  () ",[215,2736,2700],{"class":239},[215,2738,2739],{"class":284}," `post-",[215,2741,1511],{"class":239},[215,2743,2744],{"class":221},"slug.value",[215,2746,86],{"class":239},[215,2748,1519],{"class":284},[215,2750,290],{"class":221},[215,2752,2753,2755,2757,2760,2762,2764,2766,2768,2770,2772,2774,2777,2779,2781,2783,2785,2787,2789],{"class":217,"line":334},[215,2754,2734],{"class":221},[215,2756,2700],{"class":239},[215,2758,2759],{"class":723}," queryCollection",[215,2761,1505],{"class":221},[215,2763,236],{"class":280},[215,2765,1537],{"class":284},[215,2767,236],{"class":280},[215,2769,1542],{"class":221},[215,2771,1545],{"class":723},[215,2773,1505],{"class":221},[215,2775,2776],{"class":284},"`\u002Fblog\u002F",[215,2778,1511],{"class":239},[215,2780,2744],{"class":221},[215,2782,86],{"class":239},[215,2784,1519],{"class":284},[215,2786,1542],{"class":221},[215,2788,1551],{"class":723},[215,2790,1554],{"class":221},[215,2792,2793],{"class":217,"line":340},[215,2794,1559],{"class":221},[215,2796,2797],{"class":217,"line":345},[215,2798,1482],{"emptyLinePlaceholder":656},[215,2800,2801,2804,2807,2810],{"class":217,"line":365},[215,2802,2803],{"class":239},"if",[215,2805,2806],{"class":221}," (",[215,2808,2809],{"class":239},"!",[215,2811,2812],{"class":221},"post.value) {\n",[215,2814,2815,2818,2821],{"class":217,"line":385},[215,2816,2817],{"class":239},"  throw",[215,2819,2820],{"class":723}," createError",[215,2822,771],{"class":221},[215,2824,2825,2828,2830,2833],{"class":217,"line":403},[215,2826,2827],{"class":221},"    status",[215,2829,133],{"class":239},[215,2831,2832],{"class":901}," 404",[215,2834,290],{"class":221},[215,2836,2837,2840,2842,2844,2847,2849],{"class":217,"line":409},[215,2838,2839],{"class":221},"    statusText",[215,2841,133],{"class":239},[215,2843,281],{"class":280},[215,2845,2846],{"class":284},"Post não encontrado",[215,2848,236],{"class":280},[215,2850,290],{"class":221},[215,2852,2853,2856,2858],{"class":217,"line":415},[215,2854,2855],{"class":221},"    fatal",[215,2857,133],{"class":239},[215,2859,2860],{"class":901}," true\n",[215,2862,2863],{"class":217,"line":421},[215,2864,2865],{"class":221},"  });\n",[215,2867,2868],{"class":217,"line":1161},[215,2869,424],{"class":221},[215,2871,2872,2874,2876],{"class":217,"line":1166},[215,2873,1169],{"class":221},[215,2875,1070],{"class":239},[215,2877,1086],{"class":221},[215,2879,2880],{"class":217,"line":1686},[215,2881,1482],{"emptyLinePlaceholder":656},[215,2883,2884,2886,2889],{"class":217,"line":1713},[215,2885,1067],{"class":221},[215,2887,2888],{"class":239},"template",[215,2890,1086],{"class":221},[215,2892,2893,2896,2899,2902,2904,2906,2909,2911],{"class":217,"line":1723},[215,2894,2895],{"class":221},"  \u003C",[215,2897,2898],{"class":239},"article",[215,2900,2901],{"class":1073}," v-if",[215,2903,905],{"class":239},[215,2905,236],{"class":280},[215,2907,2908],{"class":284},"post",[215,2910,236],{"class":280},[215,2912,1086],{"class":221},[215,2914,2915,2918,2921,2924,2926],{"class":217,"line":1728},[215,2916,2917],{"class":221},"    \u003C",[215,2919,2920],{"class":239},"h1",[215,2922,2923],{"class":221},">{{ post.title }}\u003C\u002F",[215,2925,2920],{"class":239},[215,2927,1086],{"class":221},[215,2929,2930,2933,2935],{"class":217,"line":1734},[215,2931,2932],{"class":221},"  \u003C\u002F",[215,2934,2898],{"class":239},[215,2936,1086],{"class":221},[215,2938,2939,2941,2943],{"class":217,"line":1740},[215,2940,1169],{"class":221},[215,2942,2888],{"class":239},[215,2944,1086],{"class":221},[16,2946,2947,2948,2951,2952,2955,2956,2959,2960,2963,2964,2967,2968,2971],{},"O slug é um ",[20,2949,2950],{},"computed",", e a chave do ",[20,2953,2954],{},"useAsyncData"," é uma função que depende dele. Quando o usuário vai de ",[20,2957,2958],{},"\u002Fblog\u002Fa"," para ",[20,2961,2962],{},"\u002Fblog\u002Fb",", o Vue Router pode manter o mesmo componente de página montado, então o código do ",[20,2965,2966],{},"\u003Cscript setup>"," não necessariamente roda de novo. Com uma chave reativa, o Nuxt busca o novo post quando o slug muda. Se você ler ",[20,2969,2970],{},"route.params.slug"," uma vez e guardar em uma variável comum, a URL muda, mas a página continua mostrando o post anterior.",[16,2973,2974,2975,2978],{},"Um slug que não existe também precisa de um 404. Sem a verificação com ",[20,2976,2977],{},"createError",", ele renderiza uma página vazia com status 200, e os buscadores enxergam uma URL válida, porém vazia.",[625,2980,2982],{"id":2981},"validando-o-parâmetro-antes-de-renderizar","Validando o parâmetro antes de renderizar",[16,2984,18,2985,2988,2989,2992,2993,2996],{},[20,2986,2987],{},"definePageMeta"," aceita uma função ",[20,2990,2991],{},"validate"," que decide se a rota pode ser renderizada por aquela página. Se ela retornar ",[20,2994,2995],{},"false"," e nenhuma outra rota combinar, o Nuxt responde com 404:",[115,2998,3000],{"className":1434,"code":2999,"language":1436,"meta":123,"style":123},"\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n  validate: (route) => \u002F^[a-z0-9-]+$\u002F.test(String(route.params.slug))\n});\n\u003C\u002Fscript>\n",[20,3001,3002,3022,3028,3083,3087],{"__ignoreMap":123},[215,3003,3004,3006,3008,3010,3012,3014,3016,3018,3020],{"class":217,"line":218},[215,3005,1067],{"class":221},[215,3007,1070],{"class":239},[215,3009,1447],{"class":1073},[215,3011,1450],{"class":1073},[215,3013,905],{"class":239},[215,3015,236],{"class":280},[215,3017,1457],{"class":284},[215,3019,236],{"class":280},[215,3021,1086],{"class":221},[215,3023,3024,3026],{"class":217,"line":225},[215,3025,2987],{"class":723},[215,3027,771],{"class":221},[215,3029,3030,3033,3035,3037,3041,3044,3046,3050,3053,3056,3059,3062,3065,3068,3070,3072,3075,3077,3080],{"class":217,"line":245},[215,3031,3032],{"class":723},"  validate",[215,3034,133],{"class":239},[215,3036,2806],{"class":221},[215,3038,3040],{"class":3039},"sGEwX","route",[215,3042,3043],{"class":221},") ",[215,3045,2700],{"class":239},[215,3047,3049],{"class":3048},"s-doY"," \u002F",[215,3051,3052],{"class":239},"^",[215,3054,3055],{"class":232},"[",[215,3057,3058],{"class":901},"a-z0-9",[215,3060,3061],{"class":284},"-",[215,3063,3064],{"class":232},"]",[215,3066,3067],{"class":239},"+$",[215,3069,1678],{"class":3048},[215,3071,638],{"class":221},[215,3073,3074],{"class":723},"test",[215,3076,1505],{"class":221},[215,3078,3079],{"class":723},"String",[215,3081,3082],{"class":221},"(route.params.slug))\n",[215,3084,3085],{"class":217,"line":261},[215,3086,809],{"class":221},[215,3088,3089,3091,3093],{"class":217,"line":267},[215,3090,1169],{"class":221},[215,3092,1070],{"class":239},[215,3094,1086],{"class":221},[16,3096,18,3097,3099],{},[20,3098,2991],{}," só confere o formato do parâmetro. Saber se o post existe continua exigindo a consulta aos dados mostrada acima.",[44,3101,3103],{"id":3102},"rotas-aninhadas","Rotas aninhadas",[16,3105,3106],{},"Rotas aninhadas servem para seções de uma interface em que uma área pai permanece na tela enquanto o conteúdo filho muda. Um painel administrativo com Pedidos, Produtos e Configurações é o caso típico.",[16,3108,3109],{},"Para criar uma, você precisa de um arquivo de página pai e de uma pasta com o mesmo nome que guarda os filhos:",[115,3111,3114],{"className":3112,"code":3113,"language":120,"meta":123},[118],"app\u002Fpages\u002F\n├── admin.vue\n└── admin\u002F\n    ├── index.vue      -> \u002Fadmin\n    ├── orders.vue     -> \u002Fadmin\u002Forders\n    ├── products.vue   -> \u002Fadmin\u002Fproducts\n    └── settings.vue   -> \u002Fadmin\u002Fsettings\n",[20,3115,3113],{"__ignoreMap":123},[16,3117,3118,3119,3122],{},"O arquivo pai renderiza a interface compartilhada e usa ",[20,3120,3121],{},"\u003CNuxtPage \u002F>"," onde a página filha deve aparecer:",[115,3124,3126],{"className":1434,"code":3125,"language":1436,"meta":123,"style":123},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cnav>\n      \u003CNuxtLink to=\"\u002Fadmin\u002Forders\">Pedidos\u003C\u002FNuxtLink>\n      \u003CNuxtLink to=\"\u002Fadmin\u002Fproducts\">Produtos\u003C\u002FNuxtLink>\n      \u003CNuxtLink to=\"\u002Fadmin\u002Fsettings\">Configurações\u003C\u002FNuxtLink>\n    \u003C\u002Fnav>\n    \u003CNuxtPage \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[20,3127,3128,3136,3145,3154,3181,3205,3229,3238,3248,3256],{"__ignoreMap":123},[215,3129,3130,3132,3134],{"class":217,"line":218},[215,3131,1067],{"class":221},[215,3133,2888],{"class":239},[215,3135,1086],{"class":221},[215,3137,3138,3140,3143],{"class":217,"line":225},[215,3139,2895],{"class":221},[215,3141,3142],{"class":239},"div",[215,3144,1086],{"class":221},[215,3146,3147,3149,3152],{"class":217,"line":245},[215,3148,2917],{"class":221},[215,3150,3151],{"class":239},"nav",[215,3153,1086],{"class":221},[215,3155,3156,3159,3162,3165,3167,3169,3172,3174,3177,3179],{"class":217,"line":261},[215,3157,3158],{"class":221},"      \u003C",[215,3160,3161],{"class":239},"NuxtLink",[215,3163,3164],{"class":1073}," to",[215,3166,905],{"class":239},[215,3168,236],{"class":280},[215,3170,3171],{"class":284},"\u002Fadmin\u002Forders",[215,3173,236],{"class":280},[215,3175,3176],{"class":221},">Pedidos\u003C\u002F",[215,3178,3161],{"class":239},[215,3180,1086],{"class":221},[215,3182,3183,3185,3187,3189,3191,3193,3196,3198,3201,3203],{"class":217,"line":267},[215,3184,3158],{"class":221},[215,3186,3161],{"class":239},[215,3188,3164],{"class":1073},[215,3190,905],{"class":239},[215,3192,236],{"class":280},[215,3194,3195],{"class":284},"\u002Fadmin\u002Fproducts",[215,3197,236],{"class":280},[215,3199,3200],{"class":221},">Produtos\u003C\u002F",[215,3202,3161],{"class":239},[215,3204,1086],{"class":221},[215,3206,3207,3209,3211,3213,3215,3217,3220,3222,3225,3227],{"class":217,"line":293},[215,3208,3158],{"class":221},[215,3210,3161],{"class":239},[215,3212,3164],{"class":1073},[215,3214,905],{"class":239},[215,3216,236],{"class":280},[215,3218,3219],{"class":284},"\u002Fadmin\u002Fsettings",[215,3221,236],{"class":280},[215,3223,3224],{"class":221},">Configurações\u003C\u002F",[215,3226,3161],{"class":239},[215,3228,1086],{"class":221},[215,3230,3231,3234,3236],{"class":217,"line":314},[215,3232,3233],{"class":221},"    \u003C\u002F",[215,3235,3151],{"class":239},[215,3237,1086],{"class":221},[215,3239,3240,3242,3245],{"class":217,"line":334},[215,3241,2917],{"class":221},[215,3243,3244],{"class":239},"NuxtPage",[215,3246,3247],{"class":221}," \u002F>\n",[215,3249,3250,3252,3254],{"class":217,"line":340},[215,3251,2932],{"class":221},[215,3253,3142],{"class":239},[215,3255,1086],{"class":221},[215,3257,3258,3260,3262],{"class":217,"line":345},[215,3259,1169],{"class":221},[215,3261,2888],{"class":239},[215,3263,1086],{"class":221},[16,3265,3266,3267,3269],{},"Ao navegar entre os filhos, só o conteúdo renderizado pelo ",[20,3268,3121],{}," troca, e o menu continua no lugar.",[625,3271,3273],{"id":3272},"rotas-aninhadas-não-são-layouts","Rotas aninhadas não são layouts",[16,3275,3276,3277,3280,3281,3284],{},"O Nuxt não tem um arquivo ",[20,3278,3279],{},"layout.vue"," dentro de ",[20,3282,3283],{},"pages\u002Fadmin",". Página pai e layout são recursos separados:",[75,3286,3287,3296],{},[78,3288,3289,3290,3293,3294,638],{},"Uma página pai aninhada (",[20,3291,3292],{},"admin.vue",") faz parte da árvore de rotas. Ela renderiza as rotas filhas por meio do ",[20,3295,3121],{},[78,3297,3298,3299,3301,3302,3305],{},"Um layout fica em ",[20,3300,2543],{}," e é uma estrutura reutilizável, como um cabeçalho com rodapé. A página escolhe um com ",[20,3303,3304],{},"definePageMeta({ layout: \"dashboard\" })",", e o layout renderiza a página pelo slot.",[44,3307,3309],{"id":3308},"rotas-opcionais-e-catch-all","Rotas opcionais e catch-all",[16,3311,3312,3313,3316],{},"Dois padrões com colchetes cobrem casos que um ",[20,3314,3315],{},"[slug]"," simples não resolve:",[135,3318,3319,3328],{},[138,3320,3321],{},[141,3322,3323,3325],{},[144,3324,2501],{},[144,3326,3327],{},"Casa com",[154,3329,3330,3342],{},[141,3331,3332,3335],{},[159,3333,3334],{},"app\u002Fpages\u002F[[slug]].vue",[159,3336,3337,62,3339],{},[20,3338,1678],{},[20,3340,3341],{},"\u002Fqualquer-coisa",[141,3343,3344,3347],{},[159,3345,3346],{},"app\u002Fpages\u002F[...slug].vue",[159,3348,3349],{},"qualquer caminho, inclusive com vários trechos",[16,3351,3352],{},"Colchetes duplos tornam o trecho opcional. Os três pontos criam um catch-all que recebe o restante do caminho. Rotas catch-all servem para URLs de profundidade variável, como em documentações, e para tratar URLs antigas que você precisa redirecionar.",[44,3354,3356],{"id":3355},"caminhos-personalizados-e-aliases","Caminhos personalizados e aliases",[16,3358,3359,3360,3362,3363,3365,3366,3369],{},"Às vezes a URL pública não deve seguir a estrutura de arquivos. O ",[20,3361,2987],{}," permite definir um ",[20,3364,1545],{}," personalizado ou adicionar um ",[20,3367,3368],{},"alias"," para uma página:",[115,3371,3373],{"className":1434,"code":3372,"language":1436,"meta":123,"style":123},"\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n  path: \"\u002Fshirts\u002F:slug\",\n  alias: [\"\u002Ft-shirts\u002F:slug\"]\n});\n\u003C\u002Fscript>\n",[20,3374,3375,3395,3401,3417,3437,3441],{"__ignoreMap":123},[215,3376,3377,3379,3381,3383,3385,3387,3389,3391,3393],{"class":217,"line":218},[215,3378,1067],{"class":221},[215,3380,1070],{"class":239},[215,3382,1447],{"class":1073},[215,3384,1450],{"class":1073},[215,3386,905],{"class":239},[215,3388,236],{"class":280},[215,3390,1457],{"class":284},[215,3392,236],{"class":280},[215,3394,1086],{"class":221},[215,3396,3397,3399],{"class":217,"line":225},[215,3398,2987],{"class":723},[215,3400,771],{"class":221},[215,3402,3403,3406,3408,3410,3413,3415],{"class":217,"line":245},[215,3404,3405],{"class":221},"  path",[215,3407,133],{"class":239},[215,3409,281],{"class":280},[215,3411,3412],{"class":284},"\u002Fshirts\u002F:slug",[215,3414,236],{"class":280},[215,3416,290],{"class":221},[215,3418,3419,3422,3424,3427,3429,3432,3434],{"class":217,"line":261},[215,3420,3421],{"class":221},"  alias",[215,3423,133],{"class":239},[215,3425,3426],{"class":221}," [",[215,3428,236],{"class":280},[215,3430,3431],{"class":284},"\u002Ft-shirts\u002F:slug",[215,3433,236],{"class":280},[215,3435,3436],{"class":221},"]\n",[215,3438,3439],{"class":217,"line":267},[215,3440,809],{"class":221},[215,3442,3443,3445,3447],{"class":217,"line":293},[215,3444,1169],{"class":221},[215,3446,1070],{"class":239},[215,3448,1086],{"class":221},[16,3450,3451],{},"O alias faz a mesma página responder em mais de uma URL. Isso cria URLs duplicadas, então use só quando precisar das duas e aponte o canonical para a que você quer nos resultados de busca.",[44,3453,3455],{"id":3454},"parâmetros-de-consulta-query","Parâmetros de consulta (query)",[16,3457,3458,3459,3462,3463,133],{},"Os parâmetros de consulta carregam um estado opcional que não muda qual recurso a página representa: um filtro, a página atual de uma lista ou um termo de busca. O formato é ",[20,3460,3461],{},"?nome=valor",", e parâmetros extras são unidos com ",[20,3464,3465],{},"&",[115,3467,3470],{"className":3468,"code":3469,"language":120,"meta":123},[118],"https:\u002F\u002Flojaonline.com\u002Fcamisetas?cor=azul&tamanho=m&page=2\n",[20,3471,3469],{"__ignoreMap":123},[16,3473,3474,3475,3478,3479,3482,3483,3486,3487,3490,3491,3494],{},"Você os lê com ",[20,3476,3477],{},"useRoute().query",". O valor de um parâmetro nem sempre é uma única string. Pode ser uma string, ",[20,3480,3481],{},"null"," (para ",[20,3484,3485],{},"?cor"," sem valor), um array (para ",[20,3488,3489],{},"?tag=seo&tag=nuxt",") ou ",[20,3492,3493],{},"undefined"," quando o parâmetro não existe. Um helper pequeno evita bugs quando o valor é usado em um filtro ou em uma chamada de API:",[115,3496,3498],{"className":1774,"code":3497,"language":1457,"meta":123,"style":123},"\u002F\u002F app\u002Futils\u002Fquery.ts\nexport function firstQueryValue(\n  value: string | null | (string | null)[] | undefined\n) {\n  const first = Array.isArray(value) ? value[0] : value;\n  return first ?? undefined;\n}\n",[20,3499,3500,3505,3517,3553,3558,3594,3609],{"__ignoreMap":123},[215,3501,3502],{"class":217,"line":218},[215,3503,3504],{"class":776},"\u002F\u002F app\u002Futils\u002Fquery.ts\n",[215,3506,3507,3509,3512,3515],{"class":217,"line":225},[215,3508,762],{"class":239},[215,3510,3511],{"class":239}," function",[215,3513,3514],{"class":723}," firstQueryValue",[215,3516,2729],{"class":221},[215,3518,3519,3522,3524,3528,3531,3534,3536,3538,3541,3543,3545,3548,3550],{"class":217,"line":245},[215,3520,3521],{"class":3039},"  value",[215,3523,133],{"class":239},[215,3525,3527],{"class":3526},"sCp4m"," string",[215,3529,3530],{"class":239}," |",[215,3532,3533],{"class":3526}," null",[215,3535,3530],{"class":239},[215,3537,2806],{"class":221},[215,3539,3540],{"class":3526},"string",[215,3542,3530],{"class":239},[215,3544,3533],{"class":3526},[215,3546,3547],{"class":221},")[] ",[215,3549,100],{"class":239},[215,3551,3552],{"class":3526}," undefined\n",[215,3554,3555],{"class":217,"line":261},[215,3556,3557],{"class":221},") {\n",[215,3559,3560,3563,3566,3568,3571,3574,3577,3580,3583,3586,3589,3591],{"class":217,"line":267},[215,3561,3562],{"class":239},"  const",[215,3564,3565],{"class":221}," first ",[215,3567,905],{"class":239},[215,3569,3570],{"class":221}," Array.",[215,3572,3573],{"class":723},"isArray",[215,3575,3576],{"class":221},"(value) ",[215,3578,3579],{"class":239},"?",[215,3581,3582],{"class":221}," value[",[215,3584,3585],{"class":901},"0",[215,3587,3588],{"class":221},"] ",[215,3590,133],{"class":239},[215,3592,3593],{"class":221}," value;\n",[215,3595,3596,3599,3601,3603,3606],{"class":217,"line":293},[215,3597,3598],{"class":239},"  return",[215,3600,3565],{"class":221},[215,3602,1630],{"class":239},[215,3604,3605],{"class":901}," undefined",[215,3607,3608],{"class":221},";\n",[215,3610,3611],{"class":217,"line":314},[215,3612,424],{"class":221},[115,3614,3616],{"className":1434,"code":3615,"language":1436,"meta":123,"style":123},"\u003Cscript setup lang=\"ts\">\nconst route = useRoute();\n\nconst page = computed(() => Number(firstQueryValue(route.query.page)) || 1);\nconst cor = computed(() => firstQueryValue(route.query.cor));\n\u003C\u002Fscript>\n",[20,3617,3618,3638,3650,3654,3688,3708],{"__ignoreMap":123},[215,3619,3620,3622,3624,3626,3628,3630,3632,3634,3636],{"class":217,"line":218},[215,3621,1067],{"class":221},[215,3623,1070],{"class":239},[215,3625,1447],{"class":1073},[215,3627,1450],{"class":1073},[215,3629,905],{"class":239},[215,3631,236],{"class":280},[215,3633,1457],{"class":284},[215,3635,236],{"class":280},[215,3637,1086],{"class":221},[215,3639,3640,3642,3644,3646,3648],{"class":217,"line":225},[215,3641,1466],{"class":239},[215,3643,1469],{"class":221},[215,3645,905],{"class":239},[215,3647,1474],{"class":723},[215,3649,1477],{"class":221},[215,3651,3652],{"class":217,"line":245},[215,3653,1482],{"emptyLinePlaceholder":656},[215,3655,3656,3658,3661,3663,3665,3667,3669,3672,3674,3677,3680,3683,3686],{"class":217,"line":261},[215,3657,1466],{"class":239},[215,3659,3660],{"class":221}," page ",[215,3662,905],{"class":239},[215,3664,2694],{"class":723},[215,3666,2697],{"class":221},[215,3668,2700],{"class":239},[215,3670,3671],{"class":723}," Number",[215,3673,1505],{"class":221},[215,3675,3676],{"class":723},"firstQueryValue",[215,3678,3679],{"class":221},"(route.query.page)) ",[215,3681,3682],{"class":239},"||",[215,3684,3685],{"class":901}," 1",[215,3687,1559],{"class":221},[215,3689,3690,3692,3695,3697,3699,3701,3703,3705],{"class":217,"line":267},[215,3691,1466],{"class":239},[215,3693,3694],{"class":221}," cor ",[215,3696,905],{"class":239},[215,3698,2694],{"class":723},[215,3700,2697],{"class":221},[215,3702,2700],{"class":239},[215,3704,3514],{"class":723},[215,3706,3707],{"class":221},"(route.query.cor));\n",[215,3709,3710,3712,3714],{"class":217,"line":293},[215,3711,1169],{"class":221},[215,3713,1070],{"class":239},[215,3715,1086],{"class":221},[16,3717,3718,3719,3722],{},"O Nuxt importa o helper de ",[20,3720,3721],{},"app\u002Futils"," automaticamente, e os dois valores se atualizam quando a query muda.",[625,3724,3726],{"id":3725},"query-strings-e-seo","Query strings e SEO",[16,3728,3729],{},"Cada combinação de filtros cria uma URL diferente, e uma listagem com poucos filtros pode gerar uma quantidade enorme delas. Para URLs que só ordenam ou restringem a mesma lista, defina um canonical apontando para a listagem sem filtro:",[115,3731,3733],{"className":1434,"code":3732,"language":1436,"meta":123,"style":123},"\u003Cscript setup lang=\"ts\">\nuseHead({\n  link: [{ rel: \"canonical\", href: \"https:\u002F\u002Flojaonline.com\u002Fcamisetas\" }]\n});\n\u003C\u002Fscript>\n",[20,3734,3735,3755,3762,3796,3800],{"__ignoreMap":123},[215,3736,3737,3739,3741,3743,3745,3747,3749,3751,3753],{"class":217,"line":218},[215,3738,1067],{"class":221},[215,3740,1070],{"class":239},[215,3742,1447],{"class":1073},[215,3744,1450],{"class":1073},[215,3746,905],{"class":239},[215,3748,236],{"class":280},[215,3750,1457],{"class":284},[215,3752,236],{"class":280},[215,3754,1086],{"class":221},[215,3756,3757,3760],{"class":217,"line":225},[215,3758,3759],{"class":723},"useHead",[215,3761,771],{"class":221},[215,3763,3764,3767,3769,3772,3774,3776,3779,3781,3784,3786,3788,3791,3793],{"class":217,"line":245},[215,3765,3766],{"class":221},"  link",[215,3768,133],{"class":239},[215,3770,3771],{"class":221}," [{ rel",[215,3773,133],{"class":239},[215,3775,281],{"class":280},[215,3777,3778],{"class":284},"canonical",[215,3780,236],{"class":280},[215,3782,3783],{"class":221},", href",[215,3785,133],{"class":239},[215,3787,281],{"class":280},[215,3789,3790],{"class":284},"https:\u002F\u002Flojaonline.com\u002Fcamisetas",[215,3792,236],{"class":280},[215,3794,3795],{"class":221}," }]\n",[215,3797,3798],{"class":217,"line":261},[215,3799,809],{"class":221},[215,3801,3802,3804,3806],{"class":217,"line":267},[215,3803,1169],{"class":221},[215,3805,1070],{"class":239},[215,3807,1086],{"class":221},[16,3809,3810,3811,3814,3815,3818],{},"Páginas de paginação são outro caso: cada página da lista tem conteúdo próprio, então ",[20,3812,3813],{},"?page=2"," mantém um canonical apontando para ela mesma, não para a página 1. Um conteúdo que deve ranquear sozinho, como uma categoria, funciona melhor como caminho (",[20,3816,3817],{},"\u002Fcamisetas\u002Fazul",") do que como query string.",[44,3820,3822],{"id":3821},"navegando-entre-rotas","Navegando entre rotas",[16,3824,3825,3826,3828,3829,133],{},"Use o ",[20,3827,3161],{}," nos links que o usuário vê. Ele renderiza um elemento âncora normal e navega no cliente, sem recarregar a página. Para redirecionamentos e navegação disparada por código, use o ",[20,3830,3831],{},"navigateTo",[115,3833,3835],{"className":1434,"code":3834,"language":1436,"meta":123,"style":123},"\u003Cscript setup lang=\"ts\">\nasync function aplicarFiltro() {\n  await navigateTo({\n    path: \"\u002Fcamisetas\",\n    query: { cor: \"azul\", tamanho: \"m\" }\n  });\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CNuxtLink to=\"\u002Fblog\u002Fcomo-funciona-o-sistema-de-rotas-no-nuxt\">\n    Ler o post\n  \u003C\u002FNuxtLink>\n  \u003Cbutton @click=\"aplicarFiltro\">Azul, tamanho M\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[20,3836,3837,3857,3870,3880,3896,3930,3934,3938,3946,3950,3958,3977,3982,3990,4016],{"__ignoreMap":123},[215,3838,3839,3841,3843,3845,3847,3849,3851,3853,3855],{"class":217,"line":218},[215,3840,1067],{"class":221},[215,3842,1070],{"class":239},[215,3844,1447],{"class":1073},[215,3846,1450],{"class":1073},[215,3848,905],{"class":239},[215,3850,236],{"class":280},[215,3852,1457],{"class":284},[215,3854,236],{"class":280},[215,3856,1086],{"class":221},[215,3858,3859,3862,3864,3867],{"class":217,"line":225},[215,3860,3861],{"class":239},"async",[215,3863,3511],{"class":239},[215,3865,3866],{"class":723}," aplicarFiltro",[215,3868,3869],{"class":221},"() {\n",[215,3871,3872,3875,3878],{"class":217,"line":245},[215,3873,3874],{"class":239},"  await",[215,3876,3877],{"class":723}," navigateTo",[215,3879,771],{"class":221},[215,3881,3882,3885,3887,3889,3892,3894],{"class":217,"line":261},[215,3883,3884],{"class":221},"    path",[215,3886,133],{"class":239},[215,3888,281],{"class":280},[215,3890,3891],{"class":284},"\u002Fcamisetas",[215,3893,236],{"class":280},[215,3895,290],{"class":221},[215,3897,3898,3901,3903,3906,3908,3910,3913,3915,3918,3920,3922,3925,3927],{"class":217,"line":267},[215,3899,3900],{"class":221},"    query",[215,3902,133],{"class":239},[215,3904,3905],{"class":221}," { cor",[215,3907,133],{"class":239},[215,3909,281],{"class":280},[215,3911,3912],{"class":284},"azul",[215,3914,236],{"class":280},[215,3916,3917],{"class":221},", tamanho",[215,3919,133],{"class":239},[215,3921,281],{"class":280},[215,3923,3924],{"class":284},"m",[215,3926,236],{"class":280},[215,3928,3929],{"class":221}," }\n",[215,3931,3932],{"class":217,"line":293},[215,3933,2865],{"class":221},[215,3935,3936],{"class":217,"line":314},[215,3937,424],{"class":221},[215,3939,3940,3942,3944],{"class":217,"line":334},[215,3941,1169],{"class":221},[215,3943,1070],{"class":239},[215,3945,1086],{"class":221},[215,3947,3948],{"class":217,"line":340},[215,3949,1482],{"emptyLinePlaceholder":656},[215,3951,3952,3954,3956],{"class":217,"line":345},[215,3953,1067],{"class":221},[215,3955,2888],{"class":239},[215,3957,1086],{"class":221},[215,3959,3960,3962,3964,3966,3968,3970,3973,3975],{"class":217,"line":365},[215,3961,2895],{"class":221},[215,3963,3161],{"class":239},[215,3965,3164],{"class":1073},[215,3967,905],{"class":239},[215,3969,236],{"class":280},[215,3971,3972],{"class":284},"\u002Fblog\u002Fcomo-funciona-o-sistema-de-rotas-no-nuxt",[215,3974,236],{"class":280},[215,3976,1086],{"class":221},[215,3978,3979],{"class":217,"line":385},[215,3980,3981],{"class":221},"    Ler o post\n",[215,3983,3984,3986,3988],{"class":217,"line":403},[215,3985,2932],{"class":221},[215,3987,3161],{"class":239},[215,3989,1086],{"class":221},[215,3991,3992,3994,3997,4000,4002,4004,4007,4009,4012,4014],{"class":217,"line":409},[215,3993,2895],{"class":221},[215,3995,3996],{"class":239},"button",[215,3998,3999],{"class":1073}," @click",[215,4001,905],{"class":239},[215,4003,236],{"class":280},[215,4005,4006],{"class":284},"aplicarFiltro",[215,4008,236],{"class":280},[215,4010,4011],{"class":221},">Azul, tamanho M\u003C\u002F",[215,4013,3996],{"class":239},[215,4015,1086],{"class":221},[215,4017,4018,4020,4022],{"class":217,"line":415},[215,4019,1169],{"class":221},[215,4021,2888],{"class":239},[215,4023,1086],{"class":221},[16,4025,4026,4027,4029,4030,62,4033,4036,4037,4040,4041,4043],{},"Quando uma regra vale para muitas rotas, como exigir login, use um middleware de rota em ",[20,4028,2547],{},". Dentro do middleware, leia os argumentos ",[20,4031,4032],{},"to",[20,4034,4035],{},"from"," em vez de chamar ",[20,4038,4039],{},"useRoute()",". A documentação do Nuxt desaconselha o ",[20,4042,4039],{}," ali, porque o middleware ainda não tem uma rota atual.",[44,4045,4047],{"id":4046},"erros-comuns-de-roteamento","Erros comuns de roteamento",[75,4049,4050,4055,4061,4068,4071],{},[78,4051,4052,4053,638],{},"Usar um layout como pai de rotas aninhadas, em vez de uma página pai com ",[20,4054,3121],{},[78,4056,4057,4058,4060],{},"Ler ",[20,4059,2633],{}," uma vez em vez de derivar os dados dele de forma reativa.",[78,4062,4063,4064,4067],{},"Tratar os valores de ",[20,4065,4066],{},"route.query"," como strings simples.",[78,4069,4070],{},"Devolver uma página vazia com status 200 para um slug que não existe.",[78,4072,4073],{},"Criar URLs de filtro sem decidir qual versão os buscadores devem indexar.",[44,4075,2349],{"id":2348},[625,4077,4079],{"id":4078},"o-que-é-roteamento-baseado-em-arquivos-no-nuxt","O que é roteamento baseado em arquivos no Nuxt?",[16,4081,4082,4083,4085,4086,4089,4090,638],{},"O Nuxt cria as rotas a partir dos arquivos e pastas dentro do diretório ",[20,4084,2477],{},". Por exemplo, ",[20,4087,4088],{},"app\u002Fpages\u002Fabout.vue"," vira ",[20,4091,4092],{},"\u002Fabout",[625,4094,4096],{"id":4095},"onde-ficam-as-páginas-no-nuxt-4","Onde ficam as páginas no Nuxt 4?",[16,4098,4099,4100,4102,4103,4105],{},"Projetos novos do Nuxt 4 usam ",[20,4101,2539],{},". Projetos que ainda têm uma pasta ",[20,4104,2551],{}," na raiz continuam funcionando, porque o Nuxt detecta a estrutura antiga.",[625,4107,4109],{"id":4108},"como-criar-uma-rota-dinâmica-no-nuxt","Como criar uma rota dinâmica no Nuxt?",[16,4111,4112,4113,4115,4116,4119,4120,4122],{},"Nomeie o arquivo com um parâmetro entre colchetes, como ",[20,4114,2614],{},", e leia o valor com ",[20,4117,4118],{},"useRoute().params.slug",". Derive tudo que depende dele de um ",[20,4121,2950],{},", para atualizar quando o slug mudar.",[625,4124,4126],{"id":4125},"qual-é-a-diferença-entre-rotas-aninhadas-e-layouts","Qual é a diferença entre rotas aninhadas e layouts?",[16,4128,4129,4130,4132,4133,4135,4136,638],{},"Uma rota aninhada usa uma página pai com ",[20,4131,3121],{}," para renderizar as rotas filhas. Um layout fica em ",[20,4134,2543],{}," e envolve as páginas com uma estrutura reutilizável, escolhida com ",[20,4137,4138],{},"definePageMeta({ layout })",[625,4140,4142],{"id":4141},"como-mostrar-um-404-para-um-slug-inválido","Como mostrar um 404 para um slug inválido?",[16,4144,4145,4146,1405,4148,4151,4152,1910,4154,4156,4157,4159],{},"Lance um ",[20,4147,2977],{},[20,4149,4150],{},"status: 404"," quando os dados não existirem, ou retorne ",[20,4153,2995],{},[20,4155,2991],{}," do ",[20,4158,2987],{}," quando o parâmetro tiver um formato inválido.",[44,4161,628],{"id":627},[16,4163,4164,4165,638],{},"Antes de criar os arquivos, liste as suas URLs públicas: quais páginas são estáticas, quais precisam de slug e qual estado pertence à query string. Depois decida quais URLs filtradas devem ser indexadas. Para a lista completa de opções, consulte a ",[619,4166,4169],{"href":4167,"rel":4168},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fgetting-started\u002Frouting",[636],"documentação de roteamento do Nuxt",[640,4171,4172],{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sY_PY, html code.shiki .sY_PY{--shiki-default:#50FA7B;--shiki-default-font-style:italic}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}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 pre.shiki code .sGEwX, html code.shiki .sGEwX{--shiki-default:#FFB86C;--shiki-default-font-style:italic}html pre.shiki code .s-doY, html code.shiki .s-doY{--shiki-default:#FF5555}html pre.shiki code .sLL85, html code.shiki .sLL85{--shiki-default:#8BE9FD}html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}html pre.shiki code .sCp4m, html code.shiki .sCp4m{--shiki-default:#8BE9FD;--shiki-default-font-style:italic}",{"title":123,"searchDepth":225,"depth":225,"links":4174},[4175,4176,4177,4180,4183,4184,4185,4188,4189,4190,4197],{"id":2481,"depth":225,"text":2482},{"id":2560,"depth":225,"text":2561},{"id":2587,"depth":225,"text":2588,"children":4178},[4179],{"id":2981,"depth":245,"text":2982},{"id":3102,"depth":225,"text":3103,"children":4181},[4182],{"id":3272,"depth":245,"text":3273},{"id":3308,"depth":225,"text":3309},{"id":3355,"depth":225,"text":3356},{"id":3454,"depth":225,"text":3455,"children":4186},[4187],{"id":3725,"depth":245,"text":3726},{"id":3821,"depth":225,"text":3822},{"id":4046,"depth":225,"text":4047},{"id":2348,"depth":225,"text":2349,"children":4191},[4192,4193,4194,4195,4196],{"id":4078,"depth":245,"text":4079},{"id":4095,"depth":245,"text":4096},{"id":4108,"depth":245,"text":4109},{"id":4125,"depth":245,"text":4126},{"id":4141,"depth":245,"text":4142},{"id":627,"depth":225,"text":628},[650,651],"Como funciona o roteamento por arquivos do Nuxt: rotas dinâmicas, aninhadas e catch-all, query params, erro 404 e URLs canônicas, com exemplos do Nuxt 4.","how-the-routing-system-works-in-nuxt",{},"2025-03-12",{"title":2468,"description":4199},{"loc":3972,"lastmod":2190},"blog\u002Fcomo-funciona-o-sistema-de-rotas-no-nuxt",1446,"SlAfr9lxbNq9z6lwz58-9qTTGytpbmIu6idqDw5Wkzc",{"id":4209,"title":4210,"articleCover":4211,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":4212,"categoriesIds":4878,"description":4879,"enAlternateSlug":4880,"extension":654,"meta":4881,"navigation":656,"path":4882,"ptBrAlternateSlug":4883,"publishedAt":4884,"seo":4885,"sitemap":4886,"stem":4889,"updatedAt":664,"wordCount":4890,"__hash__":4891},"blog\u002Fblog\u002Fcomo-usar-middlewares-no-nuxtjs.md","Middlewares: O que são e como utilizar no Nuxt.JS","\u002Fimages\u002Fblog\u002Fnuxt\u002Fmiddlewares-no-front-end.webp",{"type":13,"value":4213,"toc":4871},[4214,4218,4229,4249,4252,4265,4268,4272,4279,4282,4285,4296,4299,4303,4306,4317,4333,4474,4485,4662,4671,4792,4795,4842,4855,4857,4860,4868],[44,4215,4217],{"id":4216},"o-que-são-middlewares","O que são Middlewares?",[16,4219,4220,4221,4224,4225,4228],{},"No Front-end, os ",[741,4222,4223],{},"Middlewares"," são blocos de códigos que são executados ",[741,4226,4227],{},"antes que a navegação para uma nova página aconteça",", permitindo realizar diversos controles na nossa aplicação, como:",[75,4230,4231,4237,4243],{},[78,4232,4233,4236],{},[741,4234,4235],{},"Autenticação",": verifica se o usuário está logado, caso contrário, é redirecionado para a tela de login.",[78,4238,4239,4242],{},[741,4240,4241],{},"Permissão",": verifica se o usuário tem as permissões de acessar aquela página, caso não tenha, ele é redirecionado para alguma página que tenha permissão.",[78,4244,4245,4248],{},[741,4246,4247],{},"Logs",": salvar informações sobre informações da página.",[16,4250,4251],{},"Os Middlewares funcionam como uma camada intermediária, atuando desde o início do carregamento da página até a sua renderização completa.",[16,4253,4254,4255,62,4260,638],{},"Essa funcionalidade de middlewares está presente em frameworks modernos do Front-end como ",[619,4256,4259],{"href":4257,"rel":4258},"https:\u002F\u002Fnextjs.org",[636],"Next.js",[619,4261,4264],{"href":4262,"rel":4263},"https:\u002F\u002Fnuxt.com",[636],"Nuxt.JS",[16,4266,4267],{},"Neste artigo, vou te ensinar como implementar os Middlewares de rota no Nuxt.JS.",[44,4269,4271],{"id":4270},"middlewares-de-uma-forma-não-tão-técnica","Middlewares de uma forma não tão técnica",[105,4273],{":copyright":4274,"alt":4275,"height":4276,"src":4277,"width":4278},"{\"title\": \"Pessoas em uma festa\", \"licenseId\": \"pexels\", \"author\": {\"name\": \"Cottonbro Studio\", \"url\": \"https:\u002F\u002Fwww.pexels.com\u002Fphoto\u002Fpeople-toasting-wine-glasses-3171837\" }}","pessoas em uma festa",428,"\u002Fimages\u002Fblog\u002Fpeople\u002Fpexels-cottonbro-3171837.webp",640,[16,4280,4281],{},"Para explicar de forma mais simples, vou utilizar uma analogia:",[16,4283,4284],{},"Imagine que você quer entrar em uma festa, e nesse processo você vai passar por três funcionários da segurança da festa, que são os \"middlewares\", e cada um vai ser responsável por fazer uma ação específica.",[1236,4286,4287,4290,4293],{},[78,4288,4289],{},"O primeiro segurança vai verificar se você possui um ingresso e se é válido, semelhante a um middleware de autenticação, caso não possua o ingresso, você vai ser redirecionado para a bilheteria ou tela de login.",[78,4291,4292],{},"O segundo segurança vai conferir se você é maior de idade, requisito essencial para acessar a festa, semelhante a um middleware de permissão. Se você não tiver a permissão necessária para entrar, será redirecionado para fora da fila da festa.",[78,4294,4295],{},"O terceiro e último segurança vai te dar uma pulseira de identificação que sinaliza se você possui acesso à área vip da festa, semelhante aos middlewares que podem adicionar dados em requisições.",[16,4297,4298],{},"Por fim, após passar por todas as etapas anteriores, você estará livre para acessar o salão da festa.",[44,4300,4302],{"id":4301},"tipos-de-middlewares","Tipos de Middlewares",[16,4304,4305],{},"No Nuxt.js temos os seguintes tipos de Middlewares:",[75,4307,4308,4311,4314],{},[78,4309,4310],{},"Globais",[78,4312,4313],{},"Inline ou Anônimos",[78,4315,4316],{},"Nomeados",[1236,4318,4319],{},[78,4320,4321,4322,4325,4326,4329,4330,638],{},"Os Middlewares ",[20,4323,4324],{},"globais"," são criados dentro da pasta ",[20,4327,4328],{},"\u002Fmiddleware"," e são executados em todas as páginas. É necessário adicionar o sufixo ",[20,4331,4332],{},"global",[115,4334,4338],{"className":4335,"code":4336,"language":4337,"meta":123,"style":123},"language-typescript shiki shiki-themes dracula","\u002F*\n  Exemplo de middleware Global:\n  Esse middleware verifica se a URL\u002Frota buscada existe.\n  Caso não exista o usuário é redirecionado para página de links.\n\n  Nome e Diretório do arquivo:\n  \u002Fmiddleware\u002FnotFound.global.js\n*\u002F\n\nexport default defineNuxtRouteMiddleware((to) => {\n  const hasFoundRoute = to.matched.length > 0;\n\n  if (!hasFoundRoute) {\n    return navigateTo({\n      path: \"\u002Flinks\"\n    });\n  }\n});\n","typescript",[20,4339,4340,4345,4350,4355,4360,4364,4369,4374,4379,4383,4403,4423,4427,4439,4448,4461,4466,4470],{"__ignoreMap":123},[215,4341,4342],{"class":217,"line":218},[215,4343,4344],{"class":776},"\u002F*\n",[215,4346,4347],{"class":217,"line":225},[215,4348,4349],{"class":776},"  Exemplo de middleware Global:\n",[215,4351,4352],{"class":217,"line":245},[215,4353,4354],{"class":776},"  Esse middleware verifica se a URL\u002Frota buscada existe.\n",[215,4356,4357],{"class":217,"line":261},[215,4358,4359],{"class":776},"  Caso não exista o usuário é redirecionado para página de links.\n",[215,4361,4362],{"class":217,"line":267},[215,4363,1482],{"emptyLinePlaceholder":656},[215,4365,4366],{"class":217,"line":293},[215,4367,4368],{"class":776},"  Nome e Diretório do arquivo:\n",[215,4370,4371],{"class":217,"line":314},[215,4372,4373],{"class":776},"  \u002Fmiddleware\u002FnotFound.global.js\n",[215,4375,4376],{"class":217,"line":334},[215,4377,4378],{"class":776},"*\u002F\n",[215,4380,4381],{"class":217,"line":340},[215,4382,1482],{"emptyLinePlaceholder":656},[215,4384,4385,4387,4389,4392,4395,4397,4399,4401],{"class":217,"line":345},[215,4386,762],{"class":239},[215,4388,765],{"class":239},[215,4390,4391],{"class":723}," defineNuxtRouteMiddleware",[215,4393,4394],{"class":221},"((",[215,4396,4032],{"class":3039},[215,4398,3043],{"class":221},[215,4400,2700],{"class":239},[215,4402,242],{"class":221},[215,4404,4405,4407,4410,4412,4415,4418,4421],{"class":217,"line":365},[215,4406,3562],{"class":239},[215,4408,4409],{"class":221}," hasFoundRoute ",[215,4411,905],{"class":239},[215,4413,4414],{"class":221}," to.matched.length ",[215,4416,4417],{"class":239},">",[215,4419,4420],{"class":901}," 0",[215,4422,3608],{"class":221},[215,4424,4425],{"class":217,"line":385},[215,4426,1482],{"emptyLinePlaceholder":656},[215,4428,4429,4432,4434,4436],{"class":217,"line":403},[215,4430,4431],{"class":239},"  if",[215,4433,2806],{"class":221},[215,4435,2809],{"class":239},[215,4437,4438],{"class":221},"hasFoundRoute) {\n",[215,4440,4441,4444,4446],{"class":217,"line":409},[215,4442,4443],{"class":239},"    return",[215,4445,3877],{"class":723},[215,4447,771],{"class":221},[215,4449,4450,4453,4455,4457,4459],{"class":217,"line":415},[215,4451,4452],{"class":221},"      path",[215,4454,133],{"class":239},[215,4456,281],{"class":280},[215,4458,2521],{"class":284},[215,4460,331],{"class":280},[215,4462,4463],{"class":217,"line":421},[215,4464,4465],{"class":221},"    });\n",[215,4467,4468],{"class":217,"line":1161},[215,4469,418],{"class":221},[215,4471,4472],{"class":217,"line":1166},[215,4473,809],{"class":221},[1236,4475,4476],{"start":225},[78,4477,4321,4478,30,4481,4484],{},[20,4479,4480],{},"inline",[20,4482,4483],{},"anônimos"," são middlewares específicos para uma página, definidos diretamente dentro do arquivo da página no Nuxt; Isso limita a reutilização do código, visto que ele não pode ser facilmente compartilhado com outras páginas.",[115,4486,4488],{"className":1302,"code":4487,"language":1304,"meta":123,"style":123},"\u002F*\n  Exemplo de middleware inline:\n  Esse middleware verifica se os dados estão presentes na store.\n  Caso não esteja, os dados serão requisitados.\n*\u002F\n\n\u003Cscript setup lang=\"ts\">\nimport { useStore } from 'vuex';\n\ndefinePageMeta({\n  middleware: [\n    async function (to, from) {\n      const store = useStore();\n      if (!store.state.user) {\n        await store.dispatch(\"fetchUser\");\n      }\n    },\n  ],\n});\n\u003C\u002Fscript>\n",[20,4489,4490,4494,4499,4504,4509,4513,4517,4537,4552,4556,4563,4568,4585,4600,4612,4634,4638,4643,4648,4654],{"__ignoreMap":123},[215,4491,4492],{"class":217,"line":218},[215,4493,4344],{"class":776},[215,4495,4496],{"class":217,"line":225},[215,4497,4498],{"class":776},"  Exemplo de middleware inline:\n",[215,4500,4501],{"class":217,"line":245},[215,4502,4503],{"class":776},"  Esse middleware verifica se os dados estão presentes na store.\n",[215,4505,4506],{"class":217,"line":261},[215,4507,4508],{"class":776},"  Caso não esteja, os dados serão requisitados.\n",[215,4510,4511],{"class":217,"line":267},[215,4512,4378],{"class":776},[215,4514,4515],{"class":217,"line":293},[215,4516,1482],{"emptyLinePlaceholder":656},[215,4518,4519,4521,4523,4525,4527,4529,4531,4533,4535],{"class":217,"line":314},[215,4520,1067],{"class":221},[215,4522,1070],{"class":239},[215,4524,1447],{"class":1073},[215,4526,1450],{"class":1073},[215,4528,905],{"class":239},[215,4530,236],{"class":280},[215,4532,1457],{"class":284},[215,4534,236],{"class":280},[215,4536,1086],{"class":221},[215,4538,4539,4542,4544,4547,4549],{"class":217,"line":334},[215,4540,4541],{"class":221},"import ",[215,4543,33],{"class":239},[215,4545,4546],{"class":221}," useStore ",[215,4548,86],{"class":239},[215,4550,4551],{"class":221}," from 'vuex';\n",[215,4553,4554],{"class":217,"line":340},[215,4555,1482],{"emptyLinePlaceholder":656},[215,4557,4558,4561],{"class":217,"line":345},[215,4559,4560],{"class":221},"definePageMeta(",[215,4562,222],{"class":239},[215,4564,4565],{"class":217,"line":365},[215,4566,4567],{"class":221},"  middleware: [\n",[215,4569,4570,4573,4575,4577,4579,4581,4583],{"class":217,"line":385},[215,4571,4572],{"class":239},"    async",[215,4574,3511],{"class":239},[215,4576,2806],{"class":221},[215,4578,4032],{"class":3039},[215,4580,1928],{"class":221},[215,4582,4035],{"class":3039},[215,4584,3557],{"class":221},[215,4586,4587,4590,4593,4595,4598],{"class":217,"line":403},[215,4588,4589],{"class":239},"      const",[215,4591,4592],{"class":221}," store ",[215,4594,905],{"class":239},[215,4596,4597],{"class":723}," useStore",[215,4599,1477],{"class":221},[215,4601,4602,4605,4607,4609],{"class":217,"line":409},[215,4603,4604],{"class":239},"      if",[215,4606,2806],{"class":221},[215,4608,2809],{"class":239},[215,4610,4611],{"class":221},"store.state.user) {\n",[215,4613,4614,4617,4620,4623,4625,4627,4630,4632],{"class":217,"line":415},[215,4615,4616],{"class":239},"        await",[215,4618,4619],{"class":221}," store.",[215,4621,4622],{"class":723},"dispatch",[215,4624,1505],{"class":221},[215,4626,236],{"class":280},[215,4628,4629],{"class":284},"fetchUser",[215,4631,236],{"class":280},[215,4633,1559],{"class":221},[215,4635,4636],{"class":217,"line":421},[215,4637,406],{"class":221},[215,4639,4640],{"class":217,"line":1161},[215,4641,4642],{"class":221},"    },\n",[215,4644,4645],{"class":217,"line":1166},[215,4646,4647],{"class":221},"  ],\n",[215,4649,4650,4652],{"class":217,"line":1686},[215,4651,86],{"class":239},[215,4653,1559],{"class":221},[215,4655,4656,4658,4660],{"class":217,"line":1713},[215,4657,1169],{"class":221},[215,4659,1070],{"class":239},[215,4661,1086],{"class":221},[1236,4663,4664],{"start":245},[78,4665,4321,4666,4325,4669,638],{},[20,4667,4668],{},"nomeados",[20,4670,4328],{},[115,4672,4674],{"className":1302,"code":4673,"language":1304,"meta":123,"style":123},"\u002F*\n  Exemplo de middleware Nomeado:\n  Esse middleware verifica o usuário está logado.\n  Caso não esteja logado, ele é redirecionado para página de login.\n\n  Nome e Diretório do arquivo:\n  \u002Fmiddleware\u002Fauth.js\n*\u002F\n\nexport default defineNuxtRouteMiddleware((to, from) => {\n  const { $store } = useNuxtApp();\n\n  if (!$store.auth?.loggedIn) {\n    return navigateTo(\"\u002Flogin\");\n  }\n});\n",[20,4675,4676,4680,4685,4690,4695,4699,4703,4708,4712,4716,4738,4752,4756,4767,4784,4788],{"__ignoreMap":123},[215,4677,4678],{"class":217,"line":218},[215,4679,4344],{"class":776},[215,4681,4682],{"class":217,"line":225},[215,4683,4684],{"class":776},"  Exemplo de middleware Nomeado:\n",[215,4686,4687],{"class":217,"line":245},[215,4688,4689],{"class":776},"  Esse middleware verifica o usuário está logado.\n",[215,4691,4692],{"class":217,"line":261},[215,4693,4694],{"class":776},"  Caso não esteja logado, ele é redirecionado para página de login.\n",[215,4696,4697],{"class":217,"line":267},[215,4698,1482],{"emptyLinePlaceholder":656},[215,4700,4701],{"class":217,"line":293},[215,4702,4368],{"class":776},[215,4704,4705],{"class":217,"line":314},[215,4706,4707],{"class":776},"  \u002Fmiddleware\u002Fauth.js\n",[215,4709,4710],{"class":217,"line":334},[215,4711,4378],{"class":776},[215,4713,4714],{"class":217,"line":340},[215,4715,1482],{"emptyLinePlaceholder":656},[215,4717,4718,4720,4722,4724,4726,4728,4730,4732,4734,4736],{"class":217,"line":345},[215,4719,762],{"class":239},[215,4721,765],{"class":239},[215,4723,4391],{"class":723},[215,4725,4394],{"class":221},[215,4727,4032],{"class":3039},[215,4729,1928],{"class":221},[215,4731,4035],{"class":3039},[215,4733,3043],{"class":221},[215,4735,2700],{"class":239},[215,4737,242],{"class":221},[215,4739,4740,4742,4745,4747,4750],{"class":217,"line":365},[215,4741,3562],{"class":239},[215,4743,4744],{"class":221}," { $store } ",[215,4746,905],{"class":239},[215,4748,4749],{"class":723}," useNuxtApp",[215,4751,1477],{"class":221},[215,4753,4754],{"class":217,"line":385},[215,4755,1482],{"emptyLinePlaceholder":656},[215,4757,4758,4760,4762,4764],{"class":217,"line":403},[215,4759,4431],{"class":239},[215,4761,2806],{"class":221},[215,4763,2809],{"class":239},[215,4765,4766],{"class":221},"$store.auth?.loggedIn) {\n",[215,4768,4769,4771,4773,4775,4777,4780,4782],{"class":217,"line":409},[215,4770,4443],{"class":239},[215,4772,3877],{"class":723},[215,4774,1505],{"class":221},[215,4776,236],{"class":280},[215,4778,4779],{"class":284},"\u002Flogin",[215,4781,236],{"class":280},[215,4783,1559],{"class":221},[215,4785,4786],{"class":217,"line":415},[215,4787,418],{"class":221},[215,4789,4790],{"class":217,"line":421},[215,4791,809],{"class":221},[16,4793,4794],{},"Nos Middlewares nomeados, é necessário definir quais páginas utilizam determinado middleware, uma das formas de se fazer isso é alterando os arquivos das páginas que vão utilizar aquele middleware:",[115,4796,4798],{"className":1302,"code":4797,"language":1304,"meta":123,"style":123},"\u003Cscript setup>\ndefinePageMeta({\n  middleware: \"auth-admin\"\n});\n\u003C\u002Fscript>\n",[20,4799,4800,4810,4816,4828,4834],{"__ignoreMap":123},[215,4801,4802,4804,4806,4808],{"class":217,"line":218},[215,4803,1067],{"class":221},[215,4805,1070],{"class":239},[215,4807,1447],{"class":1073},[215,4809,1086],{"class":221},[215,4811,4812,4814],{"class":217,"line":225},[215,4813,4560],{"class":221},[215,4815,222],{"class":239},[215,4817,4818,4821,4823,4826],{"class":217,"line":245},[215,4819,4820],{"class":221},"  middleware: ",[215,4822,236],{"class":280},[215,4824,4825],{"class":284},"auth-admin",[215,4827,331],{"class":280},[215,4829,4830,4832],{"class":217,"line":261},[215,4831,86],{"class":239},[215,4833,1559],{"class":221},[215,4835,4836,4838,4840],{"class":217,"line":267},[215,4837,1169],{"class":221},[215,4839,1070],{"class":239},[215,4841,1086],{"class":221},[16,4843,4844,4845,30,4848,4851,4852,638],{},"Também importante dizer que mesmo que você nomeie os arquivos de middleware em padrões como ",[20,4846,4847],{},"PascalCase",[20,4849,4850],{},"camelCase",", os nomes dos arquivos são normalizados para o padrão ",[20,4853,4854],{},"kebab-case",[625,4856,628],{"id":627},[16,4858,4859],{},"Espero que com esse artigo eu tenha conseguido te explicar de forma simples e rápida os principais pontos de criação e utilização de Middlewares no Nuxt.JS 🙋🏻‍♂️.",[16,4861,4862,4863,638],{},"Se você precisar de mais informações sobre Middlewares no Nuxt.JS, você pode buscar na ",[619,4864,4867],{"href":4865,"rel":4866},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fguide\u002Fdirectory-structure\u002Fmiddleware",[636],"documentação oficial",[640,4869,4870],{},"html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .sGEwX, html code.shiki .sGEwX{--shiki-default:#FFB86C;--shiki-default-font-style:italic}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}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 pre.shiki code .sY_PY, html code.shiki .sY_PY{--shiki-default:#50FA7B;--shiki-default-font-style:italic}",{"title":123,"searchDepth":225,"depth":225,"links":4872},[4873,4874,4875],{"id":4216,"depth":225,"text":4217},{"id":4270,"depth":225,"text":4271},{"id":4301,"depth":225,"text":4302,"children":4876},[4877],{"id":627,"depth":245,"text":628},[650,651],"Aprenda o que são e como usar middlewares no Nuxt.js para controlar navegação, autenticação e permissões das páginas, com exemplos de cada tipo de middleware.","how-to-use-middlewares-nuxtjs",{},"\u002Fblog\u002Fcomo-usar-middlewares-no-nuxtjs","como-usar-middlewares-no-nuxtjs","2025-01-14",{"title":4210,"description":4879},{"loc":4882,"images":4887,"lastmod":664},[4888],{"loc":4277},"blog\u002Fcomo-usar-middlewares-no-nuxtjs",542,"S_wDFG7R78RpBrivGcG_InljSRJ9mXWZBZgiWyO-P2Y",{"id":4893,"title":4894,"articleCover":4895,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":4896,"articleCoverWidth":4897,"authorId":4898,"body":4899,"categoriesIds":7635,"description":7636,"enAlternateSlug":7637,"extension":654,"meta":7638,"navigation":656,"path":7639,"ptBrAlternateSlug":7640,"publishedAt":7641,"seo":7642,"sitemap":7643,"stem":7644,"updatedAt":664,"wordCount":7645,"__hash__":7646},"blog\u002Fblog\u002Fo-que-sao-diretivas-no-vue-e-como-implementa-las.md","Diretivas no Vue 3: nativas e personalizadas","\u002Fimages\u002Fblog\u002Fvue\u002Fo-que-sao-diretivas-no-vue-e-como-implementa-las.webp",772,1372,"eduardo-goncalves-souza",{"type":13,"value":4900,"toc":7600},[4901,4915,4920,4924,4927,4936,4940,4954,5117,5143,5147,5151,5157,5267,5271,5276,5405,5418,5422,5427,5592,5596,5605,5793,5800,5804,5816,5987,5991,5999,6043,6047,6061,6069,6073,6076,6111,6115,6126,6135,6152,6156,6159,6163,6180,6274,6281,6285,6291,6375,6498,6502,6505,6548,6599,6603,6611,6745,6767,6771,6781,7066,7081,7085,7088,7091,7095,7110,7215,7233,7248,7320,7369,7373,7376,7424,7441,7445,7484,7486,7489,7495,7499,7516,7520,7523,7527,7539,7543,7546,7550,7561,7563,7574,7597],[16,4902,4903,4904,4907,4908,62,4911,4914],{},"Diretivas são atributos especiais de template, sempre com o prefixo ",[20,4905,4906],{},"v-",", que ligam o HTML ao estado e ao comportamento da sua aplicação. Aqui estão as diretivas nativas do Vue 3, a sintaxe com argumentos e modificadores, a escolha entre ",[20,4909,4910],{},"v-if",[20,4912,4913],{},"v-show"," e o passo a passo para criar a sua própria diretiva, inclusive no Nuxt 4, onde é preciso cuidar da renderização no servidor.",[16,4916,4917,4918,638],{},"Os exemplos usam Vue 3.5 com ",[20,4919,2966],{},[44,4921,4923],{"id":4922},"o-que-são-diretivas-no-vue","O que são diretivas no Vue?",[16,4925,4926],{},"Uma diretiva recebe uma expressão JavaScript como valor e aplica um comportamento reativo ao elemento onde está. Quando o valor muda, o Vue atualiza o elemento. Elas controlam atributos, eventos, repetição de listas e quais elementos aparecem na tela.",[16,4928,4929,4930,62,4932,4935],{},"Existem dois grupos. As diretivas nativas, como ",[20,4931,4910],{},[20,4933,4934],{},"v-for",", já vêm com o Vue. As diretivas personalizadas são criadas por você para os casos em que é preciso mexer diretamente no elemento do DOM.",[44,4937,4939],{"id":4938},"como-funciona-a-sintaxe-das-diretivas","Como funciona a sintaxe das diretivas?",[16,4941,4942,4943,4945,4946,62,4949,4945,4951,133],{},"Uma diretiva tem até quatro partes: o nome, um argumento opcional depois de dois pontos, modificadores opcionais separados por ponto e o valor. As duas primeiras linhas abaixo são equivalentes, porque ",[20,4944,133],{}," é a abreviação de ",[20,4947,4948],{},"v-bind:",[20,4950,29],{},[20,4952,4953],{},"v-on:",[115,4955,4957],{"className":1434,"code":4956,"language":1436,"meta":123,"style":123},"\u003Ctemplate>\n  \u003Ca v-bind:href=\"url\">Visite nosso site\u003C\u002Fa>\n  \u003Ca :href=\"url\">Visite nosso site\u003C\u002Fa>\n\n  \u003Cbutton @click=\"salvar\">Salvar\u003C\u002Fbutton>\n\n  \u003C!-- argumento dinâmico: o nome do atributo vem de uma variável -->\n  \u003Ca :[atributo]=\"url\">Visite nosso site\u003C\u002Fa>\n\n  \u003C!-- modificador: chama event.preventDefault() no envio -->\n  \u003Cform @submit.prevent=\"enviar\">...\u003C\u002Fform>\n\u003C\u002Ftemplate>\n",[20,4958,4959,4967,4991,5014,5018,5042,5046,5051,5074,5078,5083,5109],{"__ignoreMap":123},[215,4960,4961,4963,4965],{"class":217,"line":218},[215,4962,1067],{"class":221},[215,4964,2888],{"class":239},[215,4966,1086],{"class":221},[215,4968,4969,4971,4973,4976,4978,4980,4982,4984,4987,4989],{"class":217,"line":225},[215,4970,2895],{"class":221},[215,4972,619],{"class":239},[215,4974,4975],{"class":1073}," v-bind:href",[215,4977,905],{"class":239},[215,4979,236],{"class":280},[215,4981,273],{"class":284},[215,4983,236],{"class":280},[215,4985,4986],{"class":221},">Visite nosso site\u003C\u002F",[215,4988,619],{"class":239},[215,4990,1086],{"class":221},[215,4992,4993,4995,4997,5000,5002,5004,5006,5008,5010,5012],{"class":217,"line":245},[215,4994,2895],{"class":221},[215,4996,619],{"class":239},[215,4998,4999],{"class":1073}," :href",[215,5001,905],{"class":239},[215,5003,236],{"class":280},[215,5005,273],{"class":284},[215,5007,236],{"class":280},[215,5009,4986],{"class":221},[215,5011,619],{"class":239},[215,5013,1086],{"class":221},[215,5015,5016],{"class":217,"line":261},[215,5017,1482],{"emptyLinePlaceholder":656},[215,5019,5020,5022,5024,5026,5028,5030,5033,5035,5038,5040],{"class":217,"line":267},[215,5021,2895],{"class":221},[215,5023,3996],{"class":239},[215,5025,3999],{"class":1073},[215,5027,905],{"class":239},[215,5029,236],{"class":280},[215,5031,5032],{"class":284},"salvar",[215,5034,236],{"class":280},[215,5036,5037],{"class":221},">Salvar\u003C\u002F",[215,5039,3996],{"class":239},[215,5041,1086],{"class":221},[215,5043,5044],{"class":217,"line":293},[215,5045,1482],{"emptyLinePlaceholder":656},[215,5047,5048],{"class":217,"line":314},[215,5049,5050],{"class":776},"  \u003C!-- argumento dinâmico: o nome do atributo vem de uma variável -->\n",[215,5052,5053,5055,5057,5060,5062,5064,5066,5068,5070,5072],{"class":217,"line":334},[215,5054,2895],{"class":221},[215,5056,619],{"class":239},[215,5058,5059],{"class":1073}," :[atributo]",[215,5061,905],{"class":239},[215,5063,236],{"class":280},[215,5065,273],{"class":284},[215,5067,236],{"class":280},[215,5069,4986],{"class":221},[215,5071,619],{"class":239},[215,5073,1086],{"class":221},[215,5075,5076],{"class":217,"line":340},[215,5077,1482],{"emptyLinePlaceholder":656},[215,5079,5080],{"class":217,"line":345},[215,5081,5082],{"class":776},"  \u003C!-- modificador: chama event.preventDefault() no envio -->\n",[215,5084,5085,5087,5090,5093,5095,5097,5100,5102,5105,5107],{"class":217,"line":365},[215,5086,2895],{"class":221},[215,5088,5089],{"class":239},"form",[215,5091,5092],{"class":1073}," @submit.prevent",[215,5094,905],{"class":239},[215,5096,236],{"class":280},[215,5098,5099],{"class":284},"enviar",[215,5101,236],{"class":280},[215,5103,5104],{"class":221},">...\u003C\u002F",[215,5106,5089],{"class":239},[215,5108,1086],{"class":221},[215,5110,5111,5113,5115],{"class":217,"line":385},[215,5112,1169],{"class":221},[215,5114,2888],{"class":239},[215,5116,1086],{"class":221},[16,5118,5119,5120,62,5123,5126,5127,5130,5131,1928,5134,62,5137,5126,5140,638],{},"Nos projetos Vue, a forma curta é a mais comum. Os modificadores mudam o comportamento da diretiva, e cada diretiva tem os seus. ",[20,5121,5122],{},".prevent",[20,5124,5125],{},".stop"," valem para ",[20,5128,5129],{},"v-on",", enquanto ",[20,5132,5133],{},".lazy",[20,5135,5136],{},".number",[20,5138,5139],{},".trim",[20,5141,5142],{},"v-model",[44,5144,5146],{"id":5145},"quais-são-as-principais-diretivas-nativas","Quais são as principais diretivas nativas?",[625,5148,5150],{"id":5149},"v-bind-atributos-dinâmicos","v-bind: atributos dinâmicos",[16,5152,18,5153,5156],{},[20,5154,5155],{},"v-bind"," liga o valor de uma variável a um atributo do elemento. Se a variável mudar, o atributo muda junto:",[115,5158,5160],{"className":1434,"code":5159,"language":1436,"meta":123,"style":123},"\u003Ctemplate>\n  \u003Ca :href=\"url\">Visite nosso site\u003C\u002Fa>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref } from \"vue\";\n\nconst url = ref(\"https:\u002F\u002Fcodigoaoponto.com\u002F\");\n\u003C\u002Fscript>\n",[20,5161,5162,5170,5192,5200,5204,5214,5232,5236,5259],{"__ignoreMap":123},[215,5163,5164,5166,5168],{"class":217,"line":218},[215,5165,1067],{"class":221},[215,5167,2888],{"class":239},[215,5169,1086],{"class":221},[215,5171,5172,5174,5176,5178,5180,5182,5184,5186,5188,5190],{"class":217,"line":225},[215,5173,2895],{"class":221},[215,5175,619],{"class":239},[215,5177,4999],{"class":1073},[215,5179,905],{"class":239},[215,5181,236],{"class":280},[215,5183,273],{"class":284},[215,5185,236],{"class":280},[215,5187,4986],{"class":221},[215,5189,619],{"class":239},[215,5191,1086],{"class":221},[215,5193,5194,5196,5198],{"class":217,"line":245},[215,5195,1169],{"class":221},[215,5197,2888],{"class":239},[215,5199,1086],{"class":221},[215,5201,5202],{"class":217,"line":261},[215,5203,1482],{"emptyLinePlaceholder":656},[215,5205,5206,5208,5210,5212],{"class":217,"line":267},[215,5207,1067],{"class":221},[215,5209,1070],{"class":239},[215,5211,1447],{"class":1073},[215,5213,1086],{"class":221},[215,5215,5216,5219,5222,5224,5226,5228,5230],{"class":217,"line":293},[215,5217,5218],{"class":239},"import",[215,5220,5221],{"class":221}," { ref } ",[215,5223,4035],{"class":239},[215,5225,281],{"class":280},[215,5227,1436],{"class":284},[215,5229,236],{"class":280},[215,5231,3608],{"class":221},[215,5233,5234],{"class":217,"line":314},[215,5235,1482],{"emptyLinePlaceholder":656},[215,5237,5238,5240,5243,5245,5248,5250,5252,5255,5257],{"class":217,"line":334},[215,5239,1466],{"class":239},[215,5241,5242],{"class":221}," url ",[215,5244,905],{"class":239},[215,5246,5247],{"class":723}," ref",[215,5249,1505],{"class":221},[215,5251,236],{"class":280},[215,5253,5254],{"class":284},"https:\u002F\u002Fcodigoaoponto.com\u002F",[215,5256,236],{"class":280},[215,5258,1559],{"class":221},[215,5260,5261,5263,5265],{"class":217,"line":340},[215,5262,1169],{"class":221},[215,5264,1070],{"class":239},[215,5266,1086],{"class":221},[625,5268,5270],{"id":5269},"v-model-vinculação-bidirecional","v-model: vinculação bidirecional",[16,5272,18,5273,5275],{},[20,5274,5142],{}," sincroniza um campo de formulário com uma variável nos dois sentidos. Digitar no campo atualiza a variável, e alterar a variável atualiza o campo:",[115,5277,5279],{"className":1434,"code":5278,"language":1436,"meta":123,"style":123},"\u003Ctemplate>\n  \u003Cinput v-model=\"nome\" type=\"text\" \u002F>\n  \u003Cdiv>Olá! Meu nome é {{ nome }}\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref } from \"vue\";\n\nconst nome = ref(\"Teste\");\n\u003C\u002Fscript>\n",[20,5280,5281,5289,5320,5333,5341,5345,5355,5371,5375,5397],{"__ignoreMap":123},[215,5282,5283,5285,5287],{"class":217,"line":218},[215,5284,1067],{"class":221},[215,5286,2888],{"class":239},[215,5288,1086],{"class":221},[215,5290,5291,5293,5296,5299,5301,5303,5306,5308,5310,5312,5314,5316,5318],{"class":217,"line":225},[215,5292,2895],{"class":221},[215,5294,5295],{"class":239},"input",[215,5297,5298],{"class":1073}," v-model",[215,5300,905],{"class":239},[215,5302,236],{"class":280},[215,5304,5305],{"class":284},"nome",[215,5307,236],{"class":280},[215,5309,1074],{"class":1073},[215,5311,905],{"class":239},[215,5313,236],{"class":280},[215,5315,120],{"class":284},[215,5317,236],{"class":280},[215,5319,3247],{"class":221},[215,5321,5322,5324,5326,5329,5331],{"class":217,"line":245},[215,5323,2895],{"class":221},[215,5325,3142],{"class":239},[215,5327,5328],{"class":221},">Olá! Meu nome é {{ nome }}\u003C\u002F",[215,5330,3142],{"class":239},[215,5332,1086],{"class":221},[215,5334,5335,5337,5339],{"class":217,"line":261},[215,5336,1169],{"class":221},[215,5338,2888],{"class":239},[215,5340,1086],{"class":221},[215,5342,5343],{"class":217,"line":267},[215,5344,1482],{"emptyLinePlaceholder":656},[215,5346,5347,5349,5351,5353],{"class":217,"line":293},[215,5348,1067],{"class":221},[215,5350,1070],{"class":239},[215,5352,1447],{"class":1073},[215,5354,1086],{"class":221},[215,5356,5357,5359,5361,5363,5365,5367,5369],{"class":217,"line":314},[215,5358,5218],{"class":239},[215,5360,5221],{"class":221},[215,5362,4035],{"class":239},[215,5364,281],{"class":280},[215,5366,1436],{"class":284},[215,5368,236],{"class":280},[215,5370,3608],{"class":221},[215,5372,5373],{"class":217,"line":334},[215,5374,1482],{"emptyLinePlaceholder":656},[215,5376,5377,5379,5382,5384,5386,5388,5390,5393,5395],{"class":217,"line":340},[215,5378,1466],{"class":239},[215,5380,5381],{"class":221}," nome ",[215,5383,905],{"class":239},[215,5385,5247],{"class":723},[215,5387,1505],{"class":221},[215,5389,236],{"class":280},[215,5391,5392],{"class":284},"Teste",[215,5394,236],{"class":280},[215,5396,1559],{"class":221},[215,5398,5399,5401,5403],{"class":217,"line":345},[215,5400,1169],{"class":221},[215,5402,1070],{"class":239},[215,5404,1086],{"class":221},[16,5406,5407,5408,5410,5411,5413,5414,5417],{},"Em componentes, o ",[20,5409,5142],{}," depende de uma propriedade e de um evento compatíveis. No Vue 3 você pode ter mais de um ",[20,5412,5142],{}," no mesmo componente, cada um com um nome. A partir do Vue 3.4, a macro ",[20,5415,5416],{},"defineModel"," cria esse par para você.",[625,5419,5421],{"id":5420},"v-on-eventos","v-on: eventos",[16,5423,18,5424,5426],{},[20,5425,5129],{}," liga um evento do navegador a uma função:",[115,5428,5430],{"className":1434,"code":5429,"language":1436,"meta":123,"style":123},"\u003Ctemplate>\n  \u003Cinput v-model=\"nome\" type=\"text\" \u002F>\n  \u003Cbutton @click=\"resetar\">Resetar\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref } from \"vue\";\n\nconst nome = ref(\"Teste\");\n\nfunction resetar() {\n  nome.value = \"\";\n}\n\u003C\u002Fscript>\n",[20,5431,5432,5440,5468,5492,5500,5504,5514,5530,5534,5554,5558,5568,5580,5584],{"__ignoreMap":123},[215,5433,5434,5436,5438],{"class":217,"line":218},[215,5435,1067],{"class":221},[215,5437,2888],{"class":239},[215,5439,1086],{"class":221},[215,5441,5442,5444,5446,5448,5450,5452,5454,5456,5458,5460,5462,5464,5466],{"class":217,"line":225},[215,5443,2895],{"class":221},[215,5445,5295],{"class":239},[215,5447,5298],{"class":1073},[215,5449,905],{"class":239},[215,5451,236],{"class":280},[215,5453,5305],{"class":284},[215,5455,236],{"class":280},[215,5457,1074],{"class":1073},[215,5459,905],{"class":239},[215,5461,236],{"class":280},[215,5463,120],{"class":284},[215,5465,236],{"class":280},[215,5467,3247],{"class":221},[215,5469,5470,5472,5474,5476,5478,5480,5483,5485,5488,5490],{"class":217,"line":245},[215,5471,2895],{"class":221},[215,5473,3996],{"class":239},[215,5475,3999],{"class":1073},[215,5477,905],{"class":239},[215,5479,236],{"class":280},[215,5481,5482],{"class":284},"resetar",[215,5484,236],{"class":280},[215,5486,5487],{"class":221},">Resetar\u003C\u002F",[215,5489,3996],{"class":239},[215,5491,1086],{"class":221},[215,5493,5494,5496,5498],{"class":217,"line":261},[215,5495,1169],{"class":221},[215,5497,2888],{"class":239},[215,5499,1086],{"class":221},[215,5501,5502],{"class":217,"line":267},[215,5503,1482],{"emptyLinePlaceholder":656},[215,5505,5506,5508,5510,5512],{"class":217,"line":293},[215,5507,1067],{"class":221},[215,5509,1070],{"class":239},[215,5511,1447],{"class":1073},[215,5513,1086],{"class":221},[215,5515,5516,5518,5520,5522,5524,5526,5528],{"class":217,"line":314},[215,5517,5218],{"class":239},[215,5519,5221],{"class":221},[215,5521,4035],{"class":239},[215,5523,281],{"class":280},[215,5525,1436],{"class":284},[215,5527,236],{"class":280},[215,5529,3608],{"class":221},[215,5531,5532],{"class":217,"line":334},[215,5533,1482],{"emptyLinePlaceholder":656},[215,5535,5536,5538,5540,5542,5544,5546,5548,5550,5552],{"class":217,"line":340},[215,5537,1466],{"class":239},[215,5539,5381],{"class":221},[215,5541,905],{"class":239},[215,5543,5247],{"class":723},[215,5545,1505],{"class":221},[215,5547,236],{"class":280},[215,5549,5392],{"class":284},[215,5551,236],{"class":280},[215,5553,1559],{"class":221},[215,5555,5556],{"class":217,"line":345},[215,5557,1482],{"emptyLinePlaceholder":656},[215,5559,5560,5563,5566],{"class":217,"line":365},[215,5561,5562],{"class":239},"function",[215,5564,5565],{"class":723}," resetar",[215,5567,3869],{"class":221},[215,5569,5570,5573,5575,5578],{"class":217,"line":385},[215,5571,5572],{"class":221},"  nome.value ",[215,5574,905],{"class":239},[215,5576,5577],{"class":280}," \"\"",[215,5579,3608],{"class":221},[215,5581,5582],{"class":217,"line":403},[215,5583,424],{"class":221},[215,5585,5586,5588,5590],{"class":217,"line":409},[215,5587,1169],{"class":221},[215,5589,1070],{"class":239},[215,5591,1086],{"class":221},[625,5593,5595],{"id":5594},"v-for-listas-e-a-key","v-for: listas e a key",[16,5597,18,5598,5600,5601,5604],{},[20,5599,4934],{}," cria um elemento para cada item de um array ou objeto. Ele precisa de uma ",[20,5602,5603],{},":key"," com um valor único e estável de cada item, para o Vue saber qual elemento corresponde a qual dado:",[115,5606,5608],{"className":1434,"code":5607,"language":1436,"meta":123,"style":123},"\u003Ctemplate>\n  \u003Cul>\n    \u003Cli v-for=\"tarefa in tarefas\" :key=\"tarefa.id\">\n      {{ tarefa.titulo }}\n    \u003C\u002Fli>\n  \u003C\u002Ful>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nconst tarefas = [\n  { id: 101, titulo: \"Revisar a documentação\" },\n  { id: 102, titulo: \"Atualizar as dependências\" },\n  { id: 103, titulo: \"Publicar o artigo\" }\n];\n\u003C\u002Fscript>\n",[20,5609,5610,5618,5626,5658,5663,5671,5679,5687,5691,5701,5712,5736,5758,5780,5785],{"__ignoreMap":123},[215,5611,5612,5614,5616],{"class":217,"line":218},[215,5613,1067],{"class":221},[215,5615,2888],{"class":239},[215,5617,1086],{"class":221},[215,5619,5620,5622,5624],{"class":217,"line":225},[215,5621,2895],{"class":221},[215,5623,75],{"class":239},[215,5625,1086],{"class":221},[215,5627,5628,5630,5632,5635,5637,5639,5642,5644,5647,5649,5651,5654,5656],{"class":217,"line":245},[215,5629,2917],{"class":221},[215,5631,78],{"class":239},[215,5633,5634],{"class":1073}," v-for",[215,5636,905],{"class":239},[215,5638,236],{"class":280},[215,5640,5641],{"class":284},"tarefa in tarefas",[215,5643,236],{"class":280},[215,5645,5646],{"class":1073}," :key",[215,5648,905],{"class":239},[215,5650,236],{"class":280},[215,5652,5653],{"class":284},"tarefa.id",[215,5655,236],{"class":280},[215,5657,1086],{"class":221},[215,5659,5660],{"class":217,"line":261},[215,5661,5662],{"class":221},"      {{ tarefa.titulo }}\n",[215,5664,5665,5667,5669],{"class":217,"line":267},[215,5666,3233],{"class":221},[215,5668,78],{"class":239},[215,5670,1086],{"class":221},[215,5672,5673,5675,5677],{"class":217,"line":293},[215,5674,2932],{"class":221},[215,5676,75],{"class":239},[215,5678,1086],{"class":221},[215,5680,5681,5683,5685],{"class":217,"line":314},[215,5682,1169],{"class":221},[215,5684,2888],{"class":239},[215,5686,1086],{"class":221},[215,5688,5689],{"class":217,"line":334},[215,5690,1482],{"emptyLinePlaceholder":656},[215,5692,5693,5695,5697,5699],{"class":217,"line":340},[215,5694,1067],{"class":221},[215,5696,1070],{"class":239},[215,5698,1447],{"class":1073},[215,5700,1086],{"class":221},[215,5702,5703,5705,5708,5710],{"class":217,"line":345},[215,5704,1466],{"class":239},[215,5706,5707],{"class":221}," tarefas ",[215,5709,905],{"class":239},[215,5711,258],{"class":221},[215,5713,5714,5717,5719,5722,5725,5727,5729,5732,5734],{"class":217,"line":365},[215,5715,5716],{"class":221},"  { id",[215,5718,133],{"class":239},[215,5720,5721],{"class":901}," 101",[215,5723,5724],{"class":221},", titulo",[215,5726,133],{"class":239},[215,5728,281],{"class":280},[215,5730,5731],{"class":284},"Revisar a documentação",[215,5733,236],{"class":280},[215,5735,1683],{"class":221},[215,5737,5738,5740,5742,5745,5747,5749,5751,5754,5756],{"class":217,"line":385},[215,5739,5716],{"class":221},[215,5741,133],{"class":239},[215,5743,5744],{"class":901}," 102",[215,5746,5724],{"class":221},[215,5748,133],{"class":239},[215,5750,281],{"class":280},[215,5752,5753],{"class":284},"Atualizar as dependências",[215,5755,236],{"class":280},[215,5757,1683],{"class":221},[215,5759,5760,5762,5764,5767,5769,5771,5773,5776,5778],{"class":217,"line":403},[215,5761,5716],{"class":221},[215,5763,133],{"class":239},[215,5765,5766],{"class":901}," 103",[215,5768,5724],{"class":221},[215,5770,133],{"class":239},[215,5772,281],{"class":280},[215,5774,5775],{"class":284},"Publicar o artigo",[215,5777,236],{"class":280},[215,5779,3929],{"class":221},[215,5781,5782],{"class":217,"line":409},[215,5783,5784],{"class":221},"];\n",[215,5786,5787,5789,5791],{"class":217,"line":415},[215,5788,1169],{"class":221},[215,5790,1070],{"class":239},[215,5792,1086],{"class":221},[16,5794,5795,5796,5799],{},"O índice do array só serve de ",[20,5797,5798],{},"key"," em listas estáticas. Se a lista pode ser reordenada, filtrada ou receber e perder itens, o índice faz o Vue reaproveitar o elemento errado. O problema aparece principalmente quando cada linha tem um campo de texto, um componente ou uma animação com estado próprio.",[625,5801,5803],{"id":5802},"v-if-v-else-if-e-v-else-renderização-condicional","v-if, v-else-if e v-else: renderização condicional",[16,5805,18,5806,5808,5809,1952,5812,5815],{},[20,5807,4910],{}," inclui o elemento só quando a condição é verdadeira. O ",[20,5810,5811],{},"v-else-if",[20,5813,5814],{},"v-else"," encadeiam as alternativas:",[115,5817,5819],{"className":1434,"code":5818,"language":1436,"meta":123,"style":123},"\u003Ctemplate>\n  \u003Cinput v-model=\"nome\" type=\"text\" \u002F>\n  \u003Cdiv v-if=\"nome.length > 10\">Seu nome é muito longo!\u003C\u002Fdiv>\n  \u003Cdiv v-else-if=\"nome\">Olá! Meu nome é {{ nome }}\u003C\u002Fdiv>\n  \u003Cdiv v-else>Por favor, digite seu nome.\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref } from \"vue\";\n\nconst nome = ref(\"\");\n\u003C\u002Fscript>\n",[20,5820,5821,5829,5857,5881,5904,5920,5928,5932,5942,5958,5962,5979],{"__ignoreMap":123},[215,5822,5823,5825,5827],{"class":217,"line":218},[215,5824,1067],{"class":221},[215,5826,2888],{"class":239},[215,5828,1086],{"class":221},[215,5830,5831,5833,5835,5837,5839,5841,5843,5845,5847,5849,5851,5853,5855],{"class":217,"line":225},[215,5832,2895],{"class":221},[215,5834,5295],{"class":239},[215,5836,5298],{"class":1073},[215,5838,905],{"class":239},[215,5840,236],{"class":280},[215,5842,5305],{"class":284},[215,5844,236],{"class":280},[215,5846,1074],{"class":1073},[215,5848,905],{"class":239},[215,5850,236],{"class":280},[215,5852,120],{"class":284},[215,5854,236],{"class":280},[215,5856,3247],{"class":221},[215,5858,5859,5861,5863,5865,5867,5869,5872,5874,5877,5879],{"class":217,"line":245},[215,5860,2895],{"class":221},[215,5862,3142],{"class":239},[215,5864,2901],{"class":1073},[215,5866,905],{"class":239},[215,5868,236],{"class":280},[215,5870,5871],{"class":284},"nome.length > 10",[215,5873,236],{"class":280},[215,5875,5876],{"class":221},">Seu nome é muito longo!\u003C\u002F",[215,5878,3142],{"class":239},[215,5880,1086],{"class":221},[215,5882,5883,5885,5887,5890,5892,5894,5896,5898,5900,5902],{"class":217,"line":261},[215,5884,2895],{"class":221},[215,5886,3142],{"class":239},[215,5888,5889],{"class":1073}," v-else-if",[215,5891,905],{"class":239},[215,5893,236],{"class":280},[215,5895,5305],{"class":284},[215,5897,236],{"class":280},[215,5899,5328],{"class":221},[215,5901,3142],{"class":239},[215,5903,1086],{"class":221},[215,5905,5906,5908,5910,5913,5916,5918],{"class":217,"line":267},[215,5907,2895],{"class":221},[215,5909,3142],{"class":239},[215,5911,5912],{"class":1073}," v-else",[215,5914,5915],{"class":221},">Por favor, digite seu nome.\u003C\u002F",[215,5917,3142],{"class":239},[215,5919,1086],{"class":221},[215,5921,5922,5924,5926],{"class":217,"line":293},[215,5923,1169],{"class":221},[215,5925,2888],{"class":239},[215,5927,1086],{"class":221},[215,5929,5930],{"class":217,"line":314},[215,5931,1482],{"emptyLinePlaceholder":656},[215,5933,5934,5936,5938,5940],{"class":217,"line":334},[215,5935,1067],{"class":221},[215,5937,1070],{"class":239},[215,5939,1447],{"class":1073},[215,5941,1086],{"class":221},[215,5943,5944,5946,5948,5950,5952,5954,5956],{"class":217,"line":340},[215,5945,5218],{"class":239},[215,5947,5221],{"class":221},[215,5949,4035],{"class":239},[215,5951,281],{"class":280},[215,5953,1436],{"class":284},[215,5955,236],{"class":280},[215,5957,3608],{"class":221},[215,5959,5960],{"class":217,"line":345},[215,5961,1482],{"emptyLinePlaceholder":656},[215,5963,5964,5966,5968,5970,5972,5974,5977],{"class":217,"line":365},[215,5965,1466],{"class":239},[215,5967,5381],{"class":221},[215,5969,905],{"class":239},[215,5971,5247],{"class":723},[215,5973,1505],{"class":221},[215,5975,5976],{"class":280},"\"\"",[215,5978,1559],{"class":221},[215,5980,5981,5983,5985],{"class":217,"line":385},[215,5982,1169],{"class":221},[215,5984,1070],{"class":239},[215,5986,1086],{"class":221},[625,5988,5990],{"id":5989},"v-show-visibilidade","v-show: visibilidade",[16,5992,18,5993,5995,5996,133],{},[20,5994,4913],{}," mantém o elemento no DOM e alterna a propriedade CSS ",[20,5997,5998],{},"display",[115,6000,6002],{"className":1434,"code":6001,"language":1436,"meta":123,"style":123},"\u003Ctemplate>\n  \u003Cdiv v-show=\"nome.length > 10\">Seu nome é muito longo!\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[20,6003,6004,6012,6035],{"__ignoreMap":123},[215,6005,6006,6008,6010],{"class":217,"line":218},[215,6007,1067],{"class":221},[215,6009,2888],{"class":239},[215,6011,1086],{"class":221},[215,6013,6014,6016,6018,6021,6023,6025,6027,6029,6031,6033],{"class":217,"line":225},[215,6015,2895],{"class":221},[215,6017,3142],{"class":239},[215,6019,6020],{"class":1073}," v-show",[215,6022,905],{"class":239},[215,6024,236],{"class":280},[215,6026,5871],{"class":284},[215,6028,236],{"class":280},[215,6030,5876],{"class":221},[215,6032,3142],{"class":239},[215,6034,1086],{"class":221},[215,6036,6037,6039,6041],{"class":217,"line":245},[215,6038,1169],{"class":221},[215,6040,2888],{"class":239},[215,6042,1086],{"class":221},[625,6044,6046],{"id":6045},"v-html-e-v-text","v-html e v-text",[16,6048,18,6049,6052,6053,6056,6057,6060],{},[20,6050,6051],{},"v-text"," define o texto do elemento, como a interpolação ",[20,6054,6055],{},"{{ }}"," faz. O ",[20,6058,6059],{},"v-html"," insere a string como HTML.",[1047,6062,6063],{},[16,6064,6065,6066,6068],{},"Nunca use ",[20,6067,6059],{}," com conteúdo vindo do usuário ou de fonte externa sem sanitizar antes. Um HTML malicioso nessa string é uma falha de XSS. A interpolação comum trata tudo como texto e é a opção segura.",[625,6070,6072],{"id":6071},"outras-diretivas","Outras diretivas",[16,6074,6075],{},"O Vue tem mais cinco diretivas que aparecem menos no dia a dia:",[75,6077,6078,6084,6090,6099,6105],{},[78,6079,6080,6083],{},[20,6081,6082],{},"v-slot"," recebe o conteúdo de slots nomeados e com escopo.",[78,6085,6086,6089],{},[20,6087,6088],{},"v-once"," renderiza o elemento uma vez e ignora as atualizações seguintes.",[78,6091,6092,6095,6096,6098],{},[20,6093,6094],{},"v-pre"," pula a compilação do elemento e dos filhos, útil para mostrar ",[20,6097,6055],{}," literalmente.",[78,6100,6101,6104],{},[20,6102,6103],{},"v-cloak"," esconde o template até ele ser compilado, em projetos sem etapa de build.",[78,6106,6107,6110],{},[20,6108,6109],{},"v-memo"," evita reprocessar um trecho do template enquanto os valores listados não mudarem.",[44,6112,6114],{"id":6113},"v-if-ou-v-show-qual-escolher","v-if ou v-show: qual escolher?",[16,6116,6117,6118,6120,6121,6123,6124,638],{},"Os dois escondem um elemento, mas por caminhos diferentes. O ",[20,6119,4910],{}," só renderiza o bloco quando a condição é verdadeira e, ao mudar, destrói e recria o bloco com seus componentes e listeners. O ",[20,6122,4913],{}," renderiza o elemento sempre e só alterna o ",[20,6125,5998],{},[16,6127,6128,6129,6131,6132,6134],{},"A orientação da documentação é usar ",[20,6130,4913],{}," quando você alterna algo com muita frequência, porque o custo de alternar é menor. Use ",[20,6133,4910],{}," quando a condição raramente muda em tempo de execução, porque assim o bloco nem é criado enquanto não for necessário.",[16,6136,18,6137,6139,6140,6143,6144,6146,6147,6149,6150,638],{},[20,6138,4913],{}," tem duas limitações: não funciona em ",[20,6141,6142],{},"\u003Ctemplate>"," e não combina com ",[20,6145,5814],{},". Para esconder vários elementos de uma vez, use ",[20,6148,4910],{}," em um ",[20,6151,6142],{},[44,6153,6155],{"id":6154},"como-criar-uma-diretiva-personalizada-no-vue-3","Como criar uma diretiva personalizada no Vue 3?",[16,6157,6158],{},"Uma diretiva personalizada é um objeto com funções de ciclo de vida que recebem o elemento do DOM. A documentação do Vue recomenda preferir o template declarativo e reservar diretivas para quando você precisa de acesso direto ao elemento.",[625,6160,6162],{"id":6161},"diretiva-local-com-script-setup","Diretiva local com script setup",[16,6164,6165,6166,6168,6169,6172,6173,6176,6177,133],{},"Dentro de um ",[20,6167,2966],{},", qualquer variável em camelCase que comece com ",[20,6170,6171],{},"v"," vira uma diretiva. A variável ",[20,6174,6175],{},"vFocus"," fica disponível no template como ",[20,6178,6179],{},"v-focus",[115,6181,6183],{"className":1434,"code":6182,"language":1436,"meta":123,"style":123},"\u003Cscript setup>\nconst vFocus = {\n  mounted: (el) => el.focus()\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cinput v-focus \u002F>\n\u003C\u002Ftemplate>\n",[20,6184,6185,6195,6206,6230,6235,6243,6247,6255,6266],{"__ignoreMap":123},[215,6186,6187,6189,6191,6193],{"class":217,"line":218},[215,6188,1067],{"class":221},[215,6190,1070],{"class":239},[215,6192,1447],{"class":1073},[215,6194,1086],{"class":221},[215,6196,6197,6199,6202,6204],{"class":217,"line":225},[215,6198,1466],{"class":239},[215,6200,6201],{"class":221}," vFocus ",[215,6203,905],{"class":239},[215,6205,242],{"class":221},[215,6207,6208,6211,6213,6215,6218,6220,6222,6225,6228],{"class":217,"line":245},[215,6209,6210],{"class":723},"  mounted",[215,6212,133],{"class":239},[215,6214,2806],{"class":221},[215,6216,6217],{"class":3039},"el",[215,6219,3043],{"class":221},[215,6221,2700],{"class":239},[215,6223,6224],{"class":221}," el.",[215,6226,6227],{"class":723},"focus",[215,6229,1554],{"class":221},[215,6231,6232],{"class":217,"line":261},[215,6233,6234],{"class":221},"};\n",[215,6236,6237,6239,6241],{"class":217,"line":267},[215,6238,1169],{"class":221},[215,6240,1070],{"class":239},[215,6242,1086],{"class":221},[215,6244,6245],{"class":217,"line":293},[215,6246,1482],{"emptyLinePlaceholder":656},[215,6248,6249,6251,6253],{"class":217,"line":314},[215,6250,1067],{"class":221},[215,6252,2888],{"class":239},[215,6254,1086],{"class":221},[215,6256,6257,6259,6261,6264],{"class":217,"line":334},[215,6258,2895],{"class":221},[215,6260,5295],{"class":239},[215,6262,6263],{"class":1073}," v-focus",[215,6265,3247],{"class":221},[215,6267,6268,6270,6272],{"class":217,"line":340},[215,6269,1169],{"class":221},[215,6271,2888],{"class":239},[215,6273,1086],{"class":221},[16,6275,6276,6277,6280],{},"Aqui o hook ",[20,6278,6279],{},"mounted"," roda quando o elemento é inserido no DOM e foca o campo.",[625,6282,6284],{"id":6283},"registro-global-com-appdirective","Registro global com app.directive",[16,6286,6287,6288,133],{},"Se várias partes da aplicação usam a diretiva, escreva-a em um arquivo e registre uma vez com ",[20,6289,6290],{},"app.directive",[115,6292,6294],{"className":1774,"code":6293,"language":1457,"meta":123,"style":123},"\u002F\u002F directives\u002Ffocus.ts\nimport type { Directive } from \"vue\";\n\nexport const vFocus: Directive\u003CHTMLElement> = {\n  mounted: (el) => el.focus()\n};\n",[20,6295,6296,6301,6320,6324,6351,6371],{"__ignoreMap":123},[215,6297,6298],{"class":217,"line":218},[215,6299,6300],{"class":776},"\u002F\u002F directives\u002Ffocus.ts\n",[215,6302,6303,6305,6307,6310,6312,6314,6316,6318],{"class":217,"line":225},[215,6304,5218],{"class":239},[215,6306,1074],{"class":239},[215,6308,6309],{"class":221}," { Directive } ",[215,6311,4035],{"class":239},[215,6313,281],{"class":280},[215,6315,1436],{"class":284},[215,6317,236],{"class":280},[215,6319,3608],{"class":221},[215,6321,6322],{"class":217,"line":245},[215,6323,1482],{"emptyLinePlaceholder":656},[215,6325,6326,6328,6331,6334,6336,6339,6341,6344,6347,6349],{"class":217,"line":261},[215,6327,762],{"class":239},[215,6329,6330],{"class":239}," const",[215,6332,6333],{"class":221}," vFocus",[215,6335,133],{"class":239},[215,6337,6338],{"class":3526}," Directive",[215,6340,1067],{"class":221},[215,6342,6343],{"class":3039},"HTMLElement",[215,6345,6346],{"class":221},"> ",[215,6348,905],{"class":239},[215,6350,242],{"class":221},[215,6352,6353,6355,6357,6359,6361,6363,6365,6367,6369],{"class":217,"line":267},[215,6354,6210],{"class":723},[215,6356,133],{"class":239},[215,6358,2806],{"class":221},[215,6360,6217],{"class":3039},[215,6362,3043],{"class":221},[215,6364,2700],{"class":239},[215,6366,6224],{"class":221},[215,6368,6227],{"class":723},[215,6370,1554],{"class":221},[215,6372,6373],{"class":217,"line":293},[215,6374,6234],{"class":221},[115,6376,6378],{"className":1774,"code":6377,"language":1457,"meta":123,"style":123},"\u002F\u002F main.ts\nimport { createApp } from \"vue\";\nimport App from \".\u002FApp.vue\";\nimport { vFocus } from \".\u002Fdirectives\u002Ffocus\";\n\nconst app = createApp(App);\n\napp.directive(\"focus\", vFocus);\napp.mount(\"#app\");\n",[20,6379,6380,6385,6402,6420,6438,6442,6457,6461,6480],{"__ignoreMap":123},[215,6381,6382],{"class":217,"line":218},[215,6383,6384],{"class":776},"\u002F\u002F main.ts\n",[215,6386,6387,6389,6392,6394,6396,6398,6400],{"class":217,"line":225},[215,6388,5218],{"class":239},[215,6390,6391],{"class":221}," { createApp } ",[215,6393,4035],{"class":239},[215,6395,281],{"class":280},[215,6397,1436],{"class":284},[215,6399,236],{"class":280},[215,6401,3608],{"class":221},[215,6403,6404,6406,6409,6411,6413,6416,6418],{"class":217,"line":245},[215,6405,5218],{"class":239},[215,6407,6408],{"class":221}," App ",[215,6410,4035],{"class":239},[215,6412,281],{"class":280},[215,6414,6415],{"class":284},".\u002FApp.vue",[215,6417,236],{"class":280},[215,6419,3608],{"class":221},[215,6421,6422,6424,6427,6429,6431,6434,6436],{"class":217,"line":261},[215,6423,5218],{"class":239},[215,6425,6426],{"class":221}," { vFocus } ",[215,6428,4035],{"class":239},[215,6430,281],{"class":280},[215,6432,6433],{"class":284},".\u002Fdirectives\u002Ffocus",[215,6435,236],{"class":280},[215,6437,3608],{"class":221},[215,6439,6440],{"class":217,"line":267},[215,6441,1482],{"emptyLinePlaceholder":656},[215,6443,6444,6446,6449,6451,6454],{"class":217,"line":293},[215,6445,1466],{"class":239},[215,6447,6448],{"class":221}," app ",[215,6450,905],{"class":239},[215,6452,6453],{"class":723}," createApp",[215,6455,6456],{"class":221},"(App);\n",[215,6458,6459],{"class":217,"line":314},[215,6460,1482],{"emptyLinePlaceholder":656},[215,6462,6463,6466,6469,6471,6473,6475,6477],{"class":217,"line":334},[215,6464,6465],{"class":221},"app.",[215,6467,6468],{"class":723},"directive",[215,6470,1505],{"class":221},[215,6472,236],{"class":280},[215,6474,6227],{"class":284},[215,6476,236],{"class":280},[215,6478,6479],{"class":221},", vFocus);\n",[215,6481,6482,6484,6487,6489,6491,6494,6496],{"class":217,"line":340},[215,6483,6465],{"class":221},[215,6485,6486],{"class":723},"mount",[215,6488,1505],{"class":221},[215,6490,236],{"class":280},[215,6492,6493],{"class":284},"#app",[215,6495,236],{"class":280},[215,6497,1559],{"class":221},[625,6499,6501],{"id":6500},"hooks-do-ciclo-de-vida","Hooks do ciclo de vida",[16,6503,6504],{},"Uma diretiva pode implementar estes hooks, listados na ordem em que acontecem:",[75,6506,6507,6513,6519,6524,6530,6536,6542],{},[78,6508,6509,6512],{},[20,6510,6511],{},"created",": antes de os atributos e listeners do elemento serem aplicados.",[78,6514,6515,6518],{},[20,6516,6517],{},"beforeMount",": antes de o elemento ser inserido no DOM.",[78,6520,6521,6523],{},[20,6522,6279],{},": depois de o componente pai e todos os filhos serem montados.",[78,6525,6526,6529],{},[20,6527,6528],{},"beforeUpdate",": antes de o componente pai ser atualizado.",[78,6531,6532,6535],{},[20,6533,6534],{},"updated",": depois de o componente pai e os filhos serem atualizados.",[78,6537,6538,6541],{},[20,6539,6540],{},"beforeUnmount",": antes de o componente pai ser desmontado.",[78,6543,6544,6547],{},[20,6545,6546],{},"unmounted",": depois de o componente pai ser desmontado.",[16,6549,6550,6551,6553,6554,62,6557,6560,6561,6564,6565,6567,6568,6571,6572,6575,6576,62,6578,6580,6581,6584,6585,2407,6588,6591,6592,6595,6596,6598],{},"Todos recebem ",[20,6552,6217],{}," (o elemento), ",[20,6555,6556],{},"binding",[20,6558,6559],{},"vnode",". Os hooks de atualização recebem também ",[20,6562,6563],{},"prevVnode",". O objeto ",[20,6566,6556],{}," traz o ",[20,6569,6570],{},"value"," passado à diretiva, o ",[20,6573,6574],{},"oldValue"," (só em ",[20,6577,6528],{},[20,6579,6534],{},"), o ",[20,6582,6583],{},"arg",", os ",[20,6586,6587],{},"modifiers",[20,6589,6590],{},"instance"," do componente e a definição da própria diretiva em ",[20,6593,6594],{},"dir",". Trate tudo isso, menos o ",[20,6597,6217],{},", como somente leitura.",[625,6600,6602],{"id":6601},"valor-argumento-e-modificadores","Valor, argumento e modificadores",[16,6604,6605,6606,6608,6609,133],{},"Quando o mesmo comportamento deve rodar na montagem e em cada atualização, escreva a diretiva como uma função. Ela é chamada em ",[20,6607,6279],{}," e em ",[20,6610,6534],{},[115,6612,6614],{"className":1774,"code":6613,"language":1457,"meta":123,"style":123},"\u002F\u002F directives\u002Fdestaque.ts\nimport type { Directive } from \"vue\";\n\n\u002F\u002F uso: \u003Cp v-destaque:background.suave=\"'#fff3b0'\">Texto\u003C\u002Fp>\nexport const vDestaque: Directive\u003CHTMLElement, string> = (el, binding) => {\n  el.style.setProperty(binding.arg ?? \"color\", binding.value);\n\n  if (binding.modifiers.suave) {\n    el.style.transition = \"all 0.3s\";\n  }\n};\n",[20,6615,6616,6621,6639,6643,6648,6687,6710,6714,6721,6737,6741],{"__ignoreMap":123},[215,6617,6618],{"class":217,"line":218},[215,6619,6620],{"class":776},"\u002F\u002F directives\u002Fdestaque.ts\n",[215,6622,6623,6625,6627,6629,6631,6633,6635,6637],{"class":217,"line":225},[215,6624,5218],{"class":239},[215,6626,1074],{"class":239},[215,6628,6309],{"class":221},[215,6630,4035],{"class":239},[215,6632,281],{"class":280},[215,6634,1436],{"class":284},[215,6636,236],{"class":280},[215,6638,3608],{"class":221},[215,6640,6641],{"class":217,"line":245},[215,6642,1482],{"emptyLinePlaceholder":656},[215,6644,6645],{"class":217,"line":261},[215,6646,6647],{"class":776},"\u002F\u002F uso: \u003Cp v-destaque:background.suave=\"'#fff3b0'\">Texto\u003C\u002Fp>\n",[215,6649,6650,6652,6654,6657,6659,6661,6663,6665,6667,6669,6671,6673,6675,6677,6679,6681,6683,6685],{"class":217,"line":267},[215,6651,762],{"class":239},[215,6653,6330],{"class":239},[215,6655,6656],{"class":723}," vDestaque",[215,6658,133],{"class":239},[215,6660,6338],{"class":3526},[215,6662,1067],{"class":221},[215,6664,6343],{"class":3039},[215,6666,1928],{"class":221},[215,6668,3540],{"class":3526},[215,6670,6346],{"class":221},[215,6672,905],{"class":239},[215,6674,2806],{"class":221},[215,6676,6217],{"class":3039},[215,6678,1928],{"class":221},[215,6680,6556],{"class":3039},[215,6682,3043],{"class":221},[215,6684,2700],{"class":239},[215,6686,242],{"class":221},[215,6688,6689,6692,6695,6698,6700,6702,6705,6707],{"class":217,"line":293},[215,6690,6691],{"class":221},"  el.style.",[215,6693,6694],{"class":723},"setProperty",[215,6696,6697],{"class":221},"(binding.arg ",[215,6699,1630],{"class":239},[215,6701,281],{"class":280},[215,6703,6704],{"class":284},"color",[215,6706,236],{"class":280},[215,6708,6709],{"class":221},", binding.value);\n",[215,6711,6712],{"class":217,"line":314},[215,6713,1482],{"emptyLinePlaceholder":656},[215,6715,6716,6718],{"class":217,"line":334},[215,6717,4431],{"class":239},[215,6719,6720],{"class":221}," (binding.modifiers.suave) {\n",[215,6722,6723,6726,6728,6730,6733,6735],{"class":217,"line":340},[215,6724,6725],{"class":221},"    el.style.transition ",[215,6727,905],{"class":239},[215,6729,281],{"class":280},[215,6731,6732],{"class":284},"all 0.3s",[215,6734,236],{"class":280},[215,6736,3608],{"class":221},[215,6738,6739],{"class":217,"line":345},[215,6740,418],{"class":221},[215,6742,6743],{"class":217,"line":365},[215,6744,6234],{"class":221},[16,6746,18,6747,6749,6750,6753,6754,6757,6758,1952,6761,6763,6764,638],{},[20,6748,6583],{}," é ",[20,6751,6752],{},"background",", o modificador ",[20,6755,6756],{},"suave"," aparece em ",[20,6759,6760],{},"binding.modifiers",[20,6762,6570],{}," é a cor. Quando a diretiva precisa de várias opções, o valor pode ser um objeto: ",[20,6765,6766],{},"v-destaque=\"{ cor: 'white', duracao: 300 }\"",[625,6768,6770],{"id":6769},"limpando-listeners","Limpando listeners",[16,6772,6773,6774,30,6777,6780],{},"Toda diretiva que registra um listener em ",[20,6775,6776],{},"document",[20,6778,6779],{},"window",", um observer ou um timer precisa desfazer isso quando o elemento sai da tela. O exemplo clássico é fechar um menu ao clicar fora dele:",[115,6782,6784],{"className":1774,"code":6783,"language":1457,"meta":123,"style":123},"\u002F\u002F directives\u002FclickOutside.ts\nimport type { Directive } from \"vue\";\n\ntype ElementoComListener = HTMLElement & {\n  _aoClicarFora?: (event: MouseEvent) => void;\n};\n\nexport const vClickOutside: Directive\u003CElementoComListener, () => void> = {\n  mounted(el, binding) {\n    el._aoClicarFora = (event) => {\n      if (!el.contains(event.target as Node)) {\n        binding.value();\n      }\n    };\n\n    document.addEventListener(\"click\", el._aoClicarFora);\n  },\n\n  unmounted(el) {\n    if (el._aoClicarFora) {\n      document.removeEventListener(\"click\", el._aoClicarFora);\n      delete el._aoClicarFora;\n    }\n  }\n};\n",[20,6785,6786,6791,6809,6813,6832,6859,6863,6867,6897,6911,6931,6957,6966,6970,6975,6979,6999,7003,7007,7018,7026,7044,7052,7057,7061],{"__ignoreMap":123},[215,6787,6788],{"class":217,"line":218},[215,6789,6790],{"class":776},"\u002F\u002F directives\u002FclickOutside.ts\n",[215,6792,6793,6795,6797,6799,6801,6803,6805,6807],{"class":217,"line":225},[215,6794,5218],{"class":239},[215,6796,1074],{"class":239},[215,6798,6309],{"class":221},[215,6800,4035],{"class":239},[215,6802,281],{"class":280},[215,6804,1436],{"class":284},[215,6806,236],{"class":280},[215,6808,3608],{"class":221},[215,6810,6811],{"class":217,"line":245},[215,6812,1482],{"emptyLinePlaceholder":656},[215,6814,6815,6818,6821,6824,6827,6830],{"class":217,"line":261},[215,6816,6817],{"class":239},"type",[215,6819,6820],{"class":3526}," ElementoComListener",[215,6822,6823],{"class":239}," =",[215,6825,6826],{"class":3526}," HTMLElement",[215,6828,6829],{"class":239}," &",[215,6831,242],{"class":221},[215,6833,6834,6837,6840,6842,6845,6847,6850,6852,6854,6857],{"class":217,"line":267},[215,6835,6836],{"class":723},"  _aoClicarFora",[215,6838,6839],{"class":239},"?:",[215,6841,2806],{"class":221},[215,6843,6844],{"class":3039},"event",[215,6846,133],{"class":239},[215,6848,6849],{"class":3526}," MouseEvent",[215,6851,3043],{"class":221},[215,6853,2700],{"class":239},[215,6855,6856],{"class":3526}," void",[215,6858,3608],{"class":221},[215,6860,6861],{"class":217,"line":293},[215,6862,6234],{"class":221},[215,6864,6865],{"class":217,"line":314},[215,6866,1482],{"emptyLinePlaceholder":656},[215,6868,6869,6871,6873,6876,6878,6880,6882,6885,6887,6889,6891,6893,6895],{"class":217,"line":334},[215,6870,762],{"class":239},[215,6872,6330],{"class":239},[215,6874,6875],{"class":221}," vClickOutside",[215,6877,133],{"class":239},[215,6879,6338],{"class":3526},[215,6881,1067],{"class":221},[215,6883,6884],{"class":3039},"ElementoComListener",[215,6886,1522],{"class":221},[215,6888,2700],{"class":239},[215,6890,6856],{"class":3526},[215,6892,6346],{"class":221},[215,6894,905],{"class":239},[215,6896,242],{"class":221},[215,6898,6899,6901,6903,6905,6907,6909],{"class":217,"line":340},[215,6900,6210],{"class":723},[215,6902,1505],{"class":221},[215,6904,6217],{"class":3039},[215,6906,1928],{"class":221},[215,6908,6556],{"class":3039},[215,6910,3557],{"class":221},[215,6912,6913,6916,6919,6921,6923,6925,6927,6929],{"class":217,"line":345},[215,6914,6915],{"class":221},"    el.",[215,6917,6918],{"class":723},"_aoClicarFora",[215,6920,6823],{"class":239},[215,6922,2806],{"class":221},[215,6924,6844],{"class":3039},[215,6926,3043],{"class":221},[215,6928,2700],{"class":239},[215,6930,242],{"class":221},[215,6932,6933,6935,6937,6939,6942,6945,6948,6951,6954],{"class":217,"line":365},[215,6934,4604],{"class":239},[215,6936,2806],{"class":221},[215,6938,2809],{"class":239},[215,6940,6941],{"class":221},"el.",[215,6943,6944],{"class":723},"contains",[215,6946,6947],{"class":221},"(event.target ",[215,6949,6950],{"class":239},"as",[215,6952,6953],{"class":3526}," Node",[215,6955,6956],{"class":221},")) {\n",[215,6958,6959,6962,6964],{"class":217,"line":385},[215,6960,6961],{"class":221},"        binding.",[215,6963,6570],{"class":723},[215,6965,1477],{"class":221},[215,6967,6968],{"class":217,"line":403},[215,6969,406],{"class":221},[215,6971,6972],{"class":217,"line":409},[215,6973,6974],{"class":221},"    };\n",[215,6976,6977],{"class":217,"line":415},[215,6978,1482],{"emptyLinePlaceholder":656},[215,6980,6981,6984,6987,6989,6991,6994,6996],{"class":217,"line":421},[215,6982,6983],{"class":221},"    document.",[215,6985,6986],{"class":723},"addEventListener",[215,6988,1505],{"class":221},[215,6990,236],{"class":280},[215,6992,6993],{"class":284},"click",[215,6995,236],{"class":280},[215,6997,6998],{"class":221},", el._aoClicarFora);\n",[215,7000,7001],{"class":217,"line":1161},[215,7002,1831],{"class":221},[215,7004,7005],{"class":217,"line":1166},[215,7006,1482],{"emptyLinePlaceholder":656},[215,7008,7009,7012,7014,7016],{"class":217,"line":1686},[215,7010,7011],{"class":723},"  unmounted",[215,7013,1505],{"class":221},[215,7015,6217],{"class":3039},[215,7017,3557],{"class":221},[215,7019,7020,7023],{"class":217,"line":1713},[215,7021,7022],{"class":239},"    if",[215,7024,7025],{"class":221}," (el._aoClicarFora) {\n",[215,7027,7028,7031,7034,7036,7038,7040,7042],{"class":217,"line":1723},[215,7029,7030],{"class":221},"      document.",[215,7032,7033],{"class":723},"removeEventListener",[215,7035,1505],{"class":221},[215,7037,236],{"class":280},[215,7039,6993],{"class":284},[215,7041,236],{"class":280},[215,7043,6998],{"class":221},[215,7045,7046,7049],{"class":217,"line":1728},[215,7047,7048],{"class":239},"      delete",[215,7050,7051],{"class":221}," el._aoClicarFora;\n",[215,7053,7054],{"class":217,"line":1734},[215,7055,7056],{"class":221},"    }\n",[215,7058,7059],{"class":217,"line":1740},[215,7060,418],{"class":221},[215,7062,7064],{"class":217,"line":7063},25,[215,7065,6234],{"class":221},[16,7067,7068,7069,7071,7072,7074,7075,7078,7079,638],{},"A função é guardada no elemento para que o ",[20,7070,6546],{}," remova exatamente a mesma referência que o ",[20,7073,6279],{}," registrou. Sem isso, o listener fica vivo depois de o elemento sumir. Este exemplo lê ",[20,7076,7077],{},"binding.value"," só na montagem. Se a função passada puder mudar, atualize a referência também no hook ",[20,7080,6534],{},[44,7082,7084],{"id":7083},"diretiva-composable-ou-componente","Diretiva, composable ou componente?",[16,7086,7087],{},"A diretiva serve quando a lógica precisa tocar um elemento específico, como focar um campo. Se a lógica tem estado ou funciona sem estar presa a um elemento, como um debounce reutilizável, um composable resolve melhor. Quando existe estrutura visual, como um tooltip acessível ou um menu com teclado e submenus, o certo é um componente.",[16,7089,7090],{},"Um tooltip bom exige posicionamento, foco, teclado e a tecla Esc. Uma diretiva que tenta cobrir tudo isso costuma acabar mais complicada que o componente equivalente.",[44,7092,7094],{"id":7093},"como-usar-diretivas-personalizadas-no-nuxt-4","Como usar diretivas personalizadas no Nuxt 4?",[16,7096,7097,7098,7101,7102,7105,7106,7109],{},"No Nuxt, uma diretiva global é registrada em um plugin da pasta ",[20,7099,7100],{},"app\u002Fplugins\u002F",", usando ",[20,7103,7104],{},"nuxtApp.vueApp.directive",". Como o Nuxt renderiza a página no servidor, há uma regra a mais: durante o SSR, o Vue ignora os hooks de uma diretiva, porque eles manipulam o DOM e o servidor não tem DOM. Se a diretiva precisa colocar algum atributo no HTML gerado, use o hook ",[20,7107,7108],{},"getSSRProps",", que devolve as props a renderizar:",[115,7111,7113],{"className":1774,"code":7112,"language":1457,"meta":123,"style":123},"\u002F\u002F app\u002Fplugins\u002Fdirectives.ts\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.vueApp.directive(\"focus\", {\n    mounted: (el: HTMLElement) => el.focus(),\n    getSSRProps: () => ({ autofocus: true })\n  });\n});\n",[20,7114,7115,7120,7140,7158,7184,7207,7211],{"__ignoreMap":123},[215,7116,7117],{"class":217,"line":218},[215,7118,7119],{"class":776},"\u002F\u002F app\u002Fplugins\u002Fdirectives.ts\n",[215,7121,7122,7124,7126,7129,7131,7134,7136,7138],{"class":217,"line":225},[215,7123,762],{"class":239},[215,7125,765],{"class":239},[215,7127,7128],{"class":723}," defineNuxtPlugin",[215,7130,4394],{"class":221},[215,7132,7133],{"class":3039},"nuxtApp",[215,7135,3043],{"class":221},[215,7137,2700],{"class":239},[215,7139,242],{"class":221},[215,7141,7142,7145,7147,7149,7151,7153,7155],{"class":217,"line":245},[215,7143,7144],{"class":221},"  nuxtApp.vueApp.",[215,7146,6468],{"class":723},[215,7148,1505],{"class":221},[215,7150,236],{"class":280},[215,7152,6227],{"class":284},[215,7154,236],{"class":280},[215,7156,7157],{"class":221},", {\n",[215,7159,7160,7163,7165,7167,7169,7171,7173,7175,7177,7179,7181],{"class":217,"line":261},[215,7161,7162],{"class":723},"    mounted",[215,7164,133],{"class":239},[215,7166,2806],{"class":221},[215,7168,6217],{"class":3039},[215,7170,133],{"class":239},[215,7172,6826],{"class":3526},[215,7174,3043],{"class":221},[215,7176,2700],{"class":239},[215,7178,6224],{"class":221},[215,7180,6227],{"class":723},[215,7182,7183],{"class":221},"(),\n",[215,7185,7186,7189,7191,7194,7196,7199,7201,7204],{"class":217,"line":267},[215,7187,7188],{"class":723},"    getSSRProps",[215,7190,133],{"class":239},[215,7192,7193],{"class":221}," () ",[215,7195,2700],{"class":239},[215,7197,7198],{"class":221}," ({ autofocus",[215,7200,133],{"class":239},[215,7202,7203],{"class":901}," true",[215,7205,7206],{"class":221}," })\n",[215,7208,7209],{"class":217,"line":293},[215,7210,2865],{"class":221},[215,7212,7213],{"class":217,"line":314},[215,7214,809],{"class":221},[16,7216,7217,7218,7221,7222,7225,7226,7228,7229,7232],{},"Com esse plugin, ",[20,7219,7220],{},"\u003Cinput v-focus \u002F>"," sai do servidor como ",[20,7223,7224],{},"\u003Cinput autofocus>",". Sem o ",[20,7227,7108],{},", sairia um ",[20,7230,7231],{},"\u003Cinput>"," sem nada, e o foco só viria depois da hidratação.",[16,7234,7235,7236,7239,7240,7243,7244,7247],{},"A documentação do Nuxt pede que a diretiva seja registrada no cliente e no servidor, a menos que ela só seja usada em um dos lados. Para uma diretiva exclusiva do navegador, como o ",[20,7237,7238],{},"v-click-outside",", crie o plugin com sufixo ",[20,7241,7242],{},".client"," e um plugin ",[20,7245,7246],{},".server"," com uma diretiva vazia, só para o Vue reconhecer o nome:",[115,7249,7251],{"className":1774,"code":7250,"language":1457,"meta":123,"style":123},"\u002F\u002F app\u002Fplugins\u002Fclick-outside.client.ts\nimport { vClickOutside } from \"~\u002Fdirectives\u002FclickOutside\";\n\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.vueApp.directive(\"click-outside\", vClickOutside);\n});\n",[20,7252,7253,7258,7276,7280,7298,7316],{"__ignoreMap":123},[215,7254,7255],{"class":217,"line":218},[215,7256,7257],{"class":776},"\u002F\u002F app\u002Fplugins\u002Fclick-outside.client.ts\n",[215,7259,7260,7262,7265,7267,7269,7272,7274],{"class":217,"line":225},[215,7261,5218],{"class":239},[215,7263,7264],{"class":221}," { vClickOutside } ",[215,7266,4035],{"class":239},[215,7268,281],{"class":280},[215,7270,7271],{"class":284},"~\u002Fdirectives\u002FclickOutside",[215,7273,236],{"class":280},[215,7275,3608],{"class":221},[215,7277,7278],{"class":217,"line":245},[215,7279,1482],{"emptyLinePlaceholder":656},[215,7281,7282,7284,7286,7288,7290,7292,7294,7296],{"class":217,"line":261},[215,7283,762],{"class":239},[215,7285,765],{"class":239},[215,7287,7128],{"class":723},[215,7289,4394],{"class":221},[215,7291,7133],{"class":3039},[215,7293,3043],{"class":221},[215,7295,2700],{"class":239},[215,7297,242],{"class":221},[215,7299,7300,7302,7304,7306,7308,7311,7313],{"class":217,"line":267},[215,7301,7144],{"class":221},[215,7303,6468],{"class":723},[215,7305,1505],{"class":221},[215,7307,236],{"class":280},[215,7309,7310],{"class":284},"click-outside",[215,7312,236],{"class":280},[215,7314,7315],{"class":221},", vClickOutside);\n",[215,7317,7318],{"class":217,"line":293},[215,7319,809],{"class":221},[115,7321,7323],{"className":1774,"code":7322,"language":1457,"meta":123,"style":123},"\u002F\u002F app\u002Fplugins\u002Fclick-outside.server.ts\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.vueApp.directive(\"click-outside\", {});\n});\n",[20,7324,7325,7330,7348,7365],{"__ignoreMap":123},[215,7326,7327],{"class":217,"line":218},[215,7328,7329],{"class":776},"\u002F\u002F app\u002Fplugins\u002Fclick-outside.server.ts\n",[215,7331,7332,7334,7336,7338,7340,7342,7344,7346],{"class":217,"line":225},[215,7333,762],{"class":239},[215,7335,765],{"class":239},[215,7337,7128],{"class":723},[215,7339,4394],{"class":221},[215,7341,7133],{"class":3039},[215,7343,3043],{"class":221},[215,7345,2700],{"class":239},[215,7347,242],{"class":221},[215,7349,7350,7352,7354,7356,7358,7360,7362],{"class":217,"line":245},[215,7351,7144],{"class":221},[215,7353,6468],{"class":723},[215,7355,1505],{"class":221},[215,7357,236],{"class":280},[215,7359,7310],{"class":284},[215,7361,236],{"class":280},[215,7363,7364],{"class":221},", {});\n",[215,7366,7367],{"class":217,"line":261},[215,7368,809],{"class":221},[44,7370,7372],{"id":7371},"o-que-muda-das-diretivas-do-vue-2-para-o-vue-3","O que muda das diretivas do Vue 2 para o Vue 3?",[16,7374,7375],{},"Muitos tutoriais antigos ainda usam os hooks do Vue 2, e eles não funcionam no Vue 3. Veja a correspondência:",[75,7377,7378,7386,7393,7400,7407,7415],{},[78,7379,7380,7383,7384,638],{},[20,7381,7382],{},"bind"," virou ",[20,7385,6517],{},[78,7387,7388,7383,7391,638],{},[20,7389,7390],{},"inserted",[20,7392,6279],{},[78,7394,7395,7383,7398,638],{},[20,7396,7397],{},"componentUpdated",[20,7399,6534],{},[78,7401,7402,7383,7405,638],{},[20,7403,7404],{},"unbind",[20,7406,6546],{},[78,7408,7409,7412,7413,638],{},[20,7410,7411],{},"update"," foi removido, porque era redundante com ",[20,7414,6534],{},[78,7416,7417,1928,7419,62,7421,7423],{},[20,7418,6511],{},[20,7420,6528],{},[20,7422,6540],{}," são novos.",[16,7425,7426,7427,7429,7430,7433,7434,7437,7438,638],{},"O objeto ",[20,7428,6556],{}," também perdeu a string ",[20,7431,7432],{},"expression",", e a instância do componente deixou de vir de ",[20,7435,7436],{},"vnode.context"," e passou para ",[20,7439,7440],{},"binding.instance",[44,7442,7444],{"id":7443},"quais-são-os-erros-mais-comuns-com-diretivas","Quais são os erros mais comuns com diretivas?",[75,7446,7447,7458,7464,7470,7475,7478,7481],{},[78,7448,7449,7450,62,7452,7454,7455,7457],{},"Usar ",[20,7451,4910],{},[20,7453,4934],{}," no mesmo elemento. O ",[20,7456,4910],{}," é avaliado primeiro, e a documentação não recomenda a combinação. Filtre a lista em uma propriedade computada ou separe em dois elementos.",[78,7459,7460,7461,7463],{},"Usar o índice como ",[20,7462,5798],{}," em listas que mudam.",[78,7465,7466,7467,7469],{},"Aplicar ",[20,7468,6059],{}," a texto que veio do usuário.",[78,7471,7472,7473,638],{},"Registrar um listener ou timer sem removê-lo em ",[20,7474,6546],{},[78,7476,7477],{},"Aplicar uma diretiva personalizada em um componente com mais de um elemento raiz. A diretiva é ignorada e o Vue emite um aviso. A documentação não recomenda usar diretivas personalizadas em componentes.",[78,7479,7480],{},"Contar com a manipulação do DOM durante o SSR.",[78,7482,7483],{},"Criar uma diretiva para algo que seria mais simples como componente.",[44,7485,2349],{"id":2348},[625,7487,4923],{"id":7488},"o-que-são-diretivas-no-vue-1",[16,7490,7491,7492,7494],{},"São atributos especiais de template, iniciados por ",[20,7493,4906],{},", que aplicam comportamento reativo ao elemento, como alterar atributos, ouvir eventos, repetir elementos ou controlar a renderização.",[625,7496,7498],{"id":7497},"qual-é-a-diferença-entre-v-if-e-v-show","Qual é a diferença entre v-if e v-show?",[16,7500,18,7501,7503,7504,7506,7507,7509,7510,7512,7513,7515],{},[20,7502,4910],{}," monta e desmonta o bloco conforme a condição. O ",[20,7505,4913],{}," mantém o elemento no DOM e alterna o ",[20,7508,5998],{},". Use ",[20,7511,4913],{}," para alternâncias frequentes e ",[20,7514,4910],{}," quando a condição muda pouco.",[625,7517,7519],{"id":7518},"posso-usar-o-índice-do-array-como-key-no-v-for","Posso usar o índice do array como key no v-for?",[16,7521,7522],{},"Só em listas estáticas, que nunca mudam de ordem nem de tamanho. Em listas que mudam, use um id estável do próprio item.",[625,7524,7526],{"id":7525},"como-criar-uma-diretiva-personalizada-com-script-setup","Como criar uma diretiva personalizada com script setup?",[16,7528,7529,7530,7532,7533,7535,7536,7538],{},"Declare uma variável em camelCase iniciada por ",[20,7531,6171],{},", como ",[20,7534,6175],{},", e use no template como ",[20,7537,6179],{},". O Vue trata isso como registro local.",[625,7540,7542],{"id":7541},"quando-usar-uma-diretiva-em-vez-de-um-composable","Quando usar uma diretiva em vez de um composable?",[16,7544,7545],{},"Use a diretiva quando a lógica precisa manipular diretamente um elemento do DOM. Se a lógica tem estado ou não depende de um elemento específico, prefira um composable.",[625,7547,7549],{"id":7548},"diretivas-personalizadas-funcionam-no-ssr-do-nuxt","Diretivas personalizadas funcionam no SSR do Nuxt?",[16,7551,7552,7553,7555,7556,7558,7559,638],{},"Os hooks que mexem no DOM não rodam no servidor. Para gerar atributos no HTML do servidor, implemente ",[20,7554,7108],{},". Para uma diretiva que só faz sentido no navegador, registre-a em um plugin ",[20,7557,7242],{}," e crie um stub ",[20,7560,7246],{},[44,7562,628],{"id":627},[16,7564,7565,7566,7568,7569,62,7571,7573],{},"Para escolher entre as ferramentas, comece pelo elemento: se a lógica precisa tocar nele, use uma diretiva; se tem estado e pode ser reaproveitada, use um composable; se tem estrutura visual, use um componente. Dentro das nativas, troque o índice por um id estável no ",[20,7567,4934],{}," e decida entre ",[20,7570,4910],{},[20,7572,4913],{}," pela frequência da alternância.",[16,7575,7576,7577,1928,7582,1928,7587,62,7592,638],{},"Para se aprofundar, leia as páginas da documentação sobre ",[619,7578,7581],{"href":7579,"rel":7580},"https:\u002F\u002Fpt.vuejs.org\u002Fguide\u002Freusability\u002Fcustom-directives",[636],"diretivas personalizadas",[619,7583,7586],{"href":7584,"rel":7585},"https:\u002F\u002Fpt.vuejs.org\u002Fguide\u002Fessentials\u002Fconditional",[636],"renderização condicional",[619,7588,7591],{"href":7589,"rel":7590},"https:\u002F\u002Fpt.vuejs.org\u002Fguide\u002Fessentials\u002Flist",[636],"renderização de listas",[619,7593,7596],{"href":7594,"rel":7595},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fdirectory-structure\u002Fapp\u002Fplugins",[636],"plugins do Nuxt",[640,7598,7599],{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sY_PY, html code.shiki .sY_PY{--shiki-default:#50FA7B;--shiki-default-font-style:italic}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}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 pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}html pre.shiki code .sGEwX, html code.shiki .sGEwX{--shiki-default:#FFB86C;--shiki-default-font-style:italic}html pre.shiki code .sCp4m, html code.shiki .sCp4m{--shiki-default:#8BE9FD;--shiki-default-font-style:italic}",{"title":123,"searchDepth":225,"depth":225,"links":7601},[7602,7603,7604,7614,7615,7622,7623,7624,7625,7626,7634],{"id":4922,"depth":225,"text":4923},{"id":4938,"depth":225,"text":4939},{"id":5145,"depth":225,"text":5146,"children":7605},[7606,7607,7608,7609,7610,7611,7612,7613],{"id":5149,"depth":245,"text":5150},{"id":5269,"depth":245,"text":5270},{"id":5420,"depth":245,"text":5421},{"id":5594,"depth":245,"text":5595},{"id":5802,"depth":245,"text":5803},{"id":5989,"depth":245,"text":5990},{"id":6045,"depth":245,"text":6046},{"id":6071,"depth":245,"text":6072},{"id":6113,"depth":225,"text":6114},{"id":6154,"depth":225,"text":6155,"children":7616},[7617,7618,7619,7620,7621],{"id":6161,"depth":245,"text":6162},{"id":6283,"depth":245,"text":6284},{"id":6500,"depth":245,"text":6501},{"id":6601,"depth":245,"text":6602},{"id":6769,"depth":245,"text":6770},{"id":7083,"depth":225,"text":7084},{"id":7093,"depth":225,"text":7094},{"id":7371,"depth":225,"text":7372},{"id":7443,"depth":225,"text":7444},{"id":2348,"depth":225,"text":2349,"children":7627},[7628,7629,7630,7631,7632,7633],{"id":7488,"depth":245,"text":4923},{"id":7497,"depth":245,"text":7498},{"id":7518,"depth":245,"text":7519},{"id":7525,"depth":245,"text":7526},{"id":7541,"depth":245,"text":7542},{"id":7548,"depth":245,"text":7549},{"id":627,"depth":225,"text":628},[651],"Aprenda a usar as diretivas nativas e a criar diretivas personalizadas no Vue 3, com hooks, key, modificadores e os cuidados com SSR no Nuxt, com exemplos.","what-are-directives-in-vue-and-how-to-implement-them",{},"\u002Fblog\u002Fo-que-sao-diretivas-no-vue-e-como-implementa-las","o-que-sao-diretivas-no-vue-e-como-implementa-las","2025-03-21",{"title":4894,"description":7636},{"loc":7639,"lastmod":664},"blog\u002Fo-que-sao-diretivas-no-vue-e-como-implementa-las",2016,"tsty9dvPlBKhe0nTa6fWMD_j-LMAkYIDc7OMOGceuL0",{"id":7648,"title":7649,"articleCover":7650,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":4896,"articleCoverWidth":4897,"authorId":4898,"body":7651,"categoriesIds":8645,"description":8646,"enAlternateSlug":8647,"extension":654,"meta":8648,"navigation":656,"path":8649,"ptBrAlternateSlug":8650,"publishedAt":8651,"seo":8652,"sitemap":8653,"stem":8656,"updatedAt":664,"wordCount":8657,"__hash__":8658},"blog\u002Fblog\u002Fqual-a-diferenca-entre-a-composition-api-e-options-api-no-vuejs.md","Qual a diferença entre a Composition API e Options API no VueJS","\u002Fimages\u002Fblog\u002Fvue\u002Fqual-a-diferenca-entre-a-composition-api-e-options-api-no-vuejs.webp",{"type":13,"value":7652,"toc":8634},[7653,7663,7667,7670,7673,7676,7984,7988,8007,8030,8033,8067,8071,8074,8077,8080,8338,8341,8370,8373,8405,8408,8414,8418,8428,8521,8525,8530,8569,8574,8618,8622,8625,8628,8631],[16,7654,7655,7656,2413,7659,7662],{},"No desenvolvimento web, a escolha do framework e das ferramentas é crucial para a estrutura, escalabilidade e manutenção de um projeto. Duas estratégias se sobressaem quando falamos de Vue.js: a ",[741,7657,7658],{},"API de Opções",[741,7660,7661],{},"API de Composições",". As duas proporcionam formas diferentes de organizar e administrar a lógica de um aplicativo, cada uma com seus benefícios e casos de uso particulares.",[44,7664,7666],{"id":7665},"options-api-a-essência-do-vue","Options API: A Essência do Vue",[16,7668,7669],{},"A API de Opções é a maneira tradicional de utilizar o Vue.js. Presente desde o início, sua sintaxe se baseia em um objeto de opções para estruturar as propriedades de um componente.",[16,7671,7672],{},"Parece complicado, mas na prática é mais simples do que parece!",[16,7674,7675],{},"Veja como fica:",[115,7677,7679],{"className":1434,"code":7678,"language":1436,"meta":123,"style":123},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp>Contagem: {{ count }}\u003C\u002Fp>\n    \u003Cbr \u002F>\n    \u003Cbutton v-on:click=\"increment\">Incrementar\u003C\u002Fbutton>\n    \u003Cbutton v-on:click=\"reset\">Resetar\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  \u002F\u002F Estado do componente\n  data() {\n    return {\n      count: 0\n    };\n  },\n\n  \u002F\u002F Métodos do componente\n  methods: {\n    increment() {\n      this.count++;\n    },\n    reset() {\n      this.count = 0;\n    }\n  },\n\n  \u002F\u002F Propriedade computada\n  computed: {\n    doubleCount() {\n      return this.count * 2;\n    }\n  }\n};\n\u003C\u002Fscript>\n",[20,7680,7681,7689,7697,7710,7719,7744,7767,7775,7783,7787,7795,7803,7808,7815,7821,7831,7835,7839,7843,7848,7857,7864,7878,7882,7889,7902,7907,7912,7917,7923,7933,7941,7960,7965,7970,7975],{"__ignoreMap":123},[215,7682,7683,7685,7687],{"class":217,"line":218},[215,7684,1067],{"class":221},[215,7686,2888],{"class":239},[215,7688,1086],{"class":221},[215,7690,7691,7693,7695],{"class":217,"line":225},[215,7692,2895],{"class":221},[215,7694,3142],{"class":239},[215,7696,1086],{"class":221},[215,7698,7699,7701,7703,7706,7708],{"class":217,"line":245},[215,7700,2917],{"class":221},[215,7702,16],{"class":239},[215,7704,7705],{"class":221},">Contagem: {{ count }}\u003C\u002F",[215,7707,16],{"class":239},[215,7709,1086],{"class":221},[215,7711,7712,7714,7717],{"class":217,"line":261},[215,7713,2917],{"class":221},[215,7715,7716],{"class":239},"br",[215,7718,3247],{"class":221},[215,7720,7721,7723,7725,7728,7730,7732,7735,7737,7740,7742],{"class":217,"line":267},[215,7722,2917],{"class":221},[215,7724,3996],{"class":239},[215,7726,7727],{"class":1073}," v-on:click",[215,7729,905],{"class":239},[215,7731,236],{"class":280},[215,7733,7734],{"class":284},"increment",[215,7736,236],{"class":280},[215,7738,7739],{"class":221},">Incrementar\u003C\u002F",[215,7741,3996],{"class":239},[215,7743,1086],{"class":221},[215,7745,7746,7748,7750,7752,7754,7756,7759,7761,7763,7765],{"class":217,"line":293},[215,7747,2917],{"class":221},[215,7749,3996],{"class":239},[215,7751,7727],{"class":1073},[215,7753,905],{"class":239},[215,7755,236],{"class":280},[215,7757,7758],{"class":284},"reset",[215,7760,236],{"class":280},[215,7762,5487],{"class":221},[215,7764,3996],{"class":239},[215,7766,1086],{"class":221},[215,7768,7769,7771,7773],{"class":217,"line":314},[215,7770,2932],{"class":221},[215,7772,3142],{"class":239},[215,7774,1086],{"class":221},[215,7776,7777,7779,7781],{"class":217,"line":334},[215,7778,1169],{"class":221},[215,7780,2888],{"class":239},[215,7782,1086],{"class":221},[215,7784,7785],{"class":217,"line":340},[215,7786,1482],{"emptyLinePlaceholder":656},[215,7788,7789,7791,7793],{"class":217,"line":345},[215,7790,1067],{"class":221},[215,7792,1070],{"class":239},[215,7794,1086],{"class":221},[215,7796,7797,7799,7801],{"class":217,"line":365},[215,7798,762],{"class":239},[215,7800,765],{"class":239},[215,7802,242],{"class":221},[215,7804,7805],{"class":217,"line":385},[215,7806,7807],{"class":776},"  \u002F\u002F Estado do componente\n",[215,7809,7810,7813],{"class":217,"line":403},[215,7811,7812],{"class":723},"  data",[215,7814,3869],{"class":221},[215,7816,7817,7819],{"class":217,"line":409},[215,7818,4443],{"class":239},[215,7820,242],{"class":221},[215,7822,7823,7826,7828],{"class":217,"line":415},[215,7824,7825],{"class":221},"      count",[215,7827,133],{"class":239},[215,7829,7830],{"class":901}," 0\n",[215,7832,7833],{"class":217,"line":421},[215,7834,6974],{"class":221},[215,7836,7837],{"class":217,"line":1161},[215,7838,1831],{"class":221},[215,7840,7841],{"class":217,"line":1166},[215,7842,1482],{"emptyLinePlaceholder":656},[215,7844,7845],{"class":217,"line":1686},[215,7846,7847],{"class":776},"  \u002F\u002F Métodos do componente\n",[215,7849,7850,7853,7855],{"class":217,"line":1713},[215,7851,7852],{"class":221},"  methods",[215,7854,133],{"class":239},[215,7856,242],{"class":221},[215,7858,7859,7862],{"class":217,"line":1723},[215,7860,7861],{"class":723},"    increment",[215,7863,3869],{"class":221},[215,7865,7866,7870,7873,7876],{"class":217,"line":1728},[215,7867,7869],{"class":7868},"sqerP","      this",[215,7871,7872],{"class":221},".count",[215,7874,7875],{"class":239},"++",[215,7877,3608],{"class":221},[215,7879,7880],{"class":217,"line":1734},[215,7881,4642],{"class":221},[215,7883,7884,7887],{"class":217,"line":1740},[215,7885,7886],{"class":723},"    reset",[215,7888,3869],{"class":221},[215,7890,7891,7893,7896,7898,7900],{"class":217,"line":7063},[215,7892,7869],{"class":7868},[215,7894,7895],{"class":221},".count ",[215,7897,905],{"class":239},[215,7899,4420],{"class":901},[215,7901,3608],{"class":221},[215,7903,7905],{"class":217,"line":7904},26,[215,7906,7056],{"class":221},[215,7908,7910],{"class":217,"line":7909},27,[215,7911,1831],{"class":221},[215,7913,7915],{"class":217,"line":7914},28,[215,7916,1482],{"emptyLinePlaceholder":656},[215,7918,7920],{"class":217,"line":7919},29,[215,7921,7922],{"class":776},"  \u002F\u002F Propriedade computada\n",[215,7924,7926,7929,7931],{"class":217,"line":7925},30,[215,7927,7928],{"class":221},"  computed",[215,7930,133],{"class":239},[215,7932,242],{"class":221},[215,7934,7936,7939],{"class":217,"line":7935},31,[215,7937,7938],{"class":723},"    doubleCount",[215,7940,3869],{"class":221},[215,7942,7944,7947,7950,7952,7955,7958],{"class":217,"line":7943},32,[215,7945,7946],{"class":239},"      return",[215,7948,7949],{"class":7868}," this",[215,7951,7895],{"class":221},[215,7953,7954],{"class":239},"*",[215,7956,7957],{"class":901}," 2",[215,7959,3608],{"class":221},[215,7961,7963],{"class":217,"line":7962},33,[215,7964,7056],{"class":221},[215,7966,7968],{"class":217,"line":7967},34,[215,7969,418],{"class":221},[215,7971,7973],{"class":217,"line":7972},35,[215,7974,6234],{"class":221},[215,7976,7978,7980,7982],{"class":217,"line":7977},36,[215,7979,1169],{"class":221},[215,7981,1070],{"class":239},[215,7983,1086],{"class":221},[625,7985,7987],{"id":7986},"estrutura","Estrutura:",[75,7989,7990,7996,8002],{},[78,7991,7992,7995],{},[20,7993,7994],{},"data",": Determina as informações reativas do componente, ou seja, onde são declaradas as variáveis que terão valores dinâmicos.",[78,7997,7998,8001],{},[20,7999,8000],{},"methods",": Onde especificamos funções personalizadas. Estas funções têm a capacidade de alterar estados, reagir a eventos do usuário e realizar lógicas específicas.",[78,8003,8004,8006],{},[20,8005,2950],{},": Define propriedades computadas que derivam dos dados, ou seja, valores que só precisam ser calculados uma vez e atualizados apenas quando alguma variável relacionada muda.",[16,8008,8009,8010,8012,8013,8012,8016,8012,8019,8012,8022,8025,8026,8029],{},"O Vue.js tambem possue outras opções, como ",[20,8011,1408],{},", ",[20,8014,8015],{},"components",[20,8017,8018],{},"props",[20,8020,8021],{},"directives",[20,8023,8024],{},"watch"," e ",[20,8027,8028],{},"emits",", entre outros. No entanto, podemos explorar mais esses aspectos em um artigo futuro.",[16,8031,8032],{},"Vamos examinar algumas das propriedades e restrições apresentadas pela API de Opções:",[75,8034,8035,8053,8064],{},[78,8036,8037,8038,8041,8042,8044,8045,8048,8049,62,8051,638],{},"Acesso a dados através de ",[20,8039,8040],{},"this",": As variáveis definidas na propriedade ",[20,8043,7994],{}," só podem ser acessadas por meio do contexto ",[20,8046,8047],{},"this.",", conforme ilustrado nos métodos ",[20,8050,7734],{},[20,8052,7758],{},[78,8054,8055,8056,8058,8059,1928,8061,8063],{},"Divisão rígida no código: Funções e variáveis são categorizadas em seções específicas, tais como ",[20,8057,7994],{},",",[20,8060,8000],{},[20,8062,2950],{},". Isso pode dividir a lógica associada a uma funcionalidade semelhante, tornando mais difícil a leitura e a manutenção do código em projetos de maior escala.",[78,8065,8066],{},"Dificuldades na reutilização de lógica: A API de Opções utiliza Mixins para compartilhar lógica entre componentes, o que pode resultar em conflitos de nomenclatura e problemas na rastreabilidade do código.",[44,8068,8070],{"id":8069},"composition-api-flexibilidade-e-praticidade","Composition API: Flexibilidade e Praticidade",[16,8072,8073],{},"A API de Composição é a nova maneira de estruturar o código, introduzida no Vue 3, com a API de Composição, é possível estruturar a lógica do seu componente de maneira mais transparente e intuitiva.",[16,8075,8076],{},"Vamos ver o quanto realmente mudou e esclarecer qualquer dúvida sobre a API de Composição.",[16,8078,8079],{},"Vou criar o mesmo aplicativo, mas dessa vez com a API de Composição, observe como fica:",[115,8081,8083],{"className":1434,"code":8082,"language":1436,"meta":123,"style":123},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp>Contagem: {{ count }}\u003C\u002Fp>\n    \u003Cbr \u002F>\n    \u003Cbutton v-on:click=\"increment\">Incrementar\u003C\u002Fbutton>\n    \u003Cbutton v-on:click=\"reset\">Resetar\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref, computed } from \"vue\";\n\n\u002F\u002F Estado reativo\nconst count = ref(0);\n\n\u002F\u002F Métodos\nfunction increment() {\n  count.value++;\n}\n\nfunction reset() {\n  count.value = 0;\n}\n\n\u002F\u002F Propriedade computada\nconst doubleCount = computed(() => count.value * 2);\n\u003C\u002Fscript>\n",[20,8084,8085,8093,8101,8113,8121,8143,8165,8173,8181,8185,8195,8212,8216,8221,8238,8242,8247,8256,8265,8269,8273,8282,8293,8297,8301,8306,8330],{"__ignoreMap":123},[215,8086,8087,8089,8091],{"class":217,"line":218},[215,8088,1067],{"class":221},[215,8090,2888],{"class":239},[215,8092,1086],{"class":221},[215,8094,8095,8097,8099],{"class":217,"line":225},[215,8096,2895],{"class":221},[215,8098,3142],{"class":239},[215,8100,1086],{"class":221},[215,8102,8103,8105,8107,8109,8111],{"class":217,"line":245},[215,8104,2917],{"class":221},[215,8106,16],{"class":239},[215,8108,7705],{"class":221},[215,8110,16],{"class":239},[215,8112,1086],{"class":221},[215,8114,8115,8117,8119],{"class":217,"line":261},[215,8116,2917],{"class":221},[215,8118,7716],{"class":239},[215,8120,3247],{"class":221},[215,8122,8123,8125,8127,8129,8131,8133,8135,8137,8139,8141],{"class":217,"line":267},[215,8124,2917],{"class":221},[215,8126,3996],{"class":239},[215,8128,7727],{"class":1073},[215,8130,905],{"class":239},[215,8132,236],{"class":280},[215,8134,7734],{"class":284},[215,8136,236],{"class":280},[215,8138,7739],{"class":221},[215,8140,3996],{"class":239},[215,8142,1086],{"class":221},[215,8144,8145,8147,8149,8151,8153,8155,8157,8159,8161,8163],{"class":217,"line":293},[215,8146,2917],{"class":221},[215,8148,3996],{"class":239},[215,8150,7727],{"class":1073},[215,8152,905],{"class":239},[215,8154,236],{"class":280},[215,8156,7758],{"class":284},[215,8158,236],{"class":280},[215,8160,5487],{"class":221},[215,8162,3996],{"class":239},[215,8164,1086],{"class":221},[215,8166,8167,8169,8171],{"class":217,"line":314},[215,8168,2932],{"class":221},[215,8170,3142],{"class":239},[215,8172,1086],{"class":221},[215,8174,8175,8177,8179],{"class":217,"line":334},[215,8176,1169],{"class":221},[215,8178,2888],{"class":239},[215,8180,1086],{"class":221},[215,8182,8183],{"class":217,"line":340},[215,8184,1482],{"emptyLinePlaceholder":656},[215,8186,8187,8189,8191,8193],{"class":217,"line":345},[215,8188,1067],{"class":221},[215,8190,1070],{"class":239},[215,8192,1447],{"class":1073},[215,8194,1086],{"class":221},[215,8196,8197,8199,8202,8204,8206,8208,8210],{"class":217,"line":365},[215,8198,5218],{"class":239},[215,8200,8201],{"class":221}," { ref, computed } ",[215,8203,4035],{"class":239},[215,8205,281],{"class":280},[215,8207,1436],{"class":284},[215,8209,236],{"class":280},[215,8211,3608],{"class":221},[215,8213,8214],{"class":217,"line":385},[215,8215,1482],{"emptyLinePlaceholder":656},[215,8217,8218],{"class":217,"line":403},[215,8219,8220],{"class":776},"\u002F\u002F Estado reativo\n",[215,8222,8223,8225,8228,8230,8232,8234,8236],{"class":217,"line":409},[215,8224,1466],{"class":239},[215,8226,8227],{"class":221}," count ",[215,8229,905],{"class":239},[215,8231,5247],{"class":723},[215,8233,1505],{"class":221},[215,8235,3585],{"class":901},[215,8237,1559],{"class":221},[215,8239,8240],{"class":217,"line":415},[215,8241,1482],{"emptyLinePlaceholder":656},[215,8243,8244],{"class":217,"line":421},[215,8245,8246],{"class":776},"\u002F\u002F Métodos\n",[215,8248,8249,8251,8254],{"class":217,"line":1161},[215,8250,5562],{"class":239},[215,8252,8253],{"class":723}," increment",[215,8255,3869],{"class":221},[215,8257,8258,8261,8263],{"class":217,"line":1166},[215,8259,8260],{"class":221},"  count.value",[215,8262,7875],{"class":239},[215,8264,3608],{"class":221},[215,8266,8267],{"class":217,"line":1686},[215,8268,424],{"class":221},[215,8270,8271],{"class":217,"line":1713},[215,8272,1482],{"emptyLinePlaceholder":656},[215,8274,8275,8277,8280],{"class":217,"line":1723},[215,8276,5562],{"class":239},[215,8278,8279],{"class":723}," reset",[215,8281,3869],{"class":221},[215,8283,8284,8287,8289,8291],{"class":217,"line":1728},[215,8285,8286],{"class":221},"  count.value ",[215,8288,905],{"class":239},[215,8290,4420],{"class":901},[215,8292,3608],{"class":221},[215,8294,8295],{"class":217,"line":1734},[215,8296,424],{"class":221},[215,8298,8299],{"class":217,"line":1740},[215,8300,1482],{"emptyLinePlaceholder":656},[215,8302,8303],{"class":217,"line":7063},[215,8304,8305],{"class":776},"\u002F\u002F Propriedade computada\n",[215,8307,8308,8310,8313,8315,8317,8319,8321,8324,8326,8328],{"class":217,"line":7904},[215,8309,1466],{"class":239},[215,8311,8312],{"class":221}," doubleCount ",[215,8314,905],{"class":239},[215,8316,2694],{"class":723},[215,8318,2697],{"class":221},[215,8320,2700],{"class":239},[215,8322,8323],{"class":221}," count.value ",[215,8325,7954],{"class":239},[215,8327,7957],{"class":901},[215,8329,1559],{"class":221},[215,8331,8332,8334,8336],{"class":217,"line":7909},[215,8333,1169],{"class":221},[215,8335,1070],{"class":239},[215,8337,1086],{"class":221},[625,8339,7987],{"id":8340},"estrutura-1",[75,8342,8343,8351,8359],{},[78,8344,8345,8350],{},[741,8346,8347],{},[20,8348,8349],{},"\u003Cscript setup\u002F>",": É aonde toda logica e código que não será renderizando para o usuário é colocado.",[78,8352,8353,8358],{},[741,8354,8355],{},[20,8356,8357],{},"ref()",": É o que define se aquela variável vai ser ou não reativa.",[78,8360,8361,8366,8367,8369],{},[741,8362,8363],{},[20,8364,8365],{},"computed()",": Faz a mesma função do ",[20,8368,2950],{}," da Options API",[16,8371,8372],{},"Vamos explorar algumas das funcionalidades necessárias trazidas pela API de Composição?",[75,8374,8375,8378,8391],{},[78,8376,8377],{},"É necessário importar o que vamos usar.",[78,8379,8380,8381,8383,8384,8387,8388,8390],{},"Precisamos usar o método ",[20,8382,8357],{}," para deixar a variável reativa, por exemplo, se ",[20,8385,8386],{},"count"," não tivesse o ",[20,8389,8357],{},", quando atualizado ele não atualizaria sozinho e seria necessario regarregar a pagina.",[78,8392,8393,8394,8396,8397,8400,8401,62,8403,638],{},"Os dados das variáveis que utilizam ",[20,8395,8357],{}," só poderão ser acessados utilizando ",[20,8398,8399],{},".value"," como utilizado nas funções ",[20,8402,7734],{},[20,8404,7758],{},[16,8406,8407],{},"Abaixo está um gif do aplicativo que criamos nesse artigo com o Vue.js:",[105,8409],{"alt":8410,"height":8411,"src":8412,"width":8413},"Aplicativo simples criado para o artigo usando as APIS do Vue.js",450,"images\u002Fblog\u002Fvue\u002Fdemonstracao-do-aplicativo-criado-no-artigo-com-as-apis-do-vue.gif",800,[44,8415,8417],{"id":8416},"comparação-detalhada","Comparação Detalhada",[16,8419,8420,8421,62,8424,8427],{},"Cada API no Vue.js tem características únicas que interferem na forma como o código é organizado, gerenciado e interpretado. A seleção entre ",[741,8422,8423],{},"Options API",[741,8425,8426],{},"Composition API"," impacta principalmente a estruturação do código e facilidade de leitura. Abaixo, destacamos as principais diferenças entre elas:",[135,8429,8430,8441],{},[138,8431,8432],{},[141,8433,8434,8437,8439],{},[144,8435,8436],{},"Característica",[144,8438,8423],{},[144,8440,8426],{},[154,8442,8443,8463,8482,8495,8508],{},[141,8444,8445,8450,8460],{},[159,8446,8447],{},[741,8448,8449],{},"Estruturação do Código",[159,8451,8452,8453,1928,8455,1928,8457,8459],{},"Estrutura o código de acordo com o tipo de opção (",[20,8454,7994],{},[20,8456,8000],{},[20,8458,2950],{},", etc.), possibilitando a fragmentação da lógica em partes complexas.",[159,8461,8462],{},"Organiza o código por função, aprimorando a interpretação e aproximando-o do JavaScript moderno.",[141,8464,8465,8470,8473],{},[159,8466,8467],{},[741,8468,8469],{},"Reatividade",[159,8471,8472],{},"Gerencia automaticamente as propriedades reativas definidas em data.",[159,8474,8475,8476,62,8478,8481],{},"Requer a utilização clara de ",[20,8477,8357],{},[20,8479,8480],{},"reactive()"," para tornar variáveis com valores dinâmicos.",[141,8483,8484,8489,8492],{},[159,8485,8486],{},[741,8487,8488],{},"Legibilidade",[159,8490,8491],{},"Para aqueles que já tem experiência da Programação Orientada a Objetos (POO), pode ser mais familiar, embora possa ser menos intuitiva para projetos de grande escala.",[159,8493,8494],{},"Ideal para desenvolvedores com experiência em JavaScript, além de proporcionar um suporte excepcional ao TypeScript.",[141,8496,8497,8502,8505],{},[159,8498,8499],{},[741,8500,8501],{},"Curva de Aprendizagem",[159,8503,8504],{},"Perfeito para iniciantes, pois segue uma estrutura organizada e previsível, na qual cada função do componente é dividida em blocos específicos.",[159,8506,8507],{},"Possui uma curva de aprendizado mais acentuada, pois exige um entendimento mais profundo de conceitos como reatividade. No entanto, torna-se mais intuitiva com a prática.",[141,8509,8510,8515,8518],{},[159,8511,8512],{},[741,8513,8514],{},"Manutenção e Escalabilidade",[159,8516,8517],{},"Em projetos de grande escala, a manutenção pode se tornar um desafio, pois a lógica de um componente é fragmentada em várias seções, dificultando a leitura e a depuração.",[159,8519,8520],{},"Ideal para projetos escaláveis, pois simplifica a organização do código e permite a divisão de responsabilidades de forma modular e reutilizável.",[44,8522,8524],{"id":8523},"mas-quando-usar-cada-api","Mas quando usar cada API?",[16,8526,8527],{},[741,8528,8529],{},"A Options API é ideal para:",[75,8531,8532,8545,8551,8557,8563],{},[78,8533,8534,8537,8538,1928,8540,1928,8542,8544],{},[741,8535,8536],{},"Código mais estruturado e declarativo"," - Ideal para quem gosta a distinção clara entre estado, métodos e propriedades computadas (como (",[20,8539,7994],{},[20,8541,8000],{},[20,8543,2950],{},", etc.).",[78,8546,8547,8550],{},[741,8548,8549],{},"Fácil aprendizagem"," - Se você está iniciando no Vue.js, essa metodologia é mais intuitiva e organizada, seguindo um padrão claramente estabelecido.",[78,8552,8553,8556],{},[741,8554,8555],{},"Compatibilidade com bibliotecas antigas"," - Algumas bibliotecas do Vue ainda recorrem à Options API, o que torna essa opção mais prática para prevenir problemas de compatibilidade.",[78,8558,8559,8562],{},[741,8560,8561],{},"Depuração simplificada"," - Devido à estrutura fixa do código, torna-se mais simples compreender o fluxo e identificar falhas, principalmente para aqueles que estão habituados à arquitetura convencional do Vue.js.",[78,8564,8565,8568],{},[741,8566,8567],{},"Uso eficaz de recursos em projetos menores"," -  Em aplicações de menor escala, a Options API proporciona uma solução direta e eficiente, livre de complexidade extra .",[16,8570,8571],{},[741,8572,8573],{},"A Composition API é mais recomendada para:",[75,8575,8576,8586,8597,8606,8612],{},[78,8577,8578,8581,8582,8585],{},[741,8579,8580],{},"Maior adaptabilidade e reutilização de código"," - Por meio das ",[741,8583,8584],{},"Functions",", podemos compartilhar a lógica entre componentes sem as restrições dos Mixins da API de Opções.",[78,8587,8588,8591,8592,1928,8594,8596],{},[741,8589,8590],{},"Organização aprimorada em projetos extensos"," - Em vez de segmentar o código por opções  (",[20,8593,7994],{},[20,8595,8000],{},", etc.), a lógica pode ser agrupada por funcionalidade, simplificando a manutenção.",[78,8598,8599,8602,8603,8605],{},[741,8600,8601],{},"Gerenciamento avançado de reatividade"," - Ao utilizar ",[20,8604,8357],{},", você ganha mais controle sobre o estado.",[78,8607,8608,8611],{},[741,8609,8610],{},"Facilidade na criação de hooks personalizados"," - A Composition API possibilita a elaboração de hooks reutilizáveis, tornando uma estrutura mais modular e simplificando a manutenção do código.",[78,8613,8614,8617],{},[741,8615,8616],{},"Experiência aprimorada com TypeScript"," - Se você planeja utilizar TypeScript em seu projeto, a Composition API se integra de maneira mais fluida, proporcionando uma tipagem mais exata e um suporte aprimorado para inferência de tipos.",[44,8619,8621],{"id":8620},"considerações-finais","Considerações finais",[16,8623,8624],{},"Agora que você conhece as diferenças entre a API de Opções e a API de Composição, está pronto para escolher a melhor opção para o seu projeto. Lembre-se de que cada uma tem suas vantagens e desvantagens, tudo depende da complexidade do seu projeto.",[16,8626,8627],{},"No entanto, quando se trata de preferência pessoal, geralmente prefiro usar a Composition API em meus projetos pessoais, devido à otimização com TypeScript e à capacidade de agrupar as lógicas.",[16,8629,8630],{},"No entanto, independentemente da API escolhida, o Vue.js continua sendo uma ferramenta poderosa e flexível para o desenvolvimento Front-End. 🚀",[640,8632,8633],{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sY_PY, html code.shiki .sY_PY{--shiki-default:#50FA7B;--shiki-default-font-style:italic}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}html pre.shiki code .sqerP, html code.shiki .sqerP{--shiki-default:#BD93F9;--shiki-default-font-style:italic}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);}",{"title":123,"searchDepth":225,"depth":225,"links":8635},[8636,8639,8642,8643,8644],{"id":7665,"depth":225,"text":7666,"children":8637},[8638],{"id":7986,"depth":245,"text":7987},{"id":8069,"depth":225,"text":8070,"children":8640},[8641],{"id":8340,"depth":245,"text":7987},{"id":8416,"depth":225,"text":8417},{"id":8523,"depth":225,"text":8524},{"id":8620,"depth":225,"text":8621},[651],"Entenda as principais diferenças, vantagens e aplicações práticas da Composition API e da Options API no Vue.js, e veja quando usar cada uma nos seus projetos.","what-is-the-difference-between-composition-api-and-options-api-in-vuejs",{},"\u002Fblog\u002Fqual-a-diferenca-entre-a-composition-api-e-options-api-no-vuejs","qual-a-diferenca-entre-a-composition-api-e-options-api-no-vuejs","2025-02-20",{"title":7649,"description":8646},{"loc":8649,"images":8654,"lastmod":664},[8655],{"loc":8412},"blog\u002Fqual-a-diferenca-entre-a-composition-api-e-options-api-no-vuejs",1192,"5rEhn6ZmHKqsqK-cUNJTgA-7qp-bPR13VSPGi59qE7I",{"id":8660,"title":8661,"articleCover":8662,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":8663,"categoriesIds":9600,"description":9601,"enAlternateSlug":9602,"extension":654,"meta":9603,"navigation":656,"path":9604,"ptBrAlternateSlug":9605,"publishedAt":664,"seo":9606,"sitemap":9607,"stem":9608,"updatedAt":664,"wordCount":9609,"__hash__":9610},"blog\u002Fblog\u002Fvue-vapor-mode-como-funciona-e-como-usar.md","Vue Vapor Mode: o que é e o que muda no Vue 3.6","\u002Fimages\u002Fblog\u002Fvue\u002Fvue-vapor-mode-como-funciona-e-como-usar.webp",{"type":13,"value":8664,"toc":9574},[8665,8680,8686,8690,8693,8696,8699,8703,8727,8731,8740,8865,8872,8887,8891,8902,8967,8970,8974,8980,9037,9046,9050,9055,9099,9112,9115,9178,9189,9192,9196,9199,9255,9264,9271,9274,9281,9293,9325,9332,9336,9339,9443,9457,9461,9464,9471,9475,9478,9481,9498,9500,9504,9507,9511,9519,9523,9528,9532,9541,9545,9548,9552,9564,9568,9571],[16,8666,8667,8668,8671,8672,8675,8676,8679],{},"O Vue 3.6 ainda não saiu como versão estável. Em 6 de outubro de 2026, a versão mais recente é a ",[20,8669,8670],{},"3.6.0-rc.10"," (30 de setembro), e a estável continua sendo a ",[20,8673,8674],{},"3.5.43"," (17 de setembro). Confirmei isso no npm e nas releases do repositório ",[20,8677,8678],{},"vuejs\u002Fcore"," nesta data.",[16,8681,18,8682,8685],{},[741,8683,8684],{},"Vue Vapor Mode"," é a principal novidade dessa versão: um modo de compilação que dispensa o Virtual DOM e gera código que atualiza o DOM diretamente. Ele é opt-in, então você escolhe componente a componente onde usar, sem reescrever o app. Neste artigo você vê como o Vapor funciona, como ativar no Vue e no Nuxt 4.6, o que ainda não é suportado e quando vale a pena testar.",[44,8687,8689],{"id":8688},"como-o-vapor-funciona","Como o Vapor funciona",[16,8691,8692],{},"No modo tradicional, quando um estado reativo muda, o Vue executa a função de renderização, monta uma nova árvore de VNodes (objetos JavaScript que descrevem a interface), compara com a árvore anterior e só então altera o DOM real.",[16,8694,8695],{},"No Vapor Mode, o compilador analisa o template e gera código que manipula o DOM diretamente. Os componentes Vapor não criam VNodes nem comparam árvores em tempo de execução. Quando uma variável reativa muda, só o trecho do DOM ligado a ela é atualizado.",[16,8697,8698],{},"Segundo as notas oficiais do Vue 3.6, o objetivo é reduzir o tamanho base do bundle e melhorar a performance. As mesmas notas dizem que o Vapor mostrou, em benchmarks de terceiros (o js-framework-benchmark), o mesmo nível de performance do Solid e do Svelte 5. Como em qualquer benchmark, o resultado depende do cenário medido.",[625,8700,8702],{"id":8701},"e-o-alien-signals","E o Alien Signals?",[16,8704,8705,8706,8709,8710,8715,8716,1928,8719,1928,8722,62,8724,8726],{},"O Vue 3.6 também traz uma refatoração grande do ",[20,8707,8708],{},"@vue\u002Freactivity",", baseada na biblioteca ",[619,8711,8714],{"href":8712,"rel":8713},"https:\u002F\u002Fgithub.com\u002Fstackblitz\u002Falien-signals",[636],"alien-signals",". Segundo as notas do rc.1, ela melhora o desempenho e o uso de memória do sistema de reatividade. É uma mudança interna: ",[20,8717,8718],{},"ref",[20,8720,8721],{},"reactive",[20,8723,2950],{},[20,8725,8024],{}," continuam com a mesma API. Como ela faz parte do Vue 3.6, o benefício não depende de você usar o Vapor.",[44,8728,8730],{"id":8729},"como-ativar-o-vapor-mode","Como ativar o Vapor Mode",[16,8732,8733,8734,8736,8737,133],{},"O Vapor funciona com SFCs que usam ",[20,8735,2966],{}," ou só template. Você marca o componente com o atributo ",[20,8738,8739],{},"vapor",[115,8741,8743],{"className":1434,"code":8742,"language":1436,"meta":123,"style":123},"\u003Cscript setup vapor lang=\"ts\">\nimport { ref } from 'vue'\n\nconst count = ref(0)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cbutton @click=\"count++\">\n    count is {{ count }}\n  \u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[20,8744,8745,8768,8784,8788,8805,8813,8817,8825,8844,8849,8857],{"__ignoreMap":123},[215,8746,8747,8749,8751,8753,8756,8758,8760,8762,8764,8766],{"class":217,"line":218},[215,8748,1067],{"class":221},[215,8750,1070],{"class":239},[215,8752,1447],{"class":1073},[215,8754,8755],{"class":1073}," vapor",[215,8757,1450],{"class":1073},[215,8759,905],{"class":239},[215,8761,236],{"class":280},[215,8763,1457],{"class":284},[215,8765,236],{"class":280},[215,8767,1086],{"class":221},[215,8769,8770,8772,8774,8776,8779,8781],{"class":217,"line":225},[215,8771,5218],{"class":239},[215,8773,5221],{"class":221},[215,8775,4035],{"class":239},[215,8777,8778],{"class":280}," '",[215,8780,1436],{"class":284},[215,8782,8783],{"class":280},"'\n",[215,8785,8786],{"class":217,"line":245},[215,8787,1482],{"emptyLinePlaceholder":656},[215,8789,8790,8792,8794,8796,8798,8800,8802],{"class":217,"line":261},[215,8791,1466],{"class":239},[215,8793,8227],{"class":221},[215,8795,905],{"class":239},[215,8797,5247],{"class":723},[215,8799,1505],{"class":221},[215,8801,3585],{"class":901},[215,8803,8804],{"class":221},")\n",[215,8806,8807,8809,8811],{"class":217,"line":267},[215,8808,1169],{"class":221},[215,8810,1070],{"class":239},[215,8812,1086],{"class":221},[215,8814,8815],{"class":217,"line":293},[215,8816,1482],{"emptyLinePlaceholder":656},[215,8818,8819,8821,8823],{"class":217,"line":314},[215,8820,1067],{"class":221},[215,8822,2888],{"class":239},[215,8824,1086],{"class":221},[215,8826,8827,8829,8831,8833,8835,8837,8840,8842],{"class":217,"line":334},[215,8828,2895],{"class":221},[215,8830,3996],{"class":239},[215,8832,3999],{"class":1073},[215,8834,905],{"class":239},[215,8836,236],{"class":280},[215,8838,8839],{"class":284},"count++",[215,8841,236],{"class":280},[215,8843,1086],{"class":221},[215,8845,8846],{"class":217,"line":340},[215,8847,8848],{"class":221},"    count is {{ count }}\n",[215,8850,8851,8853,8855],{"class":217,"line":345},[215,8852,2932],{"class":221},[215,8854,3996],{"class":239},[215,8856,1086],{"class":221},[215,8858,8859,8861,8863],{"class":217,"line":365},[215,8860,1169],{"class":221},[215,8862,2888],{"class":239},[215,8864,1086],{"class":221},[16,8866,8867,8868,8871],{},"Fora do Nuxt, o ",[20,8869,8870],{},"@vitejs\u002Fplugin-vue"," compila SFCs Vapor (o changelog do plugin registra suporte a componentes Vapor, inclusive só com template), então use uma versão recente.",[16,8873,8874,8875,8878,8879,8882,8883,8886],{},"O Vue também aceita ",[20,8876,8877],{},"\u003Cscript vapor>"," como atalho de ",[20,8880,8881],{},"\u003Cscript setup vapor>",". Outra opção é colocar o marcador no template, com ",[20,8884,8885],{},"\u003Ctemplate vapor>",", para compilar o SFC inteiro em modo Vapor.",[625,8888,8890],{"id":8889},"componentes-vapor-em-um-app-existente","Componentes Vapor em um app existente",[16,8892,8893,8894,8897,8898,8901],{},"Num app criado com ",[20,8895,8896],{},"createApp()",", é preciso instalar o ",[20,8899,8900],{},"vaporInteropPlugin"," para usar componentes Vapor:",[115,8903,8905],{"className":1774,"code":8904,"language":1457,"meta":123,"style":123},"import { createApp, vaporInteropPlugin } from 'vue'\nimport App from '.\u002FApp.vue'\n\ncreateApp(App).use(vaporInteropPlugin).mount('#app')\n",[20,8906,8907,8922,8936,8940],{"__ignoreMap":123},[215,8908,8909,8911,8914,8916,8918,8920],{"class":217,"line":218},[215,8910,5218],{"class":239},[215,8912,8913],{"class":221}," { createApp, vaporInteropPlugin } ",[215,8915,4035],{"class":239},[215,8917,8778],{"class":280},[215,8919,1436],{"class":284},[215,8921,8783],{"class":280},[215,8923,8924,8926,8928,8930,8932,8934],{"class":217,"line":225},[215,8925,5218],{"class":239},[215,8927,6408],{"class":221},[215,8929,4035],{"class":239},[215,8931,8778],{"class":280},[215,8933,6415],{"class":284},[215,8935,8783],{"class":280},[215,8937,8938],{"class":217,"line":245},[215,8939,1482],{"emptyLinePlaceholder":656},[215,8941,8942,8945,8948,8951,8954,8956,8958,8961,8963,8965],{"class":217,"line":261},[215,8943,8944],{"class":723},"createApp",[215,8946,8947],{"class":221},"(App).",[215,8949,8950],{"class":723},"use",[215,8952,8953],{"class":221},"(vaporInteropPlugin).",[215,8955,6486],{"class":723},[215,8957,1505],{"class":221},[215,8959,8960],{"class":280},"'",[215,8962,6493],{"class":284},[215,8964,8960],{"class":280},[215,8966,8804],{"class":221},[16,8968,8969],{},"Com o plugin instalado, componentes Vapor e componentes comuns podem ficar aninhados uns dentro dos outros. As notas do Vue avisam que isso cobre props, eventos e slots no uso padrão, mas ainda não todos os casos de borda. A recomendação deles é manter regiões distintas no app, cada uma em um modo, e evitar o aninhamento misto sempre que der.",[625,8971,8973],{"id":8972},"app-100-vapor","App 100% Vapor",[16,8975,8976,8977,133],{},"Se todos os componentes forem Vapor, use ",[20,8978,8979],{},"createVaporApp()",[115,8981,8983],{"className":1774,"code":8982,"language":1457,"meta":123,"style":123},"import { createVaporApp } from 'vue'\nimport App from '.\u002FApp.vue'\n\ncreateVaporApp(App).mount('#app')\n",[20,8984,8985,9000,9014,9018],{"__ignoreMap":123},[215,8986,8987,8989,8992,8994,8996,8998],{"class":217,"line":218},[215,8988,5218],{"class":239},[215,8990,8991],{"class":221}," { createVaporApp } ",[215,8993,4035],{"class":239},[215,8995,8778],{"class":280},[215,8997,1436],{"class":284},[215,8999,8783],{"class":280},[215,9001,9002,9004,9006,9008,9010,9012],{"class":217,"line":225},[215,9003,5218],{"class":239},[215,9005,6408],{"class":221},[215,9007,4035],{"class":239},[215,9009,8778],{"class":280},[215,9011,6415],{"class":284},[215,9013,8783],{"class":280},[215,9015,9016],{"class":217,"line":245},[215,9017,1482],{"emptyLinePlaceholder":656},[215,9019,9020,9023,9025,9027,9029,9031,9033,9035],{"class":217,"line":261},[215,9021,9022],{"class":723},"createVaporApp",[215,9024,8947],{"class":221},[215,9026,6486],{"class":723},[215,9028,1505],{"class":221},[215,9030,8960],{"class":280},[215,9032,6493],{"class":284},[215,9034,8960],{"class":280},[215,9036,8804],{"class":221},[16,9038,9039,9040,9042,9043,9045],{},"Assim o runtime do Virtual DOM não entra no bundle. Cada componente continua precisando do marcador ",[20,9041,8739],{},". Componentes escritos com render function ou JSX seguem sendo componentes de Virtual DOM e exigem interop. Um app Vapor também pode instalar o ",[20,9044,8900],{}," para aceitar componentes de Virtual DOM, mas isso traz o runtime de volta e reduz o ganho de tamanho.",[44,9047,9049],{"id":9048},"vapor-no-nuxt-46","Vapor no Nuxt 4.6",[16,9051,9052,9053,133],{},"O Nuxt 4.6, lançado em 5 de outubro de 2026, suporta o Vapor em modo de interoperabilidade. A raiz do app continua no Virtual DOM, e você escolhe quais componentes ou páginas usam Vapor. Para habilitar, ligue a opção no ",[20,9054,750],{},[115,9056,9058],{"className":1774,"code":9057,"language":1457,"meta":123,"style":123},"export default defineNuxtConfig({\n  vue: {\n    vapor: true,\n  },\n})\n",[20,9059,9060,9070,9079,9090,9094],{"__ignoreMap":123},[215,9061,9062,9064,9066,9068],{"class":217,"line":218},[215,9063,762],{"class":239},[215,9065,765],{"class":239},[215,9067,768],{"class":723},[215,9069,771],{"class":221},[215,9071,9072,9075,9077],{"class":217,"line":225},[215,9073,9074],{"class":221},"  vue",[215,9076,133],{"class":239},[215,9078,242],{"class":221},[215,9080,9081,9084,9086,9088],{"class":217,"line":245},[215,9082,9083],{"class":221},"    vapor",[215,9085,133],{"class":239},[215,9087,7203],{"class":901},[215,9089,290],{"class":221},[215,9091,9092],{"class":217,"line":261},[215,9093,1831],{"class":221},[215,9095,9096],{"class":217,"line":267},[215,9097,9098],{"class":221},"})\n",[16,9100,9101,9102,9104,9105,9108,9109,9111],{},"Depois marque os componentes com ",[20,9103,8881],{},", como no exemplo acima. O Nuxt exige Vue ",[20,9106,9107],{},"^3.6.0-rc.2"," ou mais novo. Segundo o anúncio, rotas, ",[20,9110,2954],{},", layouts e a maioria dos componentes embutidos funcionam sem mudanças. O anúncio recomenda testar o Vapor primeiro em um projeto novo.",[16,9113,9114],{},"A documentação do Nuxt lista algumas limitações do modo de interop:",[75,9116,9117,9120,9127,9147,9153,9171],{},[78,9118,9119],{},"a raiz do app não pode ser Vapor, porque os apps 100% Vapor não estão disponíveis no Nuxt;",[78,9121,9122,9123,9126],{},"componentes Vapor não expõem ",[20,9124,9125],{},"$el"," nas template refs;",[78,9128,9129,9132,9133,9136,9137,1928,9140,62,9143,9146],{},[20,9130,9131],{},"\u003CClientOnly>"," pula o repasse de atributos, e ",[20,9134,9135],{},"\u003CNuxtIsland>",", os componentes de servidor e os componentes de head que leem texto filho (",[20,9138,9139],{},"\u003CTitle>",[20,9141,9142],{},"\u003CStyle>",[20,9144,9145],{},"\u003CNoscript>",") emitem aviso se receberem um slot Vapor;",[78,9148,9149,9152],{},[20,9150,9151],{},"onPrehydrate"," com chave volta para a forma sem chave (e avisa em desenvolvimento), porque não há instância de componente para anexar a chave;",[78,9154,9155,62,9158,9161,9162,9165,9166,1910,9168,9170],{},[20,9156,9157],{},"asyncData",[20,9159,9160],{},"fetchKey"," via ",[20,9163,9164],{},"defineNuxtComponent"," não funcionam (use ",[20,9167,2954],{},[20,9169,2966],{},");",[78,9172,9173,9174,9177],{},"composables do Nuxt chamados depois de um ",[20,9175,9176],{},"await"," podem perder o contexto.",[16,9179,9180,9181,9184,9185,9188],{},"O mesmo anúncio traz números de performance do Nuxt 4.6: o ",[20,9182,9183],{},"nuxt build"," de um projeto inicial passou de 4,4 s para 3,6 s (18% mais rápido) e a página com 300 ",[20,9186,9187],{},"\u003CNuxtLink>"," foi de 110 para 140 req\u002Fs no SSR (+26%). O Nuxt atribui esses ganhos às otimizações gerais da versão, não ao Vapor.",[16,9190,9191],{},"Se você ainda usa Nuxt 3, vale saber que ele chegou ao fim de vida em 31 de julho de 2026.",[44,9193,9195],{"id":9194},"o-que-o-vapor-ainda-não-suporta","O que o Vapor ainda não suporta",[16,9197,9198],{},"O Vapor aceita um subconjunto das APIs do Vue. As notas oficiais listam o que não está disponível ou não se aplica nos componentes Vapor:",[75,9200,9201,9215,9220,9228,9234,9238],{},[78,9202,9203,9204,1928,9206,1928,9208,9210,9211,9214],{},"Options API (",[20,9205,7994],{},[20,9207,8000],{},[20,9209,6279],{}," e companhia). Se você precisa entender a diferença, veja o artigo sobre ",[619,9212,9213],{"href":8649},"Composition API e Options API",";",[78,9216,9217,9214],{},[20,9218,9219],{},"app.config.globalProperties",[78,9221,9222,9225,9226,9214],{},[20,9223,9224],{},"getCurrentInstance()",", que retorna ",[20,9227,3481],{},[78,9229,9230,9231,9214],{},"eventos de ciclo de vida por elemento, como ",[20,9232,9233],{},"@vue:mounted",[78,9235,9236,9214],{},[20,9237,6109],{},[78,9239,9240,1928,9242,1928,9245,1928,9248,62,9251,9254],{},[20,9241,9125],{},[20,9243,9244],{},"$props",[20,9246,9247],{},"$attrs",[20,9249,9250],{},"$slots",[20,9252,9253],{},"$refs"," nas template refs de componentes.",[16,9256,9257,9258,9260,9261,9263],{},"Bibliotecas de componentes baseadas em Virtual DOM também merecem teste. As notas falam em \"arestas\" ao usar uma biblioteca de componentes de Virtual DOM dentro do Vapor, sem citar nenhuma biblioteca específica. Se alguma delas depende de ",[20,9259,9224],{},", o ",[20,9262,3481],{}," acima pode ser um problema, mas isso só um teste no seu projeto confirma.",[16,9265,9266,9267,9270],{},"As notas oficiais não listam ",[20,9268,9269],{},"\u003CSuspense>"," nem o Vue DevTools como incompatíveis com o Vapor, mas isso não garante suporte completo. O changelog das RCs registra várias correções de integração do Vapor com o Suspense, então o suporte está em andamento. Teste os dois no seu cenário antes de decidir.",[16,9272,9273],{},"O mesmo vale para SSR. O changelog das RCs traz várias correções de hidratação em componentes Vapor, o que indica que o SSR é suportado, mas ainda em ajuste. Se o seu app renderiza no servidor, teste a hidratação com atenção.",[625,9275,9277,9278],{"id":9276},"eventos-e-delegate","Eventos e ",[20,9279,9280],{},".delegate",[16,9282,9283,9284,9286,9287,9290,9291,133],{},"Nas RCs mais antigas, o Vapor delegava eventos para o ",[20,9285,6776],{},", o que fazia um ",[20,9288,9289],{},"stopPropagation()"," em um ancestral impedir o handler do filho. Desde o rc.2, os listeners são ligados direto no elemento, como no Vue tradicional, e a delegação virou opt-in com o modificador ",[20,9292,9280],{},[115,9294,9296],{"className":1434,"code":9295,"language":1436,"meta":123,"style":123},"\u003Cbutton @click.delegate=\"onClick\" \u002F>\n",[20,9297,9298],{"__ignoreMap":123},[215,9299,9300,9302,9304,9307,9309,9311,9314,9316,9318,9321,9323],{"class":217,"line":218},[215,9301,1067],{"class":221},[215,9303,3996],{"class":239},[215,9305,9306],{"class":221}," @",[215,9308,6993],{"class":1073},[215,9310,638],{"class":221},[215,9312,9313],{"class":1073},"delegate",[215,9315,905],{"class":239},[215,9317,236],{"class":280},[215,9319,9320],{"class":221},"onClick",[215,9322,236],{"class":280},[215,9324,3247],{"class":221},[16,9326,9327,9328,9331],{},"A opção ",[20,9329,9330],{},"compilerOptions.eventDelegation"," foi removida.",[625,9333,9335],{"id":9334},"diretivas-customizadas","Diretivas customizadas",[16,9337,9338],{},"As diretivas customizadas também têm outra interface no Vapor. Em vez de um objeto com hooks, você escreve uma função que recebe o elemento e um getter reativo, e pode devolver uma função de limpeza:",[115,9340,9342],{"className":1774,"code":9341,"language":1457,"meta":123,"style":123},"import { watchEffect } from 'vue'\n\nconst MyDirective = (el, source) => {\n  watchEffect(() => {\n    el.textContent = source()\n  })\n  return () => console.log('cleanup')\n}\n",[20,9343,9344,9359,9363,9387,9398,9410,9414,9439],{"__ignoreMap":123},[215,9345,9346,9348,9351,9353,9355,9357],{"class":217,"line":218},[215,9347,5218],{"class":239},[215,9349,9350],{"class":221}," { watchEffect } ",[215,9352,4035],{"class":239},[215,9354,8778],{"class":280},[215,9356,1436],{"class":284},[215,9358,8783],{"class":280},[215,9360,9361],{"class":217,"line":225},[215,9362,1482],{"emptyLinePlaceholder":656},[215,9364,9365,9367,9370,9372,9374,9376,9378,9381,9383,9385],{"class":217,"line":245},[215,9366,1466],{"class":239},[215,9368,9369],{"class":723}," MyDirective",[215,9371,6823],{"class":239},[215,9373,2806],{"class":221},[215,9375,6217],{"class":3039},[215,9377,1928],{"class":221},[215,9379,9380],{"class":3039},"source",[215,9382,3043],{"class":221},[215,9384,2700],{"class":239},[215,9386,242],{"class":221},[215,9388,9389,9392,9394,9396],{"class":217,"line":261},[215,9390,9391],{"class":723},"  watchEffect",[215,9393,2697],{"class":221},[215,9395,2700],{"class":239},[215,9397,242],{"class":221},[215,9399,9400,9403,9405,9408],{"class":217,"line":267},[215,9401,9402],{"class":221},"    el.textContent ",[215,9404,905],{"class":239},[215,9406,9407],{"class":723}," source",[215,9409,1554],{"class":221},[215,9411,9412],{"class":217,"line":293},[215,9413,1731],{"class":221},[215,9415,9416,9418,9420,9422,9425,9428,9430,9432,9435,9437],{"class":217,"line":314},[215,9417,3598],{"class":239},[215,9419,7193],{"class":221},[215,9421,2700],{"class":239},[215,9423,9424],{"class":221}," console.",[215,9426,9427],{"class":723},"log",[215,9429,1505],{"class":221},[215,9431,8960],{"class":280},[215,9433,9434],{"class":284},"cleanup",[215,9436,8960],{"class":280},[215,9438,8804],{"class":221},[215,9440,9441],{"class":217,"line":334},[215,9442,424],{"class":221},[16,9444,9445,9446,9449,9450,9452,9453,9456],{},"A assinatura completa é ",[20,9447,9448],{},"(node, value?, argument?, modifiers?)",", e ",[20,9451,6570],{}," é o getter reativo. Se quiser rever o básico, o artigo sobre ",[619,9454,9455],{"href":7639},"diretivas no Vue"," cobre o modelo tradicional.",[44,9458,9460],{"id":9459},"onde-o-vapor-ajuda","Onde o Vapor ajuda",[16,9462,9463],{},"O ganho aparece onde o app faz muito trabalho de atualização de DOM: listas e tabelas densas, componentes que mudam várias vezes por segundo e páginas em que o tamanho do JavaScript inicial pesa. Para um CRUD comum, a diferença tende a passar despercebida pelo usuário.",[16,9465,9466,9467,9470],{},"O Vapor também não resolve problemas que não vêm do renderizador. Renderizar 10.000 itens no DOM continua lento nos dois modos, e a saída é virtualizar a lista. Chamar funções pesadas direto no template, como ",[20,9468,9469],{},"{{ calcularTotal(item) }}",", continua sendo má ideia nos dois modos.",[44,9472,9474],{"id":9473},"vale-adotar-agora","Vale adotar agora?",[16,9476,9477],{},"A própria equipe do Vue diz que o conjunto de recursos do Vapor está completo no RC e recomenda, por enquanto, dois usos: aplicar o Vapor em partes de um app existente, como uma página sensível a performance, e construir apps pequenos novos inteiramente em Vapor.",[16,9479,9480],{},"Para um app em produção que já roda bem no Vue 3.5, esperar a versão estável é a escolha mais segura. Para uma página isolada onde você já mediu um gargalo, um teste no Nuxt 4.6 ou num app Vue com interop é um bom experimento. Antes de começar, confira:",[75,9482,9483,9486,9492,9495],{},[78,9484,9485],{},"você mediu o desempenho atual (o painel Performance do navegador serve) e achou um gargalo de renderização;",[78,9487,9488,9489,9491],{},"o componente usa ",[20,9490,2966],{}," ou só template;",[78,9493,9494],{},"as bibliotecas de UI que ele usa funcionam com interop no seu projeto;",[78,9496,9497],{},"você consegue isolar o Vapor numa rota ou componente e voltar atrás sem dor.",[44,9499,2349],{"id":2348},[625,9501,9503],{"id":9502},"o-que-é-vapor-mode-no-vue","O que é Vapor Mode no Vue?",[16,9505,9506],{},"É um modo de compilação opt-in para SFCs, disponível no Vue 3.6 RC. Em vez de criar e comparar uma árvore de Virtual DOM em tempo de execução, o compilador gera código que atualiza o DOM diretamente.",[625,9508,9510],{"id":9509},"o-vue-36-já-saiu-como-estável","O Vue 3.6 já saiu como estável?",[16,9512,9513,9514,9516,9517,638],{},"Ainda não. Até 6 de outubro de 2026, a versão mais recente é a ",[20,9515,8670],{},", e a estável é a ",[20,9518,8674],{},[625,9520,9522],{"id":9521},"o-vapor-mode-substitui-o-virtual-dom","O Vapor Mode substitui o Virtual DOM?",[16,9524,9525,9526,638],{},"Não. Ele é opcional e convive com o Virtual DOM no mesmo app, por meio do ",[20,9527,8900],{},[625,9529,9531],{"id":9530},"preciso-reescrever-meu-app-para-usar-o-vapor","Preciso reescrever meu app para usar o Vapor?",[16,9533,9534,9535,9537,9538,9540],{},"Não. Você pode marcar um componente por vez com ",[20,9536,8739],{},". Os componentes que usam Options API ficam fora, porque o Vapor só aceita ",[20,9539,2966],{}," e templates.",[625,9542,9544],{"id":9543},"o-vapor-mode-funciona-com-options-api","O Vapor Mode funciona com Options API?",[16,9546,9547],{},"Não. As notas oficiais listam a Options API como não suportada.",[625,9549,9551],{"id":9550},"o-vapor-mode-funciona-com-nuxt","O Vapor Mode funciona com Nuxt?",[16,9553,9554,9555,1910,9558,9560,9561,9563],{},"Sim, desde o Nuxt 4.6, em modo de interoperabilidade: ative ",[20,9556,9557],{},"vue: { vapor: true }",[20,9559,750],{}," e marque os componentes com ",[20,9562,8881],{},". A raiz do app continua no Virtual DOM.",[625,9565,9567],{"id":9566},"o-vapor-mode-é-mais-rápido-que-o-virtual-dom","O Vapor Mode é mais rápido que o Virtual DOM?",[16,9569,9570],{},"Segundo as notas do Vue 3.6, o Vapor chegou ao nível de Solid e Svelte 5 em benchmarks de terceiros. Para a maioria dos apps o ganho percebido é pequeno, e o efeito real no seu projeto só aparece medindo.",[640,9572,9573],{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sY_PY, html code.shiki .sY_PY{--shiki-default:#50FA7B;--shiki-default-font-style:italic}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}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 pre.shiki code .sGEwX, html code.shiki .sGEwX{--shiki-default:#FFB86C;--shiki-default-font-style:italic}",{"title":123,"searchDepth":225,"depth":225,"links":9575},[9576,9579,9583,9584,9589,9590,9591],{"id":8688,"depth":225,"text":8689,"children":9577},[9578],{"id":8701,"depth":245,"text":8702},{"id":8729,"depth":225,"text":8730,"children":9580},[9581,9582],{"id":8889,"depth":245,"text":8890},{"id":8972,"depth":245,"text":8973},{"id":9048,"depth":225,"text":9049},{"id":9194,"depth":225,"text":9195,"children":9585},[9586,9588],{"id":9276,"depth":245,"text":9587},"Eventos e .delegate",{"id":9334,"depth":245,"text":9335},{"id":9459,"depth":225,"text":9460},{"id":9473,"depth":225,"text":9474},{"id":2348,"depth":225,"text":2349,"children":9592},[9593,9594,9595,9596,9597,9598,9599],{"id":9502,"depth":245,"text":9503},{"id":9509,"depth":245,"text":9510},{"id":9521,"depth":245,"text":9522},{"id":9530,"depth":245,"text":9531},{"id":9543,"depth":245,"text":9544},{"id":9550,"depth":245,"text":9551},{"id":9566,"depth":245,"text":9567},[651,650],"O que é o Vue Vapor Mode, como ativar no Vue 3.6 (RC) e no Nuxt 4.6, o que ainda não funciona e quando vale a pena usar, com exemplos e perguntas frequentes.","vue-vapor-mode-what-it-is-and-how-to-use-it",{},"\u002Fblog\u002Fvue-vapor-mode-como-funciona-e-como-usar","vue-vapor-mode-como-funciona-e-como-usar",{"title":8661,"description":9601},{"loc":9604,"lastmod":664},"blog\u002Fvue-vapor-mode-como-funciona-e-como-usar",1727,"NQrIpjH60lJ3Pb8kNbXKYS5Uv15ImJPunsIToGQO9LE",1791682338412]