[{"data":1,"prerenderedAt":27790},["ShallowReactive",2],{"blog":3},[4,96,1776,4977,9245,12418,13341,14638,15492,16443,18931,20954,22856,26479,26615,27576],{"id":5,"title":6,"author":7,"body":8,"date":81,"description":82,"extension":83,"image":84,"meta":85,"navigation":86,"path":87,"seo":88,"stem":89,"tags":90,"__hash__":95},"blog\u002Fblog\u002Fpremio-nuvini-ai-2026.md","Prêmio Nuvini AI 2026: o que dois dias na Oracle confirmaram","Larissa Santos",{"type":9,"value":10,"toc":74},"minimark",[11,15,23,26,29,34,37,40,47,49,53,56,59,61,65,68,71],[12,13,14],"p",{},"Quando a IA começou a ganhar espaço de verdade no dia a dia de desenvolvimento, o medo de que ela pudesse custar empregos era generalizado, e não sem motivo: empresas já tinham feito layoffs em vários cargos por conta disso. Hoje, outro discurso ganhou espaço: o do desenvolvedor com um papel novo, onde engenharia, segurança, responsabilidade técnica e conhecimento aprofundado valem mais do que nunca.",[12,16,17,18,22],{},"Foi por conta desse movimento todo que o grupo Nuvini trouxe a provocação de que era necessário se reinventar. Os líderes entendiam o que a IA é e o que ela não é, e a aposta foi de que os talentos que já existiam dentro das empresas poderiam extrair muito mais com as ferramentas certas. O ",[19,20,21],"strong",{},"Prêmio Nuvini AI 2026 – Claude Code First Edition"," nasceu daí, como uma iniciativa para colocar os times em movimento num cenário em que quem não se reinventa fica pra trás.",[12,24,25],{},"Quando comecei a usar o Claude Code de verdade, me inspirei muito com a velocidade e com o que ela tornava possível. O que mais me surpreendeu foi o quanto ela desbloqueou meu lado de engenharia de software e arquitetura, me ajudando a aprender coisas novas, acelerar projetos e entender melhor como delegar tarefas. Não queria que o time da Datahub ficasse pra trás enquanto o cenário avançava, então comecei a estudar e a trazer isso pro time: construindo aplicações, fazendo o webinar sobre Claude Code, prototipando o DatahubIA, compartilhando o que aprendia. O reconhecimento veio daí.",[27,28],"hr",{},[30,31,33],"h2",{"id":32},"a-premiação-oracle-brasil","A premiação: Oracle Brasil",[12,35,36],{},"Os 6 profissionais destaque do grupo, selecionados com base em engajamento, performance e qualidade das soluções desenvolvidas ao longo do projeto Claude Code, foram convidados para dois dias de imersão em IA em São Paulo. Cada um representava uma empresa do grupo: Datahub, ONCLICK, Effecti, entre outras.",[12,38,39],{},"O primeiro dia, 20 de maio, aconteceu na sede da Oracle Brasil, no Centro de Inovações, um espaço dedicado a mostrar IA em uso real, não em conceito. Vimos drones e sensores para monitoramento de áreas, sistemas de pré-diagnóstico na saúde, mercados inteligentes com rastreamento de produtos e comportamento de clientes, robôs de hotelaria fazendo entregas e atendendo como baristas, monitoramento preditivo aplicado a carros de corrida. A sensação foi exatamente essa: ver na prática o que muitas vezes imaginamos apenas em teoria.",[12,41,42,43,46],{},"Além do tour, tivemos momentos de troca entre os próprios destaques do grupo: discussões sobre soluções, debates sobre o impacto de IA no mercado de trabalho, reflexões sobre como sair de lá com mais iniciativas, uma conversa sobre o que o uso de IA poderia significar para cada negócio. E no final daquele dia, na cerimônia de reconhecimento de cada participante, recebi o troféu das mãos de ",[19,44,45],{},"Pierre Schurmann",". Não imaginei que houvesse um troféu, e receber daquele jeito, naquele espaço, depois de tudo que vimos, foi um momento que ficou na memória.",[27,48],{},[30,50,52],{"id":51},"oracle-data-deep-dive-sp","Oracle Data Deep Dive SP",[12,54,55],{},"O segundo dia, 21 de maio, foi de imersão com palestras do Presidente da Oracle Brasil, VPs de tecnologia e IA para a América Latina, CEO da TDC e Paulo Silveira, founder e CEO da Alura, além de um laboratório prático sobre agentes de IA. Em algum momento daquele dia, percebi que o mesmo tema voltava em todas as falas: a diferença entre \"vibe code\" e construir software com IA de verdade. O desenvolvedor não vai desaparecer, mas o foco muda: de escrever código para entender arquitetura, conhecer o negócio, conectar o técnico ao problema do cliente e saber comunicar isso. Habilidades que, com IA no fluxo, ficaram ainda mais valiosas.",[12,57,58],{},"Eram reflexões que eu carregava há algum tempo, mas que eu não sabia se o mercado tinha chegado também. Ouvir isso de quem está construindo a infraestrutura que o mercado vai usar nos próximos anos, e não de um post no LinkedIn, foi diferente. Os discursos que diziam que a área ia acabar foram desmontados naquele evento, por quem tem mais contexto para dizer isso do que qualquer um de nós.",[27,60],{},[30,62,64],{"id":63},"o-que-ficou","O que ficou",[12,66,67],{},"Foram dois dias que ficam por razões diferentes: um pelo troféu e por tudo que vimos naquele espaço, o outro pela confirmação de uma leitura que eu carregava sem ter certeza se o mercado compartilhava.",[12,69,70],{},"Ser reconhecida pelo grupo Nuvini por ter me reinventado junto com o cenário e por ter promovido isso dentro do time foi marcante.",[12,72,73],{},"Ainda tenho muito a aprender nessa área, mas saí de lá com mais vontade do que nunca de continuar construindo e entendendo como aplicar isso.",{"title":75,"searchDepth":76,"depth":76,"links":77},"",2,[78,79,80],{"id":32,"depth":76,"text":33},{"id":51,"depth":76,"text":52},{"id":63,"depth":76,"text":64},"2026-06-16T22:15:00-03:00","Recebi o Prêmio Nuvini AI 2026 – Claude Code First Edition como destaque da Datahub no grupo Nuvini. Aqui está o relato da premiação, dos dois dias de imersão em IA em São Paulo e do que eles confirmaram sobre o papel do desenvolvedor nesse cenário.","md","\u002Fimages\u002Ffiles\u002FpremioNuniAI.jpg",{},true,"\u002Fblog\u002Fpremio-nuvini-ai-2026",{"title":6,"description":82},"blog\u002Fpremio-nuvini-ai-2026",[91,92,93,94],"ia","carreira","claude-code","produtividade","WSMTSs4wqrDun2O9EmmbQvzBliODE8pZ0wF_xog0k-U",{"id":97,"title":98,"author":7,"body":99,"date":1761,"description":1762,"extension":83,"image":1763,"meta":1764,"navigation":86,"path":1765,"seo":1766,"stem":1767,"tags":1768,"__hash__":1775},"blog\u002Fblog\u002Fsass-nuxt-layers.md","Refatorei a arquitetura do meu SaaS com Nuxt Layers antes de precisar",{"type":9,"value":100,"toc":1747},[101,116,119,121,125,128,357,364,382,385,387,391,398,401,423,426,429,431,435,438,700,706,720,727,733,736,759,766,768,772,783,873,890,896,902,1191,1212,1221,1241,1247,1316,1326,1328,1332,1335,1341,1347,1374,1380,1395,1405,1510,1525,1533,1542,1545,1604,1606,1610,1613,1627,1630,1633,1635,1639,1642,1647,1664,1668,1685,1688,1690,1694,1697,1702,1705,1708,1710,1714,1743],[12,102,103,104,111,112,115],{},"Recentemente me deparei com um ",[105,106,110],"a",{"href":107,"rel":108},"https:\u002F\u002Fwww.gabrielcaiana.com\u002Fpt\u002Fblog\u002Farquitetura-modular-frontend-nuxt-layers-revoluciona-organizacao-projetos-vue\u002F",[109],"nofollow","artigo técnico"," sobre ",[19,113,114],{},"Nuxt Layers",", uma forma de trabalhar com arquitetura modular no Nuxt. Eu ainda não havia implementado esse formato em nenhum projeto, mas durante a leitura percebi que ele fazia bastante sentido para um SaaS multitenant em que estou trabalhando.",[12,117,118],{},"Se você ainda não conhece o projeto, recomendo ler primeiro o artigo anterior, onde explico a arquitetura inicial dele. Este texto é uma continuação dessa evolução, com foco na decisão de reorganizar o projeto usando Layers.",[27,120],{},[30,122,124],{"id":123},"a-estrutura-que-eu-tinha","A estrutura que eu tinha",[12,126,127],{},"A organização inicial seguia a estrutura clássica de um projeto Nuxt, separando os arquivos por tipo:",[129,130,134],"pre",{"className":131,"code":132,"language":133,"meta":75,"style":75},"language-txt shiki shiki-themes material-theme-lighter github-dark github-dark","app\u002F\n├── components\u002F\n│   ├── admin\u002F\n│   ├── catalog\u002F\n│   ├── checkout\u002F\n│   ├── landingpage\u002F\n│   ├── pedido\u002F\n│   └── shared\u002F\n├── composables\u002F\n│   ├── admin\u002F\n│   ├── catalog\u002F\n│   ├── checkout\u002F\n│   └── pedido\u002F\n├── layouts\u002F\n│   ├── admin.vue\n│   ├── auth.vue\n│   └── catalog.vue\n├── stores\u002F\n│   ├── useCarrinhoStore.ts\n│   ├── useEmpresaStore.ts\n│   └── usePedidosStore.ts\n├── pages\u002F\n│   ├── index.vue\n│   ├── 404.vue\n│   ├── [slug]\u002F\n│   │   ├── index.vue\n│   │   ├── checkout.vue\n│   │   └── acompanhar-pedido.vue\n│   └── admin\u002F\n│       ├── index.vue\n│       ├── acervo.vue\n│       ├── categorias.vue\n│       ├── login.vue\n│       └── pedidos.vue\n└── middleware\u002F\n    ├── auth.ts\n    └── tenant.global.ts\n","txt",[135,136,137,145,150,156,162,168,174,180,186,192,197,202,207,213,219,225,231,237,243,249,255,261,267,273,279,285,291,297,303,309,315,321,327,333,339,345,351],"code",{"__ignoreMap":75},[138,139,142],"span",{"class":140,"line":141},"line",1,[138,143,144],{},"app\u002F\n",[138,146,147],{"class":140,"line":76},[138,148,149],{},"├── components\u002F\n",[138,151,153],{"class":140,"line":152},3,[138,154,155],{},"│   ├── admin\u002F\n",[138,157,159],{"class":140,"line":158},4,[138,160,161],{},"│   ├── catalog\u002F\n",[138,163,165],{"class":140,"line":164},5,[138,166,167],{},"│   ├── checkout\u002F\n",[138,169,171],{"class":140,"line":170},6,[138,172,173],{},"│   ├── landingpage\u002F\n",[138,175,177],{"class":140,"line":176},7,[138,178,179],{},"│   ├── pedido\u002F\n",[138,181,183],{"class":140,"line":182},8,[138,184,185],{},"│   └── shared\u002F\n",[138,187,189],{"class":140,"line":188},9,[138,190,191],{},"├── composables\u002F\n",[138,193,195],{"class":140,"line":194},10,[138,196,155],{},[138,198,200],{"class":140,"line":199},11,[138,201,161],{},[138,203,205],{"class":140,"line":204},12,[138,206,167],{},[138,208,210],{"class":140,"line":209},13,[138,211,212],{},"│   └── pedido\u002F\n",[138,214,216],{"class":140,"line":215},14,[138,217,218],{},"├── layouts\u002F\n",[138,220,222],{"class":140,"line":221},15,[138,223,224],{},"│   ├── admin.vue\n",[138,226,228],{"class":140,"line":227},16,[138,229,230],{},"│   ├── auth.vue\n",[138,232,234],{"class":140,"line":233},17,[138,235,236],{},"│   └── catalog.vue\n",[138,238,240],{"class":140,"line":239},18,[138,241,242],{},"├── stores\u002F\n",[138,244,246],{"class":140,"line":245},19,[138,247,248],{},"│   ├── useCarrinhoStore.ts\n",[138,250,252],{"class":140,"line":251},20,[138,253,254],{},"│   ├── useEmpresaStore.ts\n",[138,256,258],{"class":140,"line":257},21,[138,259,260],{},"│   └── usePedidosStore.ts\n",[138,262,264],{"class":140,"line":263},22,[138,265,266],{},"├── pages\u002F\n",[138,268,270],{"class":140,"line":269},23,[138,271,272],{},"│   ├── index.vue\n",[138,274,276],{"class":140,"line":275},24,[138,277,278],{},"│   ├── 404.vue\n",[138,280,282],{"class":140,"line":281},25,[138,283,284],{},"│   ├── [slug]\u002F\n",[138,286,288],{"class":140,"line":287},26,[138,289,290],{},"│   │   ├── index.vue\n",[138,292,294],{"class":140,"line":293},27,[138,295,296],{},"│   │   ├── checkout.vue\n",[138,298,300],{"class":140,"line":299},28,[138,301,302],{},"│   │   └── acompanhar-pedido.vue\n",[138,304,306],{"class":140,"line":305},29,[138,307,308],{},"│   └── admin\u002F\n",[138,310,312],{"class":140,"line":311},30,[138,313,314],{},"│       ├── index.vue\n",[138,316,318],{"class":140,"line":317},31,[138,319,320],{},"│       ├── acervo.vue\n",[138,322,324],{"class":140,"line":323},32,[138,325,326],{},"│       ├── categorias.vue\n",[138,328,330],{"class":140,"line":329},33,[138,331,332],{},"│       ├── login.vue\n",[138,334,336],{"class":140,"line":335},34,[138,337,338],{},"│       └── pedidos.vue\n",[138,340,342],{"class":140,"line":341},35,[138,343,344],{},"└── middleware\u002F\n",[138,346,348],{"class":140,"line":347},36,[138,349,350],{},"    ├── auth.ts\n",[138,352,354],{"class":140,"line":353},37,[138,355,356],{},"    └── tenant.global.ts\n",[12,358,359,360,363],{},"Ela estava organizada e funcionando. O ponto é que, conforme o projeto crescia, comecei a perceber que algumas funcionalidades estavam espalhadas por várias partes do ",[135,361,362],{},"app\u002F",".",[12,365,366,367,370,371,370,374,377,378,381],{},"Por exemplo, para mexer no checkout, eu precisava abrir arquivos em ",[135,368,369],{},"components\u002Fcheckout\u002F",", ",[135,372,373],{},"composables\u002Fcheckout\u002F",[135,375,376],{},"stores\u002FuseCarrinhoStore.ts"," e ",[135,379,380],{},"pages\u002F[slug]\u002Fcheckout.vue",". Tudo fazia parte da mesma responsabilidade, mas estava distribuído em pastas diferentes.",[12,383,384],{},"Isso ainda não era um problema real. A aplicação funcionava bem. A decisão de mudar foi mais preventiva: reorganizar a arquitetura antes que o projeto crescesse o suficiente para tornar essa separação mais difícil.",[27,386],{},[30,388,390],{"id":389},"por-que-layers-fizeram-sentido-para-esse-projeto","Por que Layers fizeram sentido para esse projeto",[12,392,393,394,397],{},"A ideia que mais fez sentido para mim no conceito de Nuxt Layers foi organizar o projeto por ",[19,395,396],{},"domínio de negócio",", e não apenas por tipo de arquivo.",[12,399,400],{},"No meu caso, o projeto tinha três contextos bem definidos:",[402,403,404,411,417],"ul",{},[405,406,407,410],"li",{},[19,408,409],{},"Cliente final",", com vitrine pública, checkout e acompanhamento de pedido",[405,412,413,416],{},[19,414,415],{},"Admin da empresa",", com painel de gestão do acervo e dos pedidos",[405,418,419,422],{},[19,420,421],{},"Super Admin",", planejado para uma fase futura, voltado à gestão dos tenants",[12,424,425],{},"Cada contexto tem seus próprios componentes, suas próprias regras, seus próprios layouts e suas próprias páginas. Eles fazem parte da mesma aplicação, mas representam áreas diferentes do produto.",[12,427,428],{},"Foi isso que fez a arquitetura com Layers encaixar. A intenção não era aplicar uma estrutura nova apenas porque parecia interessante, mas porque ela refletia melhor a forma como o projeto estava dividido na prática.",[27,430],{},[30,432,434],{"id":433},"como-desenhei-a-nova-estrutura","Como desenhei a nova estrutura",[12,436,437],{},"A partir disso, separei os principais domínios em layers independentes:",[129,439,441],{"className":131,"code":440,"language":133,"meta":75,"style":75},"layers\u002F\n├── vitrine\u002F\n│   ├── components\u002F\n│   │   ├── catalog\u002F\n│   │   └── checkout\u002F\n│   ├── composables\u002F\n│   │   ├── catalog\u002F\n│   │   └── checkout\u002F\n│   ├── layouts\u002F\n│   │   └── catalog.vue\n│   ├── stores\u002F\n│   │   ├── useCarrinhoStore.ts\n│   │   └── usePedidosStore.ts\n│   ├── pages\u002F\n│   │   └── [slug]\u002F\n│   │       ├── index.vue\n│   │       ├── checkout.vue\n│   │       └── acompanhar-pedido.vue\n│   └── nuxt.config.ts\n│\n├── admin\u002F\n│   ├── components\u002F\n│   ├── composables\u002F\n│   ├── layouts\u002F\n│   │   ├── admin.vue\n│   │   └── auth.vue\n│   ├── middleware\u002F\n│   │   └── auth.ts\n│   ├── pages\u002F\n│   │   ├── dashboard.vue\n│   │   ├── acervo.vue\n│   │   ├── categorias.vue\n│   │   ├── login.vue\n│   │   └── pedidos.vue\n│   └── nuxt.config.ts\n│\n└── landingpage\u002F\n    ├── components\u002F\n    ├── pages\u002F\n    │   └── index.vue\n    └── nuxt.config.ts\n\napp\u002F\n├── components\u002Fshared\u002F\n├── composables\u002F\n├── stores\u002F\n├── middleware\u002F\n├── pages\u002F\n│   └── 404.vue\n├── error.vue\n└── app.vue\n",[135,442,443,448,453,458,463,468,473,477,481,486,491,496,501,506,511,516,521,526,531,536,541,546,550,554,558,563,568,573,578,582,587,592,597,602,607,611,615,620,626,632,638,644,650,655,661,666,671,677,682,688,694],{"__ignoreMap":75},[138,444,445],{"class":140,"line":141},[138,446,447],{},"layers\u002F\n",[138,449,450],{"class":140,"line":76},[138,451,452],{},"├── vitrine\u002F\n",[138,454,455],{"class":140,"line":152},[138,456,457],{},"│   ├── components\u002F\n",[138,459,460],{"class":140,"line":158},[138,461,462],{},"│   │   ├── catalog\u002F\n",[138,464,465],{"class":140,"line":164},[138,466,467],{},"│   │   └── checkout\u002F\n",[138,469,470],{"class":140,"line":170},[138,471,472],{},"│   ├── composables\u002F\n",[138,474,475],{"class":140,"line":176},[138,476,462],{},[138,478,479],{"class":140,"line":182},[138,480,467],{},[138,482,483],{"class":140,"line":188},[138,484,485],{},"│   ├── layouts\u002F\n",[138,487,488],{"class":140,"line":194},[138,489,490],{},"│   │   └── catalog.vue\n",[138,492,493],{"class":140,"line":199},[138,494,495],{},"│   ├── stores\u002F\n",[138,497,498],{"class":140,"line":204},[138,499,500],{},"│   │   ├── useCarrinhoStore.ts\n",[138,502,503],{"class":140,"line":209},[138,504,505],{},"│   │   └── usePedidosStore.ts\n",[138,507,508],{"class":140,"line":215},[138,509,510],{},"│   ├── pages\u002F\n",[138,512,513],{"class":140,"line":221},[138,514,515],{},"│   │   └── [slug]\u002F\n",[138,517,518],{"class":140,"line":227},[138,519,520],{},"│   │       ├── index.vue\n",[138,522,523],{"class":140,"line":233},[138,524,525],{},"│   │       ├── checkout.vue\n",[138,527,528],{"class":140,"line":239},[138,529,530],{},"│   │       └── acompanhar-pedido.vue\n",[138,532,533],{"class":140,"line":245},[138,534,535],{},"│   └── nuxt.config.ts\n",[138,537,538],{"class":140,"line":251},[138,539,540],{},"│\n",[138,542,543],{"class":140,"line":257},[138,544,545],{},"├── admin\u002F\n",[138,547,548],{"class":140,"line":263},[138,549,457],{},[138,551,552],{"class":140,"line":269},[138,553,472],{},[138,555,556],{"class":140,"line":275},[138,557,485],{},[138,559,560],{"class":140,"line":281},[138,561,562],{},"│   │   ├── admin.vue\n",[138,564,565],{"class":140,"line":287},[138,566,567],{},"│   │   └── auth.vue\n",[138,569,570],{"class":140,"line":293},[138,571,572],{},"│   ├── middleware\u002F\n",[138,574,575],{"class":140,"line":299},[138,576,577],{},"│   │   └── auth.ts\n",[138,579,580],{"class":140,"line":305},[138,581,510],{},[138,583,584],{"class":140,"line":311},[138,585,586],{},"│   │   ├── dashboard.vue\n",[138,588,589],{"class":140,"line":317},[138,590,591],{},"│   │   ├── acervo.vue\n",[138,593,594],{"class":140,"line":323},[138,595,596],{},"│   │   ├── categorias.vue\n",[138,598,599],{"class":140,"line":329},[138,600,601],{},"│   │   ├── login.vue\n",[138,603,604],{"class":140,"line":335},[138,605,606],{},"│   │   └── pedidos.vue\n",[138,608,609],{"class":140,"line":341},[138,610,535],{},[138,612,613],{"class":140,"line":347},[138,614,540],{},[138,616,617],{"class":140,"line":353},[138,618,619],{},"└── landingpage\u002F\n",[138,621,623],{"class":140,"line":622},38,[138,624,625],{},"    ├── components\u002F\n",[138,627,629],{"class":140,"line":628},39,[138,630,631],{},"    ├── pages\u002F\n",[138,633,635],{"class":140,"line":634},40,[138,636,637],{},"    │   └── index.vue\n",[138,639,641],{"class":140,"line":640},41,[138,642,643],{},"    └── nuxt.config.ts\n",[138,645,647],{"class":140,"line":646},42,[138,648,649],{"emptyLinePlaceholder":86},"\n",[138,651,653],{"class":140,"line":652},43,[138,654,144],{},[138,656,658],{"class":140,"line":657},44,[138,659,660],{},"├── components\u002Fshared\u002F\n",[138,662,664],{"class":140,"line":663},45,[138,665,191],{},[138,667,669],{"class":140,"line":668},46,[138,670,242],{},[138,672,674],{"class":140,"line":673},47,[138,675,676],{},"├── middleware\u002F\n",[138,678,680],{"class":140,"line":679},48,[138,681,266],{},[138,683,685],{"class":140,"line":684},49,[138,686,687],{},"│   └── 404.vue\n",[138,689,691],{"class":140,"line":690},50,[138,692,693],{},"├── error.vue\n",[138,695,697],{"class":140,"line":696},51,[138,698,699],{},"└── app.vue\n",[12,701,702,703,705],{},"Mantive no ",[135,704,362],{}," aquilo que é realmente global da aplicação, como arquivos base, middleware global, stores compartilhadas e componentes comuns. As layers ficaram responsáveis pelos domínios principais do produto, ou seja, partes que têm fluxo, responsabilidade e contexto próprios.",[12,707,708,709,377,712,715,716,719],{},"Uma das decisões mais importantes foi manter ",[135,710,711],{},"catalog",[135,713,714],{},"checkout"," na mesma layer, chamada ",[135,717,718],{},"vitrine",". Tecnicamente, eu poderia separar o checkout em uma layer própria, mas no projeto isso não faria tanto sentido.",[12,721,722,723,726],{},"As páginas do checkout vivem dentro de ",[135,724,725],{},"[slug]",", o mesmo contexto da vitrine pública. Além disso, o carrinho depende diretamente dos dados do catálogo. Se eu separasse em duas layers, provavelmente criaria uma fronteira que eu precisaria atravessar o tempo todo.",[12,728,729,730,732],{},"Por isso, ",[135,731,718],{}," ficou como a jornada completa do cliente final: navegar pelo acervo, adicionar itens ao carrinho, finalizar o pedido e acompanhar o status.",[12,734,735],{},"Também movi os layouts para dentro das layers correspondentes:",[402,737,738,750],{},[405,739,740,377,743,746,747],{},[135,741,742],{},"admin.vue",[135,744,745],{},"auth.vue"," ficaram em ",[135,748,749],{},"layers\u002Fadmin\u002Flayouts\u002F",[405,751,752,755,756],{},[135,753,754],{},"catalog.vue"," ficou em ",[135,757,758],{},"layers\u002Fvitrine\u002Flayouts\u002F",[12,760,761,762,765],{},"Essa escolha deixou cada domínio mais autocontido. Quando abro a layer ",[135,763,764],{},"admin",", encontro páginas, layouts, componentes e lógica do painel no mesmo lugar.",[27,767],{},[30,769,771],{"id":770},"configurando-as-layers-no-nuxt","Configurando as Layers no Nuxt",[12,773,774,775,778,779,782],{},"No ",[135,776,777],{},"nuxt.config.ts"," principal, registrei as layers usando ",[135,780,781],{},"extends",":",[129,784,788],{"className":785,"code":786,"language":787,"meta":75,"style":75},"language-ts shiki shiki-themes material-theme-lighter github-dark github-dark","export default defineNuxtConfig({\n  extends: [\n    '.\u002Flayers\u002Fadmin',\n    '.\u002Flayers\u002Flandingpage',\n    '.\u002Flayers\u002Fvitrine',\n  ]\n})\n","ts",[135,789,790,811,822,838,849,860,865],{"__ignoreMap":75},[138,791,792,796,799,803,807],{"class":140,"line":141},[138,793,795],{"class":794},"s3Er8","export",[138,797,798],{"class":794}," default",[138,800,802],{"class":801},"sK_r7"," defineNuxtConfig",[138,804,806],{"class":805},"sMo7A","(",[138,808,810],{"class":809},"sG-J9","{\n",[138,812,813,817,819],{"class":140,"line":76},[138,814,816],{"class":815},"sdv8B","  extends",[138,818,782],{"class":809},[138,820,821],{"class":805}," [\n",[138,823,824,828,832,835],{"class":140,"line":152},[138,825,827],{"class":826},"sF_wb","    '",[138,829,831],{"class":830},"s0vBq",".\u002Flayers\u002Fadmin",[138,833,834],{"class":826},"'",[138,836,837],{"class":809},",\n",[138,839,840,842,845,847],{"class":140,"line":158},[138,841,827],{"class":826},[138,843,844],{"class":830},".\u002Flayers\u002Flandingpage",[138,846,834],{"class":826},[138,848,837],{"class":809},[138,850,851,853,856,858],{"class":140,"line":164},[138,852,827],{"class":826},[138,854,855],{"class":830},".\u002Flayers\u002Fvitrine",[138,857,834],{"class":826},[138,859,837],{"class":809},[138,861,862],{"class":140,"line":170},[138,863,864],{"class":805},"  ]\n",[138,866,867,870],{"class":140,"line":176},[138,868,869],{"class":809},"}",[138,871,872],{"class":805},")\n",[12,874,875,876,878,879,881,882,885,886,889],{},"A ordem aqui importa. Como a layer ",[135,877,718],{}," possui rotas dinâmicas com ",[135,880,725],{},", ela precisa ficar por último. Caso contrário, o Nuxt pode interpretar ",[135,883,884],{},"\u002Fadmin"," como ",[135,887,888],{},"slug = 'admin'"," antes de encontrar a rota estática do painel.",[12,891,892,893,895],{},"Cada layer também recebeu seu próprio ",[135,894,777],{},". Em alguns casos, ele ficou praticamente vazio. Em outros, como no admin, precisei configurar auto-imports, prefixo de componentes e ajuste das rotas.",[12,897,898,899,901],{},"Na layer ",[135,900,764],{},", a configuração ficou assim:",[129,903,905],{"className":785,"code":904,"language":787,"meta":75,"style":75},"\u002F\u002F layers\u002Fadmin\u002Fnuxt.config.ts\nexport default defineNuxtConfig({\n  imports: {\n    dirs: ['composables\u002F**', 'stores'],\n  },\n  components: [\n    { path: '.\u002Fcomponents', prefix: 'Adm' }\n  ],\n  hooks: {\n    'pages:extend'(pages) {\n      pages.forEach((page) => {\n        if (page.file?.includes('\u002Flayers\u002Fadmin\u002Fpages\u002F') && page.path !== '\u002Fadmin') {\n          page.path = '\u002Fadmin' + page.path\n        }\n      })\n    }\n  }\n})\n",[135,906,907,913,925,935,968,973,982,1016,1023,1032,1052,1077,1135,1163,1168,1175,1180,1185],{"__ignoreMap":75},[138,908,909],{"class":140,"line":141},[138,910,912],{"class":911},"sutJx","\u002F\u002F layers\u002Fadmin\u002Fnuxt.config.ts\n",[138,914,915,917,919,921,923],{"class":140,"line":76},[138,916,795],{"class":794},[138,918,798],{"class":794},[138,920,802],{"class":801},[138,922,806],{"class":805},[138,924,810],{"class":809},[138,926,927,930,932],{"class":140,"line":152},[138,928,929],{"class":815},"  imports",[138,931,782],{"class":809},[138,933,934],{"class":809}," {\n",[138,936,937,940,942,945,947,950,952,955,958,961,963,966],{"class":140,"line":158},[138,938,939],{"class":815},"    dirs",[138,941,782],{"class":809},[138,943,944],{"class":805}," [",[138,946,834],{"class":826},[138,948,949],{"class":830},"composables\u002F**",[138,951,834],{"class":826},[138,953,954],{"class":809},",",[138,956,957],{"class":826}," '",[138,959,960],{"class":830},"stores",[138,962,834],{"class":826},[138,964,965],{"class":805},"]",[138,967,837],{"class":809},[138,969,970],{"class":140,"line":164},[138,971,972],{"class":809},"  },\n",[138,974,975,978,980],{"class":140,"line":170},[138,976,977],{"class":815},"  components",[138,979,782],{"class":809},[138,981,821],{"class":805},[138,983,984,987,990,992,994,997,999,1001,1004,1006,1008,1011,1013],{"class":140,"line":176},[138,985,986],{"class":809},"    {",[138,988,989],{"class":815}," path",[138,991,782],{"class":809},[138,993,957],{"class":826},[138,995,996],{"class":830},".\u002Fcomponents",[138,998,834],{"class":826},[138,1000,954],{"class":809},[138,1002,1003],{"class":815}," prefix",[138,1005,782],{"class":809},[138,1007,957],{"class":826},[138,1009,1010],{"class":830},"Adm",[138,1012,834],{"class":826},[138,1014,1015],{"class":809}," }\n",[138,1017,1018,1021],{"class":140,"line":182},[138,1019,1020],{"class":805},"  ]",[138,1022,837],{"class":809},[138,1024,1025,1028,1030],{"class":140,"line":188},[138,1026,1027],{"class":815},"  hooks",[138,1029,782],{"class":809},[138,1031,934],{"class":809},[138,1033,1034,1036,1039,1041,1043,1047,1050],{"class":140,"line":194},[138,1035,827],{"class":826},[138,1037,1038],{"class":830},"pages:extend",[138,1040,834],{"class":826},[138,1042,806],{"class":809},[138,1044,1046],{"class":1045},"sk1zL","pages",[138,1048,1049],{"class":809},")",[138,1051,934],{"class":809},[138,1053,1054,1057,1059,1062,1064,1066,1069,1071,1075],{"class":140,"line":199},[138,1055,1056],{"class":805},"      pages",[138,1058,363],{"class":809},[138,1060,1061],{"class":801},"forEach",[138,1063,806],{"class":815},[138,1065,806],{"class":809},[138,1067,1068],{"class":1045},"page",[138,1070,1049],{"class":809},[138,1072,1074],{"class":1073},"sFsEu"," =>",[138,1076,934],{"class":809},[138,1078,1079,1082,1085,1087,1089,1092,1095,1098,1100,1102,1105,1107,1110,1114,1117,1119,1122,1125,1127,1129,1131,1133],{"class":140,"line":204},[138,1080,1081],{"class":794},"        if",[138,1083,1084],{"class":815}," (",[138,1086,1068],{"class":805},[138,1088,363],{"class":809},[138,1090,1091],{"class":805},"file",[138,1093,1094],{"class":809},"?.",[138,1096,1097],{"class":801},"includes",[138,1099,806],{"class":815},[138,1101,834],{"class":826},[138,1103,1104],{"class":830},"\u002Flayers\u002Fadmin\u002Fpages\u002F",[138,1106,834],{"class":826},[138,1108,1109],{"class":815},") ",[138,1111,1113],{"class":1112},"sFfmW","&&",[138,1115,1116],{"class":805}," page",[138,1118,363],{"class":809},[138,1120,1121],{"class":805},"path",[138,1123,1124],{"class":1112}," !==",[138,1126,957],{"class":826},[138,1128,884],{"class":830},[138,1130,834],{"class":826},[138,1132,1109],{"class":815},[138,1134,810],{"class":809},[138,1136,1137,1140,1142,1144,1147,1149,1151,1153,1156,1158,1160],{"class":140,"line":209},[138,1138,1139],{"class":805},"          page",[138,1141,363],{"class":809},[138,1143,1121],{"class":805},[138,1145,1146],{"class":1112}," =",[138,1148,957],{"class":826},[138,1150,884],{"class":830},[138,1152,834],{"class":826},[138,1154,1155],{"class":1112}," +",[138,1157,1116],{"class":805},[138,1159,363],{"class":809},[138,1161,1162],{"class":805},"path\n",[138,1164,1165],{"class":140,"line":215},[138,1166,1167],{"class":809},"        }\n",[138,1169,1170,1173],{"class":140,"line":221},[138,1171,1172],{"class":809},"      }",[138,1174,872],{"class":815},[138,1176,1177],{"class":140,"line":227},[138,1178,1179],{"class":809},"    }\n",[138,1181,1182],{"class":140,"line":233},[138,1183,1184],{"class":809},"  }\n",[138,1186,1187,1189],{"class":140,"line":239},[138,1188,869],{"class":809},[138,1190,872],{"class":805},[12,1192,1193,1194,1197,1198,1200,1201,370,1204,1207,1208,1211],{},"O ",[135,1195,1196],{},"imports.dirs"," com ",[135,1199,949],{}," permite manter composables organizados em subpastas, como ",[135,1202,1203],{},"composables\u002Facervo\u002F",[135,1205,1206],{},"composables\u002Fpedidos\u002F"," ou ",[135,1209,1210],{},"composables\u002Fcategorias\u002F",", sem perder o auto-import.",[12,1213,1193,1214,1217,1218,1220],{},[135,1215,1216],{},"components"," com prefixo ",[135,1219,1010],{}," evita colisão de nomes entre layers, pois o Nuxt faz o auto-import dos componentes com base no nome dos arquivos e pastas. Como diferentes layers podem ter componentes com nomes iguais, como Header.vue, Footer.vue ou Button.vue, deixar tudo com nomes genéricos poderia causar conflitos ou dificultar entender qual componente está sendo usado em cada contexto.",[12,1222,1223,1224,1226,1227,1230,1231,1234,1235,1238,1239,363],{},"O hook ",[135,1225,1038],{}," resolveu outro ponto de organização. Sem ele, para ter rotas como ",[135,1228,1229],{},"\u002Fadmin\u002Flogin",", eu precisaria criar uma estrutura como ",[135,1232,1233],{},"layers\u002Fadmin\u002Fpages\u002Fadmin\u002Flogin.vue",". Funcionaria, mas ficaria repetitivo. Com o hook, as páginas continuam na raiz de ",[135,1236,1237],{},"pages\u002F"," da layer admin, e o Nuxt prefixa as rotas com ",[135,1240,884],{},[12,1242,898,1243,1246],{},[135,1244,1245],{},"landingpage",", usei a mesma ideia de prefixo para evitar nomes genéricos:",[129,1248,1250],{"className":785,"code":1249,"language":787,"meta":75,"style":75},"\u002F\u002F layers\u002Flandingpage\u002Fnuxt.config.ts\nexport default defineNuxtConfig({\n  components: [\n    { path: '.\u002Fcomponents', prefix: 'Lp' }\n  ]\n})\n",[135,1251,1252,1257,1269,1277,1306,1310],{"__ignoreMap":75},[138,1253,1254],{"class":140,"line":141},[138,1255,1256],{"class":911},"\u002F\u002F layers\u002Flandingpage\u002Fnuxt.config.ts\n",[138,1258,1259,1261,1263,1265,1267],{"class":140,"line":76},[138,1260,795],{"class":794},[138,1262,798],{"class":794},[138,1264,802],{"class":801},[138,1266,806],{"class":805},[138,1268,810],{"class":809},[138,1270,1271,1273,1275],{"class":140,"line":152},[138,1272,977],{"class":815},[138,1274,782],{"class":809},[138,1276,821],{"class":805},[138,1278,1279,1281,1283,1285,1287,1289,1291,1293,1295,1297,1299,1302,1304],{"class":140,"line":158},[138,1280,986],{"class":809},[138,1282,989],{"class":815},[138,1284,782],{"class":809},[138,1286,957],{"class":826},[138,1288,996],{"class":830},[138,1290,834],{"class":826},[138,1292,954],{"class":809},[138,1294,1003],{"class":815},[138,1296,782],{"class":809},[138,1298,957],{"class":826},[138,1300,1301],{"class":830},"Lp",[138,1303,834],{"class":826},[138,1305,1015],{"class":809},[138,1307,1308],{"class":140,"line":164},[138,1309,864],{"class":805},[138,1311,1312,1314],{"class":140,"line":170},[138,1313,869],{"class":809},[138,1315,872],{"class":805},[12,1317,1318,1319,1322,1323,363],{},"Dessa forma, um componente como ",[135,1320,1321],{},"layers\u002Flandingpage\u002Fcomponents\u002FHero.vue"," passa a ser usado como ",[135,1324,1325],{},"\u003CLpHero \u002F>",[27,1327],{},[30,1329,1331],{"id":1330},"como-fiz-a-migração","Como fiz a migração",[12,1333,1334],{},"Fiz a migração uma layer por vez. A ideia foi reduzir risco e validar cada parte antes de seguir para a próxima.",[12,1336,1337,1338,1340],{},"Comecei pela ",[135,1339,1245],{},", porque era a parte mais isolada. Ela não dependia diretamente de stores complexas, checkout, carrinho ou middleware específico. Isso tornou a primeira migração mais simples e ajudou a entender como o Nuxt resolveria os componentes dentro da layer.",[12,1342,1343,1344,1346],{},"Depois fui para o ",[135,1345,764],{},", porque ele tinha um domínio claro e bem separado da vitrine pública. Nesse ponto apareceram decisões mais interessantes, principalmente nas rotas.",[12,1348,1349,1350,1353,1354,1357,1358,370,1361,370,1364,1367,1368,1370,1371,363],{},"As páginas do admin estavam originalmente em ",[135,1351,1352],{},"app\u002Fpages\u002Fadmin\u002F",". Ao mover para ",[135,1355,1356],{},"layers\u002Fadmin\u002Fpages\u002F",", se eu deixasse os arquivos soltos, eles seriam registrados como ",[135,1359,1360],{},"\u002Flogin",[135,1362,1363],{},"\u002Fpedidos",[135,1365,1366],{},"\u002Facervo"," e assim por diante. Eu queria manter as URLs com ",[135,1369,884],{},", mas sem criar ",[135,1372,1373],{},"layers\u002Fadmin\u002Fpages\u002Fadmin\u002F",[12,1375,1376,1377,1379],{},"Foi aí que usei o hook ",[135,1378,1038],{},". Ele permitiu manter a organização interna da layer sem repetir o nome da pasta na estrutura.",[12,1381,1382,1383,1386,1387,1390,1391,1394],{},"Outra decisão importante foi trocar o antigo ",[135,1384,1385],{},"index.vue"," do admin por ",[135,1388,1389],{},"dashboard.vue",". Como a landingpage já possuía um index.vue, os dois arquivos inicialmente competiam pelo mesmo path ",[135,1392,1393],{},"\u002F"," durante a geração das rotas.",[12,1396,1397,1398,1197,1401,1404],{},"Ao renomear a página do admin e declarar ",[135,1399,1400],{},"path: '\u002Fadmin'",[135,1402,1403],{},"definePageMeta",", deixei a rota explícita e removi essa ambiguidade da resolução.",[129,1406,1410],{"className":1407,"code":1408,"language":1409,"meta":75,"style":75},"language-vue shiki shiki-themes material-theme-lighter github-dark github-dark","\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n  layout: 'admin',\n  middleware: 'auth',\n  path: '\u002Fadmin',\n})\n\u003C\u002Fscript>\n","vue",[135,1411,1412,1441,1449,1464,1480,1495,1501],{"__ignoreMap":75},[138,1413,1414,1417,1421,1425,1428,1431,1434,1436,1438],{"class":140,"line":141},[138,1415,1416],{"class":809},"\u003C",[138,1418,1420],{"class":1419},"sqIbZ","script",[138,1422,1424],{"class":1423},"s7047"," setup",[138,1426,1427],{"class":1423}," lang",[138,1429,1430],{"class":809},"=",[138,1432,1433],{"class":826},"\"",[138,1435,787],{"class":830},[138,1437,1433],{"class":826},[138,1439,1440],{"class":809},">\n",[138,1442,1443,1445,1447],{"class":140,"line":76},[138,1444,1403],{"class":801},[138,1446,806],{"class":805},[138,1448,810],{"class":809},[138,1450,1451,1454,1456,1458,1460,1462],{"class":140,"line":152},[138,1452,1453],{"class":815},"  layout",[138,1455,782],{"class":809},[138,1457,957],{"class":826},[138,1459,764],{"class":830},[138,1461,834],{"class":826},[138,1463,837],{"class":809},[138,1465,1466,1469,1471,1473,1476,1478],{"class":140,"line":158},[138,1467,1468],{"class":815},"  middleware",[138,1470,782],{"class":809},[138,1472,957],{"class":826},[138,1474,1475],{"class":830},"auth",[138,1477,834],{"class":826},[138,1479,837],{"class":809},[138,1481,1482,1485,1487,1489,1491,1493],{"class":140,"line":164},[138,1483,1484],{"class":815},"  path",[138,1486,782],{"class":809},[138,1488,957],{"class":826},[138,1490,884],{"class":830},[138,1492,834],{"class":826},[138,1494,837],{"class":809},[138,1496,1497,1499],{"class":140,"line":170},[138,1498,869],{"class":809},[138,1500,872],{"class":805},[138,1502,1503,1506,1508],{"class":140,"line":176},[138,1504,1505],{"class":809},"\u003C\u002F",[138,1507,1420],{"class":1419},[138,1509,1440],{"class":809},[12,1511,1512,1513,370,1516,377,1519,1522,1523,363],{},"Para as demais páginas, como ",[135,1514,1515],{},"login.vue",[135,1517,1518],{},"pedidos.vue",[135,1520,1521],{},"acervo.vue,"," deixei o hook cuidar do prefixo ",[135,1524,884],{},[12,1526,1527,1528,1530,1531,363],{},"Além de resolver o conflito, o nome ",[135,1529,1389],{}," ficou mais semântico do que ",[135,1532,1385],{},[12,1534,1535,1536,1538,1539,1541],{},"Por fim, migrei a ",[135,1537,718],{},", que era a parte mais sensível por envolver ",[135,1540,725],{},", catálogo, carrinho, checkout e pedidos. Deixei essa etapa por último justamente porque ela concentra a jornada principal do cliente final.",[12,1543,1544],{},"Os commits também acompanharam essa separação por domínio:",[129,1546,1550],{"className":1547,"code":1548,"language":1549,"meta":75,"style":75},"language-bash shiki shiki-themes material-theme-lighter github-dark github-dark","git commit -m \"chore: migra landingpage para layer dedicada\"\ngit commit -m \"chore: migra painel admin para layer dedicada\"\ngit commit -m \"chore: migra vitrine (catalog + checkout + pedido) para layer dedicada\"\n","bash",[135,1551,1552,1574,1589],{"__ignoreMap":75},[138,1553,1554,1558,1561,1565,1568,1571],{"class":140,"line":141},[138,1555,1557],{"class":1556},"soiBB","git",[138,1559,1560],{"class":830}," commit",[138,1562,1564],{"class":1563},"sSJ72"," -m",[138,1566,1567],{"class":826}," \"",[138,1569,1570],{"class":830},"chore: migra landingpage para layer dedicada",[138,1572,1573],{"class":826},"\"\n",[138,1575,1576,1578,1580,1582,1584,1587],{"class":140,"line":76},[138,1577,1557],{"class":1556},[138,1579,1560],{"class":830},[138,1581,1564],{"class":1563},[138,1583,1567],{"class":826},[138,1585,1586],{"class":830},"chore: migra painel admin para layer dedicada",[138,1588,1573],{"class":826},[138,1590,1591,1593,1595,1597,1599,1602],{"class":140,"line":152},[138,1592,1557],{"class":1556},[138,1594,1560],{"class":830},[138,1596,1564],{"class":1563},[138,1598,1567],{"class":826},[138,1600,1601],{"class":830},"chore: migra vitrine (catalog + checkout + pedido) para layer dedicada",[138,1603,1573],{"class":826},[27,1605],{},[30,1607,1609],{"id":1608},"o-resultado","O resultado",[12,1611,1612],{},"A principal diferença agora é que cada domínio tem um local próprio.",[12,1614,1615,1616,1619,1620,1623,1624,363],{},"Quando preciso mexer no checkout, abro ",[135,1617,1618],{},"layers\u002Fvitrine\u002F"," e encontro os componentes, composables, stores, páginas e layout relacionados à jornada do cliente. Quando preciso mexer no painel, vou para ",[135,1621,1622],{},"layers\u002Fadmin\u002F",". Quando preciso alterar a página institucional, vou para ",[135,1625,1626],{},"layers\u002Flandingpage\u002F",[12,1628,1629],{},"Isso não torna o projeto mais simples, ainda existe complexidade. Mas agora ela está melhor distribuída.",[12,1631,1632],{},"A estrutura também fica mais preparada para o roadmap. Quando a fase de relatórios e contratos PDF chegar, posso avaliar se faz sentido criar uma nova layer. Quando o Super Admin sair do planejamento e virar código, ele também pode nascer como um domínio próprio.",[27,1634],{},[30,1636,1638],{"id":1637},"quando-layers-fazem-sentido-e-quando-são-overkill","Quando Layers fazem sentido e quando são overkill",[12,1640,1641],{},"Depois de aplicar no projeto, minha percepção é que Layers são úteis quando existe uma divisão real de domínio. Não é uma estrutura a ser usada em qualquer projeto Nuxt.",[1643,1644,1646],"h3",{"id":1645},"fazem-sentido-quando","Fazem sentido quando",[402,1648,1649,1652,1655,1658,1661],{},[405,1650,1651],{},"Você tem contextos de usuário distintos que raramente se cruzam",[405,1653,1654],{},"O projeto é multitenant e possui domínios bem definidos",[405,1656,1657],{},"O roadmap aponta para crescimento real, novas features e novos contextos",[405,1659,1660],{},"Você quer que uma pessoa nova consiga entender um domínio sem precisar conhecer o projeto inteiro",[405,1662,1663],{},"Componentes, páginas, stores e composables de uma mesma funcionalidade estão ficando espalhados demais",[1643,1665,1667],{"id":1666},"podem-ser-overkill-quando","Podem ser overkill quando",[402,1669,1670,1673,1676,1679,1682],{},[405,1671,1672],{},"O projeto é um MVP ou protótipo e velocidade importa mais que estrutura",[405,1674,1675],{},"Você tem poucos componentes e tudo ainda cabe facilmente na estrutura padrão",[405,1677,1678],{},"É um site institucional ou landing page sem domínios de negócio reais",[405,1680,1681],{},"O time é pequeno, o prazo é curto e não existe previsão de crescimento",[405,1683,1684],{},"A separação criaria mais configuração do que benefício real",[12,1686,1687],{},"Se o projeto for um CRUD simples com uma área administrativa básica, a estrutura clássica do Nuxt resolve muito bem. Layers não precisam entrar em tudo. Arquitetura também é saber quando não adicionar complexidade.",[27,1689],{},[30,1691,1693],{"id":1692},"a-pergunta-que-guiou-a-decisão","A pergunta que guiou a decisão",[12,1695,1696],{},"A mesma pergunta do artigo anterior continua válida aqui:",[12,1698,1699],{},[19,1700,1701],{},"Se eu precisar mudar isso daqui a seis meses, qual é o menor número de arquivos que vou precisar tocar?",[12,1703,1704],{},"Quando a estrutura está bem definida, a resposta tende a ser menor. Quando começa a ser vários arquivos espalhados em pastas diferentes, talvez alguma responsabilidade esteja no lugar errado.",[12,1706,1707],{},"No meu caso, Nuxt Layers ajudou a organizar melhor essas responsabilidades antes que a estrutura inicial trouxesse uma limitação.",[27,1709],{},[30,1711,1713],{"id":1712},"leituras-relacionadas","Leituras relacionadas",[402,1715,1716,1723,1730,1736],{},[405,1717,1718],{},[105,1719,1722],{"href":1720,"rel":1721},"https:\u002F\u002Flarisantos.vercel.app\u002Fblog\u002Fnuxt-saas-multitenant",[109],"Como estruturei a arquitetura inicial de um SaaS multitenant com Nuxt 3 e Supabase",[405,1724,1725],{},[105,1726,1729],{"href":1727,"rel":1728},"https:\u002F\u002Flarisantos.vercel.app\u002Fblog\u002Fprincipio-solid",[109],"SOLID: 5 Princípios para Escrever Código Limpo e Escalável",[405,1731,1732],{},[105,1733,1735],{"href":107,"rel":1734},[109],"Arquitetura Modular com Nuxt Layers",[405,1737,1738],{},[105,1739,1742],{"href":1740,"rel":1741},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fgetting-started\u002Flayers",[109],"Documentação oficial Nuxt Layers",[1744,1745,1746],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s3Er8, html code.shiki .s3Er8{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sK_r7, html code.shiki .sK_r7{--shiki-light:#6182B8;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sMo7A, html code.shiki .sMo7A{--shiki-light:#90A4AE;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sG-J9, html code.shiki .sG-J9{--shiki-light:#39ADB5;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sdv8B, html code.shiki .sdv8B{--shiki-light:#E53935;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sF_wb, html code.shiki .sF_wb{--shiki-light:#39ADB5;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .s0vBq, html code.shiki .s0vBq{--shiki-light:#91B859;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sk1zL, html code.shiki .sk1zL{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#FFAB70;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sFsEu, html code.shiki .sFsEu{--shiki-light:#9C3EDA;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sFfmW, html code.shiki .sFfmW{--shiki-light:#39ADB5;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sqIbZ, html code.shiki .sqIbZ{--shiki-light:#E53935;--shiki-default:#85E89D;--shiki-dark:#85E89D}html pre.shiki code .s7047, html code.shiki .s7047{--shiki-light:#9C3EDA;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .soiBB, html code.shiki .soiBB{--shiki-light:#E2931D;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sSJ72, html code.shiki .sSJ72{--shiki-light:#91B859;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}",{"title":75,"searchDepth":76,"depth":76,"links":1748},[1749,1750,1751,1752,1753,1754,1755,1759,1760],{"id":123,"depth":76,"text":124},{"id":389,"depth":76,"text":390},{"id":433,"depth":76,"text":434},{"id":770,"depth":76,"text":771},{"id":1330,"depth":76,"text":1331},{"id":1608,"depth":76,"text":1609},{"id":1637,"depth":76,"text":1638,"children":1756},[1757,1758],{"id":1645,"depth":152,"text":1646},{"id":1666,"depth":152,"text":1667},{"id":1692,"depth":76,"text":1693},{"id":1712,"depth":76,"text":1713},"2026-06-15T00:17:35-03:00","Um relato prático sobre como migrei um projeto SaaS multitenant para arquitetura modular com Nuxt Layers: as decisões, os ajustes e o raciocínio por trás da nova estrutura.","\u002Fimages\u002Fblog\u002Fblog-nuxt-layers.png",{},"\u002Fblog\u002Fsass-nuxt-layers",{"title":98,"description":1762},"blog\u002Fsass-nuxt-layers",[1769,1409,1770,1771,1772,1773,1774],"nuxt","arquitetura","layers","multitenant","typescript","saas","bMGTOShdAPDmh0wzDFCeafTNl-b1xHbGAfXwsbV5ytI",{"id":1777,"title":1722,"author":7,"body":1778,"date":4966,"description":4967,"extension":83,"image":4968,"meta":4969,"navigation":86,"path":4970,"seo":4971,"stem":4972,"tags":4973,"__hash__":4976},"blog\u002Fblog\u002Fnuxt-saas-multitenant.md",{"type":9,"value":1779,"toc":4946},[1780,1787,1819,1822,1864,1871,1873,1877,1880,1887,1890,1899,1902,1905,1907,1911,1914,1964,1970,1973,2013,2020,2026,2083,2088,2107,2110,2137,2145,2147,2151,2157,2160,2217,2223,2231,2237,2240,2255,2261,2270,2276,2278,2282,2288,2291,2321,2327,2330,2339,2342,2362,2365,2368,2374,2376,2380,2383,2389,2392,2491,2498,2504,2956,2962,2979,2986,2988,2992,2995,2998,3237,3248,3255,3257,3263,3266,3536,3546,3549,3638,3641,3643,3647,3655,3657,3703,3706,3708,3712,3715,3721,4248,4251,4253,4260,4263,4431,4437,4454,4457,4459,4496,4499,4552,4555,4558,4560,4564,4567,4671,4674,4676,4680,4770,4773,4786,4794,4796,4800,4861,4863,4867,4873,4892,4895,4898,4902,4908,4911,4913,4915,4943],[12,1781,1782,1783,1786],{},"Recentemente, precisei arquitetar um SaaS multitenant para um projeto que tinha um desafio central: ",[19,1784,1785],{},"permitir que várias empresas utilizassem o mesmo sistema, mas mantendo vitrines, dados e identidades visuais separadas",". Antes de pensar nas telas ou nos componentes, a primeira decisão importante foi entender como a aplicação identificaria qual empresa estava sendo acessada e como esse contexto seria compartilhado entre rotas, layout e interface.",[12,1788,1789,1790,1793,1794,1797,1798,1801,1802,1805,1806,1809,1810,370,1813,377,1816,363],{},"Para construir a base do MVP, utilizei ",[19,1791,1792],{},"Nuxt 3"," no frontend, ",[19,1795,1796],{},"Nuxt UI 4"," na construção de componentes, ",[19,1799,1800],{},"Nuxt SEO"," para apoiar a otimização das páginas, ",[19,1803,1804],{},"Supabase"," como backend principal e ",[19,1807,1808],{},"Vercel"," para o deploy. Com essa estrutura, a arquitetura foi organizada para resolver três pontos principais: ",[19,1811,1812],{},"descobrir o tenant a partir da URL",[19,1814,1815],{},"manter os dados da empresa ativa disponíveis na aplicação",[19,1817,1818],{},"refletir sua identidade visual de forma dinâmica na vitrine pública",[12,1820,1821],{},"Para isso, a arquitetura foi organizada com:",[402,1823,1824,1830,1839,1844,1849,1854,1859],{},[405,1825,1826,1829],{},[19,1827,1828],{},"um único banco compartilhado",";",[405,1831,1832,1829],{},[19,1833,1834,1835,1838],{},"uma coluna ",[135,1836,1837],{},"empresa_id"," para relacionar os dados ao tenant",[405,1840,1841,1829],{},[19,1842,1843],{},"RLS no Supabase para isolamento dos dados",[405,1845,1846,1829],{},[19,1847,1848],{},"rotas dinâmicas por slug no Nuxt",[405,1850,1851,1829],{},[19,1852,1853],{},"middleware global para resolver a empresa acessada",[405,1855,1856,1829],{},[19,1857,1858],{},"Pinia para manter a empresa ativa em memória",[405,1860,1861,363],{},[19,1862,1863],{},"CSS variables para aplicar as cores de cada empresa no layout",[12,1865,1866,1867,1870],{},"A lógica central é simples: quando o usuário acessa uma URL como ",[135,1868,1869],{},"\u002Flari-loja",", o sistema precisa transformar esse slug em uma empresa real e disponibilizar essa empresa para o restante da aplicação.",[27,1872],{},[30,1874,1876],{"id":1875},"a-estratégia-multitenant","A estratégia multitenant",[12,1878,1879],{},"Existem várias formas de estruturar um SaaS multitenant. Uma delas seria criar um banco separado para cada cliente. Para este projeto, essa opção traria complexidade cedo demais.",[12,1881,1882,1883,1886],{},"Por isso, optei por uma arquitetura baseada em ",[19,1884,1885],{},"row-level multitenancy",". Isso significa que os dados das empresas vivem nas mesmas tabelas, mas cada registro possui uma relação com a empresa dona daquele dado.",[12,1888,1889],{},"O centro dessa estratégia é o campo:",[129,1891,1893],{"className":131,"code":1892,"language":133,"meta":75,"style":75},"empresa_id\n",[135,1894,1895],{"__ignoreMap":75},[138,1896,1897],{"class":140,"line":141},[138,1898,1892],{},[12,1900,1901],{},"Esse campo aparece nas tabelas que pertencem a uma empresa e permite que o banco saiba a qual tenant cada informação pertence.",[12,1903,1904],{},"Com essa decisão, a aplicação não precisa trocar de banco quando muda de empresa. Ela muda apenas o contexto ativo.",[27,1906],{},[30,1908,1910],{"id":1909},"como-o-banco-foi-estruturado","Como o banco foi estruturado",[12,1912,1913],{},"A base da arquitetura começa com três ideias principais no banco:",[1915,1916,1917,1930],"table",{},[1918,1919,1920],"thead",{},[1921,1922,1923,1927],"tr",{},[1924,1925,1926],"th",{},"Tabela",[1924,1928,1929],{},"Papel na arquitetura",[1931,1932,1933,1944,1954],"tbody",{},[1921,1934,1935,1941],{},[1936,1937,1938],"td",{},[135,1939,1940],{},"empresas",[1936,1942,1943],{},"Guarda os dados públicos do tenant, como nome, slug, logo e cores",[1921,1945,1946,1951],{},[1936,1947,1948],{},[135,1949,1950],{},"profiles",[1936,1952,1953],{},"Relaciona o usuário autenticado a uma empresa",[1921,1955,1956,1961],{},[1936,1957,1958,1959],{},"tabelas com ",[135,1960,1837],{},[1936,1962,1963],{},"Guardam dados pertencentes a uma empresa específica",[12,1965,1966,1967,1969],{},"A tabela ",[135,1968,1940],{}," funciona como a origem do tenant público.",[12,1971,1972],{},"Ela precisa ter, no mínimo, algo como:",[129,1974,1976],{"className":131,"code":1975,"language":133,"meta":75,"style":75},"empresas\n- id\n- nome\n- slug\n- logo_url\n- descricao\n- colors\n",[135,1977,1978,1983,1988,1993,1998,2003,2008],{"__ignoreMap":75},[138,1979,1980],{"class":140,"line":141},[138,1981,1982],{},"empresas\n",[138,1984,1985],{"class":140,"line":76},[138,1986,1987],{},"- id\n",[138,1989,1990],{"class":140,"line":152},[138,1991,1992],{},"- nome\n",[138,1994,1995],{"class":140,"line":158},[138,1996,1997],{},"- slug\n",[138,1999,2000],{"class":140,"line":164},[138,2001,2002],{},"- logo_url\n",[138,2004,2005],{"class":140,"line":170},[138,2006,2007],{},"- descricao\n",[138,2009,2010],{"class":140,"line":176},[138,2011,2012],{},"- colors\n",[12,2014,2015,2016,2019],{},"O campo ",[135,2017,2018],{},"slug"," é usado na URL pública, como será explicado posteriormente.",[12,2021,2015,2022,2025],{},[135,2023,2024],{},"colors"," guarda a identidade visual da empresa:",[129,2027,2031],{"className":2028,"code":2029,"language":2030,"meta":75,"style":75},"language-json shiki shiki-themes material-theme-lighter github-dark github-dark","{\n  \"primary\": \"#2563ab\",\n  \"secondary\": \"#f97316\"\n}\n","json",[135,2032,2033,2037,2060,2078],{"__ignoreMap":75},[138,2034,2035],{"class":140,"line":141},[138,2036,810],{"class":809},[138,2038,2039,2043,2047,2049,2051,2053,2056,2058],{"class":140,"line":76},[138,2040,2042],{"class":2041},"swu5b","  \"",[138,2044,2046],{"class":2045},"sod2m","primary",[138,2048,1433],{"class":2041},[138,2050,782],{"class":809},[138,2052,1567],{"class":826},[138,2054,2055],{"class":830},"#2563ab",[138,2057,1433],{"class":826},[138,2059,837],{"class":809},[138,2061,2062,2064,2067,2069,2071,2073,2076],{"class":140,"line":152},[138,2063,2042],{"class":2041},[138,2065,2066],{"class":2045},"secondary",[138,2068,1433],{"class":2041},[138,2070,782],{"class":809},[138,2072,1567],{"class":826},[138,2074,2075],{"class":830},"#f97316",[138,2077,1573],{"class":826},[138,2079,2080],{"class":140,"line":158},[138,2081,2082],{"class":809},"}\n",[12,2084,1966,2085,2087],{},[135,2086,1950],{}," conecta um usuário autenticado a uma empresa:",[129,2089,2091],{"className":131,"code":2090,"language":133,"meta":75,"style":75},"profiles\n- id\n- empresa_id\n",[135,2092,2093,2098,2102],{"__ignoreMap":75},[138,2094,2095],{"class":140,"line":141},[138,2096,2097],{},"profiles\n",[138,2099,2100],{"class":140,"line":76},[138,2101,1987],{},[138,2103,2104],{"class":140,"line":152},[138,2105,2106],{},"- empresa_id\n",[12,2108,2109],{},"Já as tabelas que possuem dados de uma empresa seguem a mesma lógica:",[129,2111,2113],{"className":131,"code":2112,"language":133,"meta":75,"style":75},"itens\n- id\n- empresa_id\n- nome\n- ...\n",[135,2114,2115,2120,2124,2128,2132],{"__ignoreMap":75},[138,2116,2117],{"class":140,"line":141},[138,2118,2119],{},"itens\n",[138,2121,2122],{"class":140,"line":76},[138,2123,1987],{},[138,2125,2126],{"class":140,"line":152},[138,2127,2106],{},[138,2129,2130],{"class":140,"line":158},[138,2131,1992],{},[138,2133,2134],{"class":140,"line":164},[138,2135,2136],{},"- ...\n",[12,2138,2139,2140],{},"O ponto importante não é listar todos os campos. O ponto é a decisão: ",[19,2141,2142,2143,363],{},"todo dado que pertence a uma empresa precisa carregar ",[135,2144,1837],{},[27,2146],{},[30,2148,2150],{"id":2149},"como-o-banco-descobre-a-empresa-do-usuário","Como o banco descobre a empresa do usuário",[12,2152,2153,2154,363],{},"Para a parte autenticada do sistema, foi criada a função ",[135,2155,2156],{},"get_minha_empresa_id()",[12,2158,2159],{},"Ela permite que o próprio banco descubra qual empresa pertence ao usuário logado.",[129,2161,2165],{"className":2162,"code":2163,"language":2164,"meta":75,"style":75},"language-sql shiki shiki-themes material-theme-lighter github-dark github-dark","create or replace function get_minha_empresa_id()\nreturns uuid\nlanguage sql\nstable\nsecurity definer\nas $$\n  select empresa_id\n  from profiles\n  where id = auth.uid();\n$$;\n","sql",[135,2166,2167,2172,2177,2182,2187,2192,2197,2202,2207,2212],{"__ignoreMap":75},[138,2168,2169],{"class":140,"line":141},[138,2170,2171],{},"create or replace function get_minha_empresa_id()\n",[138,2173,2174],{"class":140,"line":76},[138,2175,2176],{},"returns uuid\n",[138,2178,2179],{"class":140,"line":152},[138,2180,2181],{},"language sql\n",[138,2183,2184],{"class":140,"line":158},[138,2185,2186],{},"stable\n",[138,2188,2189],{"class":140,"line":164},[138,2190,2191],{},"security definer\n",[138,2193,2194],{"class":140,"line":170},[138,2195,2196],{},"as $$\n",[138,2198,2199],{"class":140,"line":176},[138,2200,2201],{},"  select empresa_id\n",[138,2203,2204],{"class":140,"line":182},[138,2205,2206],{},"  from profiles\n",[138,2208,2209],{"class":140,"line":188},[138,2210,2211],{},"  where id = auth.uid();\n",[138,2213,2214],{"class":140,"line":194},[138,2215,2216],{},"$$;\n",[12,2218,1193,2219,2222],{},[135,2220,2221],{},"auth.uid()"," vem do Supabase Auth e representa o usuário autenticado na requisição atual.",[12,2224,2225,2226,2228,2229,363],{},"A função usa esse ID para buscar o ",[135,2227,1837],{}," correspondente na tabela ",[135,2230,1950],{},[12,2232,2233,2234,2236],{},"Com isso, as policies do RLS não precisam confiar em um ",[135,2235,1837],{}," enviado pelo frontend. O banco consulta o contexto da sessão e valida o acesso por conta própria.",[12,2238,2239],{},"Um exemplo reduzido de policy seria:",[129,2241,2243],{"className":2162,"code":2242,"language":2164,"meta":75,"style":75},"using (empresa_id = get_minha_empresa_id())\nwith check (empresa_id = get_minha_empresa_id())\n",[135,2244,2245,2250],{"__ignoreMap":75},[138,2246,2247],{"class":140,"line":141},[138,2248,2249],{},"using (empresa_id = get_minha_empresa_id())\n",[138,2251,2252],{"class":140,"line":76},[138,2253,2254],{},"with check (empresa_id = get_minha_empresa_id())\n",[12,2256,1193,2257,2260],{},[135,2258,2259],{},"using"," filtra o que o usuário pode acessar.",[12,2262,1193,2263,2266,2267,2269],{},[135,2264,2265],{},"with check"," impede que ele crie ou altere registros usando o ",[135,2268,1837],{}," de outra empresa.",[12,2271,2272,2273],{},"Essa foi uma decisão central da arquitetura: ",[19,2274,2275],{},"o isolamento dos dados não fica dependente da interface. Essa responsabilidade fica no banco.",[27,2277],{},[30,2279,2281],{"id":2280},"o-roteamento-por-slug-nas-páginas-públicas","O roteamento por slug nas páginas públicas",[12,2283,2284,2285,2287],{},"Nas páginas públicas, a empresa ativa é identificada pela URL através de um ",[19,2286,2018],{},". Esse slug funciona como a chave inicial para localizar o tenant no banco, carregar seus dados e disponibilizar esse contexto para o restante da aplicação.",[12,2289,2290],{},"A estrutura pensada foi:",[129,2292,2294],{"className":131,"code":2293,"language":133,"meta":75,"style":75},"pages\u002F\n├── index.vue\n├── 404.vue\n└── [slug]\u002F\n    └── index.vue\n",[135,2295,2296,2301,2306,2311,2316],{"__ignoreMap":75},[138,2297,2298],{"class":140,"line":141},[138,2299,2300],{},"pages\u002F\n",[138,2302,2303],{"class":140,"line":76},[138,2304,2305],{},"├── index.vue\n",[138,2307,2308],{"class":140,"line":152},[138,2309,2310],{},"├── 404.vue\n",[138,2312,2313],{"class":140,"line":158},[138,2314,2315],{},"└── [slug]\u002F\n",[138,2317,2318],{"class":140,"line":164},[138,2319,2320],{},"    └── index.vue\n",[12,2322,2323,2324,2326],{},"A rota ",[135,2325,725],{}," representa a vitrine pública de uma empresa.",[12,2328,2329],{},"Exemplo:",[129,2331,2333],{"className":131,"code":2332,"language":133,"meta":75,"style":75},"link-da-plataforma.com\u002Flari-loja\n",[135,2334,2335],{"__ignoreMap":75},[138,2336,2337],{"class":140,"line":141},[138,2338,2332],{},[12,2340,2341],{},"Nesse caso, o Nuxt entende que:",[129,2343,2345],{"className":785,"code":2344,"language":787,"meta":75,"style":75},"slug = 'lari-loja'\n",[135,2346,2347],{"__ignoreMap":75},[138,2348,2349,2352,2354,2356,2359],{"class":140,"line":141},[138,2350,2351],{"class":805},"slug ",[138,2353,1430],{"class":1112},[138,2355,957],{"class":826},[138,2357,2358],{"class":830},"lari-loja",[138,2360,2361],{"class":826},"'\n",[12,2363,2364],{},"Mas o slug sozinho não resolve o problema. Ele é apenas texto na URL.",[12,2366,2367],{},"Ainda é necessário descobrir se existe uma empresa cadastrada com esse slug e carregar seus dados.",[12,2369,2370,2371,363],{},"Essa responsabilidade ficou no ",[19,2372,2373],{},"middleware",[27,2375],{},[30,2377,2379],{"id":2378},"middleware-global-transformando-slug-em-empresa","Middleware global: transformando slug em empresa",[12,2381,2382],{},"O middleware global conecta a URL ao tenant.",[12,2384,2385,2386,2388],{},"Ele roda antes da página renderizar, lê o ",[135,2387,2018],{},", consulta o Supabase e salva a empresa encontrada na store.",[12,2390,2391],{},"Para que tudo funcione, existe uma função no banco responsável por resolver a empresa a partir do slug recebido pela URL.",[129,2393,2395],{"className":2162,"code":2394,"language":2164,"meta":75,"style":75},"-- resolver_empresa_por_slug(p_slug text)\ndeclare\n  v_empresa json;\nbegin\n  select json_build_object(\n    'id',            e.id,\n    'slug',          e.slug,\n    'nome',          e.nome,\n    'descricao',     e.descricao,\n    [...]\n    'colors',        e.colors\n  )\n  into v_empresa\n  from empresas e\n  where e.slug = lower(trim(p_slug))\n    and e.is_ativo = true;\n\n  return v_empresa;\nend;\n",[135,2396,2397,2402,2407,2412,2417,2422,2427,2432,2437,2442,2447,2452,2457,2462,2467,2472,2477,2481,2486],{"__ignoreMap":75},[138,2398,2399],{"class":140,"line":141},[138,2400,2401],{},"-- resolver_empresa_por_slug(p_slug text)\n",[138,2403,2404],{"class":140,"line":76},[138,2405,2406],{},"declare\n",[138,2408,2409],{"class":140,"line":152},[138,2410,2411],{},"  v_empresa json;\n",[138,2413,2414],{"class":140,"line":158},[138,2415,2416],{},"begin\n",[138,2418,2419],{"class":140,"line":164},[138,2420,2421],{},"  select json_build_object(\n",[138,2423,2424],{"class":140,"line":170},[138,2425,2426],{},"    'id',            e.id,\n",[138,2428,2429],{"class":140,"line":176},[138,2430,2431],{},"    'slug',          e.slug,\n",[138,2433,2434],{"class":140,"line":182},[138,2435,2436],{},"    'nome',          e.nome,\n",[138,2438,2439],{"class":140,"line":188},[138,2440,2441],{},"    'descricao',     e.descricao,\n",[138,2443,2444],{"class":140,"line":194},[138,2445,2446],{},"    [...]\n",[138,2448,2449],{"class":140,"line":199},[138,2450,2451],{},"    'colors',        e.colors\n",[138,2453,2454],{"class":140,"line":204},[138,2455,2456],{},"  )\n",[138,2458,2459],{"class":140,"line":209},[138,2460,2461],{},"  into v_empresa\n",[138,2463,2464],{"class":140,"line":215},[138,2465,2466],{},"  from empresas e\n",[138,2468,2469],{"class":140,"line":221},[138,2470,2471],{},"  where e.slug = lower(trim(p_slug))\n",[138,2473,2474],{"class":140,"line":227},[138,2475,2476],{},"    and e.is_ativo = true;\n",[138,2478,2479],{"class":140,"line":233},[138,2480,649],{"emptyLinePlaceholder":86},[138,2482,2483],{"class":140,"line":239},[138,2484,2485],{},"  return v_empresa;\n",[138,2487,2488],{"class":140,"line":245},[138,2489,2490],{},"end;\n",[12,2492,2493,2494,2497],{},"Essa função recebe o slug, normaliza o valor com ",[135,2495,2496],{},"lower(trim(p_slug))"," e busca apenas empresas ativas. O retorno é um JSON com os dados públicos que a vitrine precisa consumir, como nome, descrição, logo e cores.",[12,2499,2500,2501,2503],{},"Com isso, o middleware não precisa conhecer a estrutura completa da tabela ",[135,2502,1940],{},". Ele apenas envia o slug para a RPC e recebe uma versão pública da empresa.",[129,2505,2507],{"className":785,"code":2506,"language":787,"meta":75,"style":75},"\u002F\u002F middleware\u002Ftenant.global.ts\nconst rotasReservadas = ['admin', 'login', 'cadastro', 'checkout', '404'] \u002F\u002F Lista de rotas reservadas\n\nexport default defineNuxtRouteMiddleware(async to => {\n\n  const slugParam = to.params.slug \u002F\u002F Captura o parâmetro slug da URL atual\n\n  if (!slugParam || Array.isArray(slugParam)) return \u002F\u002F Valida o slug e interrompe middleware\n\n  const slug = slugParam \u002F\u002F Atribiu o slug como uma string simples\n\n  if (rotasReservadas.includes(slug)) return \u002F\u002F Se o slug for uma rota reservada, não tenta buscar empresa\n\n  const empresaStore = useEmpresaStore() \u002F\u002F Acessa a store responsável por guardar a empresa ativa\n\n  \u002F\u002F Se a empresa atual já estiver carregada para esse mesmo slug, evita uma nova consulta ao banco\n  if (empresaStore.empresa?.slug === slug) return\n\n  \u002F\u002F Cria o cliente do Supabase para realizar a chamada ao backend\n  const supabase = useSupabaseClient()\n\n  \u002F\u002F Chama a função RPC no Supabase para buscar a empresa correspondente ao slug da URL\n  const { data, error } = await supabase.rpc('resolver_empresa_por_slug', {\n    p_slug: slug\n  })\n\n  \u002F\u002F Se ocorrer erro ou nenhuma empresa for encontrada, redireciona para a página 404\n  if (error || !data) {\n    return navigateTo('\u002F404')\n  }\n\n  \u002F\u002F Salva os dados da empresa encontrada na store\n    empresaStore.definir(data as EmpresaPublica)\n})\n\n",[135,2508,2509,2514,2574,2578,2599,2603,2627,2631,2668,2672,2686,2690,2714,2718,2736,2740,2745,2773,2777,2782,2797,2801,2806,2849,2859,2866,2870,2875,2896,2915,2919,2923,2928,2950],{"__ignoreMap":75},[138,2510,2511],{"class":140,"line":141},[138,2512,2513],{"class":911},"\u002F\u002F middleware\u002Ftenant.global.ts\n",[138,2515,2516,2519,2523,2525,2527,2529,2531,2533,2535,2537,2540,2542,2544,2546,2549,2551,2553,2555,2557,2559,2561,2563,2566,2568,2571],{"class":140,"line":76},[138,2517,2518],{"class":1073},"const",[138,2520,2522],{"class":2521},"sVPC0"," rotasReservadas",[138,2524,1146],{"class":1112},[138,2526,944],{"class":805},[138,2528,834],{"class":826},[138,2530,764],{"class":830},[138,2532,834],{"class":826},[138,2534,954],{"class":809},[138,2536,957],{"class":826},[138,2538,2539],{"class":830},"login",[138,2541,834],{"class":826},[138,2543,954],{"class":809},[138,2545,957],{"class":826},[138,2547,2548],{"class":830},"cadastro",[138,2550,834],{"class":826},[138,2552,954],{"class":809},[138,2554,957],{"class":826},[138,2556,714],{"class":830},[138,2558,834],{"class":826},[138,2560,954],{"class":809},[138,2562,957],{"class":826},[138,2564,2565],{"class":830},"404",[138,2567,834],{"class":826},[138,2569,2570],{"class":805},"] ",[138,2572,2573],{"class":911},"\u002F\u002F Lista de rotas reservadas\n",[138,2575,2576],{"class":140,"line":152},[138,2577,649],{"emptyLinePlaceholder":86},[138,2579,2580,2582,2584,2587,2589,2592,2595,2597],{"class":140,"line":158},[138,2581,795],{"class":794},[138,2583,798],{"class":794},[138,2585,2586],{"class":801}," defineNuxtRouteMiddleware",[138,2588,806],{"class":805},[138,2590,2591],{"class":1073},"async",[138,2593,2594],{"class":1045}," to",[138,2596,1074],{"class":1073},[138,2598,934],{"class":809},[138,2600,2601],{"class":140,"line":164},[138,2602,649],{"emptyLinePlaceholder":86},[138,2604,2605,2608,2611,2613,2615,2617,2620,2622,2624],{"class":140,"line":170},[138,2606,2607],{"class":1073},"  const",[138,2609,2610],{"class":2521}," slugParam",[138,2612,1146],{"class":1112},[138,2614,2594],{"class":805},[138,2616,363],{"class":809},[138,2618,2619],{"class":805},"params",[138,2621,363],{"class":809},[138,2623,2018],{"class":805},[138,2625,2626],{"class":911}," \u002F\u002F Captura o parâmetro slug da URL atual\n",[138,2628,2629],{"class":140,"line":176},[138,2630,649],{"emptyLinePlaceholder":86},[138,2632,2633,2636,2638,2641,2644,2647,2650,2652,2655,2657,2659,2662,2665],{"class":140,"line":182},[138,2634,2635],{"class":794},"  if",[138,2637,1084],{"class":815},[138,2639,2640],{"class":1112},"!",[138,2642,2643],{"class":805},"slugParam",[138,2645,2646],{"class":1112}," ||",[138,2648,2649],{"class":805}," Array",[138,2651,363],{"class":809},[138,2653,2654],{"class":801},"isArray",[138,2656,806],{"class":815},[138,2658,2643],{"class":805},[138,2660,2661],{"class":815},")) ",[138,2663,2664],{"class":794},"return",[138,2666,2667],{"class":911}," \u002F\u002F Valida o slug e interrompe middleware\n",[138,2669,2670],{"class":140,"line":188},[138,2671,649],{"emptyLinePlaceholder":86},[138,2673,2674,2676,2679,2681,2683],{"class":140,"line":194},[138,2675,2607],{"class":1073},[138,2677,2678],{"class":2521}," slug",[138,2680,1146],{"class":1112},[138,2682,2610],{"class":805},[138,2684,2685],{"class":911}," \u002F\u002F Atribiu o slug como uma string simples\n",[138,2687,2688],{"class":140,"line":199},[138,2689,649],{"emptyLinePlaceholder":86},[138,2691,2692,2694,2696,2699,2701,2703,2705,2707,2709,2711],{"class":140,"line":204},[138,2693,2635],{"class":794},[138,2695,1084],{"class":815},[138,2697,2698],{"class":805},"rotasReservadas",[138,2700,363],{"class":809},[138,2702,1097],{"class":801},[138,2704,806],{"class":815},[138,2706,2018],{"class":805},[138,2708,2661],{"class":815},[138,2710,2664],{"class":794},[138,2712,2713],{"class":911}," \u002F\u002F Se o slug for uma rota reservada, não tenta buscar empresa\n",[138,2715,2716],{"class":140,"line":209},[138,2717,649],{"emptyLinePlaceholder":86},[138,2719,2720,2722,2725,2727,2730,2733],{"class":140,"line":215},[138,2721,2607],{"class":1073},[138,2723,2724],{"class":2521}," empresaStore",[138,2726,1146],{"class":1112},[138,2728,2729],{"class":801}," useEmpresaStore",[138,2731,2732],{"class":815},"() ",[138,2734,2735],{"class":911},"\u002F\u002F Acessa a store responsável por guardar a empresa ativa\n",[138,2737,2738],{"class":140,"line":221},[138,2739,649],{"emptyLinePlaceholder":86},[138,2741,2742],{"class":140,"line":227},[138,2743,2744],{"class":911},"  \u002F\u002F Se a empresa atual já estiver carregada para esse mesmo slug, evita uma nova consulta ao banco\n",[138,2746,2747,2749,2751,2754,2756,2759,2761,2763,2766,2768,2770],{"class":140,"line":233},[138,2748,2635],{"class":794},[138,2750,1084],{"class":815},[138,2752,2753],{"class":805},"empresaStore",[138,2755,363],{"class":809},[138,2757,2758],{"class":805},"empresa",[138,2760,1094],{"class":809},[138,2762,2018],{"class":805},[138,2764,2765],{"class":1112}," ===",[138,2767,2678],{"class":805},[138,2769,1109],{"class":815},[138,2771,2772],{"class":794},"return\n",[138,2774,2775],{"class":140,"line":239},[138,2776,649],{"emptyLinePlaceholder":86},[138,2778,2779],{"class":140,"line":245},[138,2780,2781],{"class":911},"  \u002F\u002F Cria o cliente do Supabase para realizar a chamada ao backend\n",[138,2783,2784,2786,2789,2791,2794],{"class":140,"line":251},[138,2785,2607],{"class":1073},[138,2787,2788],{"class":2521}," supabase",[138,2790,1146],{"class":1112},[138,2792,2793],{"class":801}," useSupabaseClient",[138,2795,2796],{"class":815},"()\n",[138,2798,2799],{"class":140,"line":257},[138,2800,649],{"emptyLinePlaceholder":86},[138,2802,2803],{"class":140,"line":263},[138,2804,2805],{"class":911},"  \u002F\u002F Chama a função RPC no Supabase para buscar a empresa correspondente ao slug da URL\n",[138,2807,2808,2810,2813,2816,2818,2821,2824,2826,2829,2831,2833,2836,2838,2840,2843,2845,2847],{"class":140,"line":269},[138,2809,2607],{"class":1073},[138,2811,2812],{"class":809}," {",[138,2814,2815],{"class":2521}," data",[138,2817,954],{"class":809},[138,2819,2820],{"class":2521}," error",[138,2822,2823],{"class":809}," }",[138,2825,1146],{"class":1112},[138,2827,2828],{"class":794}," await",[138,2830,2788],{"class":805},[138,2832,363],{"class":809},[138,2834,2835],{"class":801},"rpc",[138,2837,806],{"class":815},[138,2839,834],{"class":826},[138,2841,2842],{"class":830},"resolver_empresa_por_slug",[138,2844,834],{"class":826},[138,2846,954],{"class":809},[138,2848,934],{"class":809},[138,2850,2851,2854,2856],{"class":140,"line":275},[138,2852,2853],{"class":815},"    p_slug",[138,2855,782],{"class":809},[138,2857,2858],{"class":805}," slug\n",[138,2860,2861,2864],{"class":140,"line":281},[138,2862,2863],{"class":809},"  }",[138,2865,872],{"class":815},[138,2867,2868],{"class":140,"line":287},[138,2869,649],{"emptyLinePlaceholder":86},[138,2871,2872],{"class":140,"line":293},[138,2873,2874],{"class":911},"  \u002F\u002F Se ocorrer erro ou nenhuma empresa for encontrada, redireciona para a página 404\n",[138,2876,2877,2879,2881,2884,2886,2889,2892,2894],{"class":140,"line":299},[138,2878,2635],{"class":794},[138,2880,1084],{"class":815},[138,2882,2883],{"class":805},"error",[138,2885,2646],{"class":1112},[138,2887,2888],{"class":1112}," !",[138,2890,2891],{"class":805},"data",[138,2893,1109],{"class":815},[138,2895,810],{"class":809},[138,2897,2898,2901,2904,2906,2908,2911,2913],{"class":140,"line":305},[138,2899,2900],{"class":794},"    return",[138,2902,2903],{"class":801}," navigateTo",[138,2905,806],{"class":815},[138,2907,834],{"class":826},[138,2909,2910],{"class":830},"\u002F404",[138,2912,834],{"class":826},[138,2914,872],{"class":815},[138,2916,2917],{"class":140,"line":311},[138,2918,1184],{"class":809},[138,2920,2921],{"class":140,"line":317},[138,2922,649],{"emptyLinePlaceholder":86},[138,2924,2925],{"class":140,"line":323},[138,2926,2927],{"class":911},"  \u002F\u002F Salva os dados da empresa encontrada na store\n",[138,2929,2930,2933,2935,2938,2940,2942,2945,2948],{"class":140,"line":329},[138,2931,2932],{"class":805},"    empresaStore",[138,2934,363],{"class":809},[138,2936,2937],{"class":801},"definir",[138,2939,806],{"class":815},[138,2941,2891],{"class":805},[138,2943,2944],{"class":794}," as",[138,2946,2947],{"class":1556}," EmpresaPublica",[138,2949,872],{"class":815},[138,2951,2952,2954],{"class":140,"line":335},[138,2953,869],{"class":809},[138,2955,872],{"class":805},[12,2957,2958,2959,363],{},"Esse middleware tem uma responsabilidade bem específica: ",[19,2960,2961],{},"resolver o tenant da rota atual",[402,2963,2964,2967,2970,2973],{},[405,2965,2966],{},"Ele não aplica tema;",[405,2968,2969],{},"Ele não renderiza página;",[405,2971,2972],{},"Ele não monta catálogo;",[405,2974,2975,2976,2978],{},"Ele apenas pega o slug, consulta a função ",[135,2977,2842],{}," e salva o resultado na store;",[12,2980,2981,2982,2985],{},"Essa separação deixa a rota ",[135,2983,2984],{},"[slug]\u002Findex.vue"," mais simples, porque ela não precisa conhecer os detalhes de como a empresa foi encontrada.",[27,2987],{},[30,2989,2991],{"id":2990},"salvando-a-empresa-ativa-na-store","Salvando a empresa ativa na store",[12,2993,2994],{},"Depois que o middleware encontra a empresa, os dados precisam ficar disponíveis para o restante da aplicação.",[12,2996,2997],{},"Para isso, foi criada uma store com Pinia.",[129,2999,3001],{"className":785,"code":3000,"language":787,"meta":75,"style":75},"\u002F\u002F stores\u002FuseEmpresaStore.ts\nexport const useEmpresaStore = defineStore('empresa', () => {\n  const empresa = ref\u003CEmpresaPublica | null>(null)\n\n  const nomeEmpresa = computed(() => empresa.value?.nome ?? '')\n\n  function definir(dados: EmpresaPublica) {\n    empresa.value = dados\n  }\n\n  function limpar() {\n    empresa.value = null\n  }\n\n  return {\n    empresa,\n    nomeEmpresa,\n    definir,\n    limpar\n  }\n})\n",[135,3002,3003,3008,3039,3073,3077,3116,3120,3141,3155,3159,3163,3174,3187,3191,3195,3202,3208,3215,3222,3227,3231],{"__ignoreMap":75},[138,3004,3005],{"class":140,"line":141},[138,3006,3007],{"class":911},"\u002F\u002F stores\u002FuseEmpresaStore.ts\n",[138,3009,3010,3012,3015,3017,3019,3022,3024,3026,3028,3030,3032,3035,3037],{"class":140,"line":76},[138,3011,795],{"class":794},[138,3013,3014],{"class":1073}," const",[138,3016,2729],{"class":2521},[138,3018,1146],{"class":1112},[138,3020,3021],{"class":801}," defineStore",[138,3023,806],{"class":805},[138,3025,834],{"class":826},[138,3027,2758],{"class":830},[138,3029,834],{"class":826},[138,3031,954],{"class":809},[138,3033,3034],{"class":809}," ()",[138,3036,1074],{"class":1073},[138,3038,934],{"class":809},[138,3040,3041,3043,3046,3048,3051,3053,3056,3059,3063,3066,3068,3071],{"class":140,"line":152},[138,3042,2607],{"class":1073},[138,3044,3045],{"class":2521}," empresa",[138,3047,1146],{"class":1112},[138,3049,3050],{"class":801}," ref",[138,3052,1416],{"class":809},[138,3054,3055],{"class":1556},"EmpresaPublica",[138,3057,3058],{"class":1112}," |",[138,3060,3062],{"class":3061},"s3afY"," null",[138,3064,3065],{"class":809},">",[138,3067,806],{"class":815},[138,3069,3070],{"class":2041},"null",[138,3072,872],{"class":815},[138,3074,3075],{"class":140,"line":158},[138,3076,649],{"emptyLinePlaceholder":86},[138,3078,3079,3081,3084,3086,3089,3091,3094,3096,3098,3100,3103,3105,3108,3111,3114],{"class":140,"line":164},[138,3080,2607],{"class":1073},[138,3082,3083],{"class":2521}," nomeEmpresa",[138,3085,1146],{"class":1112},[138,3087,3088],{"class":801}," computed",[138,3090,806],{"class":815},[138,3092,3093],{"class":809},"()",[138,3095,1074],{"class":1073},[138,3097,3045],{"class":805},[138,3099,363],{"class":809},[138,3101,3102],{"class":805},"value",[138,3104,1094],{"class":809},[138,3106,3107],{"class":805},"nome",[138,3109,3110],{"class":1112}," ??",[138,3112,3113],{"class":826}," ''",[138,3115,872],{"class":815},[138,3117,3118],{"class":140,"line":170},[138,3119,649],{"emptyLinePlaceholder":86},[138,3121,3122,3125,3128,3130,3133,3135,3137,3139],{"class":140,"line":176},[138,3123,3124],{"class":1073},"  function",[138,3126,3127],{"class":801}," definir",[138,3129,806],{"class":809},[138,3131,3132],{"class":1045},"dados",[138,3134,782],{"class":1112},[138,3136,2947],{"class":1556},[138,3138,1049],{"class":809},[138,3140,934],{"class":809},[138,3142,3143,3146,3148,3150,3152],{"class":140,"line":182},[138,3144,3145],{"class":805},"    empresa",[138,3147,363],{"class":809},[138,3149,3102],{"class":805},[138,3151,1146],{"class":1112},[138,3153,3154],{"class":805}," dados\n",[138,3156,3157],{"class":140,"line":188},[138,3158,1184],{"class":809},[138,3160,3161],{"class":140,"line":194},[138,3162,649],{"emptyLinePlaceholder":86},[138,3164,3165,3167,3170,3172],{"class":140,"line":199},[138,3166,3124],{"class":1073},[138,3168,3169],{"class":801}," limpar",[138,3171,3093],{"class":809},[138,3173,934],{"class":809},[138,3175,3176,3178,3180,3182,3184],{"class":140,"line":204},[138,3177,3145],{"class":805},[138,3179,363],{"class":809},[138,3181,3102],{"class":805},[138,3183,1146],{"class":1112},[138,3185,3186],{"class":2041}," null\n",[138,3188,3189],{"class":140,"line":209},[138,3190,1184],{"class":809},[138,3192,3193],{"class":140,"line":215},[138,3194,649],{"emptyLinePlaceholder":86},[138,3196,3197,3200],{"class":140,"line":221},[138,3198,3199],{"class":794},"  return",[138,3201,934],{"class":809},[138,3203,3204,3206],{"class":140,"line":227},[138,3205,3145],{"class":805},[138,3207,837],{"class":809},[138,3209,3210,3213],{"class":140,"line":233},[138,3211,3212],{"class":805},"    nomeEmpresa",[138,3214,837],{"class":809},[138,3216,3217,3220],{"class":140,"line":239},[138,3218,3219],{"class":805},"    definir",[138,3221,837],{"class":809},[138,3223,3224],{"class":140,"line":245},[138,3225,3226],{"class":805},"    limpar\n",[138,3228,3229],{"class":140,"line":251},[138,3230,1184],{"class":809},[138,3232,3233,3235],{"class":140,"line":257},[138,3234,869],{"class":809},[138,3236,872],{"class":805},[402,3238,3239,3242,3245],{},[405,3240,3241],{},"A store não busca dados;",[405,3243,3244],{},"Não chama Supabase;",[405,3246,3247],{},"Não conhece a rota;",[12,3249,3250,3251,3254],{},"Ela apenas ",[19,3252,3253],{},"guarda a empresa ativa",". Essa é a vantagem de separar as camadas: o middleware resolve, a store armazena e as páginas consomem.",[27,3256],{},[30,3258,3260,3261],{"id":3259},"consumindo-a-store-na-rota-slugindexvue","Consumindo a store na rota ",[135,3262,2984],{},[12,3264,3265],{},"Com a empresa já salva na store, a página da vitrine consome esse contexto sem repetir a lógica de resolução do slug.",[129,3267,3269],{"className":785,"code":3268,"language":787,"meta":75,"style":75},"\u002F\u002F pages\u002F[slug]\u002Findex.vue\ndefinePageMeta({ layout: 'catalog' })\n\nconst empresaStore = useEmpresaStore() \u002F\u002F Acessa a store da empresa\nconst empresa = computed(() => empresaStore.empresa) \u002F\u002F Cria uma referência reativa para a empresa ativa\n\n\u002F\u002F Define os metadados SEO com base na empresa ativa\nuseSeoMeta({\n  title: computed(() => empresa.value?.nome || ''),\n  description: computed(() => empresa.value?.descricao || ''),\n  ogTitle: computed(() => empresa.value?.nome || ''),\n  ogDescription: computed(() => empresa.value?.descricao || ''),\n  ogImage: computed(() => empresa.value?.logo_url || '')\n})\n\n",[135,3270,3271,3276,3300,3304,3319,3345,3349,3354,3363,3398,3432,3465,3498,3530],{"__ignoreMap":75},[138,3272,3273],{"class":140,"line":141},[138,3274,3275],{"class":911},"\u002F\u002F pages\u002F[slug]\u002Findex.vue\n",[138,3277,3278,3280,3282,3285,3288,3290,3292,3294,3296,3298],{"class":140,"line":76},[138,3279,1403],{"class":801},[138,3281,806],{"class":805},[138,3283,3284],{"class":809},"{",[138,3286,3287],{"class":815}," layout",[138,3289,782],{"class":809},[138,3291,957],{"class":826},[138,3293,711],{"class":830},[138,3295,834],{"class":826},[138,3297,2823],{"class":809},[138,3299,872],{"class":805},[138,3301,3302],{"class":140,"line":152},[138,3303,649],{"emptyLinePlaceholder":86},[138,3305,3306,3308,3310,3312,3314,3316],{"class":140,"line":158},[138,3307,2518],{"class":1073},[138,3309,2724],{"class":2521},[138,3311,1146],{"class":1112},[138,3313,2729],{"class":801},[138,3315,2732],{"class":805},[138,3317,3318],{"class":911},"\u002F\u002F Acessa a store da empresa\n",[138,3320,3321,3323,3325,3327,3329,3331,3333,3335,3337,3339,3342],{"class":140,"line":164},[138,3322,2518],{"class":1073},[138,3324,3045],{"class":2521},[138,3326,1146],{"class":1112},[138,3328,3088],{"class":801},[138,3330,806],{"class":805},[138,3332,3093],{"class":809},[138,3334,1074],{"class":1073},[138,3336,2724],{"class":805},[138,3338,363],{"class":809},[138,3340,3341],{"class":805},"empresa) ",[138,3343,3344],{"class":911},"\u002F\u002F Cria uma referência reativa para a empresa ativa\n",[138,3346,3347],{"class":140,"line":170},[138,3348,649],{"emptyLinePlaceholder":86},[138,3350,3351],{"class":140,"line":176},[138,3352,3353],{"class":911},"\u002F\u002F Define os metadados SEO com base na empresa ativa\n",[138,3355,3356,3359,3361],{"class":140,"line":182},[138,3357,3358],{"class":801},"useSeoMeta",[138,3360,806],{"class":805},[138,3362,810],{"class":809},[138,3364,3365,3368,3370,3372,3374,3376,3378,3380,3382,3384,3386,3389,3392,3394,3396],{"class":140,"line":188},[138,3366,3367],{"class":815},"  title",[138,3369,782],{"class":809},[138,3371,3088],{"class":801},[138,3373,806],{"class":805},[138,3375,3093],{"class":809},[138,3377,1074],{"class":1073},[138,3379,3045],{"class":805},[138,3381,363],{"class":809},[138,3383,3102],{"class":805},[138,3385,1094],{"class":809},[138,3387,3388],{"class":805},"nome ",[138,3390,3391],{"class":1112},"||",[138,3393,3113],{"class":826},[138,3395,1049],{"class":805},[138,3397,837],{"class":809},[138,3399,3400,3403,3405,3407,3409,3411,3413,3415,3417,3419,3421,3424,3426,3428,3430],{"class":140,"line":194},[138,3401,3402],{"class":815},"  description",[138,3404,782],{"class":809},[138,3406,3088],{"class":801},[138,3408,806],{"class":805},[138,3410,3093],{"class":809},[138,3412,1074],{"class":1073},[138,3414,3045],{"class":805},[138,3416,363],{"class":809},[138,3418,3102],{"class":805},[138,3420,1094],{"class":809},[138,3422,3423],{"class":805},"descricao ",[138,3425,3391],{"class":1112},[138,3427,3113],{"class":826},[138,3429,1049],{"class":805},[138,3431,837],{"class":809},[138,3433,3434,3437,3439,3441,3443,3445,3447,3449,3451,3453,3455,3457,3459,3461,3463],{"class":140,"line":199},[138,3435,3436],{"class":815},"  ogTitle",[138,3438,782],{"class":809},[138,3440,3088],{"class":801},[138,3442,806],{"class":805},[138,3444,3093],{"class":809},[138,3446,1074],{"class":1073},[138,3448,3045],{"class":805},[138,3450,363],{"class":809},[138,3452,3102],{"class":805},[138,3454,1094],{"class":809},[138,3456,3388],{"class":805},[138,3458,3391],{"class":1112},[138,3460,3113],{"class":826},[138,3462,1049],{"class":805},[138,3464,837],{"class":809},[138,3466,3467,3470,3472,3474,3476,3478,3480,3482,3484,3486,3488,3490,3492,3494,3496],{"class":140,"line":204},[138,3468,3469],{"class":815},"  ogDescription",[138,3471,782],{"class":809},[138,3473,3088],{"class":801},[138,3475,806],{"class":805},[138,3477,3093],{"class":809},[138,3479,1074],{"class":1073},[138,3481,3045],{"class":805},[138,3483,363],{"class":809},[138,3485,3102],{"class":805},[138,3487,1094],{"class":809},[138,3489,3423],{"class":805},[138,3491,3391],{"class":1112},[138,3493,3113],{"class":826},[138,3495,1049],{"class":805},[138,3497,837],{"class":809},[138,3499,3500,3503,3505,3507,3509,3511,3513,3515,3517,3519,3521,3524,3526,3528],{"class":140,"line":209},[138,3501,3502],{"class":815},"  ogImage",[138,3504,782],{"class":809},[138,3506,3088],{"class":801},[138,3508,806],{"class":805},[138,3510,3093],{"class":809},[138,3512,1074],{"class":1073},[138,3514,3045],{"class":805},[138,3516,363],{"class":809},[138,3518,3102],{"class":805},[138,3520,1094],{"class":809},[138,3522,3523],{"class":805},"logo_url ",[138,3525,3391],{"class":1112},[138,3527,3113],{"class":826},[138,3529,872],{"class":805},[138,3531,3532,3534],{"class":140,"line":215},[138,3533,869],{"class":809},[138,3535,872],{"class":805},[12,3537,3538,3539,377,3542,3545],{},"O papel dessa página é ",[19,3540,3541],{},"consumir o tenant ativo",[19,3543,3544],{},"renderizar a experiência pública"," daquela empresa, sem buscar tudo novamente. Essa responsabilidade pertence ao middleware.",[12,3547,3548],{},"Na page, o uso fica direto:",[129,3550,3552],{"className":1407,"code":3551,"language":1409,"meta":75,"style":75},"\u003Ctemplate>\n  \u003Csection>\n    \u003Cp class=\"text-sm font-medium text-(--company-primary)\">Vitrine\u003C\u002Fp>\n    \u003Ch1>{{ empresa?.nome }}\u003C\u002Fh1>\n  \u003C\u002Fsection>\n\u003C\u002Ftemplate>\n",[135,3553,3554,3563,3573,3603,3621,3630],{"__ignoreMap":75},[138,3555,3556,3558,3561],{"class":140,"line":141},[138,3557,1416],{"class":809},[138,3559,3560],{"class":1419},"template",[138,3562,1440],{"class":809},[138,3564,3565,3568,3571],{"class":140,"line":76},[138,3566,3567],{"class":809},"  \u003C",[138,3569,3570],{"class":1419},"section",[138,3572,1440],{"class":809},[138,3574,3575,3578,3580,3583,3585,3587,3590,3592,3594,3597,3599,3601],{"class":140,"line":152},[138,3576,3577],{"class":809},"    \u003C",[138,3579,12],{"class":1419},[138,3581,3582],{"class":1423}," class",[138,3584,1430],{"class":809},[138,3586,1433],{"class":826},[138,3588,3589],{"class":830},"text-sm font-medium text-(--company-primary)",[138,3591,1433],{"class":826},[138,3593,3065],{"class":809},[138,3595,3596],{"class":805},"Vitrine",[138,3598,1505],{"class":809},[138,3600,12],{"class":1419},[138,3602,1440],{"class":809},[138,3604,3605,3607,3610,3612,3615,3617,3619],{"class":140,"line":158},[138,3606,3577],{"class":809},[138,3608,3609],{"class":1419},"h1",[138,3611,3065],{"class":809},[138,3613,3614],{"class":805},"{{ empresa?.nome }}",[138,3616,1505],{"class":809},[138,3618,3609],{"class":1419},[138,3620,1440],{"class":809},[138,3622,3623,3626,3628],{"class":140,"line":164},[138,3624,3625],{"class":809},"  \u003C\u002F",[138,3627,3570],{"class":1419},[138,3629,1440],{"class":809},[138,3631,3632,3634,3636],{"class":140,"line":170},[138,3633,1505],{"class":809},[138,3635,3560],{"class":1419},[138,3637,1440],{"class":809},[12,3639,3640],{},"A página consome a empresa ativa vinda da store. Ela não precisa saber todo o caminho feito até essa empresa chegar ali.",[27,3642],{},[30,3644,3646],{"id":3645},"identidade-visual-por-empresa","Identidade visual por empresa",[12,3648,3649,3650,3652,3653,363],{},"Além de resolver qual empresa está ativa, a arquitetura também precisava refletir a identidade visual de cada tenant.\nA decisão foi salvar as cores no campo ",[135,3651,2024],{}," da tabela ",[135,3654,1940],{},[12,3656,2329],{},[129,3658,3659],{"className":2028,"code":2029,"language":2030,"meta":75,"style":75},[135,3660,3661,3665,3683,3699],{"__ignoreMap":75},[138,3662,3663],{"class":140,"line":141},[138,3664,810],{"class":809},[138,3666,3667,3669,3671,3673,3675,3677,3679,3681],{"class":140,"line":76},[138,3668,2042],{"class":2041},[138,3670,2046],{"class":2045},[138,3672,1433],{"class":2041},[138,3674,782],{"class":809},[138,3676,1567],{"class":826},[138,3678,2055],{"class":830},[138,3680,1433],{"class":826},[138,3682,837],{"class":809},[138,3684,3685,3687,3689,3691,3693,3695,3697],{"class":140,"line":152},[138,3686,2042],{"class":2041},[138,3688,2066],{"class":2045},[138,3690,1433],{"class":2041},[138,3692,782],{"class":809},[138,3694,1567],{"class":826},[138,3696,2075],{"class":830},[138,3698,1573],{"class":826},[138,3700,3701],{"class":140,"line":158},[138,3702,2082],{"class":809},[12,3704,3705],{},"Essas cores são carregadas junto com a empresa no middleware, salvas na store e depois aplicadas no layout da vitrine.",[27,3707],{},[30,3709,3711],{"id":3710},"composable-de-tema-da-empresa","Composable de tema da empresa",[12,3713,3714],{},"Para evitar lógica repetida, foi criado um composable específico para transformar as cores da empresa em CSS variables.",[12,3716,1193,3717,3720],{},[135,3718,3719],{},"useEmpresaTheme()"," centraliza a leitura e validação das cores.",[129,3722,3724],{"className":785,"code":3723,"language":787,"meta":75,"style":75},"\u002F\u002F composables\u002FuseEmpresaTheme.ts\nimport type { CSSProperties } from 'vue'\n\n\u002F\u002F Define um tipo para aceitar propriedades CSS comuns\n\u002F\u002F e também variáveis CSS customizadas, como --company-primary\ntype CSSVars = CSSProperties & Record\u003C`--${string}`, string>\n\n\u002F\u002F Valida se a cor recebida está no formato hexadecimal\nfunction isHexColor(color?: string | null) {\n  if (!color) return false\n  return \u002F^#([0-9A-F]{3}){1,2}$\u002Fi.test(color)\n}\n\nexport function useEmpresaTheme() {\n\n  const empresaStore = useEmpresaStore() \u002F\u002F Acessa a store da empresa ativa\n\n  \u002F\u002F Define a cor primária da empresa\n  const primary = computed(() => {\n    const color = empresaStore.empresa?.colors?.primary\n    return isHexColor(color) ? color : '#2563ab'\n  })\n\n  \u002F\u002F Define a cor secundária da empresa\n  const secondary = computed(() => {\n    const color = empresaStore.empresa?.colors?.secondary\n    return isHexColor(color) ? color : '#f97316'\n  })\n\n  \u002F\u002F Monta as variáveis CSS que serão aplicadas no layout\n  const themeStyle = computed\u003CCSSVars>(() => ({\n    '--company-primary': primary.value,\n    '--company-secondary': secondary.value\n  }))\n\n  \u002F\u002F Expõe as cores e o objeto de estilo para uso no layout\n  return {\n    primary,\n    secondary,\n    themeStyle\n  }\n}\n\n",[135,3725,3726,3731,3755,3759,3764,3769,3811,3815,3820,3846,3864,3917,3921,3925,3939,3943,3958,3962,3967,3986,4011,4037,4043,4047,4052,4071,4094,4118,4124,4128,4133,4161,4181,4199,4206,4210,4215,4221,4228,4235,4240,4244],{"__ignoreMap":75},[138,3727,3728],{"class":140,"line":141},[138,3729,3730],{"class":911},"\u002F\u002F composables\u002FuseEmpresaTheme.ts\n",[138,3732,3733,3736,3739,3741,3744,3746,3749,3751,3753],{"class":140,"line":76},[138,3734,3735],{"class":794},"import",[138,3737,3738],{"class":794}," type",[138,3740,2812],{"class":809},[138,3742,3743],{"class":805}," CSSProperties",[138,3745,2823],{"class":809},[138,3747,3748],{"class":794}," from",[138,3750,957],{"class":826},[138,3752,1409],{"class":830},[138,3754,2361],{"class":826},[138,3756,3757],{"class":140,"line":152},[138,3758,649],{"emptyLinePlaceholder":86},[138,3760,3761],{"class":140,"line":158},[138,3762,3763],{"class":911},"\u002F\u002F Define um tipo para aceitar propriedades CSS comuns\n",[138,3765,3766],{"class":140,"line":164},[138,3767,3768],{"class":911},"\u002F\u002F e também variáveis CSS customizadas, como --company-primary\n",[138,3770,3771,3774,3777,3779,3781,3784,3787,3789,3792,3795,3798,3801,3804,3806,3809],{"class":140,"line":170},[138,3772,3773],{"class":1073},"type",[138,3775,3776],{"class":1556}," CSSVars",[138,3778,1146],{"class":1112},[138,3780,3743],{"class":1556},[138,3782,3783],{"class":1112}," &",[138,3785,3786],{"class":1556}," Record",[138,3788,1416],{"class":809},[138,3790,3791],{"class":826},"`",[138,3793,3794],{"class":830},"--",[138,3796,3797],{"class":826},"${",[138,3799,3800],{"class":3061},"string",[138,3802,3803],{"class":826},"}`",[138,3805,954],{"class":809},[138,3807,3808],{"class":3061}," string",[138,3810,1440],{"class":809},[138,3812,3813],{"class":140,"line":176},[138,3814,649],{"emptyLinePlaceholder":86},[138,3816,3817],{"class":140,"line":182},[138,3818,3819],{"class":911},"\u002F\u002F Valida se a cor recebida está no formato hexadecimal\n",[138,3821,3822,3825,3828,3830,3833,3836,3838,3840,3842,3844],{"class":140,"line":188},[138,3823,3824],{"class":1073},"function",[138,3826,3827],{"class":801}," isHexColor",[138,3829,806],{"class":809},[138,3831,3832],{"class":1045},"color",[138,3834,3835],{"class":1112},"?:",[138,3837,3808],{"class":3061},[138,3839,3058],{"class":1112},[138,3841,3062],{"class":3061},[138,3843,1049],{"class":809},[138,3845,934],{"class":809},[138,3847,3848,3850,3852,3854,3856,3858,3860],{"class":140,"line":194},[138,3849,2635],{"class":794},[138,3851,1084],{"class":815},[138,3853,2640],{"class":1112},[138,3855,3832],{"class":805},[138,3857,1109],{"class":815},[138,3859,2664],{"class":794},[138,3861,3863],{"class":3862},"sMrrN"," false\n",[138,3865,3866,3868,3871,3874,3878,3881,3884,3887,3889,3892,3894,3897,3900,3902,3906,3908,3911,3913,3915],{"class":140,"line":199},[138,3867,3199],{"class":794},[138,3869,3870],{"class":826}," \u002F",[138,3872,3873],{"class":794},"^",[138,3875,3877],{"class":3876},"sm4dI","#",[138,3879,806],{"class":3880},"svU9d",[138,3882,3883],{"class":2041},"[",[138,3885,3886],{"class":1563},"0-9A-F",[138,3888,965],{"class":2041},[138,3890,3891],{"class":1112},"{3}",[138,3893,1049],{"class":3880},[138,3895,3896],{"class":1112},"{1,2}",[138,3898,3899],{"class":794},"$",[138,3901,1393],{"class":826},[138,3903,3905],{"class":3904},"s1Wpa","i",[138,3907,363],{"class":809},[138,3909,3910],{"class":801},"test",[138,3912,806],{"class":815},[138,3914,3832],{"class":805},[138,3916,872],{"class":815},[138,3918,3919],{"class":140,"line":204},[138,3920,2082],{"class":809},[138,3922,3923],{"class":140,"line":209},[138,3924,649],{"emptyLinePlaceholder":86},[138,3926,3927,3929,3932,3935,3937],{"class":140,"line":215},[138,3928,795],{"class":794},[138,3930,3931],{"class":1073}," function",[138,3933,3934],{"class":801}," useEmpresaTheme",[138,3936,3093],{"class":809},[138,3938,934],{"class":809},[138,3940,3941],{"class":140,"line":221},[138,3942,649],{"emptyLinePlaceholder":86},[138,3944,3945,3947,3949,3951,3953,3955],{"class":140,"line":227},[138,3946,2607],{"class":1073},[138,3948,2724],{"class":2521},[138,3950,1146],{"class":1112},[138,3952,2729],{"class":801},[138,3954,2732],{"class":815},[138,3956,3957],{"class":911},"\u002F\u002F Acessa a store da empresa ativa\n",[138,3959,3960],{"class":140,"line":233},[138,3961,649],{"emptyLinePlaceholder":86},[138,3963,3964],{"class":140,"line":239},[138,3965,3966],{"class":911},"  \u002F\u002F Define a cor primária da empresa\n",[138,3968,3969,3971,3974,3976,3978,3980,3982,3984],{"class":140,"line":245},[138,3970,2607],{"class":1073},[138,3972,3973],{"class":2521}," primary",[138,3975,1146],{"class":1112},[138,3977,3088],{"class":801},[138,3979,806],{"class":815},[138,3981,3093],{"class":809},[138,3983,1074],{"class":1073},[138,3985,934],{"class":809},[138,3987,3988,3991,3994,3996,3998,4000,4002,4004,4006,4008],{"class":140,"line":251},[138,3989,3990],{"class":1073},"    const",[138,3992,3993],{"class":2521}," color",[138,3995,1146],{"class":1112},[138,3997,2724],{"class":805},[138,3999,363],{"class":809},[138,4001,2758],{"class":805},[138,4003,1094],{"class":809},[138,4005,2024],{"class":805},[138,4007,1094],{"class":809},[138,4009,4010],{"class":805},"primary\n",[138,4012,4013,4015,4017,4019,4021,4023,4026,4028,4031,4033,4035],{"class":140,"line":257},[138,4014,2900],{"class":794},[138,4016,3827],{"class":801},[138,4018,806],{"class":815},[138,4020,3832],{"class":805},[138,4022,1109],{"class":815},[138,4024,4025],{"class":1112},"?",[138,4027,3993],{"class":805},[138,4029,4030],{"class":1112}," :",[138,4032,957],{"class":826},[138,4034,2055],{"class":830},[138,4036,2361],{"class":826},[138,4038,4039,4041],{"class":140,"line":263},[138,4040,2863],{"class":809},[138,4042,872],{"class":815},[138,4044,4045],{"class":140,"line":269},[138,4046,649],{"emptyLinePlaceholder":86},[138,4048,4049],{"class":140,"line":275},[138,4050,4051],{"class":911},"  \u002F\u002F Define a cor secundária da empresa\n",[138,4053,4054,4056,4059,4061,4063,4065,4067,4069],{"class":140,"line":281},[138,4055,2607],{"class":1073},[138,4057,4058],{"class":2521}," secondary",[138,4060,1146],{"class":1112},[138,4062,3088],{"class":801},[138,4064,806],{"class":815},[138,4066,3093],{"class":809},[138,4068,1074],{"class":1073},[138,4070,934],{"class":809},[138,4072,4073,4075,4077,4079,4081,4083,4085,4087,4089,4091],{"class":140,"line":287},[138,4074,3990],{"class":1073},[138,4076,3993],{"class":2521},[138,4078,1146],{"class":1112},[138,4080,2724],{"class":805},[138,4082,363],{"class":809},[138,4084,2758],{"class":805},[138,4086,1094],{"class":809},[138,4088,2024],{"class":805},[138,4090,1094],{"class":809},[138,4092,4093],{"class":805},"secondary\n",[138,4095,4096,4098,4100,4102,4104,4106,4108,4110,4112,4114,4116],{"class":140,"line":293},[138,4097,2900],{"class":794},[138,4099,3827],{"class":801},[138,4101,806],{"class":815},[138,4103,3832],{"class":805},[138,4105,1109],{"class":815},[138,4107,4025],{"class":1112},[138,4109,3993],{"class":805},[138,4111,4030],{"class":1112},[138,4113,957],{"class":826},[138,4115,2075],{"class":830},[138,4117,2361],{"class":826},[138,4119,4120,4122],{"class":140,"line":299},[138,4121,2863],{"class":809},[138,4123,872],{"class":815},[138,4125,4126],{"class":140,"line":305},[138,4127,649],{"emptyLinePlaceholder":86},[138,4129,4130],{"class":140,"line":311},[138,4131,4132],{"class":911},"  \u002F\u002F Monta as variáveis CSS que serão aplicadas no layout\n",[138,4134,4135,4137,4140,4142,4144,4146,4149,4151,4153,4155,4157,4159],{"class":140,"line":317},[138,4136,2607],{"class":1073},[138,4138,4139],{"class":2521}," themeStyle",[138,4141,1146],{"class":1112},[138,4143,3088],{"class":801},[138,4145,1416],{"class":809},[138,4147,4148],{"class":1556},"CSSVars",[138,4150,3065],{"class":809},[138,4152,806],{"class":815},[138,4154,3093],{"class":809},[138,4156,1074],{"class":1073},[138,4158,1084],{"class":815},[138,4160,810],{"class":809},[138,4162,4163,4165,4169,4171,4173,4175,4177,4179],{"class":140,"line":323},[138,4164,827],{"class":826},[138,4166,4168],{"class":4167},"swHuM","--company-primary",[138,4170,834],{"class":826},[138,4172,782],{"class":809},[138,4174,3973],{"class":805},[138,4176,363],{"class":809},[138,4178,3102],{"class":805},[138,4180,837],{"class":809},[138,4182,4183,4185,4188,4190,4192,4194,4196],{"class":140,"line":329},[138,4184,827],{"class":826},[138,4186,4187],{"class":4167},"--company-secondary",[138,4189,834],{"class":826},[138,4191,782],{"class":809},[138,4193,4058],{"class":805},[138,4195,363],{"class":809},[138,4197,4198],{"class":805},"value\n",[138,4200,4201,4203],{"class":140,"line":335},[138,4202,2863],{"class":809},[138,4204,4205],{"class":815},"))\n",[138,4207,4208],{"class":140,"line":341},[138,4209,649],{"emptyLinePlaceholder":86},[138,4211,4212],{"class":140,"line":347},[138,4213,4214],{"class":911},"  \u002F\u002F Expõe as cores e o objeto de estilo para uso no layout\n",[138,4216,4217,4219],{"class":140,"line":353},[138,4218,3199],{"class":794},[138,4220,934],{"class":809},[138,4222,4223,4226],{"class":140,"line":622},[138,4224,4225],{"class":805},"    primary",[138,4227,837],{"class":809},[138,4229,4230,4233],{"class":140,"line":628},[138,4231,4232],{"class":805},"    secondary",[138,4234,837],{"class":809},[138,4236,4237],{"class":140,"line":634},[138,4238,4239],{"class":805},"    themeStyle\n",[138,4241,4242],{"class":140,"line":640},[138,4243,1184],{"class":809},[138,4245,4246],{"class":140,"line":646},[138,4247,2082],{"class":809},[12,4249,4250],{},"Esse composable evita que a validação de cor fique espalhada em várias páginas. Se a empresa tiver uma cor válida, o sistema usa a cor cadastrada, se não tiver, usa uma cor padrão.\nAssim, a interface nunca fica sem tema.",[27,4252],{},[30,4254,4256,4257],{"id":4255},"aplicando-as-cores-no-layoutcatalogvue","Aplicando as cores no ",[135,4258,4259],{},"layout\u002Fcatalog.vue",[12,4261,4262],{},"O layout da vitrine é o melhor lugar para aplicar o tema, porque ele envolve tudo que pertence à experiência pública da empresa.",[129,4264,4266],{"className":1407,"code":4265,"language":1409,"meta":75,"style":75},"\u003C!-- layouts\u002Fcatalog.vue -->\n\u003Ctemplate>\n  \u003Cdiv\n    class=\"min-h-screen bg-neutral-50 text-neutral-900 dark:bg-neutral-950 dark:text-white\"\n  >\n    \u003Cmain :style=\"themeStyle\"> \u003C!-- Estilo será herdado em todas as paginas e componentes -->\n      \u003CSharedHeader \u002F>\n      \u003Cslot \u002F>\n    \u003C\u002Fmain>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup lang=\"ts\">\nconst { themeStyle } = useEmpresaTheme() \u002F\u002F Carrega o tema aqui\n\u003C\u002Fscript>\n",[135,4267,4268,4273,4281,4288,4302,4307,4331,4342,4354,4363,4372,4380,4384,4404,4423],{"__ignoreMap":75},[138,4269,4270],{"class":140,"line":141},[138,4271,4272],{"class":911},"\u003C!-- layouts\u002Fcatalog.vue -->\n",[138,4274,4275,4277,4279],{"class":140,"line":76},[138,4276,1416],{"class":809},[138,4278,3560],{"class":1419},[138,4280,1440],{"class":809},[138,4282,4283,4285],{"class":140,"line":152},[138,4284,3567],{"class":809},[138,4286,4287],{"class":1419},"div\n",[138,4289,4290,4293,4295,4297,4300],{"class":140,"line":158},[138,4291,4292],{"class":1423},"    class",[138,4294,1430],{"class":809},[138,4296,1433],{"class":826},[138,4298,4299],{"class":830},"min-h-screen bg-neutral-50 text-neutral-900 dark:bg-neutral-950 dark:text-white",[138,4301,1573],{"class":826},[138,4303,4304],{"class":140,"line":164},[138,4305,4306],{"class":809},"  >\n",[138,4308,4309,4311,4314,4317,4319,4321,4324,4326,4328],{"class":140,"line":170},[138,4310,3577],{"class":809},[138,4312,4313],{"class":1419},"main",[138,4315,4316],{"class":1423}," :style",[138,4318,1430],{"class":809},[138,4320,1433],{"class":826},[138,4322,4323],{"class":830},"themeStyle",[138,4325,1433],{"class":826},[138,4327,3065],{"class":809},[138,4329,4330],{"class":911}," \u003C!-- Estilo será herdado em todas as paginas e componentes -->\n",[138,4332,4333,4336,4339],{"class":140,"line":176},[138,4334,4335],{"class":809},"      \u003C",[138,4337,4338],{"class":1419},"SharedHeader",[138,4340,4341],{"class":809}," \u002F>\n",[138,4343,4344,4346,4349,4352],{"class":140,"line":182},[138,4345,4335],{"class":809},[138,4347,4348],{"class":1419},"slot",[138,4350,3870],{"class":4351},"sM3K6",[138,4353,1440],{"class":809},[138,4355,4356,4359,4361],{"class":140,"line":188},[138,4357,4358],{"class":809},"    \u003C\u002F",[138,4360,4313],{"class":1419},[138,4362,1440],{"class":809},[138,4364,4365,4367,4370],{"class":140,"line":194},[138,4366,3625],{"class":809},[138,4368,4369],{"class":1419},"div",[138,4371,1440],{"class":809},[138,4373,4374,4376,4378],{"class":140,"line":199},[138,4375,1505],{"class":809},[138,4377,3560],{"class":1419},[138,4379,1440],{"class":809},[138,4381,4382],{"class":140,"line":204},[138,4383,649],{"emptyLinePlaceholder":86},[138,4385,4386,4388,4390,4392,4394,4396,4398,4400,4402],{"class":140,"line":209},[138,4387,1416],{"class":809},[138,4389,1420],{"class":1419},[138,4391,1424],{"class":1423},[138,4393,1427],{"class":1423},[138,4395,1430],{"class":809},[138,4397,1433],{"class":826},[138,4399,787],{"class":830},[138,4401,1433],{"class":826},[138,4403,1440],{"class":809},[138,4405,4406,4408,4410,4412,4414,4416,4418,4420],{"class":140,"line":215},[138,4407,2518],{"class":1073},[138,4409,2812],{"class":809},[138,4411,4139],{"class":2521},[138,4413,2823],{"class":809},[138,4415,1146],{"class":1112},[138,4417,3934],{"class":801},[138,4419,2732],{"class":805},[138,4421,4422],{"class":911},"\u002F\u002F Carrega o tema aqui\n",[138,4424,4425,4427,4429],{"class":140,"line":221},[138,4426,1505],{"class":809},[138,4428,1420],{"class":1419},[138,4430,1440],{"class":809},[12,4432,4433,4434,4436],{},"Quando o middleware salva a empresa na store, o composable lê as cores, monta o ",[135,4435,4323],{}," e o layout aplica as variáveis CSS:",[129,4438,4442],{"className":4439,"code":4440,"language":4441,"meta":75,"style":75},"language-css shiki shiki-themes material-theme-lighter github-dark github-dark","--company-primary\n--company-secondary\n","css",[135,4443,4444,4449],{"__ignoreMap":75},[138,4445,4446],{"class":140,"line":141},[138,4447,4448],{"class":805},"--company-primary\n",[138,4450,4451],{"class":140,"line":76},[138,4452,4453],{"class":805},"--company-secondary\n",[12,4455,4456],{},"A partir daí, qualquer componente dentro do layout pode usar as cores da empresa sem receber props e sem consultar a store diretamente.",[12,4458,2329],{},[129,4460,4462],{"className":1407,"code":4461,"language":1409,"meta":75,"style":75},"\u003Cp class=\"text-(--company-primary)\">\n  Vitrine\n\u003C\u002Fp>\n",[135,4463,4464,4483,4488],{"__ignoreMap":75},[138,4465,4466,4468,4470,4472,4474,4476,4479,4481],{"class":140,"line":141},[138,4467,1416],{"class":809},[138,4469,12],{"class":1419},[138,4471,3582],{"class":1423},[138,4473,1430],{"class":809},[138,4475,1433],{"class":826},[138,4477,4478],{"class":830},"text-(--company-primary)",[138,4480,1433],{"class":826},[138,4482,1440],{"class":809},[138,4484,4485],{"class":140,"line":76},[138,4486,4487],{"class":805},"  Vitrine\n",[138,4489,4490,4492,4494],{"class":140,"line":152},[138,4491,1505],{"class":809},[138,4493,12],{"class":1419},[138,4495,1440],{"class":809},[12,4497,4498],{},"Ou:",[129,4500,4502],{"className":1407,"code":4501,"language":1409,"meta":75,"style":75},"\u003Cbutton style=\"background-color: var(--company-primary)\">\n  Ver detalhes\n\u003C\u002Fbutton>\n",[135,4503,4504,4539,4544],{"__ignoreMap":75},[138,4505,4506,4508,4511,4514,4516,4518,4522,4524,4528,4530,4533,4535,4537],{"class":140,"line":141},[138,4507,1416],{"class":809},[138,4509,4510],{"class":1419},"button",[138,4512,4513],{"class":1423}," style",[138,4515,1430],{"class":809},[138,4517,1433],{"class":826},[138,4519,4521],{"class":4520},"seOON","background-color",[138,4523,782],{"class":809},[138,4525,4527],{"class":4526},"sUkpR"," var",[138,4529,806],{"class":809},[138,4531,4168],{"class":4532},"syox6",[138,4534,1049],{"class":809},[138,4536,1433],{"class":826},[138,4538,1440],{"class":809},[138,4540,4541],{"class":140,"line":76},[138,4542,4543],{"class":805},"  Ver detalhes\n",[138,4545,4546,4548,4550],{"class":140,"line":152},[138,4547,1505],{"class":809},[138,4549,4510],{"class":1419},[138,4551,1440],{"class":809},[12,4553,4554],{},"A cor vem do banco através da empresa obtida no banco pelo middleware, é salva na store, tratada no composable e chega ao componente como CSS variable.",[12,4556,4557],{},"Esse fluxo evita acoplamento desnecessário.",[27,4559],{},[30,4561,4563],{"id":4562},"o-fluxo-completo-da-arquitetura","O fluxo completo da arquitetura",[12,4565,4566],{},"A arquitetura inicial funciona assim:",[129,4568,4570],{"className":131,"code":4569,"language":133,"meta":75,"style":75},"1. Usuário acessa \u002Flari-loja\n\n2. O Nuxt identifica o parâmetro [slug]\n   slug = \"lari-loja\"\n\n3. O middleware tenant.global.ts roda\n   valida se o slug deve ser tratado como tenant\n   chama resolver_empresa_por_slug no Supabase\n\n4. A empresa encontrada é salva na useEmpresaStore\n   empresaStore.definir(data)\n\n5. A página [slug]\u002Findex.vue consome a store\n   usa nome, descrição, logo e dados públicos da empresa\n\n6. O layout catalog.vue chama useEmpresaTheme\n   lê empresa.colors\n   aplica --company-primary e --company-secondary\n\n7. Os componentes usam as variáveis CSS\n   sem precisar saber qual empresa está ativa\n",[135,4571,4572,4577,4581,4586,4591,4595,4600,4605,4610,4614,4619,4624,4628,4633,4638,4642,4647,4652,4657,4661,4666],{"__ignoreMap":75},[138,4573,4574],{"class":140,"line":141},[138,4575,4576],{},"1. Usuário acessa \u002Flari-loja\n",[138,4578,4579],{"class":140,"line":76},[138,4580,649],{"emptyLinePlaceholder":86},[138,4582,4583],{"class":140,"line":152},[138,4584,4585],{},"2. O Nuxt identifica o parâmetro [slug]\n",[138,4587,4588],{"class":140,"line":158},[138,4589,4590],{},"   slug = \"lari-loja\"\n",[138,4592,4593],{"class":140,"line":164},[138,4594,649],{"emptyLinePlaceholder":86},[138,4596,4597],{"class":140,"line":170},[138,4598,4599],{},"3. O middleware tenant.global.ts roda\n",[138,4601,4602],{"class":140,"line":176},[138,4603,4604],{},"   valida se o slug deve ser tratado como tenant\n",[138,4606,4607],{"class":140,"line":182},[138,4608,4609],{},"   chama resolver_empresa_por_slug no Supabase\n",[138,4611,4612],{"class":140,"line":188},[138,4613,649],{"emptyLinePlaceholder":86},[138,4615,4616],{"class":140,"line":194},[138,4617,4618],{},"4. A empresa encontrada é salva na useEmpresaStore\n",[138,4620,4621],{"class":140,"line":199},[138,4622,4623],{},"   empresaStore.definir(data)\n",[138,4625,4626],{"class":140,"line":204},[138,4627,649],{"emptyLinePlaceholder":86},[138,4629,4630],{"class":140,"line":209},[138,4631,4632],{},"5. A página [slug]\u002Findex.vue consome a store\n",[138,4634,4635],{"class":140,"line":215},[138,4636,4637],{},"   usa nome, descrição, logo e dados públicos da empresa\n",[138,4639,4640],{"class":140,"line":221},[138,4641,649],{"emptyLinePlaceholder":86},[138,4643,4644],{"class":140,"line":227},[138,4645,4646],{},"6. O layout catalog.vue chama useEmpresaTheme\n",[138,4648,4649],{"class":140,"line":233},[138,4650,4651],{},"   lê empresa.colors\n",[138,4653,4654],{"class":140,"line":239},[138,4655,4656],{},"   aplica --company-primary e --company-secondary\n",[138,4658,4659],{"class":140,"line":245},[138,4660,649],{"emptyLinePlaceholder":86},[138,4662,4663],{"class":140,"line":251},[138,4664,4665],{},"7. Os componentes usam as variáveis CSS\n",[138,4667,4668],{"class":140,"line":257},[138,4669,4670],{},"   sem precisar saber qual empresa está ativa\n",[12,4672,4673],{},"Esse fluxo mantém cada responsabilidade em seu lugar.",[27,4675],{},[30,4677,4679],{"id":4678},"a-divisão-de-responsabilidades","A divisão de responsabilidades",[1915,4681,4682,4692],{},[1918,4683,4684],{},[1921,4685,4686,4689],{},[1924,4687,4688],{},"Camada",[1924,4690,4691],{},"Responsabilidade",[1931,4693,4694,4705,4713,4721,4729,4739,4752,4762],{},[1921,4695,4696,4699],{},[1936,4697,4698],{},"Banco",[1936,4700,4701,4702,4704],{},"Relacionar dados por ",[135,4703,1837],{}," e proteger acesso com RLS",[1921,4706,4707,4710],{},[1936,4708,4709],{},"RPC",[1936,4711,4712],{},"Resolver uma empresa a partir do slug",[1921,4714,4715,4718],{},[1936,4716,4717],{},"Middleware",[1936,4719,4720],{},"Ler o slug da URL e carregar a empresa ativa",[1921,4722,4723,4726],{},[1936,4724,4725],{},"Store",[1936,4727,4728],{},"Guardar a empresa ativa para a aplicação",[1921,4730,4731,4736],{},[1936,4732,4733,4734],{},"Page ",[135,4735,2984],{},[1936,4737,4738],{},"Consumir a empresa e renderizar a vitrine",[1921,4740,4741,4746],{},[1936,4742,4743,4744],{},"Composable ",[135,4745,3719],{},[1936,4747,4748,4749,4751],{},"Transformar ",[135,4750,2024],{}," em CSS variables seguras",[1921,4753,4754,4759],{},[1936,4755,4756,4757],{},"Layout ",[135,4758,754],{},[1936,4760,4761],{},"Aplicar as variáveis de tema na árvore da vitrine",[1921,4763,4764,4767],{},[1936,4765,4766],{},"Componentes",[1936,4768,4769],{},"Usar as variáveis sem conhecer a lógica de tenant",[12,4771,4772],{},"Essa separação é o que deixa a arquitetura sustentável:",[402,4774,4775,4778,4783],{},[405,4776,4777],{},"Se amanhã a forma de resolver o tenant mudar, o ajuste fica no middleware ou na RPC.",[405,4779,4780,4781,363],{},"Se amanhã o formato das cores mudar, o ajuste fica no ",[135,4782,3719],{},[405,4784,4785],{},"Se amanhã a vitrine mudar visualmente, o ajuste fica na page ou nos componentes.",[12,4787,4788,4789,4793],{},"Isso é o principio ",[105,4790,4792],{"href":1727,"rel":4791},[109],"SOLID"," sendo aplicado na arquitetura frontend.",[27,4795],{},[30,4797,4799],{"id":4798},"decisões-técnicas","Decisões técnicas",[402,4801,4802,4807,4815,4821,4826,4832,4841,4855],{},[405,4803,4804,4806],{},[19,4805,1792],{}," foi usado como base do frontend por organizar bem rotas, layouts, middleware, SSR e composables. Essa estrutura permite criar uma arquitetura onde o slug da URL identifica a empresa ativa sem misturar essa responsabilidade diretamente nas páginas.",[405,4808,4809,4811,4812,4814],{},[19,4810,1804],{}," foi utilizado como backend principal, reunindo banco PostgreSQL, autenticação, funções RPC e Row Level Security. A decisão arquitetural foi concentrar nele a base do multitenant: dados relacionados por ",[135,4813,1837],{},", tenant resolvido por slug no banco e isolamento reforçado com políticas de acesso.",[405,4816,4817,4820],{},[19,4818,4819],{},"Pinia"," foi usado para manter a empresa ativa disponível globalmente depois que o middleware resolve o slug. Assim, layouts, páginas e composables conseguem consumir o contexto do tenant sem repetir chamadas ao Supabase.",[405,4822,4823,4825],{},[19,4824,1796],{}," faz parte da base visual do projeto e ajuda a construir interfaces com componentes prontos e consistentes, mantendo uma estrutura visual sólida para a vitrine pública.",[405,4827,4828,4831],{},[19,4829,4830],{},"Tailwind CSS"," foi usado para controlar o estilo da aplicação de forma flexível e produtiva. Em conjunto com CSS variables, ele permite aplicar as cores de cada empresa diretamente na interface sem criar estilos separados por tenant.",[405,4833,4834,4837,4838,4840],{},[19,4835,4836],{},"Composables"," foram usados para isolar regras reutilizáveis, como o tratamento das cores no ",[135,4839,3719],{},". Isso evita espalhar validações e transformações visuais dentro das páginas ou componentes.",[405,4842,4843,4846,4847,4849,4850,377,4852,4854],{},[19,4844,4845],{},"CSS Variables"," foram aplicadas para refletir a identidade visual de cada tenant. As cores cadastradas no campo ",[135,4848,2024],{}," da empresa são transformadas em ",[135,4851,4168],{},[135,4853,4187],{},", permitindo que os componentes usem o tema da empresa sem depender diretamente da store.",[405,4856,4857,4860],{},[19,4858,4859],{},"TypeScript"," ajuda a dar mais previsibilidade aos dados da aplicação, principalmente no contrato da empresa pública consumida pelo middleware, store, layout e página dinâmica.",[27,4862],{},[30,4864,4866],{"id":4865},"conclusão","Conclusão",[12,4868,4869,4870],{},"A arquitetura inicial do projeto foi construída em cima de uma ideia simples: ",[19,4871,4872],{},"o slug define o tenant, a store compartilha o contexto e o layout reflete a identidade visual da empresa.",[402,4874,4875,4880,4883,4886,4889],{},[405,4876,4877,4878,363],{},"O banco fica como base da separação por ",[135,4879,1837],{},[405,4881,4882],{},"O middleware transforma a URL em contexto real.",[405,4884,4885],{},"A store mantém esse contexto disponível.",[405,4887,4888],{},"O layout aplica as cores da empresa.",[405,4890,4891],{},"E os componentes apenas consomem o resultado.",[12,4893,4894],{},"No fim, o mais importante não foi apenas fazer funcionar, foi definir onde cada responsabilidade deveria morar e porque.",[12,4896,4897],{},"Essa é a diferença entre uma implementação que só resolve o problema de hoje e uma arquitetura que continua compreensível quando o projeto começa a crescer. Isso diferencia um desenvolvedor que somente escreve código, de um que arquiteta sistemas completos e tem visão de projeto.",[30,4899,4901],{"id":4900},"a-pergunta-que-guia-cada-decisão","A pergunta que guia cada decisão",[12,4903,4904,4905],{},"A pergunta que guiou as principais decisões foi: ",[19,4906,4907],{},"se eu precisar mudar isso daqui a seis meses, qual é o menor número de arquivos que vou precisar tocar?",[12,4909,4910],{},"Quando a resposta é \"um\", a separação está certa. Quando a resposta é \"vários espalhados\", alguma responsabilidade está no lugar errado e isso pode comprometer o projeto ou torna-lo de dficil manutenção no futuro.",[27,4912],{},[30,4914,1713],{"id":1712},[402,4916,4917,4924,4931,4936],{},[405,4918,4919],{},[105,4920,4923],{"href":4921,"rel":4922},"https:\u002F\u002Flarisantos.vercel.app\u002Fblog\u002Fvisitcard-generator-nuxt-pdf-do-zero",[109],"Gerador de cartões de visita em PDF do zero com Nuxt 3",[405,4925,4926],{},[105,4927,4930],{"href":4928,"rel":4929},"https:\u002F\u002Flarisantos.vercel.app\u002Fblog\u002Fvue-composition-vs-options-api",[109],"Options API vs Composition API: qual usar e quando?",[405,4932,4933],{},[105,4934,1729],{"href":1727,"rel":4935},[109],[405,4937,4938],{},[105,4939,4942],{"href":4940,"rel":4941},"https:\u002F\u002Flarisantos.vercel.app\u002Fblog\u002Fvuex-vs-pinia",[109],"Vuex vs Pinia: Guia Completo de Gerenciamento de Estado",[1744,4944,4945],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sG-J9, html code.shiki .sG-J9{--shiki-light:#39ADB5;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .swu5b, html code.shiki .swu5b{--shiki-light:#39ADB5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sod2m, html code.shiki .sod2m{--shiki-light:#9C3EDA;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sF_wb, html code.shiki .sF_wb{--shiki-light:#39ADB5;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .s0vBq, html code.shiki .s0vBq{--shiki-light:#91B859;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sMo7A, html code.shiki .sMo7A{--shiki-light:#90A4AE;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sFfmW, html code.shiki .sFfmW{--shiki-light:#39ADB5;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sFsEu, html code.shiki .sFsEu{--shiki-light:#9C3EDA;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sVPC0, html code.shiki .sVPC0{--shiki-light:#90A4AE;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s3Er8, html code.shiki .s3Er8{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sK_r7, html code.shiki .sK_r7{--shiki-light:#6182B8;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sk1zL, html code.shiki .sk1zL{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#FFAB70;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sdv8B, html code.shiki .sdv8B{--shiki-light:#E53935;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .soiBB, html code.shiki .soiBB{--shiki-light:#E2931D;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .s3afY, html code.shiki .s3afY{--shiki-light:#E2931D;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sqIbZ, html code.shiki .sqIbZ{--shiki-light:#E53935;--shiki-default:#85E89D;--shiki-dark:#85E89D}html pre.shiki code .s7047, html code.shiki .s7047{--shiki-light:#9C3EDA;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sMrrN, html code.shiki .sMrrN{--shiki-light:#FF5370;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sm4dI, html code.shiki .sm4dI{--shiki-light:#91B859;--shiki-default:#DBEDFF;--shiki-dark:#DBEDFF}html pre.shiki code .svU9d, html code.shiki .svU9d{--shiki-light:#39ADB5;--shiki-default:#DBEDFF;--shiki-dark:#DBEDFF}html pre.shiki code .sSJ72, html code.shiki .sSJ72{--shiki-light:#91B859;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s1Wpa, html code.shiki .s1Wpa{--shiki-light:#F76D47;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .swHuM, html code.shiki .swHuM{--shiki-light:#E53935;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sM3K6, html code.shiki .sM3K6{--shiki-light:#39ADB5;--shiki-light-font-style:inherit;--shiki-default:#FDAEB7;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .seOON, html code.shiki .seOON{--shiki-light:#8796B0;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sUkpR, html code.shiki .sUkpR{--shiki-light:#6182B8;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .syox6, html code.shiki .syox6{--shiki-light:#90A4AE;--shiki-default:#FFAB70;--shiki-dark:#FFAB70}",{"title":75,"searchDepth":76,"depth":76,"links":4947},[4948,4949,4950,4951,4952,4953,4954,4956,4957,4958,4960,4961,4962,4963,4964,4965],{"id":1875,"depth":76,"text":1876},{"id":1909,"depth":76,"text":1910},{"id":2149,"depth":76,"text":2150},{"id":2280,"depth":76,"text":2281},{"id":2378,"depth":76,"text":2379},{"id":2990,"depth":76,"text":2991},{"id":3259,"depth":76,"text":4955},"Consumindo a store na rota [slug]\u002Findex.vue",{"id":3645,"depth":76,"text":3646},{"id":3710,"depth":76,"text":3711},{"id":4255,"depth":76,"text":4959},"Aplicando as cores no layout\u002Fcatalog.vue",{"id":4562,"depth":76,"text":4563},{"id":4678,"depth":76,"text":4679},{"id":4798,"depth":76,"text":4799},{"id":4865,"depth":76,"text":4866},{"id":4900,"depth":76,"text":4901},{"id":1712,"depth":76,"text":1713},"2026-05-14T18:12:40-03:00","Um artigo prático sobre a estratégia inicial de arquitetura de um SaaS multitenant: roteamento por slug, resolução da empresa no middleware, armazenamento em store e identidade visual dinâmica por tenant.","\u002Fimages\u002Fblog\u002Fmultitenant.png",{},"\u002Fblog\u002Fnuxt-saas-multitenant",{"title":1722,"description":4967},"blog\u002Fnuxt-saas-multitenant",[1769,1409,1770,4974,1772,4975,1773,1774],"supabase","pinia","ZNOx8j-jYe3n2EEKQt16ykA3zE02uwowmOkt1VbQSzo",{"id":4978,"title":4979,"author":7,"body":4980,"date":9231,"description":9232,"extension":83,"image":9233,"meta":9234,"navigation":86,"path":9235,"seo":9236,"stem":9237,"tags":9238,"__hash__":9244},"blog\u002Fblog\u002Fvuex-vs-pinia.md","Vuex vs Pinia: Guia Completo de Gerenciamento de Estado no Vue.js",{"type":9,"value":4981,"toc":9197},[4982,4990,4993,4995,4999,5002,5012,5019,5022,5031,5033,5037,5040,5044,5051,5057,5063,5069,5079,5082,5667,5670,5860,5864,5882,6223,6237,6241,6244,6247,6251,6262,6265,6282,6284,6288,6291,6298,6302,6305,6310,6315,6320,6327,6330,6837,6843,7081,7091,7095,7118,7478,7490,7494,7497,7803,7828,7832,7835,7991,7997,8001,8013,8063,8066,8068,8072,8075,8079,8142,8146,8149,8294,8297,8413,8417,8427,8540,8543,8713,8717,8728,8730,8734,8738,8741,8745,8748,8752,8755,8759,8762,8766,8772,8774,8778,8783,8786,8791,8794,8796,8800,8809,8823,9130,9133,9135,9137,9140,9143,9150,9154,9176,9180,9194],[12,4983,4984,4985,4989],{},"Se você trabalha com Vue há algum tempo, já deve ter se perguntado: ",[4986,4987,4988],"em",{},"quando usar Vuex e quando usar Pinia?"," Ou talvez você esteja iniciando um novo projeto e não sabe qual das duas escolher. Esse artigo foi escrito exatamente para responder essas perguntas de forma prática, com exemplos reais e sem enrolação.",[12,4991,4992],{},"Vamos do conceito fundamental até a comparação técnica profunda entre as duas bibliotecas, passando por exemplos de código lado a lado. Ao final, você vai saber exatamente o que cada uma oferece e quando faz sentido usar cada uma.",[27,4994],{},[30,4996,4998],{"id":4997},"o-que-é-gerenciamento-de-estado-e-por-que-ele-importa","O que é Gerenciamento de Estado e por que ele importa?",[12,5000,5001],{},"Antes de comparar as ferramentas, vale entender o problema que elas resolvem.",[12,5003,5004,5005,1207,5008,5011],{},"Em aplicações Vue pequenas, o estado de cada componente vive dentro dele mesmo. Um campo de formulário, um toggle de menu, um contador: tudo funciona muito bem com ",[135,5006,5007],{},"data()",[135,5009,5010],{},"ref()"," localmente. O problema começa quando componentes que não possuem relação direta na árvore de componentes precisam compartilhar os mesmos dados.",[12,5013,5014,5015,5018],{},"Imagine um e-commerce com um componente de cabeçalho que exibe o número de itens no carrinho, um componente de lista de produtos que adiciona itens a ele, e uma página de checkout que consome esses mesmos dados. Passar essas informações via props através de múltiplas camadas de componentes (o famoso ",[4986,5016,5017],{},"prop drilling",") torna o código frágil e difícil de manter. Usar eventos para comunicar componentes que não são pai e filho é ainda mais trabalhoso.",[12,5020,5021],{},"A solução é centralizar o estado compartilhado em um lugar único, acessível por qualquer componente da aplicação. É exatamente isso que bibliotecas como Vuex e Pinia fazem.",[12,5023,5024,5025,5030],{},"Vale mencionar que a ideia por trás de uma store não é tão diferente de algo que o JavaScript já faz nativamente: ",[105,5026,5029],{"href":5027,"rel":5028},"https:\u002F\u002Flarisantos.vercel.app\u002Fblog\u002Fclosures-javascript",[109],"closures",". Uma função que mantém um estado interno protegido e expõe métodos para manipulá-lo é, na essência, o mesmo princípio que os frameworks formalizaram. Se esse conceito ainda não está claro para você, vale a leitura antes de seguir em frente.",[27,5032],{},[30,5034,5036],{"id":5035},"vuex-a-solução-consagrada","Vuex: a solução consagrada",[12,5038,5039],{},"O Vuex foi por anos a biblioteca oficial de gerenciamento de estado do Vue.js. Inspirado no padrão Flux (criado pelo Facebook) e no Redux (do ecossistema React), ele introduziu um modelo bastante estruturado e previsível para controlar o estado da aplicação.",[1643,5041,5043],{"id":5042},"a-arquitetura-do-vuex","A arquitetura do Vuex",[12,5045,5046,5047,5050],{},"O Vuex organiza o estado em um único objeto global chamado ",[19,5048,5049],{},"store",", e estabelece regras rígidas sobre como esse estado pode ser modificado. A estrutura é composta por quatro conceitos principais:",[12,5052,5053,5056],{},[19,5054,5055],{},"State"," é onde os dados vivem. É o \"banco de dados\" da sua aplicação no lado do cliente.",[12,5058,5059,5062],{},[19,5060,5061],{},"Getters"," são equivalentes a propriedades computadas para o store. Você os usa quando precisa derivar dados do estado, como filtrar uma lista ou calcular um total.",[12,5064,5065,5068],{},[19,5066,5067],{},"Mutations"," são as únicas formas permitidas de modificar o estado de forma síncrona. Toda alteração no estado precisa passar por uma mutation, o que torna as mudanças rastreáveis e previsíveis.",[12,5070,5071,5074,5075,5078],{},[19,5072,5073],{},"Actions"," lidam com operações assíncronas (como chamadas à API) e, ao final, fazem ",[135,5076,5077],{},"commit"," de uma mutation para de fato modificar o estado.",[12,5080,5081],{},"Veja como um store Vuex clássico se parece na prática:",[129,5083,5087],{"className":5084,"code":5085,"language":5086,"meta":75,"style":75},"language-js shiki shiki-themes material-theme-lighter github-dark github-dark","import { createStore } from 'vuex'\n\nexport default createStore({\n  \u002F\u002F state: é onde todos os dados da aplicação vivem\n  \u002F\u002F pense nele como o \"banco de dados\" do lado do cliente\n  state: {\n    usuario: null,\n    carrinho: []\n  },\n\n  \u002F\u002F getters: são como computed properties, mas para o store\n  \u002F\u002F recebem o state como primeiro argumento e retornam um valor derivado\n  getters: {\n    totalItens(state) {\n      return state.carrinho.length\n    },\n    totalValor(state) {\n      \u002F\u002F reduce percorre o array somando o preço de cada item\n      return state.carrinho.reduce((acc, item) => acc + item.preco, 0)\n    }\n  },\n\n  \u002F\u002F mutations: a ÚNICA forma de modificar o state no Vuex\n  \u002F\u002F são sempre síncronas e recebem o state como primeiro argumento\n  \u002F\u002F por convenção, seus nomes são escritos em SNAKE_CASE maiúsculo\n  mutations: {\n    SET_USUARIO(state, usuario) {\n      state.usuario = usuario\n    },\n    ADICIONAR_AO_CARRINHO(state, produto) {\n      state.carrinho.push(produto)\n    },\n    REMOVER_DO_CARRINHO(state, produtoId) {\n      state.carrinho = state.carrinho.filter(item => item.id !== produtoId)\n    }\n  },\n\n  \u002F\u002F actions: lidam com lógica assíncrona (chamadas de API, etc.)\n  \u002F\u002F recebem um objeto de contexto { commit, state, getters, dispatch }\n  \u002F\u002F ao final, chamam commit() para acionar a mutation correspondente\n  actions: {\n    async buscarUsuario({ commit }, userId) {\n      const response = await fetch(`\u002Fapi\u002Fusuarios\u002F${userId}`)\n      const usuario = await response.json()\n      \u002F\u002F aqui está o ponto de atenção: a action não altera o state diretamente,\n      \u002F\u002F ela comita uma mutation que faz isso\n      commit('SET_USUARIO', usuario)\n    },\n    adicionarAoCarrinho({ commit }, produto) {\n      commit('ADICIONAR_AO_CARRINHO', produto)\n    }\n  }\n})\n","js",[135,5088,5089,5109,5113,5125,5130,5135,5144,5155,5165,5169,5173,5178,5183,5192,5207,5225,5230,5243,5248,5299,5303,5307,5311,5316,5321,5326,5335,5353,5368,5372,5390,5410,5414,5432,5473,5477,5481,5485,5490,5495,5500,5509,5532,5563,5581,5586,5591,5611,5615,5632,5651,5655,5660],{"__ignoreMap":75},[138,5090,5091,5093,5095,5098,5100,5102,5104,5107],{"class":140,"line":141},[138,5092,3735],{"class":794},[138,5094,2812],{"class":809},[138,5096,5097],{"class":805}," createStore",[138,5099,2823],{"class":809},[138,5101,3748],{"class":794},[138,5103,957],{"class":826},[138,5105,5106],{"class":830},"vuex",[138,5108,2361],{"class":826},[138,5110,5111],{"class":140,"line":76},[138,5112,649],{"emptyLinePlaceholder":86},[138,5114,5115,5117,5119,5121,5123],{"class":140,"line":152},[138,5116,795],{"class":794},[138,5118,798],{"class":794},[138,5120,5097],{"class":801},[138,5122,806],{"class":805},[138,5124,810],{"class":809},[138,5126,5127],{"class":140,"line":158},[138,5128,5129],{"class":911},"  \u002F\u002F state: é onde todos os dados da aplicação vivem\n",[138,5131,5132],{"class":140,"line":164},[138,5133,5134],{"class":911},"  \u002F\u002F pense nele como o \"banco de dados\" do lado do cliente\n",[138,5136,5137,5140,5142],{"class":140,"line":170},[138,5138,5139],{"class":815},"  state",[138,5141,782],{"class":809},[138,5143,934],{"class":809},[138,5145,5146,5149,5151,5153],{"class":140,"line":176},[138,5147,5148],{"class":815},"    usuario",[138,5150,782],{"class":809},[138,5152,3062],{"class":2041},[138,5154,837],{"class":809},[138,5156,5157,5160,5162],{"class":140,"line":182},[138,5158,5159],{"class":815},"    carrinho",[138,5161,782],{"class":809},[138,5163,5164],{"class":805}," []\n",[138,5166,5167],{"class":140,"line":188},[138,5168,972],{"class":809},[138,5170,5171],{"class":140,"line":194},[138,5172,649],{"emptyLinePlaceholder":86},[138,5174,5175],{"class":140,"line":199},[138,5176,5177],{"class":911},"  \u002F\u002F getters: são como computed properties, mas para o store\n",[138,5179,5180],{"class":140,"line":204},[138,5181,5182],{"class":911},"  \u002F\u002F recebem o state como primeiro argumento e retornam um valor derivado\n",[138,5184,5185,5188,5190],{"class":140,"line":209},[138,5186,5187],{"class":815},"  getters",[138,5189,782],{"class":809},[138,5191,934],{"class":809},[138,5193,5194,5198,5200,5203,5205],{"class":140,"line":215},[138,5195,5197],{"class":5196},"s0u7J","    totalItens",[138,5199,806],{"class":809},[138,5201,5202],{"class":1045},"state",[138,5204,1049],{"class":809},[138,5206,934],{"class":809},[138,5208,5209,5212,5215,5217,5220,5222],{"class":140,"line":221},[138,5210,5211],{"class":794},"      return",[138,5213,5214],{"class":805}," state",[138,5216,363],{"class":809},[138,5218,5219],{"class":805},"carrinho",[138,5221,363],{"class":809},[138,5223,5224],{"class":2521},"length\n",[138,5226,5227],{"class":140,"line":227},[138,5228,5229],{"class":809},"    },\n",[138,5231,5232,5235,5237,5239,5241],{"class":140,"line":233},[138,5233,5234],{"class":5196},"    totalValor",[138,5236,806],{"class":809},[138,5238,5202],{"class":1045},[138,5240,1049],{"class":809},[138,5242,934],{"class":809},[138,5244,5245],{"class":140,"line":239},[138,5246,5247],{"class":911},"      \u002F\u002F reduce percorre o array somando o preço de cada item\n",[138,5249,5250,5252,5254,5256,5258,5260,5263,5265,5267,5270,5272,5275,5277,5279,5282,5284,5286,5288,5291,5293,5297],{"class":140,"line":245},[138,5251,5211],{"class":794},[138,5253,5214],{"class":805},[138,5255,363],{"class":809},[138,5257,5219],{"class":805},[138,5259,363],{"class":809},[138,5261,5262],{"class":801},"reduce",[138,5264,806],{"class":815},[138,5266,806],{"class":809},[138,5268,5269],{"class":1045},"acc",[138,5271,954],{"class":809},[138,5273,5274],{"class":1045}," item",[138,5276,1049],{"class":809},[138,5278,1074],{"class":1073},[138,5280,5281],{"class":805}," acc",[138,5283,1155],{"class":1112},[138,5285,5274],{"class":805},[138,5287,363],{"class":809},[138,5289,5290],{"class":805},"preco",[138,5292,954],{"class":809},[138,5294,5296],{"class":5295},"s_k96"," 0",[138,5298,872],{"class":815},[138,5300,5301],{"class":140,"line":251},[138,5302,1179],{"class":809},[138,5304,5305],{"class":140,"line":257},[138,5306,972],{"class":809},[138,5308,5309],{"class":140,"line":263},[138,5310,649],{"emptyLinePlaceholder":86},[138,5312,5313],{"class":140,"line":269},[138,5314,5315],{"class":911},"  \u002F\u002F mutations: a ÚNICA forma de modificar o state no Vuex\n",[138,5317,5318],{"class":140,"line":275},[138,5319,5320],{"class":911},"  \u002F\u002F são sempre síncronas e recebem o state como primeiro argumento\n",[138,5322,5323],{"class":140,"line":281},[138,5324,5325],{"class":911},"  \u002F\u002F por convenção, seus nomes são escritos em SNAKE_CASE maiúsculo\n",[138,5327,5328,5331,5333],{"class":140,"line":287},[138,5329,5330],{"class":815},"  mutations",[138,5332,782],{"class":809},[138,5334,934],{"class":809},[138,5336,5337,5340,5342,5344,5346,5349,5351],{"class":140,"line":293},[138,5338,5339],{"class":5196},"    SET_USUARIO",[138,5341,806],{"class":809},[138,5343,5202],{"class":1045},[138,5345,954],{"class":809},[138,5347,5348],{"class":1045}," usuario",[138,5350,1049],{"class":809},[138,5352,934],{"class":809},[138,5354,5355,5358,5360,5363,5365],{"class":140,"line":299},[138,5356,5357],{"class":805},"      state",[138,5359,363],{"class":809},[138,5361,5362],{"class":805},"usuario",[138,5364,1146],{"class":1112},[138,5366,5367],{"class":805}," usuario\n",[138,5369,5370],{"class":140,"line":305},[138,5371,5229],{"class":809},[138,5373,5374,5377,5379,5381,5383,5386,5388],{"class":140,"line":311},[138,5375,5376],{"class":5196},"    ADICIONAR_AO_CARRINHO",[138,5378,806],{"class":809},[138,5380,5202],{"class":1045},[138,5382,954],{"class":809},[138,5384,5385],{"class":1045}," produto",[138,5387,1049],{"class":809},[138,5389,934],{"class":809},[138,5391,5392,5394,5396,5398,5400,5403,5405,5408],{"class":140,"line":317},[138,5393,5357],{"class":805},[138,5395,363],{"class":809},[138,5397,5219],{"class":805},[138,5399,363],{"class":809},[138,5401,5402],{"class":801},"push",[138,5404,806],{"class":815},[138,5406,5407],{"class":805},"produto",[138,5409,872],{"class":815},[138,5411,5412],{"class":140,"line":323},[138,5413,5229],{"class":809},[138,5415,5416,5419,5421,5423,5425,5428,5430],{"class":140,"line":329},[138,5417,5418],{"class":5196},"    REMOVER_DO_CARRINHO",[138,5420,806],{"class":809},[138,5422,5202],{"class":1045},[138,5424,954],{"class":809},[138,5426,5427],{"class":1045}," produtoId",[138,5429,1049],{"class":809},[138,5431,934],{"class":809},[138,5433,5434,5436,5438,5440,5442,5444,5446,5448,5450,5453,5455,5458,5460,5462,5464,5467,5469,5471],{"class":140,"line":335},[138,5435,5357],{"class":805},[138,5437,363],{"class":809},[138,5439,5219],{"class":805},[138,5441,1146],{"class":1112},[138,5443,5214],{"class":805},[138,5445,363],{"class":809},[138,5447,5219],{"class":805},[138,5449,363],{"class":809},[138,5451,5452],{"class":801},"filter",[138,5454,806],{"class":815},[138,5456,5457],{"class":1045},"item",[138,5459,1074],{"class":1073},[138,5461,5274],{"class":805},[138,5463,363],{"class":809},[138,5465,5466],{"class":805},"id",[138,5468,1124],{"class":1112},[138,5470,5427],{"class":805},[138,5472,872],{"class":815},[138,5474,5475],{"class":140,"line":341},[138,5476,1179],{"class":809},[138,5478,5479],{"class":140,"line":347},[138,5480,972],{"class":809},[138,5482,5483],{"class":140,"line":353},[138,5484,649],{"emptyLinePlaceholder":86},[138,5486,5487],{"class":140,"line":622},[138,5488,5489],{"class":911},"  \u002F\u002F actions: lidam com lógica assíncrona (chamadas de API, etc.)\n",[138,5491,5492],{"class":140,"line":628},[138,5493,5494],{"class":911},"  \u002F\u002F recebem um objeto de contexto { commit, state, getters, dispatch }\n",[138,5496,5497],{"class":140,"line":634},[138,5498,5499],{"class":911},"  \u002F\u002F ao final, chamam commit() para acionar a mutation correspondente\n",[138,5501,5502,5505,5507],{"class":140,"line":640},[138,5503,5504],{"class":815},"  actions",[138,5506,782],{"class":809},[138,5508,934],{"class":809},[138,5510,5511,5514,5517,5520,5522,5525,5528,5530],{"class":140,"line":646},[138,5512,5513],{"class":1073},"    async",[138,5515,5516],{"class":5196}," buscarUsuario",[138,5518,5519],{"class":809},"({",[138,5521,1560],{"class":1045},[138,5523,5524],{"class":809}," },",[138,5526,5527],{"class":1045}," userId",[138,5529,1049],{"class":809},[138,5531,934],{"class":809},[138,5533,5534,5537,5540,5542,5544,5547,5549,5551,5554,5556,5559,5561],{"class":140,"line":652},[138,5535,5536],{"class":1073},"      const",[138,5538,5539],{"class":2521}," response",[138,5541,1146],{"class":1112},[138,5543,2828],{"class":794},[138,5545,5546],{"class":801}," fetch",[138,5548,806],{"class":815},[138,5550,3791],{"class":826},[138,5552,5553],{"class":830},"\u002Fapi\u002Fusuarios\u002F",[138,5555,3797],{"class":826},[138,5557,5558],{"class":805},"userId",[138,5560,3803],{"class":826},[138,5562,872],{"class":815},[138,5564,5565,5567,5569,5571,5573,5575,5577,5579],{"class":140,"line":657},[138,5566,5536],{"class":1073},[138,5568,5348],{"class":2521},[138,5570,1146],{"class":1112},[138,5572,2828],{"class":794},[138,5574,5539],{"class":805},[138,5576,363],{"class":809},[138,5578,2030],{"class":801},[138,5580,2796],{"class":815},[138,5582,5583],{"class":140,"line":663},[138,5584,5585],{"class":911},"      \u002F\u002F aqui está o ponto de atenção: a action não altera o state diretamente,\n",[138,5587,5588],{"class":140,"line":668},[138,5589,5590],{"class":911},"      \u002F\u002F ela comita uma mutation que faz isso\n",[138,5592,5593,5596,5598,5600,5603,5605,5607,5609],{"class":140,"line":673},[138,5594,5595],{"class":801},"      commit",[138,5597,806],{"class":815},[138,5599,834],{"class":826},[138,5601,5602],{"class":830},"SET_USUARIO",[138,5604,834],{"class":826},[138,5606,954],{"class":809},[138,5608,5348],{"class":805},[138,5610,872],{"class":815},[138,5612,5613],{"class":140,"line":679},[138,5614,5229],{"class":809},[138,5616,5617,5620,5622,5624,5626,5628,5630],{"class":140,"line":684},[138,5618,5619],{"class":5196},"    adicionarAoCarrinho",[138,5621,5519],{"class":809},[138,5623,1560],{"class":1045},[138,5625,5524],{"class":809},[138,5627,5385],{"class":1045},[138,5629,1049],{"class":809},[138,5631,934],{"class":809},[138,5633,5634,5636,5638,5640,5643,5645,5647,5649],{"class":140,"line":690},[138,5635,5595],{"class":801},[138,5637,806],{"class":815},[138,5639,834],{"class":826},[138,5641,5642],{"class":830},"ADICIONAR_AO_CARRINHO",[138,5644,834],{"class":826},[138,5646,954],{"class":809},[138,5648,5385],{"class":805},[138,5650,872],{"class":815},[138,5652,5653],{"class":140,"line":696},[138,5654,1179],{"class":809},[138,5656,5658],{"class":140,"line":5657},52,[138,5659,1184],{"class":809},[138,5661,5663,5665],{"class":140,"line":5662},53,[138,5664,869],{"class":809},[138,5666,872],{"class":805},[12,5668,5669],{},"E para consumir esse store dentro de um componente Vue 2 com Options API:",[129,5671,5673],{"className":5084,"code":5672,"language":5086,"meta":75,"style":75},"import { mapState, mapGetters, mapActions } from 'vuex'\n\nexport default {\n  computed: {\n    \u002F\u002F mapState mapeia state.usuario e state.carrinho como computed properties\n    \u002F\u002F agora você acessa via this.usuario e this.carrinho no template\n    ...mapState(['usuario', 'carrinho']),\n\n    \u002F\u002F mapGetters faz o mesmo para os getters definidos no store\n    ...mapGetters(['totalItens', 'totalValor'])\n  },\n  methods: {\n    \u002F\u002F mapActions transforma as actions em métodos do componente\n    \u002F\u002F this.buscarUsuario(id) vai chamar store.dispatch('buscarUsuario', id)\n    ...mapActions(['buscarUsuario', 'adicionarAoCarrinho'])\n  }\n}\n",[135,5674,5675,5704,5708,5716,5725,5730,5735,5765,5769,5774,5802,5806,5815,5820,5825,5852,5856],{"__ignoreMap":75},[138,5676,5677,5679,5681,5684,5686,5689,5691,5694,5696,5698,5700,5702],{"class":140,"line":141},[138,5678,3735],{"class":794},[138,5680,2812],{"class":809},[138,5682,5683],{"class":805}," mapState",[138,5685,954],{"class":809},[138,5687,5688],{"class":805}," mapGetters",[138,5690,954],{"class":809},[138,5692,5693],{"class":805}," mapActions",[138,5695,2823],{"class":809},[138,5697,3748],{"class":794},[138,5699,957],{"class":826},[138,5701,5106],{"class":830},[138,5703,2361],{"class":826},[138,5705,5706],{"class":140,"line":76},[138,5707,649],{"emptyLinePlaceholder":86},[138,5709,5710,5712,5714],{"class":140,"line":152},[138,5711,795],{"class":794},[138,5713,798],{"class":794},[138,5715,934],{"class":809},[138,5717,5718,5721,5723],{"class":140,"line":158},[138,5719,5720],{"class":815},"  computed",[138,5722,782],{"class":809},[138,5724,934],{"class":809},[138,5726,5727],{"class":140,"line":164},[138,5728,5729],{"class":911},"    \u002F\u002F mapState mapeia state.usuario e state.carrinho como computed properties\n",[138,5731,5732],{"class":140,"line":170},[138,5733,5734],{"class":911},"    \u002F\u002F agora você acessa via this.usuario e this.carrinho no template\n",[138,5736,5737,5740,5743,5746,5748,5750,5752,5754,5756,5758,5760,5763],{"class":140,"line":176},[138,5738,5739],{"class":1112},"    ...",[138,5741,5742],{"class":801},"mapState",[138,5744,5745],{"class":805},"([",[138,5747,834],{"class":826},[138,5749,5362],{"class":830},[138,5751,834],{"class":826},[138,5753,954],{"class":809},[138,5755,957],{"class":826},[138,5757,5219],{"class":830},[138,5759,834],{"class":826},[138,5761,5762],{"class":805},"])",[138,5764,837],{"class":809},[138,5766,5767],{"class":140,"line":182},[138,5768,649],{"emptyLinePlaceholder":86},[138,5770,5771],{"class":140,"line":188},[138,5772,5773],{"class":911},"    \u002F\u002F mapGetters faz o mesmo para os getters definidos no store\n",[138,5775,5776,5778,5781,5783,5785,5788,5790,5792,5794,5797,5799],{"class":140,"line":194},[138,5777,5739],{"class":1112},[138,5779,5780],{"class":801},"mapGetters",[138,5782,5745],{"class":805},[138,5784,834],{"class":826},[138,5786,5787],{"class":830},"totalItens",[138,5789,834],{"class":826},[138,5791,954],{"class":809},[138,5793,957],{"class":826},[138,5795,5796],{"class":830},"totalValor",[138,5798,834],{"class":826},[138,5800,5801],{"class":805},"])\n",[138,5803,5804],{"class":140,"line":199},[138,5805,972],{"class":809},[138,5807,5808,5811,5813],{"class":140,"line":204},[138,5809,5810],{"class":815},"  methods",[138,5812,782],{"class":809},[138,5814,934],{"class":809},[138,5816,5817],{"class":140,"line":209},[138,5818,5819],{"class":911},"    \u002F\u002F mapActions transforma as actions em métodos do componente\n",[138,5821,5822],{"class":140,"line":215},[138,5823,5824],{"class":911},"    \u002F\u002F this.buscarUsuario(id) vai chamar store.dispatch('buscarUsuario', id)\n",[138,5826,5827,5829,5832,5834,5836,5839,5841,5843,5845,5848,5850],{"class":140,"line":221},[138,5828,5739],{"class":1112},[138,5830,5831],{"class":801},"mapActions",[138,5833,5745],{"class":805},[138,5835,834],{"class":826},[138,5837,5838],{"class":830},"buscarUsuario",[138,5840,834],{"class":826},[138,5842,954],{"class":809},[138,5844,957],{"class":826},[138,5846,5847],{"class":830},"adicionarAoCarrinho",[138,5849,834],{"class":826},[138,5851,5801],{"class":805},[138,5853,5854],{"class":140,"line":227},[138,5855,1184],{"class":809},[138,5857,5858],{"class":140,"line":233},[138,5859,2082],{"class":809},[1643,5861,5863],{"id":5862},"modules-escalando-com-vuex","Modules: escalando com Vuex",[12,5865,5866,5867,5870,5871,370,5873,370,5876,377,5879,363],{},"Em projetos maiores, colocar tudo em um único arquivo de store rapidamente se torna inviável. O Vuex resolve isso com ",[19,5868,5869],{},"modules",": é possível dividir o store em fatias menores, cada uma com seu próprio ",[135,5872,5202],{},[135,5874,5875],{},"mutations",[135,5877,5878],{},"actions",[135,5880,5881],{},"getters",[129,5883,5885],{"className":5084,"code":5884,"language":5086,"meta":75,"style":75},"const moduloCarrinho = {\n  \u002F\u002F namespaced: true isola as mutations e actions deste módulo\n  \u002F\u002F sem isso, elas ficam no namespace global e podem colidir com outros módulos\n  namespaced: true,\n  state: () => ({ itens: [] }),\n  mutations: {\n    ADICIONAR(state, item) { state.itens.push(item) }\n  },\n  actions: {\n    \u002F\u002F para chamar esta action de um componente:\n    \u002F\u002F store.dispatch('carrinho\u002Fadicionar', item)\n    adicionar({ commit }, item) {\n      commit('ADICIONAR', item)\n    }\n  }\n}\n\nconst moduloUsuario = {\n  namespaced: true,\n  state: () => ({ dados: null }),\n  mutations: {\n    \u002F\u002F para chamar esta mutation:\n    \u002F\u002F store.commit('usuario\u002FSET', dados)\n    SET(state, usuario) { state.dados = usuario }\n  }\n}\n\nexport default createStore({\n  modules: {\n    carrinho: moduloCarrinho,\n    usuario: moduloUsuario\n  }\n})\n",[135,5886,5887,5898,5903,5908,5920,5948,5956,5992,5996,6004,6009,6014,6031,6050,6054,6058,6062,6066,6077,6087,6114,6122,6127,6132,6161,6165,6169,6173,6185,6194,6204,6213,6217],{"__ignoreMap":75},[138,5888,5889,5891,5894,5896],{"class":140,"line":141},[138,5890,2518],{"class":1073},[138,5892,5893],{"class":2521}," moduloCarrinho",[138,5895,1146],{"class":1112},[138,5897,934],{"class":809},[138,5899,5900],{"class":140,"line":76},[138,5901,5902],{"class":911},"  \u002F\u002F namespaced: true isola as mutations e actions deste módulo\n",[138,5904,5905],{"class":140,"line":152},[138,5906,5907],{"class":911},"  \u002F\u002F sem isso, elas ficam no namespace global e podem colidir com outros módulos\n",[138,5909,5910,5913,5915,5918],{"class":140,"line":158},[138,5911,5912],{"class":815},"  namespaced",[138,5914,782],{"class":809},[138,5916,5917],{"class":3862}," true",[138,5919,837],{"class":809},[138,5921,5922,5924,5926,5928,5930,5932,5934,5937,5939,5942,5944,5946],{"class":140,"line":164},[138,5923,5139],{"class":801},[138,5925,782],{"class":809},[138,5927,3034],{"class":809},[138,5929,1074],{"class":1073},[138,5931,1084],{"class":805},[138,5933,3284],{"class":809},[138,5935,5936],{"class":815}," itens",[138,5938,782],{"class":809},[138,5940,5941],{"class":805}," [] ",[138,5943,869],{"class":809},[138,5945,1049],{"class":805},[138,5947,837],{"class":809},[138,5949,5950,5952,5954],{"class":140,"line":170},[138,5951,5330],{"class":815},[138,5953,782],{"class":809},[138,5955,934],{"class":809},[138,5957,5958,5961,5963,5965,5967,5969,5971,5973,5975,5977,5980,5982,5984,5986,5988,5990],{"class":140,"line":176},[138,5959,5960],{"class":5196},"    ADICIONAR",[138,5962,806],{"class":809},[138,5964,5202],{"class":1045},[138,5966,954],{"class":809},[138,5968,5274],{"class":1045},[138,5970,1049],{"class":809},[138,5972,2812],{"class":809},[138,5974,5214],{"class":805},[138,5976,363],{"class":809},[138,5978,5979],{"class":805},"itens",[138,5981,363],{"class":809},[138,5983,5402],{"class":801},[138,5985,806],{"class":815},[138,5987,5457],{"class":805},[138,5989,1109],{"class":815},[138,5991,2082],{"class":809},[138,5993,5994],{"class":140,"line":182},[138,5995,972],{"class":809},[138,5997,5998,6000,6002],{"class":140,"line":188},[138,5999,5504],{"class":815},[138,6001,782],{"class":809},[138,6003,934],{"class":809},[138,6005,6006],{"class":140,"line":194},[138,6007,6008],{"class":911},"    \u002F\u002F para chamar esta action de um componente:\n",[138,6010,6011],{"class":140,"line":199},[138,6012,6013],{"class":911},"    \u002F\u002F store.dispatch('carrinho\u002Fadicionar', item)\n",[138,6015,6016,6019,6021,6023,6025,6027,6029],{"class":140,"line":204},[138,6017,6018],{"class":5196},"    adicionar",[138,6020,5519],{"class":809},[138,6022,1560],{"class":1045},[138,6024,5524],{"class":809},[138,6026,5274],{"class":1045},[138,6028,1049],{"class":809},[138,6030,934],{"class":809},[138,6032,6033,6035,6037,6039,6042,6044,6046,6048],{"class":140,"line":209},[138,6034,5595],{"class":801},[138,6036,806],{"class":815},[138,6038,834],{"class":826},[138,6040,6041],{"class":830},"ADICIONAR",[138,6043,834],{"class":826},[138,6045,954],{"class":809},[138,6047,5274],{"class":805},[138,6049,872],{"class":815},[138,6051,6052],{"class":140,"line":215},[138,6053,1179],{"class":809},[138,6055,6056],{"class":140,"line":221},[138,6057,1184],{"class":809},[138,6059,6060],{"class":140,"line":227},[138,6061,2082],{"class":809},[138,6063,6064],{"class":140,"line":233},[138,6065,649],{"emptyLinePlaceholder":86},[138,6067,6068,6070,6073,6075],{"class":140,"line":239},[138,6069,2518],{"class":1073},[138,6071,6072],{"class":2521}," moduloUsuario",[138,6074,1146],{"class":1112},[138,6076,934],{"class":809},[138,6078,6079,6081,6083,6085],{"class":140,"line":245},[138,6080,5912],{"class":815},[138,6082,782],{"class":809},[138,6084,5917],{"class":3862},[138,6086,837],{"class":809},[138,6088,6089,6091,6093,6095,6097,6099,6101,6104,6106,6108,6110,6112],{"class":140,"line":251},[138,6090,5139],{"class":801},[138,6092,782],{"class":809},[138,6094,3034],{"class":809},[138,6096,1074],{"class":1073},[138,6098,1084],{"class":805},[138,6100,3284],{"class":809},[138,6102,6103],{"class":815}," dados",[138,6105,782],{"class":809},[138,6107,3062],{"class":2041},[138,6109,2823],{"class":809},[138,6111,1049],{"class":805},[138,6113,837],{"class":809},[138,6115,6116,6118,6120],{"class":140,"line":257},[138,6117,5330],{"class":815},[138,6119,782],{"class":809},[138,6121,934],{"class":809},[138,6123,6124],{"class":140,"line":263},[138,6125,6126],{"class":911},"    \u002F\u002F para chamar esta mutation:\n",[138,6128,6129],{"class":140,"line":269},[138,6130,6131],{"class":911},"    \u002F\u002F store.commit('usuario\u002FSET', dados)\n",[138,6133,6134,6137,6139,6141,6143,6145,6147,6149,6151,6153,6155,6157,6159],{"class":140,"line":275},[138,6135,6136],{"class":5196},"    SET",[138,6138,806],{"class":809},[138,6140,5202],{"class":1045},[138,6142,954],{"class":809},[138,6144,5348],{"class":1045},[138,6146,1049],{"class":809},[138,6148,2812],{"class":809},[138,6150,5214],{"class":805},[138,6152,363],{"class":809},[138,6154,3132],{"class":805},[138,6156,1146],{"class":1112},[138,6158,5348],{"class":805},[138,6160,1015],{"class":809},[138,6162,6163],{"class":140,"line":281},[138,6164,1184],{"class":809},[138,6166,6167],{"class":140,"line":287},[138,6168,2082],{"class":809},[138,6170,6171],{"class":140,"line":293},[138,6172,649],{"emptyLinePlaceholder":86},[138,6174,6175,6177,6179,6181,6183],{"class":140,"line":299},[138,6176,795],{"class":794},[138,6178,798],{"class":794},[138,6180,5097],{"class":801},[138,6182,806],{"class":805},[138,6184,810],{"class":809},[138,6186,6187,6190,6192],{"class":140,"line":305},[138,6188,6189],{"class":815},"  modules",[138,6191,782],{"class":809},[138,6193,934],{"class":809},[138,6195,6196,6198,6200,6202],{"class":140,"line":311},[138,6197,5159],{"class":815},[138,6199,782],{"class":809},[138,6201,5893],{"class":805},[138,6203,837],{"class":809},[138,6205,6206,6208,6210],{"class":140,"line":317},[138,6207,5148],{"class":815},[138,6209,782],{"class":809},[138,6211,6212],{"class":805}," moduloUsuario\n",[138,6214,6215],{"class":140,"line":323},[138,6216,1184],{"class":809},[138,6218,6219,6221],{"class":140,"line":329},[138,6220,869],{"class":809},[138,6222,872],{"class":805},[12,6224,6225,6226,6229,6230,1207,6233,6236],{},"Com ",[135,6227,6228],{},"namespaced: true",", as actions e mutations ficam acessíveis com o prefixo do módulo: ",[135,6231,6232],{},"dispatch('carrinho\u002Fadicionar', item)",[135,6234,6235],{},"commit('usuario\u002FSET', dados)",". Isso evita conflitos de nomes, mas adiciona uma camada de verbosidade que muitos desenvolvedores acham trabalhosa, especialmente em projetos com muitos módulos.",[1643,6238,6240],{"id":6239},"pontos-fortes-do-vuex","Pontos fortes do Vuex",[12,6242,6243],{},"O Vuex tem uma estrutura muito bem definida, o que é uma vantagem real em equipes grandes. Quando todo mundo segue as mesmas regras (state só é alterado via mutations, lógica assíncrona vai em actions), o código fica previsível. É fácil auditar um bug rastreando qual mutation foi chamada, graças à integração com o Vue DevTools.",[12,6245,6246],{},"Além disso, a comunidade em torno do Vuex é imensa. Anos de produção geraram uma quantidade enorme de tutoriais, soluções no Stack Overflow e padrões documentados para praticamente qualquer cenário.",[1643,6248,6250],{"id":6249},"fricções-que-o-vuex-carrega","Fricções que o Vuex carrega",[12,6252,6253,6254,6257,6258,6261],{},"A principal crítica ao Vuex é o ",[19,6255,6256],{},"boilerplate",". Para uma simples atualização de dados, você precisa: definir o estado, criar uma mutation, criar uma action que chama essa mutation, e então dentro do componente fazer ",[135,6259,6260],{},"dispatch"," da action. São muitas camadas para uma operação que, conceitualmente, é simples.",[12,6263,6264],{},"Outro ponto é a experiência com TypeScript. O Vuex foi projetado na era do Vue 2, antes de TypeScript se tornar central no ecossistema. Embora seja possível tipar o Vuex, isso exige configurações extras e workarounds que nunca foram elegantes. A inferência de tipos automática simplesmente não está lá.",[12,6266,6267,6268,6275,6276,6278,6279,6281],{},"Por fim, há uma dúvida clássica que assombra desenvolvedores Vuex: ",[4986,6269,6270,6271,1207,6273,4025],{},"devo usar ",[135,6272,5077],{},[135,6274,6260],{}," ",[135,6277,5077],{}," chama mutations, ",[135,6280,6260],{}," chama actions. Parece simples, mas na prática isso gera confusão, especialmente para quem está aprendendo.",[27,6283],{},[30,6285,6287],{"id":6286},"pinia-o-futuro-do-gerenciamento-de-estado","Pinia: o futuro do gerenciamento de estado",[12,6289,6290],{},"O Pinia surgiu como um experimento em 2019, criado por Eduardo San Martin Morote, o mesmo membro do core team do Vue responsável pelo Vue Router. A ideia era reimaginar como um store Vue poderia funcionar com a Composition API em mente desde o primeiro dia.",[12,6292,6293,6294,6297],{},"O experimento deu tão certo que hoje o Pinia é a biblioteca de gerenciamento de estado ",[19,6295,6296],{},"oficialmente recomendada"," para Vue.js. A própria documentação do Vuex reconhece isso e orienta novos projetos a usarem Pinia. Evan You, criador do Vue, chegou a se referir ao Pinia como o que seria o Vuex 5.",[1643,6299,6301],{"id":6300},"a-arquitetura-do-pinia","A arquitetura do Pinia",[12,6303,6304],{},"O Pinia simplifica o modelo do Vuex eliminando as mutations. O ciclo de vida do estado agora tem apenas três conceitos:",[12,6306,6307,6309],{},[19,6308,5055],{}," continua sendo os dados da aplicação.",[12,6311,6312,6314],{},[19,6313,5061],{}," continuam sendo propriedades computadas derivadas do estado.",[12,6316,6317,6319],{},[19,6318,5073],{}," agora fazem tudo: lógica síncrona, assíncrona, e modificação direta do estado. Não existe mais a distinção entre mutation e action.",[12,6321,6322,6323,6326],{},"Além disso, o Pinia é ",[19,6324,6325],{},"modular por design",". Em vez de um único store global com módulos internos, você cria múltiplos stores independentes, um para cada domínio da sua aplicação.",[12,6328,6329],{},"Veja o equivalente do exemplo anterior, mas em Pinia:",[129,6331,6333],{"className":5084,"code":6332,"language":5086,"meta":75,"style":75},"import { defineStore } from 'pinia'\n\n\u002F\u002F defineStore recebe dois argumentos:\n\u002F\u002F 1. um ID único (string) que identifica o store na devtools e no sistema de reatividade\n\u002F\u002F 2. um objeto de configuração (ou uma função, como veremos no Setup Store)\n\u002F\u002F por convenção, o nome da função começa com \"use\" e termina com \"Store\"\nexport const useCarrinhoStore = defineStore('carrinho', {\n  \u002F\u002F state agora é uma função que retorna o objeto de dados\n  \u002F\u002F isso garante que cada instância da aplicação tenha seu próprio estado (importante para SSR)\n  state: () => ({\n    itens: []\n  }),\n\n  \u002F\u002F getters funcionam igual ao Vuex: recebem state e retornam um valor derivado\n  getters: {\n    totalItens: (state) => state.itens.length,\n    totalValor: (state) => state.itens.reduce((acc, item) => acc + item.preco, 0)\n  },\n\n  \u002F\u002F actions aqui fazem TUDO: lógica síncrona, assíncrona e modificação de estado\n  \u002F\u002F não existe mais a separação entre mutation (síncrono) e action (assíncrono)\n  \u002F\u002F dentro das actions, \"this\" se refere ao próprio store\n  actions: {\n    adicionar(produto) {\n      this.itens.push(produto) \u002F\u002F modificação direta, sem commit!\n    },\n    remover(produtoId) {\n      this.itens = this.itens.filter(item => item.id !== produtoId)\n    }\n  }\n})\n\n\u002F\u002F cada domínio da aplicação tem seu próprio store independente\n\u002F\u002F não precisamos de módulos aninhados como no Vuex\nexport const useUsuarioStore = defineStore('usuario', {\n  state: () => ({\n    dados: null\n  }),\n  actions: {\n    async buscar(userId) {\n      const response = await fetch(`\u002Fapi\u002Fusuarios\u002F${userId}`)\n      \u002F\u002F modificação direta do state dentro da action, sem intermediário\n      this.dados = await response.json()\n    }\n  }\n})\n",[135,6334,6335,6353,6357,6362,6367,6372,6377,6402,6407,6412,6426,6435,6443,6447,6452,6460,6487,6543,6547,6551,6556,6561,6566,6574,6586,6608,6612,6626,6665,6669,6673,6679,6683,6688,6693,6718,6732,6741,6749,6757,6772,6798,6803,6823,6827,6831],{"__ignoreMap":75},[138,6336,6337,6339,6341,6343,6345,6347,6349,6351],{"class":140,"line":141},[138,6338,3735],{"class":794},[138,6340,2812],{"class":809},[138,6342,3021],{"class":805},[138,6344,2823],{"class":809},[138,6346,3748],{"class":794},[138,6348,957],{"class":826},[138,6350,4975],{"class":830},[138,6352,2361],{"class":826},[138,6354,6355],{"class":140,"line":76},[138,6356,649],{"emptyLinePlaceholder":86},[138,6358,6359],{"class":140,"line":152},[138,6360,6361],{"class":911},"\u002F\u002F defineStore recebe dois argumentos:\n",[138,6363,6364],{"class":140,"line":158},[138,6365,6366],{"class":911},"\u002F\u002F 1. um ID único (string) que identifica o store na devtools e no sistema de reatividade\n",[138,6368,6369],{"class":140,"line":164},[138,6370,6371],{"class":911},"\u002F\u002F 2. um objeto de configuração (ou uma função, como veremos no Setup Store)\n",[138,6373,6374],{"class":140,"line":170},[138,6375,6376],{"class":911},"\u002F\u002F por convenção, o nome da função começa com \"use\" e termina com \"Store\"\n",[138,6378,6379,6381,6383,6386,6388,6390,6392,6394,6396,6398,6400],{"class":140,"line":176},[138,6380,795],{"class":794},[138,6382,3014],{"class":1073},[138,6384,6385],{"class":2521}," useCarrinhoStore",[138,6387,1146],{"class":1112},[138,6389,3021],{"class":801},[138,6391,806],{"class":805},[138,6393,834],{"class":826},[138,6395,5219],{"class":830},[138,6397,834],{"class":826},[138,6399,954],{"class":809},[138,6401,934],{"class":809},[138,6403,6404],{"class":140,"line":182},[138,6405,6406],{"class":911},"  \u002F\u002F state agora é uma função que retorna o objeto de dados\n",[138,6408,6409],{"class":140,"line":188},[138,6410,6411],{"class":911},"  \u002F\u002F isso garante que cada instância da aplicação tenha seu próprio estado (importante para SSR)\n",[138,6413,6414,6416,6418,6420,6422,6424],{"class":140,"line":194},[138,6415,5139],{"class":801},[138,6417,782],{"class":809},[138,6419,3034],{"class":809},[138,6421,1074],{"class":1073},[138,6423,1084],{"class":805},[138,6425,810],{"class":809},[138,6427,6428,6431,6433],{"class":140,"line":199},[138,6429,6430],{"class":815},"    itens",[138,6432,782],{"class":809},[138,6434,5164],{"class":805},[138,6436,6437,6439,6441],{"class":140,"line":204},[138,6438,2863],{"class":809},[138,6440,1049],{"class":805},[138,6442,837],{"class":809},[138,6444,6445],{"class":140,"line":209},[138,6446,649],{"emptyLinePlaceholder":86},[138,6448,6449],{"class":140,"line":215},[138,6450,6451],{"class":911},"  \u002F\u002F getters funcionam igual ao Vuex: recebem state e retornam um valor derivado\n",[138,6453,6454,6456,6458],{"class":140,"line":221},[138,6455,5187],{"class":815},[138,6457,782],{"class":809},[138,6459,934],{"class":809},[138,6461,6462,6464,6466,6468,6470,6472,6474,6476,6478,6480,6482,6485],{"class":140,"line":227},[138,6463,5197],{"class":801},[138,6465,782],{"class":809},[138,6467,1084],{"class":809},[138,6469,5202],{"class":1045},[138,6471,1049],{"class":809},[138,6473,1074],{"class":1073},[138,6475,5214],{"class":805},[138,6477,363],{"class":809},[138,6479,5979],{"class":805},[138,6481,363],{"class":809},[138,6483,6484],{"class":2521},"length",[138,6486,837],{"class":809},[138,6488,6489,6491,6493,6495,6497,6499,6501,6503,6505,6507,6509,6511,6513,6515,6517,6519,6521,6523,6525,6528,6531,6533,6535,6537,6539,6541],{"class":140,"line":233},[138,6490,5234],{"class":801},[138,6492,782],{"class":809},[138,6494,1084],{"class":809},[138,6496,5202],{"class":1045},[138,6498,1049],{"class":809},[138,6500,1074],{"class":1073},[138,6502,5214],{"class":805},[138,6504,363],{"class":809},[138,6506,5979],{"class":805},[138,6508,363],{"class":809},[138,6510,5262],{"class":801},[138,6512,806],{"class":805},[138,6514,806],{"class":809},[138,6516,5269],{"class":1045},[138,6518,954],{"class":809},[138,6520,5274],{"class":1045},[138,6522,1049],{"class":809},[138,6524,1074],{"class":1073},[138,6526,6527],{"class":805}," acc ",[138,6529,6530],{"class":1112},"+",[138,6532,5274],{"class":805},[138,6534,363],{"class":809},[138,6536,5290],{"class":805},[138,6538,954],{"class":809},[138,6540,5296],{"class":5295},[138,6542,872],{"class":805},[138,6544,6545],{"class":140,"line":239},[138,6546,972],{"class":809},[138,6548,6549],{"class":140,"line":245},[138,6550,649],{"emptyLinePlaceholder":86},[138,6552,6553],{"class":140,"line":251},[138,6554,6555],{"class":911},"  \u002F\u002F actions aqui fazem TUDO: lógica síncrona, assíncrona e modificação de estado\n",[138,6557,6558],{"class":140,"line":257},[138,6559,6560],{"class":911},"  \u002F\u002F não existe mais a separação entre mutation (síncrono) e action (assíncrono)\n",[138,6562,6563],{"class":140,"line":263},[138,6564,6565],{"class":911},"  \u002F\u002F dentro das actions, \"this\" se refere ao próprio store\n",[138,6567,6568,6570,6572],{"class":140,"line":269},[138,6569,5504],{"class":815},[138,6571,782],{"class":809},[138,6573,934],{"class":809},[138,6575,6576,6578,6580,6582,6584],{"class":140,"line":275},[138,6577,6018],{"class":5196},[138,6579,806],{"class":809},[138,6581,5407],{"class":1045},[138,6583,1049],{"class":809},[138,6585,934],{"class":809},[138,6587,6588,6591,6593,6595,6597,6599,6601,6603,6605],{"class":140,"line":281},[138,6589,6590],{"class":2041},"      this",[138,6592,363],{"class":809},[138,6594,5979],{"class":805},[138,6596,363],{"class":809},[138,6598,5402],{"class":801},[138,6600,806],{"class":815},[138,6602,5407],{"class":805},[138,6604,1109],{"class":815},[138,6606,6607],{"class":911},"\u002F\u002F modificação direta, sem commit!\n",[138,6609,6610],{"class":140,"line":287},[138,6611,5229],{"class":809},[138,6613,6614,6617,6619,6622,6624],{"class":140,"line":293},[138,6615,6616],{"class":5196},"    remover",[138,6618,806],{"class":809},[138,6620,6621],{"class":1045},"produtoId",[138,6623,1049],{"class":809},[138,6625,934],{"class":809},[138,6627,6628,6630,6632,6634,6636,6639,6641,6643,6645,6647,6649,6651,6653,6655,6657,6659,6661,6663],{"class":140,"line":299},[138,6629,6590],{"class":2041},[138,6631,363],{"class":809},[138,6633,5979],{"class":805},[138,6635,1146],{"class":1112},[138,6637,6638],{"class":2041}," this",[138,6640,363],{"class":809},[138,6642,5979],{"class":805},[138,6644,363],{"class":809},[138,6646,5452],{"class":801},[138,6648,806],{"class":815},[138,6650,5457],{"class":1045},[138,6652,1074],{"class":1073},[138,6654,5274],{"class":805},[138,6656,363],{"class":809},[138,6658,5466],{"class":805},[138,6660,1124],{"class":1112},[138,6662,5427],{"class":805},[138,6664,872],{"class":815},[138,6666,6667],{"class":140,"line":305},[138,6668,1179],{"class":809},[138,6670,6671],{"class":140,"line":311},[138,6672,1184],{"class":809},[138,6674,6675,6677],{"class":140,"line":317},[138,6676,869],{"class":809},[138,6678,872],{"class":805},[138,6680,6681],{"class":140,"line":323},[138,6682,649],{"emptyLinePlaceholder":86},[138,6684,6685],{"class":140,"line":329},[138,6686,6687],{"class":911},"\u002F\u002F cada domínio da aplicação tem seu próprio store independente\n",[138,6689,6690],{"class":140,"line":335},[138,6691,6692],{"class":911},"\u002F\u002F não precisamos de módulos aninhados como no Vuex\n",[138,6694,6695,6697,6699,6702,6704,6706,6708,6710,6712,6714,6716],{"class":140,"line":341},[138,6696,795],{"class":794},[138,6698,3014],{"class":1073},[138,6700,6701],{"class":2521}," useUsuarioStore",[138,6703,1146],{"class":1112},[138,6705,3021],{"class":801},[138,6707,806],{"class":805},[138,6709,834],{"class":826},[138,6711,5362],{"class":830},[138,6713,834],{"class":826},[138,6715,954],{"class":809},[138,6717,934],{"class":809},[138,6719,6720,6722,6724,6726,6728,6730],{"class":140,"line":347},[138,6721,5139],{"class":801},[138,6723,782],{"class":809},[138,6725,3034],{"class":809},[138,6727,1074],{"class":1073},[138,6729,1084],{"class":805},[138,6731,810],{"class":809},[138,6733,6734,6737,6739],{"class":140,"line":353},[138,6735,6736],{"class":815},"    dados",[138,6738,782],{"class":809},[138,6740,3186],{"class":2041},[138,6742,6743,6745,6747],{"class":140,"line":622},[138,6744,2863],{"class":809},[138,6746,1049],{"class":805},[138,6748,837],{"class":809},[138,6750,6751,6753,6755],{"class":140,"line":628},[138,6752,5504],{"class":815},[138,6754,782],{"class":809},[138,6756,934],{"class":809},[138,6758,6759,6761,6764,6766,6768,6770],{"class":140,"line":634},[138,6760,5513],{"class":1073},[138,6762,6763],{"class":5196}," buscar",[138,6765,806],{"class":809},[138,6767,5558],{"class":1045},[138,6769,1049],{"class":809},[138,6771,934],{"class":809},[138,6773,6774,6776,6778,6780,6782,6784,6786,6788,6790,6792,6794,6796],{"class":140,"line":640},[138,6775,5536],{"class":1073},[138,6777,5539],{"class":2521},[138,6779,1146],{"class":1112},[138,6781,2828],{"class":794},[138,6783,5546],{"class":801},[138,6785,806],{"class":815},[138,6787,3791],{"class":826},[138,6789,5553],{"class":830},[138,6791,3797],{"class":826},[138,6793,5558],{"class":805},[138,6795,3803],{"class":826},[138,6797,872],{"class":815},[138,6799,6800],{"class":140,"line":646},[138,6801,6802],{"class":911},"      \u002F\u002F modificação direta do state dentro da action, sem intermediário\n",[138,6804,6805,6807,6809,6811,6813,6815,6817,6819,6821],{"class":140,"line":652},[138,6806,6590],{"class":2041},[138,6808,363],{"class":809},[138,6810,3132],{"class":805},[138,6812,1146],{"class":1112},[138,6814,2828],{"class":794},[138,6816,5539],{"class":805},[138,6818,363],{"class":809},[138,6820,2030],{"class":801},[138,6822,2796],{"class":815},[138,6824,6825],{"class":140,"line":657},[138,6826,1179],{"class":809},[138,6828,6829],{"class":140,"line":663},[138,6830,1184],{"class":809},[138,6832,6833,6835],{"class":140,"line":668},[138,6834,869],{"class":809},[138,6836,872],{"class":805},[12,6838,6839,6840,782],{},"E consumindo dentro de um componente com ",[135,6841,6842],{},"\u003Cscript setup>",[129,6844,6846],{"className":1407,"code":6845,"language":1409,"meta":75,"style":75},"\u003Cscript setup>\nimport { useCarrinhoStore } from '@\u002Fstores\u002Fcarrinho'\nimport { useUsuarioStore } from '@\u002Fstores\u002Fusuario'\n\n\u002F\u002F chamar o composable retorna a instância reativa do store\n\u002F\u002F sem mapState, sem mapGetters, sem mapActions\n\u002F\u002F state, getters e actions ficam acessíveis diretamente no objeto\nconst carrinho = useCarrinhoStore()\nconst usuario = useUsuarioStore()\n\n\u002F\u002F chamar uma action é tão simples quanto chamar um método normal\ncarrinho.adicionar({ id: 1, nome: 'Produto', preco: 99.90 })\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003C!-- getters acessados diretamente, sem nenhum helper -->\n    \u003Cp>Itens no carrinho: {{ carrinho.totalItens }}\u003C\u002Fp>\n    \u003Cp>Total: R$ {{ carrinho.totalValor.toFixed(2) }}\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[135,6847,6848,6858,6877,6896,6900,6905,6910,6915,6928,6940,6944,6949,6998,7006,7010,7018,7026,7031,7048,7065,7073],{"__ignoreMap":75},[138,6849,6850,6852,6854,6856],{"class":140,"line":141},[138,6851,1416],{"class":809},[138,6853,1420],{"class":1419},[138,6855,1424],{"class":1423},[138,6857,1440],{"class":809},[138,6859,6860,6862,6864,6866,6868,6870,6872,6875],{"class":140,"line":76},[138,6861,3735],{"class":794},[138,6863,2812],{"class":809},[138,6865,6385],{"class":805},[138,6867,2823],{"class":809},[138,6869,3748],{"class":794},[138,6871,957],{"class":826},[138,6873,6874],{"class":830},"@\u002Fstores\u002Fcarrinho",[138,6876,2361],{"class":826},[138,6878,6879,6881,6883,6885,6887,6889,6891,6894],{"class":140,"line":152},[138,6880,3735],{"class":794},[138,6882,2812],{"class":809},[138,6884,6701],{"class":805},[138,6886,2823],{"class":809},[138,6888,3748],{"class":794},[138,6890,957],{"class":826},[138,6892,6893],{"class":830},"@\u002Fstores\u002Fusuario",[138,6895,2361],{"class":826},[138,6897,6898],{"class":140,"line":158},[138,6899,649],{"emptyLinePlaceholder":86},[138,6901,6902],{"class":140,"line":164},[138,6903,6904],{"class":911},"\u002F\u002F chamar o composable retorna a instância reativa do store\n",[138,6906,6907],{"class":140,"line":170},[138,6908,6909],{"class":911},"\u002F\u002F sem mapState, sem mapGetters, sem mapActions\n",[138,6911,6912],{"class":140,"line":176},[138,6913,6914],{"class":911},"\u002F\u002F state, getters e actions ficam acessíveis diretamente no objeto\n",[138,6916,6917,6919,6922,6924,6926],{"class":140,"line":182},[138,6918,2518],{"class":1073},[138,6920,6921],{"class":2521}," carrinho",[138,6923,1146],{"class":1112},[138,6925,6385],{"class":801},[138,6927,2796],{"class":805},[138,6929,6930,6932,6934,6936,6938],{"class":140,"line":188},[138,6931,2518],{"class":1073},[138,6933,5348],{"class":2521},[138,6935,1146],{"class":1112},[138,6937,6701],{"class":801},[138,6939,2796],{"class":805},[138,6941,6942],{"class":140,"line":194},[138,6943,649],{"emptyLinePlaceholder":86},[138,6945,6946],{"class":140,"line":199},[138,6947,6948],{"class":911},"\u002F\u002F chamar uma action é tão simples quanto chamar um método normal\n",[138,6950,6951,6953,6955,6958,6960,6962,6965,6967,6970,6972,6975,6977,6979,6982,6984,6986,6989,6991,6994,6996],{"class":140,"line":204},[138,6952,5219],{"class":805},[138,6954,363],{"class":809},[138,6956,6957],{"class":801},"adicionar",[138,6959,806],{"class":805},[138,6961,3284],{"class":809},[138,6963,6964],{"class":815}," id",[138,6966,782],{"class":809},[138,6968,6969],{"class":5295}," 1",[138,6971,954],{"class":809},[138,6973,6974],{"class":815}," nome",[138,6976,782],{"class":809},[138,6978,957],{"class":826},[138,6980,6981],{"class":830},"Produto",[138,6983,834],{"class":826},[138,6985,954],{"class":809},[138,6987,6988],{"class":815}," preco",[138,6990,782],{"class":809},[138,6992,6993],{"class":5295}," 99.90",[138,6995,2823],{"class":809},[138,6997,872],{"class":805},[138,6999,7000,7002,7004],{"class":140,"line":209},[138,7001,1505],{"class":809},[138,7003,1420],{"class":1419},[138,7005,1440],{"class":809},[138,7007,7008],{"class":140,"line":215},[138,7009,649],{"emptyLinePlaceholder":86},[138,7011,7012,7014,7016],{"class":140,"line":221},[138,7013,1416],{"class":809},[138,7015,3560],{"class":1419},[138,7017,1440],{"class":809},[138,7019,7020,7022,7024],{"class":140,"line":227},[138,7021,3567],{"class":809},[138,7023,4369],{"class":1419},[138,7025,1440],{"class":809},[138,7027,7028],{"class":140,"line":233},[138,7029,7030],{"class":911},"    \u003C!-- getters acessados diretamente, sem nenhum helper -->\n",[138,7032,7033,7035,7037,7039,7042,7044,7046],{"class":140,"line":239},[138,7034,3577],{"class":809},[138,7036,12],{"class":1419},[138,7038,3065],{"class":809},[138,7040,7041],{"class":805},"Itens no carrinho: {{ carrinho.totalItens }}",[138,7043,1505],{"class":809},[138,7045,12],{"class":1419},[138,7047,1440],{"class":809},[138,7049,7050,7052,7054,7056,7059,7061,7063],{"class":140,"line":245},[138,7051,3577],{"class":809},[138,7053,12],{"class":1419},[138,7055,3065],{"class":809},[138,7057,7058],{"class":805},"Total: R$ {{ carrinho.totalValor.toFixed(2) }}",[138,7060,1505],{"class":809},[138,7062,12],{"class":1419},[138,7064,1440],{"class":809},[138,7066,7067,7069,7071],{"class":140,"line":251},[138,7068,3625],{"class":809},[138,7070,4369],{"class":1419},[138,7072,1440],{"class":809},[138,7074,7075,7077,7079],{"class":140,"line":257},[138,7076,1505],{"class":809},[138,7078,3560],{"class":1419},[138,7080,1440],{"class":809},[12,7082,7083,7084,370,7086,1207,7088,7090],{},"Perceba o quanto o código fica mais limpo. Não há necessidade de ",[135,7085,5742],{},[135,7087,5780],{},[135,7089,5831],{},". Você importa o store, chama a função composable, e acessa estado, getters e actions diretamente.",[1643,7092,7094],{"id":7093},"o-estilo-setup-store-máxima-flexibilidade","O estilo Setup Store: máxima flexibilidade",[12,7096,7097,7098,7101,7102,7105,7106,370,7109,7112,7113,7117],{},"Além da API de objeto (parecida com o estilo Options do Vue), o Pinia também oferece um estilo baseado na Composition API chamado ",[19,7099,7100],{},"Setup Store",". Ele funciona exatamente como um ",[135,7103,7104],{},"setup()",", usando ",[135,7107,7108],{},"ref",[135,7110,7111],{},"computed"," e funções normais. Por baixo dos panos, o mecanismo que mantém o estado vivo entre chamadas é o mesmo de uma ",[105,7114,7116],{"href":5027,"rel":7115},[109],"closure",": o ambiente criado quando o store é inicializado persiste enquanto a aplicação estiver rodando, e as funções expostas continuam com acesso a ele.",[129,7119,7121],{"className":5084,"code":7120,"language":5086,"meta":75,"style":75},"import { ref, computed } from 'vue'\nimport { defineStore } from 'pinia'\n\n\u002F\u002F no Setup Store, o segundo argumento é uma função (como o setup() de um componente)\n\u002F\u002F em vez de um objeto com state\u002Fgetters\u002Factions\nexport const useCarrinhoStore = defineStore('carrinho', () => {\n  \u002F\u002F ref() => equivale ao \"state\" no Options Store\n  const itens = ref([])\n\n  \u002F\u002F computed() => equivale aos \"getters\"\n  const totalItens = computed(() => itens.value.length)\n  const totalValor = computed(() =>\n    itens.value.reduce((acc, item) => acc + item.preco, 0)\n  )\n\n  \u002F\u002F funções normais => equivalem às \"actions\"\n  function adicionar(produto) {\n    itens.value.push(produto)\n  }\n\n  function remover(produtoId) {\n    itens.value = itens.value.filter(item => item.id !== produtoId)\n  }\n\n  \u002F\u002F tudo que for retornado aqui fica acessível publicamente no store\n  \u002F\u002F o que não for retornado fica encapsulado (útil para lógica interna privada)\n  return { itens, totalItens, totalValor, adicionar, remover }\n})\n",[135,7122,7123,7145,7163,7167,7172,7177,7205,7210,7223,7227,7232,7261,7279,7321,7325,7329,7334,7349,7367,7371,7375,7390,7428,7432,7436,7441,7446,7472],{"__ignoreMap":75},[138,7124,7125,7127,7129,7131,7133,7135,7137,7139,7141,7143],{"class":140,"line":141},[138,7126,3735],{"class":794},[138,7128,2812],{"class":809},[138,7130,3050],{"class":805},[138,7132,954],{"class":809},[138,7134,3088],{"class":805},[138,7136,2823],{"class":809},[138,7138,3748],{"class":794},[138,7140,957],{"class":826},[138,7142,1409],{"class":830},[138,7144,2361],{"class":826},[138,7146,7147,7149,7151,7153,7155,7157,7159,7161],{"class":140,"line":76},[138,7148,3735],{"class":794},[138,7150,2812],{"class":809},[138,7152,3021],{"class":805},[138,7154,2823],{"class":809},[138,7156,3748],{"class":794},[138,7158,957],{"class":826},[138,7160,4975],{"class":830},[138,7162,2361],{"class":826},[138,7164,7165],{"class":140,"line":152},[138,7166,649],{"emptyLinePlaceholder":86},[138,7168,7169],{"class":140,"line":158},[138,7170,7171],{"class":911},"\u002F\u002F no Setup Store, o segundo argumento é uma função (como o setup() de um componente)\n",[138,7173,7174],{"class":140,"line":164},[138,7175,7176],{"class":911},"\u002F\u002F em vez de um objeto com state\u002Fgetters\u002Factions\n",[138,7178,7179,7181,7183,7185,7187,7189,7191,7193,7195,7197,7199,7201,7203],{"class":140,"line":170},[138,7180,795],{"class":794},[138,7182,3014],{"class":1073},[138,7184,6385],{"class":2521},[138,7186,1146],{"class":1112},[138,7188,3021],{"class":801},[138,7190,806],{"class":805},[138,7192,834],{"class":826},[138,7194,5219],{"class":830},[138,7196,834],{"class":826},[138,7198,954],{"class":809},[138,7200,3034],{"class":809},[138,7202,1074],{"class":1073},[138,7204,934],{"class":809},[138,7206,7207],{"class":140,"line":176},[138,7208,7209],{"class":911},"  \u002F\u002F ref() => equivale ao \"state\" no Options Store\n",[138,7211,7212,7214,7216,7218,7220],{"class":140,"line":182},[138,7213,2607],{"class":1073},[138,7215,5936],{"class":2521},[138,7217,1146],{"class":1112},[138,7219,3050],{"class":801},[138,7221,7222],{"class":815},"([])\n",[138,7224,7225],{"class":140,"line":188},[138,7226,649],{"emptyLinePlaceholder":86},[138,7228,7229],{"class":140,"line":194},[138,7230,7231],{"class":911},"  \u002F\u002F computed() => equivale aos \"getters\"\n",[138,7233,7234,7236,7239,7241,7243,7245,7247,7249,7251,7253,7255,7257,7259],{"class":140,"line":199},[138,7235,2607],{"class":1073},[138,7237,7238],{"class":2521}," totalItens",[138,7240,1146],{"class":1112},[138,7242,3088],{"class":801},[138,7244,806],{"class":815},[138,7246,3093],{"class":809},[138,7248,1074],{"class":1073},[138,7250,5936],{"class":805},[138,7252,363],{"class":809},[138,7254,3102],{"class":805},[138,7256,363],{"class":809},[138,7258,6484],{"class":2521},[138,7260,872],{"class":815},[138,7262,7263,7265,7268,7270,7272,7274,7276],{"class":140,"line":204},[138,7264,2607],{"class":1073},[138,7266,7267],{"class":2521}," totalValor",[138,7269,1146],{"class":1112},[138,7271,3088],{"class":801},[138,7273,806],{"class":815},[138,7275,3093],{"class":809},[138,7277,7278],{"class":1073}," =>\n",[138,7280,7281,7283,7285,7287,7289,7291,7293,7295,7297,7299,7301,7303,7305,7307,7309,7311,7313,7315,7317,7319],{"class":140,"line":209},[138,7282,6430],{"class":805},[138,7284,363],{"class":809},[138,7286,3102],{"class":805},[138,7288,363],{"class":809},[138,7290,5262],{"class":801},[138,7292,806],{"class":815},[138,7294,806],{"class":809},[138,7296,5269],{"class":1045},[138,7298,954],{"class":809},[138,7300,5274],{"class":1045},[138,7302,1049],{"class":809},[138,7304,1074],{"class":1073},[138,7306,5281],{"class":805},[138,7308,1155],{"class":1112},[138,7310,5274],{"class":805},[138,7312,363],{"class":809},[138,7314,5290],{"class":805},[138,7316,954],{"class":809},[138,7318,5296],{"class":5295},[138,7320,872],{"class":815},[138,7322,7323],{"class":140,"line":215},[138,7324,2456],{"class":815},[138,7326,7327],{"class":140,"line":221},[138,7328,649],{"emptyLinePlaceholder":86},[138,7330,7331],{"class":140,"line":227},[138,7332,7333],{"class":911},"  \u002F\u002F funções normais => equivalem às \"actions\"\n",[138,7335,7336,7338,7341,7343,7345,7347],{"class":140,"line":233},[138,7337,3124],{"class":1073},[138,7339,7340],{"class":801}," adicionar",[138,7342,806],{"class":809},[138,7344,5407],{"class":1045},[138,7346,1049],{"class":809},[138,7348,934],{"class":809},[138,7350,7351,7353,7355,7357,7359,7361,7363,7365],{"class":140,"line":239},[138,7352,6430],{"class":805},[138,7354,363],{"class":809},[138,7356,3102],{"class":805},[138,7358,363],{"class":809},[138,7360,5402],{"class":801},[138,7362,806],{"class":815},[138,7364,5407],{"class":805},[138,7366,872],{"class":815},[138,7368,7369],{"class":140,"line":245},[138,7370,1184],{"class":809},[138,7372,7373],{"class":140,"line":251},[138,7374,649],{"emptyLinePlaceholder":86},[138,7376,7377,7379,7382,7384,7386,7388],{"class":140,"line":257},[138,7378,3124],{"class":1073},[138,7380,7381],{"class":801}," remover",[138,7383,806],{"class":809},[138,7385,6621],{"class":1045},[138,7387,1049],{"class":809},[138,7389,934],{"class":809},[138,7391,7392,7394,7396,7398,7400,7402,7404,7406,7408,7410,7412,7414,7416,7418,7420,7422,7424,7426],{"class":140,"line":263},[138,7393,6430],{"class":805},[138,7395,363],{"class":809},[138,7397,3102],{"class":805},[138,7399,1146],{"class":1112},[138,7401,5936],{"class":805},[138,7403,363],{"class":809},[138,7405,3102],{"class":805},[138,7407,363],{"class":809},[138,7409,5452],{"class":801},[138,7411,806],{"class":815},[138,7413,5457],{"class":1045},[138,7415,1074],{"class":1073},[138,7417,5274],{"class":805},[138,7419,363],{"class":809},[138,7421,5466],{"class":805},[138,7423,1124],{"class":1112},[138,7425,5427],{"class":805},[138,7427,872],{"class":815},[138,7429,7430],{"class":140,"line":269},[138,7431,1184],{"class":809},[138,7433,7434],{"class":140,"line":275},[138,7435,649],{"emptyLinePlaceholder":86},[138,7437,7438],{"class":140,"line":281},[138,7439,7440],{"class":911},"  \u002F\u002F tudo que for retornado aqui fica acessível publicamente no store\n",[138,7442,7443],{"class":140,"line":287},[138,7444,7445],{"class":911},"  \u002F\u002F o que não for retornado fica encapsulado (útil para lógica interna privada)\n",[138,7447,7448,7450,7452,7454,7456,7458,7460,7462,7464,7466,7468,7470],{"class":140,"line":293},[138,7449,3199],{"class":794},[138,7451,2812],{"class":809},[138,7453,5936],{"class":805},[138,7455,954],{"class":809},[138,7457,7238],{"class":805},[138,7459,954],{"class":809},[138,7461,7267],{"class":805},[138,7463,954],{"class":809},[138,7465,7340],{"class":805},[138,7467,954],{"class":809},[138,7469,7381],{"class":805},[138,7471,1015],{"class":809},[138,7473,7474,7476],{"class":140,"line":299},[138,7475,869],{"class":809},[138,7477,872],{"class":805},[12,7479,7480,7481,7484,7485,7489],{},"Esse estilo é especialmente poderoso porque permite reutilizar composables dentro do store, compartilhar lógica entre stores com facilidade, e escrever um código que se parece exatamente com um composable Vue comum. Se você quiser ver um exemplo real do Setup Store em um projeto Nuxt 3, escrevi sobre como a ",[135,7482,7483],{},"useEditorStore"," foi estruturada no ",[105,7486,7488],{"href":4921,"rel":7487},[109],"VisitCardGenerator",", um gerador de cartões de visita em PDF construído do zero com Nuxt 3 e Pinia.",[1643,7491,7493],{"id":7492},"typescript-nativo","TypeScript nativo",[12,7495,7496],{},"Uma das maiores vantagens do Pinia é o suporte a TypeScript sem nenhuma configuração adicional. Os tipos são inferidos automaticamente a partir do estado definido. Você não precisa criar interfaces manualmente para o store, nem usar plugins ou hacks para ter autocomplete funcionando:",[129,7498,7500],{"className":785,"code":7499,"language":787,"meta":75,"style":75},"import { defineStore } from 'pinia'\n\n\u002F\u002F interface TypeScript definindo o formato de um produto\n\u002F\u002F o Pinia vai usar isso para inferir os tipos automaticamente\ninterface Produto {\n  id: number\n  nome: string\n  preco: number\n}\n\nexport const useCarrinhoStore = defineStore('carrinho', {\n  state: () => ({\n    \u002F\u002F \"as Produto[]\" diz ao TypeScript que este array só aceita objetos do tipo Produto\n    \u002F\u002F a partir daqui, toda a store tem inferência de tipos automática\n    itens: [] as Produto[]\n  }),\n  getters: {\n    \u002F\u002F a anotação \": number\" é necessária aqui por conta de uma limitação do TS\n    \u002F\u002F quando o getter referencia outro getter via \"this\" — neste caso é boa prática anotar\n    totalValor: (state): number =>\n      state.itens.reduce((acc, item) => acc + item.preco, 0)\n  },\n  actions: {\n    \u002F\u002F TypeScript já sabe que \"produto\" deve ser do tipo Produto\n    \u002F\u002F se você tentar passar um objeto sem o campo \"preco\", o editor vai reclamar\n    adicionar(produto: Produto) {\n      this.itens.push(produto)\n    }\n  }\n})\n",[135,7501,7502,7520,7524,7529,7534,7544,7555,7565,7574,7578,7582,7606,7620,7625,7630,7646,7654,7662,7667,7672,7691,7733,7737,7745,7750,7755,7771,7789,7793,7797],{"__ignoreMap":75},[138,7503,7504,7506,7508,7510,7512,7514,7516,7518],{"class":140,"line":141},[138,7505,3735],{"class":794},[138,7507,2812],{"class":809},[138,7509,3021],{"class":805},[138,7511,2823],{"class":809},[138,7513,3748],{"class":794},[138,7515,957],{"class":826},[138,7517,4975],{"class":830},[138,7519,2361],{"class":826},[138,7521,7522],{"class":140,"line":76},[138,7523,649],{"emptyLinePlaceholder":86},[138,7525,7526],{"class":140,"line":152},[138,7527,7528],{"class":911},"\u002F\u002F interface TypeScript definindo o formato de um produto\n",[138,7530,7531],{"class":140,"line":158},[138,7532,7533],{"class":911},"\u002F\u002F o Pinia vai usar isso para inferir os tipos automaticamente\n",[138,7535,7536,7539,7542],{"class":140,"line":164},[138,7537,7538],{"class":1073},"interface",[138,7540,7541],{"class":1556}," Produto",[138,7543,934],{"class":809},[138,7545,7546,7550,7552],{"class":140,"line":170},[138,7547,7549],{"class":7548},"sTbKH","  id",[138,7551,782],{"class":1112},[138,7553,7554],{"class":3061}," number\n",[138,7556,7557,7560,7562],{"class":140,"line":176},[138,7558,7559],{"class":7548},"  nome",[138,7561,782],{"class":1112},[138,7563,7564],{"class":3061}," string\n",[138,7566,7567,7570,7572],{"class":140,"line":182},[138,7568,7569],{"class":7548},"  preco",[138,7571,782],{"class":1112},[138,7573,7554],{"class":3061},[138,7575,7576],{"class":140,"line":188},[138,7577,2082],{"class":809},[138,7579,7580],{"class":140,"line":194},[138,7581,649],{"emptyLinePlaceholder":86},[138,7583,7584,7586,7588,7590,7592,7594,7596,7598,7600,7602,7604],{"class":140,"line":199},[138,7585,795],{"class":794},[138,7587,3014],{"class":1073},[138,7589,6385],{"class":2521},[138,7591,1146],{"class":1112},[138,7593,3021],{"class":801},[138,7595,806],{"class":805},[138,7597,834],{"class":826},[138,7599,5219],{"class":830},[138,7601,834],{"class":826},[138,7603,954],{"class":809},[138,7605,934],{"class":809},[138,7607,7608,7610,7612,7614,7616,7618],{"class":140,"line":204},[138,7609,5139],{"class":801},[138,7611,782],{"class":809},[138,7613,3034],{"class":809},[138,7615,1074],{"class":1073},[138,7617,1084],{"class":805},[138,7619,810],{"class":809},[138,7621,7622],{"class":140,"line":209},[138,7623,7624],{"class":911},"    \u002F\u002F \"as Produto[]\" diz ao TypeScript que este array só aceita objetos do tipo Produto\n",[138,7626,7627],{"class":140,"line":215},[138,7628,7629],{"class":911},"    \u002F\u002F a partir daqui, toda a store tem inferência de tipos automática\n",[138,7631,7632,7634,7636,7638,7641,7643],{"class":140,"line":221},[138,7633,6430],{"class":815},[138,7635,782],{"class":809},[138,7637,5941],{"class":805},[138,7639,7640],{"class":794},"as",[138,7642,7541],{"class":1556},[138,7644,7645],{"class":805},"[]\n",[138,7647,7648,7650,7652],{"class":140,"line":227},[138,7649,2863],{"class":809},[138,7651,1049],{"class":805},[138,7653,837],{"class":809},[138,7655,7656,7658,7660],{"class":140,"line":233},[138,7657,5187],{"class":815},[138,7659,782],{"class":809},[138,7661,934],{"class":809},[138,7663,7664],{"class":140,"line":239},[138,7665,7666],{"class":911},"    \u002F\u002F a anotação \": number\" é necessária aqui por conta de uma limitação do TS\n",[138,7668,7669],{"class":140,"line":245},[138,7670,7671],{"class":911},"    \u002F\u002F quando o getter referencia outro getter via \"this\" — neste caso é boa prática anotar\n",[138,7673,7674,7676,7678,7680,7682,7684,7686,7689],{"class":140,"line":251},[138,7675,5234],{"class":801},[138,7677,782],{"class":809},[138,7679,1084],{"class":809},[138,7681,5202],{"class":1045},[138,7683,1049],{"class":809},[138,7685,782],{"class":1112},[138,7687,7688],{"class":3061}," number",[138,7690,7278],{"class":1073},[138,7692,7693,7695,7697,7699,7701,7703,7705,7707,7709,7711,7713,7715,7717,7719,7721,7723,7725,7727,7729,7731],{"class":140,"line":257},[138,7694,5357],{"class":805},[138,7696,363],{"class":809},[138,7698,5979],{"class":805},[138,7700,363],{"class":809},[138,7702,5262],{"class":801},[138,7704,806],{"class":805},[138,7706,806],{"class":809},[138,7708,5269],{"class":1045},[138,7710,954],{"class":809},[138,7712,5274],{"class":1045},[138,7714,1049],{"class":809},[138,7716,1074],{"class":1073},[138,7718,6527],{"class":805},[138,7720,6530],{"class":1112},[138,7722,5274],{"class":805},[138,7724,363],{"class":809},[138,7726,5290],{"class":805},[138,7728,954],{"class":809},[138,7730,5296],{"class":5295},[138,7732,872],{"class":805},[138,7734,7735],{"class":140,"line":263},[138,7736,972],{"class":809},[138,7738,7739,7741,7743],{"class":140,"line":269},[138,7740,5504],{"class":815},[138,7742,782],{"class":809},[138,7744,934],{"class":809},[138,7746,7747],{"class":140,"line":275},[138,7748,7749],{"class":911},"    \u002F\u002F TypeScript já sabe que \"produto\" deve ser do tipo Produto\n",[138,7751,7752],{"class":140,"line":281},[138,7753,7754],{"class":911},"    \u002F\u002F se você tentar passar um objeto sem o campo \"preco\", o editor vai reclamar\n",[138,7756,7757,7759,7761,7763,7765,7767,7769],{"class":140,"line":287},[138,7758,6018],{"class":5196},[138,7760,806],{"class":809},[138,7762,5407],{"class":1045},[138,7764,782],{"class":1112},[138,7766,7541],{"class":1556},[138,7768,1049],{"class":809},[138,7770,934],{"class":809},[138,7772,7773,7775,7777,7779,7781,7783,7785,7787],{"class":140,"line":293},[138,7774,6590],{"class":2041},[138,7776,363],{"class":809},[138,7778,5979],{"class":805},[138,7780,363],{"class":809},[138,7782,5402],{"class":801},[138,7784,806],{"class":815},[138,7786,5407],{"class":805},[138,7788,872],{"class":815},[138,7790,7791],{"class":140,"line":299},[138,7792,1179],{"class":809},[138,7794,7795],{"class":140,"line":305},[138,7796,1184],{"class":809},[138,7798,7799,7801],{"class":140,"line":311},[138,7800,869],{"class":809},[138,7802,872],{"class":805},[12,7804,7805,7806,7809,7810,7812,7813,7816,7817,7820,7821,7824,7825,7827],{},"A partir daqui, o TypeScript vai inferir que ",[135,7807,7808],{},"carrinho.itens"," é um array de ",[135,7811,6981],{},", que ",[135,7814,7815],{},"carrinho.totalValor"," é um ",[135,7818,7819],{},"number",", e que ",[135,7822,7823],{},"carrinho.adicionar"," espera um objeto do tipo ",[135,7826,6981],{},". Tudo isso sem configuração extra.",[1643,7829,7831],{"id":7830},"modificação-direta-de-estado","Modificação direta de estado",[12,7833,7834],{},"Uma funcionalidade que surpreende quem vem do Vuex é que no Pinia é possível modificar o estado diretamente fora de uma action, quando necessário:",[129,7836,7838],{"className":5084,"code":7837,"language":5086,"meta":75,"style":75},"const carrinho = useCarrinhoStore()\n\n\u002F\u002F forma 1: modificação direta de uma propriedade do state\n\u002F\u002F funciona, mas não é rastreada como uma única operação no DevTools\ncarrinho.itens = []\n\n\u002F\u002F forma 2: $patch com objeto — ideal para mudanças simples em uma ou mais propriedades\n\u002F\u002F aparece como uma única mutação no Vue DevTools\ncarrinho.$patch({ itens: [] })\n\n\u002F\u002F forma 3: $patch com função — ideal quando a nova mudança depende do estado atual\n\u002F\u002F recebe o state como argumento e você o modifica diretamente\ncarrinho.$patch((state) => {\n  state.itens = state.itens.filter(item => item.ativo)\n})\n",[135,7839,7840,7852,7856,7861,7866,7879,7883,7888,7893,7916,7920,7925,7930,7950,7985],{"__ignoreMap":75},[138,7841,7842,7844,7846,7848,7850],{"class":140,"line":141},[138,7843,2518],{"class":1073},[138,7845,6921],{"class":2521},[138,7847,1146],{"class":1112},[138,7849,6385],{"class":801},[138,7851,2796],{"class":805},[138,7853,7854],{"class":140,"line":76},[138,7855,649],{"emptyLinePlaceholder":86},[138,7857,7858],{"class":140,"line":152},[138,7859,7860],{"class":911},"\u002F\u002F forma 1: modificação direta de uma propriedade do state\n",[138,7862,7863],{"class":140,"line":158},[138,7864,7865],{"class":911},"\u002F\u002F funciona, mas não é rastreada como uma única operação no DevTools\n",[138,7867,7868,7870,7872,7875,7877],{"class":140,"line":164},[138,7869,5219],{"class":805},[138,7871,363],{"class":809},[138,7873,7874],{"class":805},"itens ",[138,7876,1430],{"class":1112},[138,7878,5164],{"class":805},[138,7880,7881],{"class":140,"line":170},[138,7882,649],{"emptyLinePlaceholder":86},[138,7884,7885],{"class":140,"line":176},[138,7886,7887],{"class":911},"\u002F\u002F forma 2: $patch com objeto — ideal para mudanças simples em uma ou mais propriedades\n",[138,7889,7890],{"class":140,"line":182},[138,7891,7892],{"class":911},"\u002F\u002F aparece como uma única mutação no Vue DevTools\n",[138,7894,7895,7897,7899,7902,7904,7906,7908,7910,7912,7914],{"class":140,"line":188},[138,7896,5219],{"class":805},[138,7898,363],{"class":809},[138,7900,7901],{"class":801},"$patch",[138,7903,806],{"class":805},[138,7905,3284],{"class":809},[138,7907,5936],{"class":815},[138,7909,782],{"class":809},[138,7911,5941],{"class":805},[138,7913,869],{"class":809},[138,7915,872],{"class":805},[138,7917,7918],{"class":140,"line":194},[138,7919,649],{"emptyLinePlaceholder":86},[138,7921,7922],{"class":140,"line":199},[138,7923,7924],{"class":911},"\u002F\u002F forma 3: $patch com função — ideal quando a nova mudança depende do estado atual\n",[138,7926,7927],{"class":140,"line":204},[138,7928,7929],{"class":911},"\u002F\u002F recebe o state como argumento e você o modifica diretamente\n",[138,7931,7932,7934,7936,7938,7940,7942,7944,7946,7948],{"class":140,"line":209},[138,7933,5219],{"class":805},[138,7935,363],{"class":809},[138,7937,7901],{"class":801},[138,7939,806],{"class":805},[138,7941,806],{"class":809},[138,7943,5202],{"class":1045},[138,7945,1049],{"class":809},[138,7947,1074],{"class":1073},[138,7949,934],{"class":809},[138,7951,7952,7954,7956,7958,7960,7962,7964,7966,7968,7970,7972,7974,7976,7978,7980,7983],{"class":140,"line":215},[138,7953,5139],{"class":805},[138,7955,363],{"class":809},[138,7957,5979],{"class":805},[138,7959,1146],{"class":1112},[138,7961,5214],{"class":805},[138,7963,363],{"class":809},[138,7965,5979],{"class":805},[138,7967,363],{"class":809},[138,7969,5452],{"class":801},[138,7971,806],{"class":815},[138,7973,5457],{"class":1045},[138,7975,1074],{"class":1073},[138,7977,5274],{"class":805},[138,7979,363],{"class":809},[138,7981,7982],{"class":805},"ativo",[138,7984,872],{"class":815},[138,7986,7987,7989],{"class":140,"line":221},[138,7988,869],{"class":809},[138,7990,872],{"class":805},[12,7992,7993,7994,7996],{},"O método ",[135,7995,7901],{}," é especialmente útil para aplicar múltiplas mudanças de forma atômica, sem precisar criar uma action específica para cada pequena atualização. Isso não significa que você deva abandonar as actions, elas continuam sendo o lugar ideal para lógica reutilizável e operações assíncronas, mas você tem a flexibilidade de agir diretamente quando faz sentido.",[1643,7998,8000],{"id":7999},"pinia-com-nuxt-3","Pinia com Nuxt 3",[12,8002,8003,8004,8007,8008,8012],{},"Para quem usa Nuxt (como é o caso da stack descrita neste blog), o Pinia é suporte oficial. O módulo ",[135,8005,8006],{},"@pinia\u002Fnuxt"," se integra perfeitamente com SSR, garantindo que o estado não vaze entre requisições de diferentes usuários, um problema real em aplicações server-side rendered. Você pode ver isso funcionando na prática no artigo ",[105,8009,8011],{"href":4921,"rel":8010},[109],"VisitCardGenerator: gerador de cartões de visita em PDF do zero com Nuxt 3",", onde o estado do editor fica inteiramente em uma store Pinia com getters computados, persistência em localStorage e reset de estado.",[129,8014,8016],{"className":785,"code":8015,"language":787,"meta":75,"style":75},"export default defineNuxtConfig({\n  \u002F\u002F adicionar o módulo aqui é tudo que você precisa fazer\n  \u002F\u002F o Pinia fica disponível globalmente, com suporte a SSR configurado automaticamente\n  modules: ['@pinia\u002Fnuxt']\n})\n",[135,8017,8018,8030,8035,8040,8057],{"__ignoreMap":75},[138,8019,8020,8022,8024,8026,8028],{"class":140,"line":141},[138,8021,795],{"class":794},[138,8023,798],{"class":794},[138,8025,802],{"class":801},[138,8027,806],{"class":805},[138,8029,810],{"class":809},[138,8031,8032],{"class":140,"line":76},[138,8033,8034],{"class":911},"  \u002F\u002F adicionar o módulo aqui é tudo que você precisa fazer\n",[138,8036,8037],{"class":140,"line":152},[138,8038,8039],{"class":911},"  \u002F\u002F o Pinia fica disponível globalmente, com suporte a SSR configurado automaticamente\n",[138,8041,8042,8044,8046,8048,8050,8052,8054],{"class":140,"line":158},[138,8043,6189],{"class":815},[138,8045,782],{"class":809},[138,8047,944],{"class":805},[138,8049,834],{"class":826},[138,8051,8006],{"class":830},[138,8053,834],{"class":826},[138,8055,8056],{"class":805},"]\n",[138,8058,8059,8061],{"class":140,"line":164},[138,8060,869],{"class":809},[138,8062,872],{"class":805},[12,8064,8065],{},"Com esse módulo, os stores do Pinia são automaticamente registrados e funcionam corretamente tanto no servidor quanto no cliente, sem nenhuma configuração adicional para hidratação de estado.",[27,8067],{},[30,8069,8071],{"id":8070},"comparação-direta-vuex-vs-pinia","Comparação direta: Vuex vs Pinia",[12,8073,8074],{},"Para deixar as diferenças ainda mais claras, veja uma comparação objetiva dos dois nos principais aspectos:",[1643,8076,8078],{"id":8077},"estrutura-conceitual","Estrutura conceitual",[1915,8080,8081,8093],{},[1918,8082,8083],{},[1921,8084,8085,8088,8091],{},[1924,8086,8087],{},"Conceito",[1924,8089,8090],{},"Vuex",[1924,8092,4819],{},[1931,8094,8095,8104,8112,8122,8131],{},[1921,8096,8097,8099,8102],{},[1936,8098,5055],{},[1936,8100,8101],{},"Sim",[1936,8103,8101],{},[1921,8105,8106,8108,8110],{},[1936,8107,5061],{},[1936,8109,8101],{},[1936,8111,8101],{},[1921,8113,8114,8116,8119],{},[1936,8115,5067],{},[1936,8117,8118],{},"Sim (obrigatório)",[1936,8120,8121],{},"Não existe",[1921,8123,8124,8126,8128],{},[1936,8125,5073],{},[1936,8127,8101],{},[1936,8129,8130],{},"Sim (tudo vai aqui)",[1921,8132,8133,8136,8139],{},[1936,8134,8135],{},"Modules",[1936,8137,8138],{},"Sim (nested)",[1936,8140,8141],{},"Múltiplos stores independentes",[1643,8143,8145],{"id":8144},"instalação-e-configuração","Instalação e configuração",[12,8147,8148],{},"No Vuex, a instalação é direta, mas a configuração do store exige mais estrutura desde o início:",[129,8150,8152],{"className":5084,"code":8151,"language":5086,"meta":75,"style":75},"import { createApp } from 'vue'\nimport { createStore } from 'vuex'\nimport App from '.\u002FApp.vue'\n\n\u002F\u002F createStore recebe toda a configuração de uma vez\n\u002F\u002F state, mutations, actions, getters e modules ficam centralizados aqui\nconst store = createStore({ \u002F* ... *\u002F })\nconst app = createApp(App)\napp.use(store) \u002F\u002F registra o store globalmente, acessível via this.$store em qualquer componente\napp.mount('#app')\n",[135,8153,8154,8173,8191,8208,8212,8217,8222,8244,8258,8274],{"__ignoreMap":75},[138,8155,8156,8158,8160,8163,8165,8167,8169,8171],{"class":140,"line":141},[138,8157,3735],{"class":794},[138,8159,2812],{"class":809},[138,8161,8162],{"class":805}," createApp",[138,8164,2823],{"class":809},[138,8166,3748],{"class":794},[138,8168,957],{"class":826},[138,8170,1409],{"class":830},[138,8172,2361],{"class":826},[138,8174,8175,8177,8179,8181,8183,8185,8187,8189],{"class":140,"line":76},[138,8176,3735],{"class":794},[138,8178,2812],{"class":809},[138,8180,5097],{"class":805},[138,8182,2823],{"class":809},[138,8184,3748],{"class":794},[138,8186,957],{"class":826},[138,8188,5106],{"class":830},[138,8190,2361],{"class":826},[138,8192,8193,8195,8198,8201,8203,8206],{"class":140,"line":152},[138,8194,3735],{"class":794},[138,8196,8197],{"class":805}," App ",[138,8199,8200],{"class":794},"from",[138,8202,957],{"class":826},[138,8204,8205],{"class":830},".\u002FApp.vue",[138,8207,2361],{"class":826},[138,8209,8210],{"class":140,"line":158},[138,8211,649],{"emptyLinePlaceholder":86},[138,8213,8214],{"class":140,"line":164},[138,8215,8216],{"class":911},"\u002F\u002F createStore recebe toda a configuração de uma vez\n",[138,8218,8219],{"class":140,"line":170},[138,8220,8221],{"class":911},"\u002F\u002F state, mutations, actions, getters e modules ficam centralizados aqui\n",[138,8223,8224,8226,8229,8231,8233,8235,8237,8240,8242],{"class":140,"line":176},[138,8225,2518],{"class":1073},[138,8227,8228],{"class":2521}," store",[138,8230,1146],{"class":1112},[138,8232,5097],{"class":801},[138,8234,806],{"class":805},[138,8236,3284],{"class":809},[138,8238,8239],{"class":911}," \u002F* ... *\u002F",[138,8241,2823],{"class":809},[138,8243,872],{"class":805},[138,8245,8246,8248,8251,8253,8255],{"class":140,"line":182},[138,8247,2518],{"class":1073},[138,8249,8250],{"class":2521}," app",[138,8252,1146],{"class":1112},[138,8254,8162],{"class":801},[138,8256,8257],{"class":805},"(App)\n",[138,8259,8260,8263,8265,8268,8271],{"class":140,"line":188},[138,8261,8262],{"class":805},"app",[138,8264,363],{"class":809},[138,8266,8267],{"class":801},"use",[138,8269,8270],{"class":805},"(store) ",[138,8272,8273],{"class":911},"\u002F\u002F registra o store globalmente, acessível via this.$store em qualquer componente\n",[138,8275,8276,8278,8280,8283,8285,8287,8290,8292],{"class":140,"line":194},[138,8277,8262],{"class":805},[138,8279,363],{"class":809},[138,8281,8282],{"class":801},"mount",[138,8284,806],{"class":805},[138,8286,834],{"class":826},[138,8288,8289],{"class":830},"#app",[138,8291,834],{"class":826},[138,8293,872],{"class":805},[12,8295,8296],{},"No Pinia, a configuração é igualmente simples e os stores são criados de forma independente conforme a necessidade:",[129,8298,8300],{"className":5084,"code":8299,"language":5086,"meta":75,"style":75},"import { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport App from '.\u002FApp.vue'\n\nconst app = createApp(App)\n\u002F\u002F createPinia() inicializa o sistema de stores\n\u002F\u002F cada store individual é criado e importado separadamente, sob demanda\napp.use(createPinia())\napp.mount('#app')\n",[135,8301,8302,8320,8339,8353,8357,8369,8374,8379,8395],{"__ignoreMap":75},[138,8303,8304,8306,8308,8310,8312,8314,8316,8318],{"class":140,"line":141},[138,8305,3735],{"class":794},[138,8307,2812],{"class":809},[138,8309,8162],{"class":805},[138,8311,2823],{"class":809},[138,8313,3748],{"class":794},[138,8315,957],{"class":826},[138,8317,1409],{"class":830},[138,8319,2361],{"class":826},[138,8321,8322,8324,8326,8329,8331,8333,8335,8337],{"class":140,"line":76},[138,8323,3735],{"class":794},[138,8325,2812],{"class":809},[138,8327,8328],{"class":805}," createPinia",[138,8330,2823],{"class":809},[138,8332,3748],{"class":794},[138,8334,957],{"class":826},[138,8336,4975],{"class":830},[138,8338,2361],{"class":826},[138,8340,8341,8343,8345,8347,8349,8351],{"class":140,"line":152},[138,8342,3735],{"class":794},[138,8344,8197],{"class":805},[138,8346,8200],{"class":794},[138,8348,957],{"class":826},[138,8350,8205],{"class":830},[138,8352,2361],{"class":826},[138,8354,8355],{"class":140,"line":158},[138,8356,649],{"emptyLinePlaceholder":86},[138,8358,8359,8361,8363,8365,8367],{"class":140,"line":164},[138,8360,2518],{"class":1073},[138,8362,8250],{"class":2521},[138,8364,1146],{"class":1112},[138,8366,8162],{"class":801},[138,8368,8257],{"class":805},[138,8370,8371],{"class":140,"line":170},[138,8372,8373],{"class":911},"\u002F\u002F createPinia() inicializa o sistema de stores\n",[138,8375,8376],{"class":140,"line":176},[138,8377,8378],{"class":911},"\u002F\u002F cada store individual é criado e importado separadamente, sob demanda\n",[138,8380,8381,8383,8385,8387,8389,8392],{"class":140,"line":182},[138,8382,8262],{"class":805},[138,8384,363],{"class":809},[138,8386,8267],{"class":801},[138,8388,806],{"class":805},[138,8390,8391],{"class":801},"createPinia",[138,8393,8394],{"class":805},"())\n",[138,8396,8397,8399,8401,8403,8405,8407,8409,8411],{"class":140,"line":188},[138,8398,8262],{"class":805},[138,8400,363],{"class":809},[138,8402,8282],{"class":801},[138,8404,806],{"class":805},[138,8406,834],{"class":826},[138,8408,8289],{"class":830},[138,8410,834],{"class":826},[138,8412,872],{"class":805},[1643,8414,8416],{"id":8415},"como-os-stores-se-comunicam","Como os stores se comunicam",[12,8418,8419,8420,377,8423,8426],{},"No Vuex, a comunicação entre módulos pode ser feita via ",[135,8421,8422],{},"rootState",[135,8424,8425],{},"rootGetters",", o que tende a criar acoplamentos não muito explícitos:",[129,8428,8430],{"className":5084,"code":8429,"language":5086,"meta":75,"style":75},"const moduloA = {\n  actions: {\n    \u002F\u002F para acessar o estado de outro módulo no Vuex,\n    \u002F\u002F você precisa do rootState que é injetado pelo próprio Vuex no contexto da action\n    \u002F\u002F isso funciona, mas o acoplamento é implícito: você não sabe de onde vem rootState\n    fazAlgo({ commit, rootState }) {\n      if (rootState.usuario.autenticado) {\n        commit('ATUALIZAR')\n      }\n    }\n  }\n}\n",[135,8431,8432,8443,8451,8456,8461,8466,8485,8507,8523,8528,8532,8536],{"__ignoreMap":75},[138,8433,8434,8436,8439,8441],{"class":140,"line":141},[138,8435,2518],{"class":1073},[138,8437,8438],{"class":2521}," moduloA",[138,8440,1146],{"class":1112},[138,8442,934],{"class":809},[138,8444,8445,8447,8449],{"class":140,"line":76},[138,8446,5504],{"class":815},[138,8448,782],{"class":809},[138,8450,934],{"class":809},[138,8452,8453],{"class":140,"line":152},[138,8454,8455],{"class":911},"    \u002F\u002F para acessar o estado de outro módulo no Vuex,\n",[138,8457,8458],{"class":140,"line":158},[138,8459,8460],{"class":911},"    \u002F\u002F você precisa do rootState que é injetado pelo próprio Vuex no contexto da action\n",[138,8462,8463],{"class":140,"line":164},[138,8464,8465],{"class":911},"    \u002F\u002F isso funciona, mas o acoplamento é implícito: você não sabe de onde vem rootState\n",[138,8467,8468,8471,8473,8475,8477,8480,8483],{"class":140,"line":170},[138,8469,8470],{"class":5196},"    fazAlgo",[138,8472,5519],{"class":809},[138,8474,1560],{"class":1045},[138,8476,954],{"class":809},[138,8478,8479],{"class":1045}," rootState",[138,8481,8482],{"class":809}," })",[138,8484,934],{"class":809},[138,8486,8487,8490,8492,8494,8496,8498,8500,8503,8505],{"class":140,"line":176},[138,8488,8489],{"class":794},"      if",[138,8491,1084],{"class":815},[138,8493,8422],{"class":805},[138,8495,363],{"class":809},[138,8497,5362],{"class":805},[138,8499,363],{"class":809},[138,8501,8502],{"class":805},"autenticado",[138,8504,1109],{"class":815},[138,8506,810],{"class":809},[138,8508,8509,8512,8514,8516,8519,8521],{"class":140,"line":182},[138,8510,8511],{"class":801},"        commit",[138,8513,806],{"class":815},[138,8515,834],{"class":826},[138,8517,8518],{"class":830},"ATUALIZAR",[138,8520,834],{"class":826},[138,8522,872],{"class":815},[138,8524,8525],{"class":140,"line":188},[138,8526,8527],{"class":809},"      }\n",[138,8529,8530],{"class":140,"line":194},[138,8531,1179],{"class":809},[138,8533,8534],{"class":140,"line":199},[138,8535,1184],{"class":809},[138,8537,8538],{"class":140,"line":204},[138,8539,2082],{"class":809},[12,8541,8542],{},"No Pinia, você simplesmente importa e usa outro store dentro de um store, de forma completamente explícita:",[129,8544,8546],{"className":5084,"code":8545,"language":5086,"meta":75,"style":75},"import { useUsuarioStore } from '.\u002Fusuario'\n\nexport const useCarrinhoStore = defineStore('carrinho', {\n  actions: {\n    adicionar(produto) {\n      \u002F\u002F importar e usar outro store é simples e explícito\n      \u002F\u002F o acoplamento fica visível logo no import, sem mágica\n      const usuario = useUsuarioStore()\n      if (!usuario.autenticado) {\n        throw new Error('Usuário precisa estar logado')\n      }\n      this.itens.push(produto)\n    }\n  }\n})\n",[135,8547,8548,8567,8571,8595,8603,8615,8620,8625,8637,8655,8677,8681,8699,8703,8707],{"__ignoreMap":75},[138,8549,8550,8552,8554,8556,8558,8560,8562,8565],{"class":140,"line":141},[138,8551,3735],{"class":794},[138,8553,2812],{"class":809},[138,8555,6701],{"class":805},[138,8557,2823],{"class":809},[138,8559,3748],{"class":794},[138,8561,957],{"class":826},[138,8563,8564],{"class":830},".\u002Fusuario",[138,8566,2361],{"class":826},[138,8568,8569],{"class":140,"line":76},[138,8570,649],{"emptyLinePlaceholder":86},[138,8572,8573,8575,8577,8579,8581,8583,8585,8587,8589,8591,8593],{"class":140,"line":152},[138,8574,795],{"class":794},[138,8576,3014],{"class":1073},[138,8578,6385],{"class":2521},[138,8580,1146],{"class":1112},[138,8582,3021],{"class":801},[138,8584,806],{"class":805},[138,8586,834],{"class":826},[138,8588,5219],{"class":830},[138,8590,834],{"class":826},[138,8592,954],{"class":809},[138,8594,934],{"class":809},[138,8596,8597,8599,8601],{"class":140,"line":158},[138,8598,5504],{"class":815},[138,8600,782],{"class":809},[138,8602,934],{"class":809},[138,8604,8605,8607,8609,8611,8613],{"class":140,"line":164},[138,8606,6018],{"class":5196},[138,8608,806],{"class":809},[138,8610,5407],{"class":1045},[138,8612,1049],{"class":809},[138,8614,934],{"class":809},[138,8616,8617],{"class":140,"line":170},[138,8618,8619],{"class":911},"      \u002F\u002F importar e usar outro store é simples e explícito\n",[138,8621,8622],{"class":140,"line":176},[138,8623,8624],{"class":911},"      \u002F\u002F o acoplamento fica visível logo no import, sem mágica\n",[138,8626,8627,8629,8631,8633,8635],{"class":140,"line":182},[138,8628,5536],{"class":1073},[138,8630,5348],{"class":2521},[138,8632,1146],{"class":1112},[138,8634,6701],{"class":801},[138,8636,2796],{"class":815},[138,8638,8639,8641,8643,8645,8647,8649,8651,8653],{"class":140,"line":188},[138,8640,8489],{"class":794},[138,8642,1084],{"class":815},[138,8644,2640],{"class":1112},[138,8646,5362],{"class":805},[138,8648,363],{"class":809},[138,8650,8502],{"class":805},[138,8652,1109],{"class":815},[138,8654,810],{"class":809},[138,8656,8657,8660,8663,8666,8668,8670,8673,8675],{"class":140,"line":194},[138,8658,8659],{"class":794},"        throw",[138,8661,8662],{"class":1112}," new",[138,8664,8665],{"class":801}," Error",[138,8667,806],{"class":815},[138,8669,834],{"class":826},[138,8671,8672],{"class":830},"Usuário precisa estar logado",[138,8674,834],{"class":826},[138,8676,872],{"class":815},[138,8678,8679],{"class":140,"line":199},[138,8680,8527],{"class":809},[138,8682,8683,8685,8687,8689,8691,8693,8695,8697],{"class":140,"line":204},[138,8684,6590],{"class":2041},[138,8686,363],{"class":809},[138,8688,5979],{"class":805},[138,8690,363],{"class":809},[138,8692,5402],{"class":801},[138,8694,806],{"class":815},[138,8696,5407],{"class":805},[138,8698,872],{"class":815},[138,8700,8701],{"class":140,"line":209},[138,8702,1179],{"class":809},[138,8704,8705],{"class":140,"line":215},[138,8706,1184],{"class":809},[138,8708,8709,8711],{"class":140,"line":221},[138,8710,869],{"class":809},[138,8712,872],{"class":805},[1643,8714,8716],{"id":8715},"tamanho-do-bundle","Tamanho do bundle",[12,8718,8719,8720,8723,8724,8727],{},"O Pinia pesa aproximadamente ",[19,8721,8722],{},"1.5kb"," gzipado. O Vuex 4 fica em torno de ",[19,8725,8726],{},"6.5kb",". Para a maioria das aplicações isso não faz diferença prática, mas em projetos onde performance de carregamento é crítica, esse detalhe importa.",[27,8729],{},[30,8731,8733],{"id":8732},"dúvidas-frequentes-respondidas","Dúvidas frequentes respondidas",[1643,8735,8737],{"id":8736},"preciso-migrar-meu-projeto-vuex-para-pinia-agora","\"Preciso migrar meu projeto Vuex para Pinia agora?\"",[12,8739,8740],{},"Não. Se você tem um projeto Vue 2 ou Vue 3 com Vuex funcionando bem, não há urgência. O Vuex 3 e 4 continuam sendo mantidos. A migração faz sentido quando você está iniciando um novo projeto, quando a dívida técnica do Vuex está atrapalhando a evolução do código, ou quando a equipe quer aproveitar melhor TypeScript e Composition API.",[1643,8742,8744],{"id":8743},"dá-para-usar-pinia-com-vue-2","\"Dá para usar Pinia com Vue 2?\"",[12,8746,8747],{},"Sim, mas com limitações. O suporte a Vue 2 no Pinia foi descontinuado em 2025. Para projetos Vue 2 ativos, o Vuex 3 continua sendo a escolha mais adequada.",[1643,8749,8751],{"id":8750},"pinia-é-mais-performático-que-vuex","\"Pinia é mais performático que Vuex?\"",[12,8753,8754],{},"Em termos de execução em tempo real, as diferenças são mínimas na maioria dos cenários. O ganho real do Pinia em performance vem da arquitetura modular, que permite ao bundler fazer tree-shaking mais eficiente, carregando apenas os stores que a aplicação realmente usa.",[1643,8756,8758],{"id":8757},"posso-usar-vuex-e-pinia-no-mesmo-projeto","\"Posso usar Vuex e Pinia no mesmo projeto?\"",[12,8760,8761],{},"Tecnicamente sim. Durante uma migração gradual de Vuex para Pinia em um projeto grande, é possível ter os dois instalados e ir substituindo módulo por módulo. Mas manter os dois permanentemente não é uma prática recomendada.",[1643,8763,8765],{"id":8764},"as-devtools-funcionam-igual-nos-dois","\"As DevTools funcionam igual nos dois?\"",[12,8767,8768,8769,8771],{},"Ambos têm integração com o Vue DevTools, mas a experiência é diferente. No Pinia, cada store aparece de forma independente na aba de estado, você consegue visualizar mutations (que no Pinia são as modificações diretas via actions ou ",[135,8770,7901],{},"), fazer time-travel debugging e inspecionar getters. O Pinia também traz melhorias na visibilidade de quais componentes estão consumindo cada store.",[27,8773],{},[30,8775,8777],{"id":8776},"quando-usar-cada-um","Quando usar cada um",[12,8779,8780],{},[19,8781,8782],{},"Use Vuex quando:",[12,8784,8785],{},"Você está mantendo um projeto Vue 2 que não tem planos de migração para Vue 3 no curto prazo. Ou quando você tem uma aplicação Vue 3 com uma arquitetura Vuex muito bem estabelecida e migrar não traria ganhos proporcionais ao esforço.",[12,8787,8788],{},[19,8789,8790],{},"Use Pinia quando:",[12,8792,8793],{},"Você está iniciando qualquer projeto Vue 3 novo. Quando a aplicação usa Nuxt 3. Quando TypeScript é importante para o projeto. Quando a equipe inclui desenvolvedores em diferentes níveis de experiência e você quer um modelo mental mais simples. Em resumo: para tudo que é novo com Vue 3, Pinia é a escolha natural e recomendada.",[27,8795],{},[30,8797,8799],{"id":8798},"migrando-de-vuex-para-pinia","Migrando de Vuex para Pinia",[12,8801,8802,8803,8808],{},"Se você decidiu migrar, o processo é mais tranquilo do que parece. A documentação oficial do Pinia tem um ",[105,8804,8807],{"href":8805,"rel":8806},"https:\u002F\u002Fpinia.vuejs.org\u002Fcookbook\u002Fmigration-vuex.html",[109],"guia de migração"," dedicado. A lógica geral é:",[12,8810,8811,8812,8814,8815,8818,8819,8822],{},"Cada módulo Vuex com ",[135,8813,6228],{}," vira um store Pinia independente. As mutations deixam de existir e sua lógica é absorvida pelas actions. Os getters permanecem praticamente iguais. As actions assíncronas ficam quase idênticas, apenas removendo o parâmetro de context (",[135,8816,8817],{},"{ commit, state }",") e usando ",[135,8820,8821],{},"this"," diretamente.",[129,8824,8826],{"className":5084,"code":8825,"language":5086,"meta":75,"style":75},"\u002F\u002F ------ ANTES: módulo Vuex ------\nconst moduloVuex = {\n  namespaced: true,\n  state: () => ({ lista: [] }),\n  \u002F\u002F mutation obrigatória só para atualizar o state\n  mutations: {\n    SET_LISTA(state, lista) { state.lista = lista }\n  },\n  \u002F\u002F action que chama a API e depois precisa \"comitar\" a mutation\n  actions: {\n    async buscarLista({ commit }) {\n      const data = await api.getLista()\n      commit('SET_LISTA', data) \u002F\u002F duas etapas para uma operação simples\n    }\n  }\n}\n\n\u002F\u002F ------ DEPOIS: store Pinia equivalente ------\nexport const useListaStore = defineStore('lista', {\n  state: () => ({ lista: [] }),\n  \u002F\u002F sem mutation, a action faz tudo diretamente\n  actions: {\n    async buscarLista() {\n      this.lista = await api.getLista() \u002F\u002F uma única linha\n    }\n  }\n})\n",[135,8827,8828,8833,8844,8854,8881,8886,8894,8924,8928,8933,8941,8956,8976,8998,9002,9006,9010,9014,9019,9044,9070,9075,9083,9093,9116,9120,9124],{"__ignoreMap":75},[138,8829,8830],{"class":140,"line":141},[138,8831,8832],{"class":911},"\u002F\u002F ------ ANTES: módulo Vuex ------\n",[138,8834,8835,8837,8840,8842],{"class":140,"line":76},[138,8836,2518],{"class":1073},[138,8838,8839],{"class":2521}," moduloVuex",[138,8841,1146],{"class":1112},[138,8843,934],{"class":809},[138,8845,8846,8848,8850,8852],{"class":140,"line":152},[138,8847,5912],{"class":815},[138,8849,782],{"class":809},[138,8851,5917],{"class":3862},[138,8853,837],{"class":809},[138,8855,8856,8858,8860,8862,8864,8866,8868,8871,8873,8875,8877,8879],{"class":140,"line":158},[138,8857,5139],{"class":801},[138,8859,782],{"class":809},[138,8861,3034],{"class":809},[138,8863,1074],{"class":1073},[138,8865,1084],{"class":805},[138,8867,3284],{"class":809},[138,8869,8870],{"class":815}," lista",[138,8872,782],{"class":809},[138,8874,5941],{"class":805},[138,8876,869],{"class":809},[138,8878,1049],{"class":805},[138,8880,837],{"class":809},[138,8882,8883],{"class":140,"line":164},[138,8884,8885],{"class":911},"  \u002F\u002F mutation obrigatória só para atualizar o state\n",[138,8887,8888,8890,8892],{"class":140,"line":170},[138,8889,5330],{"class":815},[138,8891,782],{"class":809},[138,8893,934],{"class":809},[138,8895,8896,8899,8901,8903,8905,8907,8909,8911,8913,8915,8918,8920,8922],{"class":140,"line":176},[138,8897,8898],{"class":5196},"    SET_LISTA",[138,8900,806],{"class":809},[138,8902,5202],{"class":1045},[138,8904,954],{"class":809},[138,8906,8870],{"class":1045},[138,8908,1049],{"class":809},[138,8910,2812],{"class":809},[138,8912,5214],{"class":805},[138,8914,363],{"class":809},[138,8916,8917],{"class":805},"lista",[138,8919,1146],{"class":1112},[138,8921,8870],{"class":805},[138,8923,1015],{"class":809},[138,8925,8926],{"class":140,"line":182},[138,8927,972],{"class":809},[138,8929,8930],{"class":140,"line":188},[138,8931,8932],{"class":911},"  \u002F\u002F action que chama a API e depois precisa \"comitar\" a mutation\n",[138,8934,8935,8937,8939],{"class":140,"line":194},[138,8936,5504],{"class":815},[138,8938,782],{"class":809},[138,8940,934],{"class":809},[138,8942,8943,8945,8948,8950,8952,8954],{"class":140,"line":199},[138,8944,5513],{"class":1073},[138,8946,8947],{"class":5196}," buscarLista",[138,8949,5519],{"class":809},[138,8951,1560],{"class":1045},[138,8953,8482],{"class":809},[138,8955,934],{"class":809},[138,8957,8958,8960,8962,8964,8966,8969,8971,8974],{"class":140,"line":204},[138,8959,5536],{"class":1073},[138,8961,2815],{"class":2521},[138,8963,1146],{"class":1112},[138,8965,2828],{"class":794},[138,8967,8968],{"class":805}," api",[138,8970,363],{"class":809},[138,8972,8973],{"class":801},"getLista",[138,8975,2796],{"class":815},[138,8977,8978,8980,8982,8984,8987,8989,8991,8993,8995],{"class":140,"line":209},[138,8979,5595],{"class":801},[138,8981,806],{"class":815},[138,8983,834],{"class":826},[138,8985,8986],{"class":830},"SET_LISTA",[138,8988,834],{"class":826},[138,8990,954],{"class":809},[138,8992,2815],{"class":805},[138,8994,1109],{"class":815},[138,8996,8997],{"class":911},"\u002F\u002F duas etapas para uma operação simples\n",[138,8999,9000],{"class":140,"line":215},[138,9001,1179],{"class":809},[138,9003,9004],{"class":140,"line":221},[138,9005,1184],{"class":809},[138,9007,9008],{"class":140,"line":227},[138,9009,2082],{"class":809},[138,9011,9012],{"class":140,"line":233},[138,9013,649],{"emptyLinePlaceholder":86},[138,9015,9016],{"class":140,"line":239},[138,9017,9018],{"class":911},"\u002F\u002F ------ DEPOIS: store Pinia equivalente ------\n",[138,9020,9021,9023,9025,9028,9030,9032,9034,9036,9038,9040,9042],{"class":140,"line":245},[138,9022,795],{"class":794},[138,9024,3014],{"class":1073},[138,9026,9027],{"class":2521}," useListaStore",[138,9029,1146],{"class":1112},[138,9031,3021],{"class":801},[138,9033,806],{"class":805},[138,9035,834],{"class":826},[138,9037,8917],{"class":830},[138,9039,834],{"class":826},[138,9041,954],{"class":809},[138,9043,934],{"class":809},[138,9045,9046,9048,9050,9052,9054,9056,9058,9060,9062,9064,9066,9068],{"class":140,"line":251},[138,9047,5139],{"class":801},[138,9049,782],{"class":809},[138,9051,3034],{"class":809},[138,9053,1074],{"class":1073},[138,9055,1084],{"class":805},[138,9057,3284],{"class":809},[138,9059,8870],{"class":815},[138,9061,782],{"class":809},[138,9063,5941],{"class":805},[138,9065,869],{"class":809},[138,9067,1049],{"class":805},[138,9069,837],{"class":809},[138,9071,9072],{"class":140,"line":257},[138,9073,9074],{"class":911},"  \u002F\u002F sem mutation, a action faz tudo diretamente\n",[138,9076,9077,9079,9081],{"class":140,"line":263},[138,9078,5504],{"class":815},[138,9080,782],{"class":809},[138,9082,934],{"class":809},[138,9084,9085,9087,9089,9091],{"class":140,"line":269},[138,9086,5513],{"class":1073},[138,9088,8947],{"class":5196},[138,9090,3093],{"class":809},[138,9092,934],{"class":809},[138,9094,9095,9097,9099,9101,9103,9105,9107,9109,9111,9113],{"class":140,"line":275},[138,9096,6590],{"class":2041},[138,9098,363],{"class":809},[138,9100,8917],{"class":805},[138,9102,1146],{"class":1112},[138,9104,2828],{"class":794},[138,9106,8968],{"class":805},[138,9108,363],{"class":809},[138,9110,8973],{"class":801},[138,9112,2732],{"class":815},[138,9114,9115],{"class":911},"\u002F\u002F uma única linha\n",[138,9117,9118],{"class":140,"line":281},[138,9119,1179],{"class":809},[138,9121,9122],{"class":140,"line":287},[138,9123,1184],{"class":809},[138,9125,9126,9128],{"class":140,"line":293},[138,9127,869],{"class":809},[138,9129,872],{"class":805},[12,9131,9132],{},"A diferença em termos de linhas de código já é visível em um exemplo simples. Em stores mais complexos, o ganho é ainda maior.",[27,9134],{},[30,9136,4866],{"id":4865},[12,9138,9139],{},"O Vuex foi uma solução sólida que moldou como desenvolvedores Vue pensam sobre estado global por anos. Ele ainda é relevante para projetos legados e situações específicas, mas carrega o peso de uma época em que Composition API e TypeScript não eram prioridades centrais do ecossistema.",[12,9141,9142],{},"O Pinia é o que o Vuex teria sido se tivesse sido projetado hoje. Mais simples, mais leve, mais alinhado com a forma moderna de escrever Vue, e com suporte a TypeScript que realmente funciona sem dor. Não é à toa que o time do Vue o elegeu como a solução oficial.",[12,9144,9145,9146,9149],{},"Para qualquer projeto novo com Vue 3, a resposta é direta: use Pinia. O aprendizado é rápido, a API é intuitiva e você vai escrever código mais limpo desde o primeiro dia. E se quiser ver como tudo isso se aplica em um projeto real, o artigo do ",[105,9147,7488],{"href":4921,"rel":9148},[109]," mostra cada decisão técnica documentada, do setup da store à geração do PDF.",[30,9151,9153],{"id":9152},"referências","Referências",[402,9155,9156,9163,9170],{},[405,9157,9158],{},[105,9159,9162],{"href":9160,"rel":9161},"https:\u002F\u002Fvuex.vuejs.org",[109],"Documentação oficial do Vuex",[405,9164,9165],{},[105,9166,9169],{"href":9167,"rel":9168},"https:\u002F\u002Fpinia.vuejs.org",[109],"Documentação oficial do Pinia",[405,9171,9172],{},[105,9173,9175],{"href":8805,"rel":9174},[109],"Guia de migração: Vuex para Pinia",[30,9177,9179],{"id":9178},"artigos-complementares","Artigos complementares",[402,9181,9182,9188],{},[405,9183,9184],{},[105,9185,9187],{"href":5027,"rel":9186},[109],"Closures no JavaScript: o conceito por trás de uma store",[405,9189,9190],{},[105,9191,9193],{"href":4921,"rel":9192},[109],"VisitCardGenerator: construindo um gerador de cartões de visita em PDF do zero com Nuxt 3 e Pinia",[1744,9195,9196],{},"html pre.shiki code .s3Er8, html code.shiki .s3Er8{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sG-J9, html code.shiki .sG-J9{--shiki-light:#39ADB5;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sMo7A, html code.shiki .sMo7A{--shiki-light:#90A4AE;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sF_wb, html code.shiki .sF_wb{--shiki-light:#39ADB5;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .s0vBq, html code.shiki .s0vBq{--shiki-light:#91B859;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sK_r7, html code.shiki .sK_r7{--shiki-light:#6182B8;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sdv8B, html code.shiki .sdv8B{--shiki-light:#E53935;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .swu5b, html code.shiki .swu5b{--shiki-light:#39ADB5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s0u7J, html code.shiki .s0u7J{--shiki-light:#E53935;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sk1zL, html code.shiki .sk1zL{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#FFAB70;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sVPC0, html code.shiki .sVPC0{--shiki-light:#90A4AE;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sFsEu, html code.shiki .sFsEu{--shiki-light:#9C3EDA;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sFfmW, html code.shiki .sFfmW{--shiki-light:#39ADB5;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .s_k96, html code.shiki .s_k96{--shiki-light:#F76D47;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMrrN, html code.shiki .sMrrN{--shiki-light:#FF5370;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sqIbZ, html code.shiki .sqIbZ{--shiki-light:#E53935;--shiki-default:#85E89D;--shiki-dark:#85E89D}html pre.shiki code .s7047, html code.shiki .s7047{--shiki-light:#9C3EDA;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .soiBB, html code.shiki .soiBB{--shiki-light:#E2931D;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sTbKH, html code.shiki .sTbKH{--shiki-light:#E53935;--shiki-default:#FFAB70;--shiki-dark:#FFAB70}html pre.shiki code .s3afY, html code.shiki .s3afY{--shiki-light:#E2931D;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}",{"title":75,"searchDepth":76,"depth":76,"links":9198},[9199,9200,9206,9213,9219,9226,9227,9228,9229,9230],{"id":4997,"depth":76,"text":4998},{"id":5035,"depth":76,"text":5036,"children":9201},[9202,9203,9204,9205],{"id":5042,"depth":152,"text":5043},{"id":5862,"depth":152,"text":5863},{"id":6239,"depth":152,"text":6240},{"id":6249,"depth":152,"text":6250},{"id":6286,"depth":76,"text":6287,"children":9207},[9208,9209,9210,9211,9212],{"id":6300,"depth":152,"text":6301},{"id":7093,"depth":152,"text":7094},{"id":7492,"depth":152,"text":7493},{"id":7830,"depth":152,"text":7831},{"id":7999,"depth":152,"text":8000},{"id":8070,"depth":76,"text":8071,"children":9214},[9215,9216,9217,9218],{"id":8077,"depth":152,"text":8078},{"id":8144,"depth":152,"text":8145},{"id":8415,"depth":152,"text":8416},{"id":8715,"depth":152,"text":8716},{"id":8732,"depth":76,"text":8733,"children":9220},[9221,9222,9223,9224,9225],{"id":8736,"depth":152,"text":8737},{"id":8743,"depth":152,"text":8744},{"id":8750,"depth":152,"text":8751},{"id":8757,"depth":152,"text":8758},{"id":8764,"depth":152,"text":8765},{"id":8776,"depth":76,"text":8777},{"id":8798,"depth":76,"text":8799},{"id":4865,"depth":76,"text":4866},{"id":9152,"depth":76,"text":9153},{"id":9178,"depth":76,"text":9179},"2026-04-23T19:31:00-03:00","Entenda as diferenças entre Vuex e Pinia com exemplos práticos, comparação de arquitetura, TypeScript, performance e quando usar cada biblioteca no Vue 3.","\u002Fimages\u002Fblog\u002Fvuex-vs-pinia.png",{},"\u002Fblog\u002Fvuex-vs-pinia",{"title":4979,"description":9232},"blog\u002Fvuex-vs-pinia",[9239,9240,9241,9242,8090,4819,9243],"Vue","Vue3","JavaScript","Frontend","Gerenciamento de Estado","n0zgHzeRqsVpdXqWmY2xk0k5WMF4uVExvjmYICxkE_Q",{"id":9246,"title":9247,"author":7,"body":9248,"date":12409,"description":12410,"extension":83,"image":12411,"meta":12412,"navigation":86,"path":12413,"seo":12414,"stem":12415,"tags":12416,"__hash__":12417},"blog\u002Fblog\u002Fvue-composition-vs-options-api.md","Options API vs Composition API no Vue.js: qual usar e quando?",{"type":9,"value":9249,"toc":12385},[9250,9256,9259,9262,9264,9268,9275,9282,9284,9288,9291,9340,9349,9353,9356,9763,9766,9768,9772,9787,9790,9797,9812,9815,9817,9821,9828,9837,9840,9886,9890,9895,10275,10278,10280,10289,10298,10317,10364,10376,10459,10474,10476,10480,10483,10572,10585,10668,10671,10673,10677,10684,10690,10931,10934,11037,11044,11047,11049,11053,11056,11062,11065,11209,11216,11218,11222,11234,11237,11240,11242,11246,11252,11392,11395,11410,11412,11416,11419,11424,11438,11443,11460,11469,11471,11475,11478,11909,11912,12193,12196,12322,12324,12326,12329,12339,12342,12344,12346,12350,12364,12368,12382],[12,9251,9252,9253],{},"Se você trabalha com Vue.js há algum tempo, com certeza já se deparou com essa dúvida, ou pelo menos ouviu alguém debater sobre ela. Afinal, o Vue 3 trouxe uma nova forma de escrever componentes, e desde então a pergunta não sai do radar da comunidade: ",[19,9254,9255],{},"Options API ou Composition API?",[12,9257,9258],{},"A boa notícia é que não existe uma resposta única e definitiva. A escolha depende do contexto, do projeto e, também, do seu gosto pessoal. Mas para fazer uma escolha consciente, você precisa entender bem as duas abordagens: o que cada uma oferece, como cada uma pensa a organização do código e onde cada uma começa a mostrar suas limitações.",[12,9260,9261],{},"É exatamente isso que vamos explorar aqui, de forma aprofundada e com exemplos práticos.",[27,9263],{},[30,9265,9267],{"id":9266},"um-pouco-de-contexto-como-chegamos-até-aqui","Um pouco de contexto: como chegamos até aqui",[12,9269,9270,9271,9274],{},"Até o Vue 2, existia basicamente uma única forma de estruturar um componente, o que hoje chamamos de ",[19,9272,9273],{},"Options API",". Era o jeito Vue de fazer as coisas, e funcionava bem. Mas à medida que projetos cresciam em complexidade e a comunidade de desenvolvedores foi amadurecendo, algumas limitações foram ficando mais evidentes.",[12,9276,9277,9278,9281],{},"Em 2020, o Vue 3 foi lançado trazendo a ",[19,9279,9280],{},"Composition API"," como nova alternativa, inspirada em parte pelos React Hooks, mas com uma abordagem própria e mais alinhada ao modelo de reatividade do Vue. Desde então, as duas coexistem e a documentação oficial deixa claro que nenhuma será descontinuada.",[27,9283],{},[30,9285,9287],{"id":9286},"o-que-é-a-options-api","O que é a Options API?",[12,9289,9290],{},"A Options API é a forma clássica de criar componentes no Vue. Você exporta um objeto com \"opções\" bem definidas, cada uma com sua responsabilidade:",[402,9292,9293,9298,9304,9309,9315,9324],{},[405,9294,9295,9297],{},[135,9296,2891],{},": onde vivem os dados reativos do componente",[405,9299,9300,9303],{},[135,9301,9302],{},"methods",": onde ficam as funções que manipulam o estado ou respondem a eventos",[405,9305,9306,9308],{},[135,9307,7111],{},": propriedades derivadas, calculadas com base em dados reativos",[405,9310,9311,9314],{},[135,9312,9313],{},"watch",": observadores que reagem a mudanças em valores específicos",[405,9316,9317,377,9320,9323],{},[135,9318,9319],{},"props",[135,9321,9322],{},"emits",": para comunicação com componentes pai\u002Ffilho",[405,9325,9326,9327,370,9330,370,9333,370,9336,9339],{},"Lifecycle hooks como ",[135,9328,9329],{},"mounted",[135,9331,9332],{},"created",[135,9334,9335],{},"updated",[135,9337,9338],{},"unmounted",", entre outros",[12,9341,9342,9343,9345,9346,9348],{},"A estrutura é clara e previsível. Você sabe exatamente onde procurar cada coisa. Quer ver os dados? Vai em ",[135,9344,2891],{},". Quer ver os métodos? Vai em ",[135,9347,9302],{},". Isso torna a curva de entrada bastante suave, especialmente para quem está começando.",[1643,9350,9352],{"id":9351},"exemplo-prático-com-options-api","Exemplo prático com Options API",[12,9354,9355],{},"Imagine um componente simples de contador com uma mensagem derivada:",[129,9357,9359],{"className":1407,"code":9358,"language":1409,"meta":75,"style":75},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp>Contagem: {{ count }}\u003C\u002Fp>\n    \u003Cp>{{ message }}\u003C\u002Fp>\n    \u003Cbutton @click=\"increment\">Incrementar\u003C\u002Fbutton>\n    \u003Cbutton @click=\"reset\">Resetar\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  name: 'Counter',\n\n  data() {\n    return {\n      count: 0\n    }\n  },\n\n  computed: {\n    message() {\n      return this.count === 0\n        ? 'Nenhum clique ainda.'\n        : `Você clicou ${this.count} vez(es).`\n    }\n  },\n\n  methods: {\n    increment() {\n      this.count++\n    },\n    reset() {\n      this.count = 0\n    }\n  },\n\n  mounted() {\n    console.log('Componente montado com count:', this.count)\n  }\n}\n\u003C\u002Fscript>\n",[135,9360,9361,9369,9377,9394,9411,9440,9468,9476,9484,9488,9496,9504,9520,9524,9533,9539,9549,9553,9557,9561,9569,9578,9593,9605,9632,9636,9640,9644,9652,9661,9672,9676,9685,9697,9701,9705,9709,9718,9747,9751,9755],{"__ignoreMap":75},[138,9362,9363,9365,9367],{"class":140,"line":141},[138,9364,1416],{"class":809},[138,9366,3560],{"class":1419},[138,9368,1440],{"class":809},[138,9370,9371,9373,9375],{"class":140,"line":76},[138,9372,3567],{"class":809},[138,9374,4369],{"class":1419},[138,9376,1440],{"class":809},[138,9378,9379,9381,9383,9385,9388,9390,9392],{"class":140,"line":152},[138,9380,3577],{"class":809},[138,9382,12],{"class":1419},[138,9384,3065],{"class":809},[138,9386,9387],{"class":805},"Contagem: {{ count }}",[138,9389,1505],{"class":809},[138,9391,12],{"class":1419},[138,9393,1440],{"class":809},[138,9395,9396,9398,9400,9402,9405,9407,9409],{"class":140,"line":158},[138,9397,3577],{"class":809},[138,9399,12],{"class":1419},[138,9401,3065],{"class":809},[138,9403,9404],{"class":805},"{{ message }}",[138,9406,1505],{"class":809},[138,9408,12],{"class":1419},[138,9410,1440],{"class":809},[138,9412,9413,9415,9417,9420,9422,9424,9427,9429,9431,9434,9436,9438],{"class":140,"line":164},[138,9414,3577],{"class":809},[138,9416,4510],{"class":1419},[138,9418,9419],{"class":1423}," @click",[138,9421,1430],{"class":809},[138,9423,1433],{"class":826},[138,9425,9426],{"class":830},"increment",[138,9428,1433],{"class":826},[138,9430,3065],{"class":809},[138,9432,9433],{"class":805},"Incrementar",[138,9435,1505],{"class":809},[138,9437,4510],{"class":1419},[138,9439,1440],{"class":809},[138,9441,9442,9444,9446,9448,9450,9452,9455,9457,9459,9462,9464,9466],{"class":140,"line":170},[138,9443,3577],{"class":809},[138,9445,4510],{"class":1419},[138,9447,9419],{"class":1423},[138,9449,1430],{"class":809},[138,9451,1433],{"class":826},[138,9453,9454],{"class":830},"reset",[138,9456,1433],{"class":826},[138,9458,3065],{"class":809},[138,9460,9461],{"class":805},"Resetar",[138,9463,1505],{"class":809},[138,9465,4510],{"class":1419},[138,9467,1440],{"class":809},[138,9469,9470,9472,9474],{"class":140,"line":176},[138,9471,3625],{"class":809},[138,9473,4369],{"class":1419},[138,9475,1440],{"class":809},[138,9477,9478,9480,9482],{"class":140,"line":182},[138,9479,1505],{"class":809},[138,9481,3560],{"class":1419},[138,9483,1440],{"class":809},[138,9485,9486],{"class":140,"line":188},[138,9487,649],{"emptyLinePlaceholder":86},[138,9489,9490,9492,9494],{"class":140,"line":194},[138,9491,1416],{"class":809},[138,9493,1420],{"class":1419},[138,9495,1440],{"class":809},[138,9497,9498,9500,9502],{"class":140,"line":199},[138,9499,795],{"class":794},[138,9501,798],{"class":794},[138,9503,934],{"class":809},[138,9505,9506,9509,9511,9513,9516,9518],{"class":140,"line":204},[138,9507,9508],{"class":815},"  name",[138,9510,782],{"class":809},[138,9512,957],{"class":826},[138,9514,9515],{"class":830},"Counter",[138,9517,834],{"class":826},[138,9519,837],{"class":809},[138,9521,9522],{"class":140,"line":209},[138,9523,649],{"emptyLinePlaceholder":86},[138,9525,9526,9529,9531],{"class":140,"line":215},[138,9527,9528],{"class":5196},"  data",[138,9530,3093],{"class":809},[138,9532,934],{"class":809},[138,9534,9535,9537],{"class":140,"line":221},[138,9536,2900],{"class":794},[138,9538,934],{"class":809},[138,9540,9541,9544,9546],{"class":140,"line":227},[138,9542,9543],{"class":815},"      count",[138,9545,782],{"class":809},[138,9547,9548],{"class":5295}," 0\n",[138,9550,9551],{"class":140,"line":233},[138,9552,1179],{"class":809},[138,9554,9555],{"class":140,"line":239},[138,9556,972],{"class":809},[138,9558,9559],{"class":140,"line":245},[138,9560,649],{"emptyLinePlaceholder":86},[138,9562,9563,9565,9567],{"class":140,"line":251},[138,9564,5720],{"class":815},[138,9566,782],{"class":809},[138,9568,934],{"class":809},[138,9570,9571,9574,9576],{"class":140,"line":257},[138,9572,9573],{"class":5196},"    message",[138,9575,3093],{"class":809},[138,9577,934],{"class":809},[138,9579,9580,9582,9584,9586,9589,9591],{"class":140,"line":263},[138,9581,5211],{"class":794},[138,9583,6638],{"class":2041},[138,9585,363],{"class":809},[138,9587,9588],{"class":805},"count",[138,9590,2765],{"class":1112},[138,9592,9548],{"class":5295},[138,9594,9595,9598,9600,9603],{"class":140,"line":269},[138,9596,9597],{"class":1112},"        ?",[138,9599,957],{"class":826},[138,9601,9602],{"class":830},"Nenhum clique ainda.",[138,9604,2361],{"class":826},[138,9606,9607,9610,9613,9616,9618,9620,9622,9624,9626,9629],{"class":140,"line":275},[138,9608,9609],{"class":1112},"        :",[138,9611,9612],{"class":826}," `",[138,9614,9615],{"class":830},"Você clicou ",[138,9617,3797],{"class":826},[138,9619,8821],{"class":2041},[138,9621,363],{"class":826},[138,9623,9588],{"class":805},[138,9625,869],{"class":826},[138,9627,9628],{"class":830}," vez(es).",[138,9630,9631],{"class":826},"`\n",[138,9633,9634],{"class":140,"line":281},[138,9635,1179],{"class":809},[138,9637,9638],{"class":140,"line":287},[138,9639,972],{"class":809},[138,9641,9642],{"class":140,"line":293},[138,9643,649],{"emptyLinePlaceholder":86},[138,9645,9646,9648,9650],{"class":140,"line":299},[138,9647,5810],{"class":815},[138,9649,782],{"class":809},[138,9651,934],{"class":809},[138,9653,9654,9657,9659],{"class":140,"line":305},[138,9655,9656],{"class":5196},"    increment",[138,9658,3093],{"class":809},[138,9660,934],{"class":809},[138,9662,9663,9665,9667,9669],{"class":140,"line":311},[138,9664,6590],{"class":2041},[138,9666,363],{"class":809},[138,9668,9588],{"class":805},[138,9670,9671],{"class":1112},"++\n",[138,9673,9674],{"class":140,"line":317},[138,9675,5229],{"class":809},[138,9677,9678,9681,9683],{"class":140,"line":323},[138,9679,9680],{"class":5196},"    reset",[138,9682,3093],{"class":809},[138,9684,934],{"class":809},[138,9686,9687,9689,9691,9693,9695],{"class":140,"line":329},[138,9688,6590],{"class":2041},[138,9690,363],{"class":809},[138,9692,9588],{"class":805},[138,9694,1146],{"class":1112},[138,9696,9548],{"class":5295},[138,9698,9699],{"class":140,"line":335},[138,9700,1179],{"class":809},[138,9702,9703],{"class":140,"line":341},[138,9704,972],{"class":809},[138,9706,9707],{"class":140,"line":347},[138,9708,649],{"emptyLinePlaceholder":86},[138,9710,9711,9714,9716],{"class":140,"line":353},[138,9712,9713],{"class":5196},"  mounted",[138,9715,3093],{"class":809},[138,9717,934],{"class":809},[138,9719,9720,9723,9725,9728,9730,9732,9735,9737,9739,9741,9743,9745],{"class":140,"line":622},[138,9721,9722],{"class":805},"    console",[138,9724,363],{"class":809},[138,9726,9727],{"class":801},"log",[138,9729,806],{"class":815},[138,9731,834],{"class":826},[138,9733,9734],{"class":830},"Componente montado com count:",[138,9736,834],{"class":826},[138,9738,954],{"class":809},[138,9740,6638],{"class":2041},[138,9742,363],{"class":809},[138,9744,9588],{"class":805},[138,9746,872],{"class":815},[138,9748,9749],{"class":140,"line":628},[138,9750,1184],{"class":809},[138,9752,9753],{"class":140,"line":634},[138,9754,2082],{"class":809},[138,9756,9757,9759,9761],{"class":140,"line":640},[138,9758,1505],{"class":809},[138,9760,1420],{"class":1419},[138,9762,1440],{"class":809},[12,9764,9765],{},"Repare que toda a lógica fica organizada dentro de \"gavetas\" separadas. Para quem vem do Vue 2 ou está aprendendo o framework agora, essa estrutura é bastante intuitiva. O problema começa quando o componente cresce.",[27,9767],{},[30,9769,9771],{"id":9770},"o-problema-de-escala-na-options-api","O problema de escala na Options API",[12,9773,9774,9775,9778,9779,9781,9782,9781,9784,9786],{},"Imagine agora que esse mesmo componente precisa, além do contador, gerenciar também um formulário de contato, um estado de loading para uma requisição HTTP e uma lógica de validação. Com a Options API, o código de cada uma dessas responsabilidades vai estar ",[19,9776,9777],{},"fragmentado",": parte em ",[135,9780,2891],{},", parte em ",[135,9783,9302],{},[135,9785,7111],{},", outra parte nos lifecycle hooks.",[12,9788,9789],{},"Você passa a ter um arquivo com 300 linhas onde, para entender uma única funcionalidade, precisa ficar rolando o componente de cima para baixo, saltando entre as seções. A documentação oficial do Vue ilustra esse problema com um diagrama que mostra exatamente como os blocos de código relacionados ficam espalhados quando usamos Options API em componentes complexos.",[12,9791,9792,9793,9796],{},"Outro ponto crítico é o reuso de lógica. Na Options API, a solução tradicional para compartilhar comportamento entre componentes eram os ",[19,9794,9795],{},"mixins",", que trazem uma série de problemas:",[402,9798,9799,9802,9809],{},[405,9800,9801],{},"Colisão de nomes: se dois mixins definem uma propriedade com o mesmo nome, há conflito",[405,9803,9804,9805,9808],{},"Origem obscura: ao olhar para ",[135,9806,9807],{},"this.algumDado",", é difícil saber se ele veio do próprio componente, de um mixin A ou de um mixin B",[405,9810,9811],{},"Acoplamento implícito: mixins podem depender de propriedades do componente host sem deixar isso claro na assinatura",[12,9813,9814],{},"Esses problemas ficam sérios em bases de código grandes, onde múltiplos mixins são combinados no mesmo componente.",[27,9816],{},[30,9818,9820],{"id":9819},"o-que-é-a-composition-api","O que é a Composition API?",[12,9822,9823,9824,9827],{},"A Composition API é uma alternativa que permite escrever a lógica do componente usando ",[19,9825,9826],{},"funções importadas diretamente da API do Vue",", em vez de declarar um objeto de opções. O nome \"composição\" vem exatamente da ideia de compor o comportamento do componente a partir de funções menores e reutilizáveis.",[12,9829,9830,9831,9833,9834,9836],{},"Ela é usada principalmente junto com a sintaxe ",[135,9832,6842],{},", que é um \"açúcar sintático\" (syntactic sugar) que reduz o boilerplate necessário. Com ",[135,9835,6842],{},", tudo que você declara no script fica automaticamente disponível no template, sem precisar retornar explicitamente nada.",[12,9838,9839],{},"As principais funções da Composition API incluem:",[402,9841,9842,9850,9856,9865,9877],{},[405,9843,9844,377,9846,9849],{},[135,9845,5010],{},[135,9847,9848],{},"reactive()",": para criar estado reativo",[405,9851,9852,9855],{},[135,9853,9854],{},"computed()",": para criar propriedades computadas",[405,9857,9858,377,9861,9864],{},[135,9859,9860],{},"watch()",[135,9862,9863],{},"watchEffect()",": para observar mudanças reativas",[405,9866,9867,370,9870,370,9873,9876],{},[135,9868,9869],{},"onMounted()",[135,9871,9872],{},"onUnmounted()",[135,9874,9875],{},"onUpdated()"," e outros hooks de ciclo de vida",[405,9878,9879,377,9882,9885],{},[135,9880,9881],{},"provide()",[135,9883,9884],{},"inject()",": para injeção de dependências entre componentes",[1643,9887,9889],{"id":9888},"exemplo-prático-com-composition-api","Exemplo prático com Composition API",[12,9891,9892,9893,782],{},"O mesmo componente de contador, reescrito com Composition API e ",[135,9894,6842],{},[129,9896,9898],{"className":1407,"code":9897,"language":1409,"meta":75,"style":75},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp>Contagem: {{ count }}\u003C\u002Fp>\n    \u003Cp>{{ message }}\u003C\u002Fp>\n    \u003Cbutton @click=\"increment\">Incrementar\u003C\u002Fbutton>\n    \u003Cbutton @click=\"reset\">Resetar\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref, computed, onMounted } from 'vue'\n\nconst count = ref(0)\n\nconst message = computed(() => {\n  return count.value === 0\n    ? 'Nenhum clique ainda.'\n    : `Você clicou ${count.value} vez(es).`\n})\n\nfunction increment() {\n  count.value++\n}\n\nfunction reset() {\n  count.value = 0\n}\n\nonMounted(() => {\n  console.log('Componente montado com count:', count.value)\n})\n\u003C\u002Fscript>\n",[135,9899,9900,9908,9916,9932,9948,9974,10000,10008,10016,10020,10030,10057,10061,10079,10083,10102,10116,10127,10150,10156,10160,10171,10182,10186,10190,10201,10213,10217,10221,10234,10261,10267],{"__ignoreMap":75},[138,9901,9902,9904,9906],{"class":140,"line":141},[138,9903,1416],{"class":809},[138,9905,3560],{"class":1419},[138,9907,1440],{"class":809},[138,9909,9910,9912,9914],{"class":140,"line":76},[138,9911,3567],{"class":809},[138,9913,4369],{"class":1419},[138,9915,1440],{"class":809},[138,9917,9918,9920,9922,9924,9926,9928,9930],{"class":140,"line":152},[138,9919,3577],{"class":809},[138,9921,12],{"class":1419},[138,9923,3065],{"class":809},[138,9925,9387],{"class":805},[138,9927,1505],{"class":809},[138,9929,12],{"class":1419},[138,9931,1440],{"class":809},[138,9933,9934,9936,9938,9940,9942,9944,9946],{"class":140,"line":158},[138,9935,3577],{"class":809},[138,9937,12],{"class":1419},[138,9939,3065],{"class":809},[138,9941,9404],{"class":805},[138,9943,1505],{"class":809},[138,9945,12],{"class":1419},[138,9947,1440],{"class":809},[138,9949,9950,9952,9954,9956,9958,9960,9962,9964,9966,9968,9970,9972],{"class":140,"line":164},[138,9951,3577],{"class":809},[138,9953,4510],{"class":1419},[138,9955,9419],{"class":1423},[138,9957,1430],{"class":809},[138,9959,1433],{"class":826},[138,9961,9426],{"class":830},[138,9963,1433],{"class":826},[138,9965,3065],{"class":809},[138,9967,9433],{"class":805},[138,9969,1505],{"class":809},[138,9971,4510],{"class":1419},[138,9973,1440],{"class":809},[138,9975,9976,9978,9980,9982,9984,9986,9988,9990,9992,9994,9996,9998],{"class":140,"line":170},[138,9977,3577],{"class":809},[138,9979,4510],{"class":1419},[138,9981,9419],{"class":1423},[138,9983,1430],{"class":809},[138,9985,1433],{"class":826},[138,9987,9454],{"class":830},[138,9989,1433],{"class":826},[138,9991,3065],{"class":809},[138,9993,9461],{"class":805},[138,9995,1505],{"class":809},[138,9997,4510],{"class":1419},[138,9999,1440],{"class":809},[138,10001,10002,10004,10006],{"class":140,"line":176},[138,10003,3625],{"class":809},[138,10005,4369],{"class":1419},[138,10007,1440],{"class":809},[138,10009,10010,10012,10014],{"class":140,"line":182},[138,10011,1505],{"class":809},[138,10013,3560],{"class":1419},[138,10015,1440],{"class":809},[138,10017,10018],{"class":140,"line":188},[138,10019,649],{"emptyLinePlaceholder":86},[138,10021,10022,10024,10026,10028],{"class":140,"line":194},[138,10023,1416],{"class":809},[138,10025,1420],{"class":1419},[138,10027,1424],{"class":1423},[138,10029,1440],{"class":809},[138,10031,10032,10034,10036,10038,10040,10042,10044,10047,10049,10051,10053,10055],{"class":140,"line":199},[138,10033,3735],{"class":794},[138,10035,2812],{"class":809},[138,10037,3050],{"class":805},[138,10039,954],{"class":809},[138,10041,3088],{"class":805},[138,10043,954],{"class":809},[138,10045,10046],{"class":805}," onMounted",[138,10048,2823],{"class":809},[138,10050,3748],{"class":794},[138,10052,957],{"class":826},[138,10054,1409],{"class":830},[138,10056,2361],{"class":826},[138,10058,10059],{"class":140,"line":204},[138,10060,649],{"emptyLinePlaceholder":86},[138,10062,10063,10065,10068,10070,10072,10074,10077],{"class":140,"line":209},[138,10064,2518],{"class":1073},[138,10066,10067],{"class":2521}," count",[138,10069,1146],{"class":1112},[138,10071,3050],{"class":801},[138,10073,806],{"class":805},[138,10075,10076],{"class":5295},"0",[138,10078,872],{"class":805},[138,10080,10081],{"class":140,"line":215},[138,10082,649],{"emptyLinePlaceholder":86},[138,10084,10085,10087,10090,10092,10094,10096,10098,10100],{"class":140,"line":221},[138,10086,2518],{"class":1073},[138,10088,10089],{"class":2521}," message",[138,10091,1146],{"class":1112},[138,10093,3088],{"class":801},[138,10095,806],{"class":805},[138,10097,3093],{"class":809},[138,10099,1074],{"class":1073},[138,10101,934],{"class":809},[138,10103,10104,10106,10108,10110,10112,10114],{"class":140,"line":227},[138,10105,3199],{"class":794},[138,10107,10067],{"class":805},[138,10109,363],{"class":809},[138,10111,3102],{"class":805},[138,10113,2765],{"class":1112},[138,10115,9548],{"class":5295},[138,10117,10118,10121,10123,10125],{"class":140,"line":233},[138,10119,10120],{"class":1112},"    ?",[138,10122,957],{"class":826},[138,10124,9602],{"class":830},[138,10126,2361],{"class":826},[138,10128,10129,10132,10134,10136,10138,10140,10142,10144,10146,10148],{"class":140,"line":239},[138,10130,10131],{"class":1112},"    :",[138,10133,9612],{"class":826},[138,10135,9615],{"class":830},[138,10137,3797],{"class":826},[138,10139,9588],{"class":805},[138,10141,363],{"class":826},[138,10143,3102],{"class":805},[138,10145,869],{"class":826},[138,10147,9628],{"class":830},[138,10149,9631],{"class":826},[138,10151,10152,10154],{"class":140,"line":245},[138,10153,869],{"class":809},[138,10155,872],{"class":805},[138,10157,10158],{"class":140,"line":251},[138,10159,649],{"emptyLinePlaceholder":86},[138,10161,10162,10164,10167,10169],{"class":140,"line":257},[138,10163,3824],{"class":1073},[138,10165,10166],{"class":801}," increment",[138,10168,3093],{"class":809},[138,10170,934],{"class":809},[138,10172,10173,10176,10178,10180],{"class":140,"line":263},[138,10174,10175],{"class":805},"  count",[138,10177,363],{"class":809},[138,10179,3102],{"class":805},[138,10181,9671],{"class":1112},[138,10183,10184],{"class":140,"line":269},[138,10185,2082],{"class":809},[138,10187,10188],{"class":140,"line":275},[138,10189,649],{"emptyLinePlaceholder":86},[138,10191,10192,10194,10197,10199],{"class":140,"line":281},[138,10193,3824],{"class":1073},[138,10195,10196],{"class":801}," reset",[138,10198,3093],{"class":809},[138,10200,934],{"class":809},[138,10202,10203,10205,10207,10209,10211],{"class":140,"line":287},[138,10204,10175],{"class":805},[138,10206,363],{"class":809},[138,10208,3102],{"class":805},[138,10210,1146],{"class":1112},[138,10212,9548],{"class":5295},[138,10214,10215],{"class":140,"line":293},[138,10216,2082],{"class":809},[138,10218,10219],{"class":140,"line":299},[138,10220,649],{"emptyLinePlaceholder":86},[138,10222,10223,10226,10228,10230,10232],{"class":140,"line":305},[138,10224,10225],{"class":801},"onMounted",[138,10227,806],{"class":805},[138,10229,3093],{"class":809},[138,10231,1074],{"class":1073},[138,10233,934],{"class":809},[138,10235,10236,10239,10241,10243,10245,10247,10249,10251,10253,10255,10257,10259],{"class":140,"line":311},[138,10237,10238],{"class":805},"  console",[138,10240,363],{"class":809},[138,10242,9727],{"class":801},[138,10244,806],{"class":815},[138,10246,834],{"class":826},[138,10248,9734],{"class":830},[138,10250,834],{"class":826},[138,10252,954],{"class":809},[138,10254,10067],{"class":805},[138,10256,363],{"class":809},[138,10258,3102],{"class":805},[138,10260,872],{"class":815},[138,10262,10263,10265],{"class":140,"line":317},[138,10264,869],{"class":809},[138,10266,872],{"class":805},[138,10268,10269,10271,10273],{"class":140,"line":323},[138,10270,1505],{"class":809},[138,10272,1420],{"class":1419},[138,10274,1440],{"class":809},[12,10276,10277],{},"À primeira vista, o código parece muito similar. E de fato é: para componentes simples, a diferença não é tão gritante. A grande vantagem começa a aparecer quando o componente cresce ou quando você precisa reutilizar lógica.",[27,10279],{},[30,10281,10283,10284,377,10286],{"id":10282},"uma-diferença-importante-ref-e-value","Uma diferença importante: ",[135,10285,7108],{},[135,10287,10288],{},".value",[12,10290,10291,10292],{},"Uma das primeiras dúvidas de quem migra para a Composition API é: ",[19,10293,10294,10295,10297],{},"por que precisamos de ",[135,10296,10288],{}," para acessar o dado?",[12,10299,10300,10301,10303,10304,10306,10307,10309,10310,10313,10314,363],{},"A razão é técnica. ",[135,10302,5010],{}," retorna um objeto reativo que encapsula o valor. Isso permite que o Vue rastreie as dependências corretamente. Dentro do ",[135,10305,6842],{},", você acessa e modifica o valor via ",[135,10308,10288],{},". Já no template, o Vue faz o \"unwrap\" automaticamente. Você escreve ",[135,10311,10312],{},"{{ count }}"," e não ",[135,10315,10316],{},"{{ count.value }}",[129,10318,10320],{"className":5084,"code":10319,"language":5086,"meta":75,"style":75},"const count = ref(0)\n\ncount.value++ \u002F\u002F dentro do script: usa .value\n\u002F\u002F {{ count }}       \u002F\u002F no template: sem .value, Vue desempacota automaticamente\n",[135,10321,10322,10338,10342,10356],{"__ignoreMap":75},[138,10323,10324,10326,10328,10330,10332,10334,10336],{"class":140,"line":141},[138,10325,2518],{"class":1073},[138,10327,10067],{"class":2521},[138,10329,1146],{"class":1112},[138,10331,3050],{"class":801},[138,10333,806],{"class":805},[138,10335,10076],{"class":5295},[138,10337,872],{"class":805},[138,10339,10340],{"class":140,"line":76},[138,10341,649],{"emptyLinePlaceholder":86},[138,10343,10344,10346,10348,10350,10353],{"class":140,"line":152},[138,10345,9588],{"class":805},[138,10347,363],{"class":809},[138,10349,3102],{"class":805},[138,10351,10352],{"class":1112},"++",[138,10354,10355],{"class":911}," \u002F\u002F dentro do script: usa .value\n",[138,10357,10358,10361],{"class":140,"line":158},[138,10359,10360],{"class":911},"\u002F\u002F {{ count }}",[138,10362,10363],{"class":911},"       \u002F\u002F no template: sem .value, Vue desempacota automaticamente\n",[12,10365,10366,10367,10369,10370,10372,10373,10375],{},"Para objetos e arrays, você pode usar ",[135,10368,9848],{}," em vez de ",[135,10371,5010],{},". Nesse caso, não há ",[135,10374,10288],{},", pois o próprio objeto é reativo:",[129,10377,10379],{"className":5084,"code":10378,"language":5086,"meta":75,"style":75},"const state = reactive({\n  count: 0,\n  name: 'Vue'\n})\n\nstate.count++ \u002F\u002F sem .value\nstate.name = 'Nuxt'\n",[135,10380,10381,10396,10406,10418,10424,10428,10441],{"__ignoreMap":75},[138,10382,10383,10385,10387,10389,10392,10394],{"class":140,"line":141},[138,10384,2518],{"class":1073},[138,10386,5214],{"class":2521},[138,10388,1146],{"class":1112},[138,10390,10391],{"class":801}," reactive",[138,10393,806],{"class":805},[138,10395,810],{"class":809},[138,10397,10398,10400,10402,10404],{"class":140,"line":76},[138,10399,10175],{"class":815},[138,10401,782],{"class":809},[138,10403,5296],{"class":5295},[138,10405,837],{"class":809},[138,10407,10408,10410,10412,10414,10416],{"class":140,"line":152},[138,10409,9508],{"class":815},[138,10411,782],{"class":809},[138,10413,957],{"class":826},[138,10415,9239],{"class":830},[138,10417,2361],{"class":826},[138,10419,10420,10422],{"class":140,"line":158},[138,10421,869],{"class":809},[138,10423,872],{"class":805},[138,10425,10426],{"class":140,"line":164},[138,10427,649],{"emptyLinePlaceholder":86},[138,10429,10430,10432,10434,10436,10438],{"class":140,"line":170},[138,10431,5202],{"class":805},[138,10433,363],{"class":809},[138,10435,9588],{"class":805},[138,10437,10352],{"class":1112},[138,10439,10440],{"class":911}," \u002F\u002F sem .value\n",[138,10442,10443,10445,10447,10450,10452,10454,10457],{"class":140,"line":176},[138,10444,5202],{"class":805},[138,10446,363],{"class":809},[138,10448,10449],{"class":805},"name ",[138,10451,1430],{"class":1112},[138,10453,957],{"class":826},[138,10455,10456],{"class":830},"Nuxt",[138,10458,2361],{"class":826},[12,10460,10461,10462,377,10464,10467,10468,10470,10471,10473],{},"A escolha entre ",[135,10463,7108],{},[135,10465,10466],{},"reactive"," é uma questão de preferência e contexto. Muitos desenvolvedores usam ",[135,10469,7108],{}," para tudo por consistência, enquanto outros preferem ",[135,10472,10466],{}," para agrupar estados relacionados.",[27,10475],{},[30,10477,10479],{"id":10478},"comparando-lado-a-lado-os-lifecycle-hooks","Comparando lado a lado: os lifecycle hooks",[12,10481,10482],{},"Uma das diferenças mais práticas entre as duas abordagens é como lidar com os hooks de ciclo de vida. Na Options API, eles são métodos do objeto de opções. Na Composition API, são funções importadas.",[1915,10484,10485,10493],{},[1918,10486,10487],{},[1921,10488,10489,10491],{},[1924,10490,9273],{},[1924,10492,9280],{},[1931,10494,10495,10504,10514,10525,10536,10548,10560],{},[1921,10496,10497,10501],{},[1936,10498,10499],{},[135,10500,9332],{},[1936,10502,10503],{},"(executa no setup)",[1921,10505,10506,10510],{},[1936,10507,10508],{},[135,10509,9329],{},[1936,10511,10512],{},[135,10513,10225],{},[1921,10515,10516,10520],{},[1936,10517,10518],{},[135,10519,9335],{},[1936,10521,10522],{},[135,10523,10524],{},"onUpdated",[1921,10526,10527,10531],{},[1936,10528,10529],{},[135,10530,9338],{},[1936,10532,10533],{},[135,10534,10535],{},"onUnmounted",[1921,10537,10538,10543],{},[1936,10539,10540],{},[135,10541,10542],{},"beforeMount",[1936,10544,10545],{},[135,10546,10547],{},"onBeforeMount",[1921,10549,10550,10555],{},[1936,10551,10552],{},[135,10553,10554],{},"beforeUpdate",[1936,10556,10557],{},[135,10558,10559],{},"onBeforeUpdate",[1921,10561,10562,10567],{},[1936,10563,10564],{},[135,10565,10566],{},"beforeUnmount",[1936,10568,10569],{},[135,10570,10571],{},"onBeforeUnmount",[12,10573,10574,10575,10578,10579,10581,10582,10584],{},"Vale destacar um comportamento interessante: na Composition API, você pode ",[19,10576,10577],{},"chamar o mesmo hook várias vezes"," dentro do mesmo componente, e todas as chamadas serão executadas. Na Options API, só há um ",[135,10580,9329],{},", um ",[135,10583,9335],{},", etc.",[129,10586,10588],{"className":5084,"code":10587,"language":5086,"meta":75,"style":75},"onMounted(() => {\n  console.log('Inicializando o mapa...')\n})\n\nonMounted(() => {\n  console.log('Buscando dados iniciais...')\n})\n",[135,10589,10590,10602,10621,10627,10631,10643,10662],{"__ignoreMap":75},[138,10591,10592,10594,10596,10598,10600],{"class":140,"line":141},[138,10593,10225],{"class":801},[138,10595,806],{"class":805},[138,10597,3093],{"class":809},[138,10599,1074],{"class":1073},[138,10601,934],{"class":809},[138,10603,10604,10606,10608,10610,10612,10614,10617,10619],{"class":140,"line":76},[138,10605,10238],{"class":805},[138,10607,363],{"class":809},[138,10609,9727],{"class":801},[138,10611,806],{"class":815},[138,10613,834],{"class":826},[138,10615,10616],{"class":830},"Inicializando o mapa...",[138,10618,834],{"class":826},[138,10620,872],{"class":815},[138,10622,10623,10625],{"class":140,"line":152},[138,10624,869],{"class":809},[138,10626,872],{"class":805},[138,10628,10629],{"class":140,"line":158},[138,10630,649],{"emptyLinePlaceholder":86},[138,10632,10633,10635,10637,10639,10641],{"class":140,"line":164},[138,10634,10225],{"class":801},[138,10636,806],{"class":805},[138,10638,3093],{"class":809},[138,10640,1074],{"class":1073},[138,10642,934],{"class":809},[138,10644,10645,10647,10649,10651,10653,10655,10658,10660],{"class":140,"line":170},[138,10646,10238],{"class":805},[138,10648,363],{"class":809},[138,10650,9727],{"class":801},[138,10652,806],{"class":815},[138,10654,834],{"class":826},[138,10656,10657],{"class":830},"Buscando dados iniciais...",[138,10659,834],{"class":826},[138,10661,872],{"class":815},[138,10663,10664,10666],{"class":140,"line":176},[138,10665,869],{"class":809},[138,10667,872],{"class":805},[12,10669,10670],{},"Isso pode parecer estranho no começo, mas faz muito sentido quando você começa a extrair lógica para composables, pois cada um pode registrar seus próprios hooks sem interferir nos outros.",[27,10672],{},[30,10674,10676],{"id":10675},"o-grande-diferencial-os-composables","O grande diferencial: os Composables",[12,10678,10679,10680,10683],{},"Se a Composition API tem um superpoder, esse superpoder tem nome: ",[19,10681,10682],{},"composables",". São funções que encapsulam lógica com estado e podem ser reutilizadas em qualquer componente, sem os problemas dos mixins.",[12,10685,10686,10687,10689],{},"Por convenção, composables começam com ",[135,10688,8267],{}," (assim como os hooks do React). Veja um exemplo real de um composable para rastrear a posição do mouse:",[129,10691,10693],{"className":5084,"code":10692,"language":5086,"meta":75,"style":75},"\u002F\u002F useMouse.js\nimport { ref, onMounted, onUnmounted } from 'vue'\n\nexport function useMouse() {\n  const x = ref(0)\n  const y = ref(0)\n\n  function update(event) {\n    x.value = event.pageX\n    y.value = event.pageY\n  }\n\n  onMounted(() => window.addEventListener('mousemove', update))\n  onUnmounted(() => window.removeEventListener('mousemove', update))\n\n  return { x, y }\n}\n",[135,10694,10695,10700,10727,10731,10744,10761,10778,10782,10798,10817,10835,10839,10843,10877,10909,10913,10927],{"__ignoreMap":75},[138,10696,10697],{"class":140,"line":141},[138,10698,10699],{"class":911},"\u002F\u002F useMouse.js\n",[138,10701,10702,10704,10706,10708,10710,10712,10714,10717,10719,10721,10723,10725],{"class":140,"line":76},[138,10703,3735],{"class":794},[138,10705,2812],{"class":809},[138,10707,3050],{"class":805},[138,10709,954],{"class":809},[138,10711,10046],{"class":805},[138,10713,954],{"class":809},[138,10715,10716],{"class":805}," onUnmounted",[138,10718,2823],{"class":809},[138,10720,3748],{"class":794},[138,10722,957],{"class":826},[138,10724,1409],{"class":830},[138,10726,2361],{"class":826},[138,10728,10729],{"class":140,"line":152},[138,10730,649],{"emptyLinePlaceholder":86},[138,10732,10733,10735,10737,10740,10742],{"class":140,"line":158},[138,10734,795],{"class":794},[138,10736,3931],{"class":1073},[138,10738,10739],{"class":801}," useMouse",[138,10741,3093],{"class":809},[138,10743,934],{"class":809},[138,10745,10746,10748,10751,10753,10755,10757,10759],{"class":140,"line":164},[138,10747,2607],{"class":1073},[138,10749,10750],{"class":2521}," x",[138,10752,1146],{"class":1112},[138,10754,3050],{"class":801},[138,10756,806],{"class":815},[138,10758,10076],{"class":5295},[138,10760,872],{"class":815},[138,10762,10763,10765,10768,10770,10772,10774,10776],{"class":140,"line":170},[138,10764,2607],{"class":1073},[138,10766,10767],{"class":2521}," y",[138,10769,1146],{"class":1112},[138,10771,3050],{"class":801},[138,10773,806],{"class":815},[138,10775,10076],{"class":5295},[138,10777,872],{"class":815},[138,10779,10780],{"class":140,"line":176},[138,10781,649],{"emptyLinePlaceholder":86},[138,10783,10784,10786,10789,10791,10794,10796],{"class":140,"line":182},[138,10785,3124],{"class":1073},[138,10787,10788],{"class":801}," update",[138,10790,806],{"class":809},[138,10792,10793],{"class":1045},"event",[138,10795,1049],{"class":809},[138,10797,934],{"class":809},[138,10799,10800,10803,10805,10807,10809,10812,10814],{"class":140,"line":188},[138,10801,10802],{"class":805},"    x",[138,10804,363],{"class":809},[138,10806,3102],{"class":805},[138,10808,1146],{"class":1112},[138,10810,10811],{"class":805}," event",[138,10813,363],{"class":809},[138,10815,10816],{"class":805},"pageX\n",[138,10818,10819,10822,10824,10826,10828,10830,10832],{"class":140,"line":194},[138,10820,10821],{"class":805},"    y",[138,10823,363],{"class":809},[138,10825,3102],{"class":805},[138,10827,1146],{"class":1112},[138,10829,10811],{"class":805},[138,10831,363],{"class":809},[138,10833,10834],{"class":805},"pageY\n",[138,10836,10837],{"class":140,"line":199},[138,10838,1184],{"class":809},[138,10840,10841],{"class":140,"line":204},[138,10842,649],{"emptyLinePlaceholder":86},[138,10844,10845,10848,10850,10852,10854,10857,10859,10862,10864,10866,10869,10871,10873,10875],{"class":140,"line":209},[138,10846,10847],{"class":801},"  onMounted",[138,10849,806],{"class":815},[138,10851,3093],{"class":809},[138,10853,1074],{"class":1073},[138,10855,10856],{"class":805}," window",[138,10858,363],{"class":809},[138,10860,10861],{"class":801},"addEventListener",[138,10863,806],{"class":815},[138,10865,834],{"class":826},[138,10867,10868],{"class":830},"mousemove",[138,10870,834],{"class":826},[138,10872,954],{"class":809},[138,10874,10788],{"class":805},[138,10876,4205],{"class":815},[138,10878,10879,10882,10884,10886,10888,10890,10892,10895,10897,10899,10901,10903,10905,10907],{"class":140,"line":215},[138,10880,10881],{"class":801},"  onUnmounted",[138,10883,806],{"class":815},[138,10885,3093],{"class":809},[138,10887,1074],{"class":1073},[138,10889,10856],{"class":805},[138,10891,363],{"class":809},[138,10893,10894],{"class":801},"removeEventListener",[138,10896,806],{"class":815},[138,10898,834],{"class":826},[138,10900,10868],{"class":830},[138,10902,834],{"class":826},[138,10904,954],{"class":809},[138,10906,10788],{"class":805},[138,10908,4205],{"class":815},[138,10910,10911],{"class":140,"line":221},[138,10912,649],{"emptyLinePlaceholder":86},[138,10914,10915,10917,10919,10921,10923,10925],{"class":140,"line":227},[138,10916,3199],{"class":794},[138,10918,2812],{"class":809},[138,10920,10750],{"class":805},[138,10922,954],{"class":809},[138,10924,10767],{"class":805},[138,10926,1015],{"class":809},[138,10928,10929],{"class":140,"line":233},[138,10930,2082],{"class":809},[12,10932,10933],{},"E agora, para usar em qualquer componente:",[129,10935,10937],{"className":1407,"code":10936,"language":1409,"meta":75,"style":75},"\u003Cscript setup>\nimport { useMouse } from '.\u002Fcomposables\u002FuseMouse'\n\nconst { x, y } = useMouse()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>Posição do mouse: {{ x }}, {{ y }}\u003C\u002Fp>\n\u003C\u002Ftemplate>\n",[135,10938,10939,10949,10968,10972,10992,11000,11004,11012,11029],{"__ignoreMap":75},[138,10940,10941,10943,10945,10947],{"class":140,"line":141},[138,10942,1416],{"class":809},[138,10944,1420],{"class":1419},[138,10946,1424],{"class":1423},[138,10948,1440],{"class":809},[138,10950,10951,10953,10955,10957,10959,10961,10963,10966],{"class":140,"line":76},[138,10952,3735],{"class":794},[138,10954,2812],{"class":809},[138,10956,10739],{"class":805},[138,10958,2823],{"class":809},[138,10960,3748],{"class":794},[138,10962,957],{"class":826},[138,10964,10965],{"class":830},".\u002Fcomposables\u002FuseMouse",[138,10967,2361],{"class":826},[138,10969,10970],{"class":140,"line":152},[138,10971,649],{"emptyLinePlaceholder":86},[138,10973,10974,10976,10978,10980,10982,10984,10986,10988,10990],{"class":140,"line":158},[138,10975,2518],{"class":1073},[138,10977,2812],{"class":809},[138,10979,10750],{"class":2521},[138,10981,954],{"class":809},[138,10983,10767],{"class":2521},[138,10985,2823],{"class":809},[138,10987,1146],{"class":1112},[138,10989,10739],{"class":801},[138,10991,2796],{"class":805},[138,10993,10994,10996,10998],{"class":140,"line":164},[138,10995,1505],{"class":809},[138,10997,1420],{"class":1419},[138,10999,1440],{"class":809},[138,11001,11002],{"class":140,"line":170},[138,11003,649],{"emptyLinePlaceholder":86},[138,11005,11006,11008,11010],{"class":140,"line":176},[138,11007,1416],{"class":809},[138,11009,3560],{"class":1419},[138,11011,1440],{"class":809},[138,11013,11014,11016,11018,11020,11023,11025,11027],{"class":140,"line":182},[138,11015,3567],{"class":809},[138,11017,12],{"class":1419},[138,11019,3065],{"class":809},[138,11021,11022],{"class":805},"Posição do mouse: {{ x }}, {{ y }}",[138,11024,1505],{"class":809},[138,11026,12],{"class":1419},[138,11028,1440],{"class":809},[138,11030,11031,11033,11035],{"class":140,"line":188},[138,11032,1505],{"class":809},[138,11034,3560],{"class":1419},[138,11036,1440],{"class":809},[12,11038,11039,11040,11043],{},"Repare o que acontece aqui: a lógica de adicionar e remover o event listener está ",[19,11041,11042],{},"completamente encapsulada"," no composable. O componente não precisa saber como isso funciona internamente, ele só consome o resultado. Não há risco de colisão de nomes, não há ambiguidade sobre a origem dos dados, e o composable pode ser testado de forma independente.",[12,11045,11046],{},"Isso seria muito mais difícil de alcançar com mixins na Options API.",[27,11048],{},[30,11050,11052],{"id":11051},"typescript-onde-a-composition-api-brilha-mais","TypeScript: onde a Composition API brilha mais",[12,11054,11055],{},"Com o crescimento do TypeScript no ecossistema frontend, essa é uma dimensão cada vez mais relevante na escolha entre as duas APIs.",[12,11057,11058,11059,11061],{},"A Options API foi projetada antes do TypeScript se tornar popular, e isso cria fricção. O sistema de tipos precisa \"adivinhar\" o que está disponível em ",[135,11060,8821],{},", o que exige malabarismos internos no Vue. Para mixins, a situação piora ainda mais, pois o TypeScript não consegue inferir de onde vêm as propriedades.",[12,11063,11064],{},"A Composition API, por outro lado, trabalha com variáveis e funções comuns do JavaScript, algo que o TypeScript entende naturalmente. A inferência de tipos funciona quase que automaticamente:",[129,11066,11068],{"className":785,"code":11067,"language":787,"meta":75,"style":75},"\u003Cscript setup lang=\"ts\">\nimport { ref, computed } from 'vue'\n\nconst count = ref(0)          \u002F\u002F TypeScript infere: Ref\u003Cnumber>\nconst double = computed(() => count.value * 2) \u002F\u002F infere: ComputedRef\u003Cnumber>\n\nfunction increment(): void {\n  count.value++\n}\n\u003C\u002Fscript>\n",[135,11069,11070,11087,11109,11113,11133,11168,11172,11187,11197,11201],{"__ignoreMap":75},[138,11071,11072,11074,11077,11079,11081,11083,11085],{"class":140,"line":141},[138,11073,1416],{"class":1112},[138,11075,11076],{"class":805},"script setup lang",[138,11078,1430],{"class":1112},[138,11080,1433],{"class":826},[138,11082,787],{"class":830},[138,11084,1433],{"class":826},[138,11086,1440],{"class":1112},[138,11088,11089,11091,11093,11095,11097,11099,11101,11103,11105,11107],{"class":140,"line":76},[138,11090,3735],{"class":794},[138,11092,2812],{"class":809},[138,11094,3050],{"class":805},[138,11096,954],{"class":809},[138,11098,3088],{"class":805},[138,11100,2823],{"class":809},[138,11102,3748],{"class":794},[138,11104,957],{"class":826},[138,11106,1409],{"class":830},[138,11108,2361],{"class":826},[138,11110,11111],{"class":140,"line":152},[138,11112,649],{"emptyLinePlaceholder":86},[138,11114,11115,11117,11119,11121,11123,11125,11127,11130],{"class":140,"line":158},[138,11116,2518],{"class":1073},[138,11118,10067],{"class":2521},[138,11120,1146],{"class":1112},[138,11122,3050],{"class":801},[138,11124,806],{"class":805},[138,11126,10076],{"class":5295},[138,11128,11129],{"class":805},")          ",[138,11131,11132],{"class":911},"\u002F\u002F TypeScript infere: Ref\u003Cnumber>\n",[138,11134,11135,11137,11140,11142,11144,11146,11148,11150,11152,11154,11157,11160,11163,11165],{"class":140,"line":164},[138,11136,2518],{"class":1073},[138,11138,11139],{"class":2521}," double",[138,11141,1146],{"class":1112},[138,11143,3088],{"class":801},[138,11145,806],{"class":805},[138,11147,3093],{"class":809},[138,11149,1074],{"class":1073},[138,11151,10067],{"class":805},[138,11153,363],{"class":809},[138,11155,11156],{"class":805},"value ",[138,11158,11159],{"class":1112},"*",[138,11161,11162],{"class":5295}," 2",[138,11164,1109],{"class":805},[138,11166,11167],{"class":911},"\u002F\u002F infere: ComputedRef\u003Cnumber>\n",[138,11169,11170],{"class":140,"line":170},[138,11171,649],{"emptyLinePlaceholder":86},[138,11173,11174,11176,11178,11180,11182,11185],{"class":140,"line":176},[138,11175,3824],{"class":1073},[138,11177,10166],{"class":801},[138,11179,3093],{"class":809},[138,11181,782],{"class":1112},[138,11183,11184],{"class":3061}," void",[138,11186,934],{"class":809},[138,11188,11189,11191,11193,11195],{"class":140,"line":182},[138,11190,10175],{"class":805},[138,11192,363],{"class":809},[138,11194,3102],{"class":805},[138,11196,9671],{"class":1112},[138,11198,11199],{"class":140,"line":188},[138,11200,2082],{"class":809},[138,11202,11203,11205,11207],{"class":140,"line":194},[138,11204,1505],{"class":1112},[138,11206,1420],{"class":805},[138,11208,1440],{"class":1112},[12,11210,11211,11212,11215],{},"Para projetos que usam TypeScript, a Composition API é claramente a abordagem mais ergonômica. Com a Options API e TypeScript, é necessário usar ",[135,11213,11214],{},"defineComponent"," e ainda assim algumas situações de tipagem ficam trabalhosas.",[27,11217],{},[30,11219,11221],{"id":11220},"performance-e-bundle-size","Performance e bundle size",[12,11223,11224,11225,11227,11228,11230,11231,11233],{},"Outro ponto que a documentação oficial destaca é que código escrito com ",[135,11226,6842],{}," é mais eficiente na fase de minificação. Isso acontece porque, diferente da Options API onde as propriedades são acessadas via ",[135,11229,8821],{},", no ",[135,11232,6842],{}," tudo é declarado no escopo local, e os minificadores conseguem renomear variáveis locais de forma muito mais agressiva.",[12,11235,11236],{},"O resultado prático é um bundle final um pouco menor, o que impacta positivamente o tempo de carregamento da aplicação, especialmente relevante para projetos grandes.",[12,11238,11239],{},"Além disso, se você usar exclusivamente a Composition API em um projeto, é possível configurar uma flag de compilação que remove o código da Options API do bundle do Vue em si, economizando alguns kilobytes adicionais.",[27,11241],{},[30,11243,11245],{"id":11244},"podem-ser-usadas-juntas","Podem ser usadas juntas?",[12,11247,11248,11249,11251],{},"Sim! Uma dúvida comum é se é possível misturar as duas abordagens. A resposta é sim: você pode usar ",[135,11250,7104],{}," como uma opção dentro de um componente Options API:",[129,11253,11255],{"className":1407,"code":11254,"language":1409,"meta":75,"style":75},"\u003Cscript>\nimport { ref } from 'vue'\n\nexport default {\n  data() {\n    return {\n      legacyData: 'sou da Options API'\n    }\n  },\n\n  setup() {\n    const newData = ref('sou da Composition API')\n    return { newData }\n  }\n}\n\u003C\u002Fscript>\n",[135,11256,11257,11265,11283,11287,11295,11303,11309,11323,11327,11331,11335,11344,11366,11376,11380,11384],{"__ignoreMap":75},[138,11258,11259,11261,11263],{"class":140,"line":141},[138,11260,1416],{"class":809},[138,11262,1420],{"class":1419},[138,11264,1440],{"class":809},[138,11266,11267,11269,11271,11273,11275,11277,11279,11281],{"class":140,"line":76},[138,11268,3735],{"class":794},[138,11270,2812],{"class":809},[138,11272,3050],{"class":805},[138,11274,2823],{"class":809},[138,11276,3748],{"class":794},[138,11278,957],{"class":826},[138,11280,1409],{"class":830},[138,11282,2361],{"class":826},[138,11284,11285],{"class":140,"line":152},[138,11286,649],{"emptyLinePlaceholder":86},[138,11288,11289,11291,11293],{"class":140,"line":158},[138,11290,795],{"class":794},[138,11292,798],{"class":794},[138,11294,934],{"class":809},[138,11296,11297,11299,11301],{"class":140,"line":164},[138,11298,9528],{"class":5196},[138,11300,3093],{"class":809},[138,11302,934],{"class":809},[138,11304,11305,11307],{"class":140,"line":170},[138,11306,2900],{"class":794},[138,11308,934],{"class":809},[138,11310,11311,11314,11316,11318,11321],{"class":140,"line":176},[138,11312,11313],{"class":815},"      legacyData",[138,11315,782],{"class":809},[138,11317,957],{"class":826},[138,11319,11320],{"class":830},"sou da Options API",[138,11322,2361],{"class":826},[138,11324,11325],{"class":140,"line":182},[138,11326,1179],{"class":809},[138,11328,11329],{"class":140,"line":188},[138,11330,972],{"class":809},[138,11332,11333],{"class":140,"line":194},[138,11334,649],{"emptyLinePlaceholder":86},[138,11336,11337,11340,11342],{"class":140,"line":199},[138,11338,11339],{"class":5196},"  setup",[138,11341,3093],{"class":809},[138,11343,934],{"class":809},[138,11345,11346,11348,11351,11353,11355,11357,11359,11362,11364],{"class":140,"line":204},[138,11347,3990],{"class":1073},[138,11349,11350],{"class":2521}," newData",[138,11352,1146],{"class":1112},[138,11354,3050],{"class":801},[138,11356,806],{"class":815},[138,11358,834],{"class":826},[138,11360,11361],{"class":830},"sou da Composition API",[138,11363,834],{"class":826},[138,11365,872],{"class":815},[138,11367,11368,11370,11372,11374],{"class":140,"line":209},[138,11369,2900],{"class":794},[138,11371,2812],{"class":809},[138,11373,11350],{"class":805},[138,11375,1015],{"class":809},[138,11377,11378],{"class":140,"line":215},[138,11379,1184],{"class":809},[138,11381,11382],{"class":140,"line":221},[138,11383,2082],{"class":809},[138,11385,11386,11388,11390],{"class":140,"line":227},[138,11387,1505],{"class":809},[138,11389,1420],{"class":1419},[138,11391,1440],{"class":809},[12,11393,11394],{},"Isso é especialmente útil durante migrações graduais, pois você pode ir introduzindo a Composition API em componentes existentes sem precisar reescrever tudo de uma vez.",[12,11396,11397,11398,11401,11402,11404,11405,1207,11407,11409],{},"O que ",[19,11399,11400],{},"não"," é recomendado é usar ",[135,11403,6842],{}," (a sintaxe mais moderna) junto com opções tradicionais como ",[135,11406,5007],{},[135,11408,9302],{}," no mesmo componente. Nesse caso, você deve escolher um ou outro.",[27,11411],{},[30,11413,11415],{"id":11414},"quando-usar-cada-uma","Quando usar cada uma?",[12,11417,11418],{},"Depois de tudo isso, a pergunta inevitável: quando optar por cada abordagem?",[12,11420,11421],{},[19,11422,11423],{},"Options API faz sentido quando:",[402,11425,11426,11429,11432,11435],{},[405,11427,11428],{},"Você está iniciando no Vue e quer uma curva de aprendizado menor",[405,11430,11431],{},"O projeto é pequeno ou de baixa complexidade",[405,11433,11434],{},"A equipe tem familiaridade com Vue 2 e a migração precisa ser gradual",[405,11436,11437],{},"Componentes têm responsabilidades bem delimitadas e não crescem muito",[12,11439,11440],{},[19,11441,11442],{},"Composition API faz sentido quando:",[402,11444,11445,11448,11451,11454,11457],{},[405,11446,11447],{},"O projeto é de médio a grande porte",[405,11449,11450],{},"Você usa TypeScript (ou pretende usar)",[405,11452,11453],{},"Há lógica que precisa ser reutilizada entre múltiplos componentes",[405,11455,11456],{},"A equipe preza por testes unitários, já que composables são muito mais fáceis de testar isoladamente",[405,11458,11459],{},"Você está construindo uma biblioteca de componentes ou um design system",[12,11461,11462,11465,11466,11468],{},[19,11463,11464],{},"Na prática:"," projetos novos iniciados com Vue 3 dificilmente têm motivo para escolher a Options API como padrão. A Composition API com ",[135,11467,6842],{}," é a direção clara do framework: é o que a documentação recomenda, é o que o ecossistema (Nuxt 3, Pinia, VueUse) adota nativamente.",[27,11470],{},[30,11472,11474],{"id":11473},"um-exemplo-real-de-organização-com-composition-api","Um exemplo real de organização com Composition API",[12,11476,11477],{},"Para fechar, um exemplo mais próximo do mundo real: um componente que busca uma lista de usuários de uma API.",[129,11479,11481],{"className":1407,"code":11480,"language":1409,"meta":75,"style":75},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp v-if=\"loading\">Carregando...\u003C\u002Fp>\n    \u003Cp v-if=\"error\">Erro: {{ error }}\u003C\u002Fp>\n    \u003Cul v-if=\"!loading && !error\">\n      \u003Cli v-for=\"user in users\" :key=\"user.id\">{{ user.name }}\u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref, onMounted } from 'vue'\n\nconst users = ref([])\nconst loading = ref(false)\nconst error = ref(null)\n\nasync function fetchUsers() {\n  loading.value = true\n  error.value = null\n\n  try {\n    const response = await fetch('https:\u002F\u002Fjsonplaceholder.typicode.com\u002Fusers')\n    users.value = await response.json()\n  } catch (err) {\n    error.value = err.message\n  } finally {\n    loading.value = false\n  }\n}\n\nonMounted(fetchUsers)\n\u003C\u002Fscript>\n",[135,11482,11483,11491,11499,11528,11555,11574,11615,11623,11631,11639,11643,11653,11675,11679,11692,11710,11726,11730,11743,11757,11770,11774,11781,11804,11825,11841,11860,11869,11882,11886,11890,11894,11901],{"__ignoreMap":75},[138,11484,11485,11487,11489],{"class":140,"line":141},[138,11486,1416],{"class":809},[138,11488,3560],{"class":1419},[138,11490,1440],{"class":809},[138,11492,11493,11495,11497],{"class":140,"line":76},[138,11494,3567],{"class":809},[138,11496,4369],{"class":1419},[138,11498,1440],{"class":809},[138,11500,11501,11503,11505,11508,11510,11512,11515,11517,11519,11522,11524,11526],{"class":140,"line":152},[138,11502,3577],{"class":809},[138,11504,12],{"class":1419},[138,11506,11507],{"class":1423}," v-if",[138,11509,1430],{"class":809},[138,11511,1433],{"class":826},[138,11513,11514],{"class":830},"loading",[138,11516,1433],{"class":826},[138,11518,3065],{"class":809},[138,11520,11521],{"class":805},"Carregando...",[138,11523,1505],{"class":809},[138,11525,12],{"class":1419},[138,11527,1440],{"class":809},[138,11529,11530,11532,11534,11536,11538,11540,11542,11544,11546,11549,11551,11553],{"class":140,"line":158},[138,11531,3577],{"class":809},[138,11533,12],{"class":1419},[138,11535,11507],{"class":1423},[138,11537,1430],{"class":809},[138,11539,1433],{"class":826},[138,11541,2883],{"class":830},[138,11543,1433],{"class":826},[138,11545,3065],{"class":809},[138,11547,11548],{"class":805},"Erro: {{ error }}",[138,11550,1505],{"class":809},[138,11552,12],{"class":1419},[138,11554,1440],{"class":809},[138,11556,11557,11559,11561,11563,11565,11567,11570,11572],{"class":140,"line":164},[138,11558,3577],{"class":809},[138,11560,402],{"class":1419},[138,11562,11507],{"class":1423},[138,11564,1430],{"class":809},[138,11566,1433],{"class":826},[138,11568,11569],{"class":830},"!loading && !error",[138,11571,1433],{"class":826},[138,11573,1440],{"class":809},[138,11575,11576,11578,11580,11583,11585,11587,11590,11592,11595,11597,11599,11602,11604,11606,11609,11611,11613],{"class":140,"line":170},[138,11577,4335],{"class":809},[138,11579,405],{"class":1419},[138,11581,11582],{"class":1423}," v-for",[138,11584,1430],{"class":809},[138,11586,1433],{"class":826},[138,11588,11589],{"class":830},"user in users",[138,11591,1433],{"class":826},[138,11593,11594],{"class":1423}," :key",[138,11596,1430],{"class":809},[138,11598,1433],{"class":826},[138,11600,11601],{"class":830},"user.id",[138,11603,1433],{"class":826},[138,11605,3065],{"class":809},[138,11607,11608],{"class":805},"{{ user.name }}",[138,11610,1505],{"class":809},[138,11612,405],{"class":1419},[138,11614,1440],{"class":809},[138,11616,11617,11619,11621],{"class":140,"line":176},[138,11618,4358],{"class":809},[138,11620,402],{"class":1419},[138,11622,1440],{"class":809},[138,11624,11625,11627,11629],{"class":140,"line":182},[138,11626,3625],{"class":809},[138,11628,4369],{"class":1419},[138,11630,1440],{"class":809},[138,11632,11633,11635,11637],{"class":140,"line":188},[138,11634,1505],{"class":809},[138,11636,3560],{"class":1419},[138,11638,1440],{"class":809},[138,11640,11641],{"class":140,"line":194},[138,11642,649],{"emptyLinePlaceholder":86},[138,11644,11645,11647,11649,11651],{"class":140,"line":199},[138,11646,1416],{"class":809},[138,11648,1420],{"class":1419},[138,11650,1424],{"class":1423},[138,11652,1440],{"class":809},[138,11654,11655,11657,11659,11661,11663,11665,11667,11669,11671,11673],{"class":140,"line":204},[138,11656,3735],{"class":794},[138,11658,2812],{"class":809},[138,11660,3050],{"class":805},[138,11662,954],{"class":809},[138,11664,10046],{"class":805},[138,11666,2823],{"class":809},[138,11668,3748],{"class":794},[138,11670,957],{"class":826},[138,11672,1409],{"class":830},[138,11674,2361],{"class":826},[138,11676,11677],{"class":140,"line":209},[138,11678,649],{"emptyLinePlaceholder":86},[138,11680,11681,11683,11686,11688,11690],{"class":140,"line":215},[138,11682,2518],{"class":1073},[138,11684,11685],{"class":2521}," users",[138,11687,1146],{"class":1112},[138,11689,3050],{"class":801},[138,11691,7222],{"class":805},[138,11693,11694,11696,11699,11701,11703,11705,11708],{"class":140,"line":221},[138,11695,2518],{"class":1073},[138,11697,11698],{"class":2521}," loading",[138,11700,1146],{"class":1112},[138,11702,3050],{"class":801},[138,11704,806],{"class":805},[138,11706,11707],{"class":3862},"false",[138,11709,872],{"class":805},[138,11711,11712,11714,11716,11718,11720,11722,11724],{"class":140,"line":227},[138,11713,2518],{"class":1073},[138,11715,2820],{"class":2521},[138,11717,1146],{"class":1112},[138,11719,3050],{"class":801},[138,11721,806],{"class":805},[138,11723,3070],{"class":2041},[138,11725,872],{"class":805},[138,11727,11728],{"class":140,"line":233},[138,11729,649],{"emptyLinePlaceholder":86},[138,11731,11732,11734,11736,11739,11741],{"class":140,"line":239},[138,11733,2591],{"class":1073},[138,11735,3931],{"class":1073},[138,11737,11738],{"class":801}," fetchUsers",[138,11740,3093],{"class":809},[138,11742,934],{"class":809},[138,11744,11745,11748,11750,11752,11754],{"class":140,"line":245},[138,11746,11747],{"class":805},"  loading",[138,11749,363],{"class":809},[138,11751,3102],{"class":805},[138,11753,1146],{"class":1112},[138,11755,11756],{"class":3862}," true\n",[138,11758,11759,11762,11764,11766,11768],{"class":140,"line":251},[138,11760,11761],{"class":805},"  error",[138,11763,363],{"class":809},[138,11765,3102],{"class":805},[138,11767,1146],{"class":1112},[138,11769,3186],{"class":2041},[138,11771,11772],{"class":140,"line":257},[138,11773,649],{"emptyLinePlaceholder":86},[138,11775,11776,11779],{"class":140,"line":263},[138,11777,11778],{"class":794},"  try",[138,11780,934],{"class":809},[138,11782,11783,11785,11787,11789,11791,11793,11795,11797,11800,11802],{"class":140,"line":269},[138,11784,3990],{"class":1073},[138,11786,5539],{"class":2521},[138,11788,1146],{"class":1112},[138,11790,2828],{"class":794},[138,11792,5546],{"class":801},[138,11794,806],{"class":815},[138,11796,834],{"class":826},[138,11798,11799],{"class":830},"https:\u002F\u002Fjsonplaceholder.typicode.com\u002Fusers",[138,11801,834],{"class":826},[138,11803,872],{"class":815},[138,11805,11806,11809,11811,11813,11815,11817,11819,11821,11823],{"class":140,"line":275},[138,11807,11808],{"class":805},"    users",[138,11810,363],{"class":809},[138,11812,3102],{"class":805},[138,11814,1146],{"class":1112},[138,11816,2828],{"class":794},[138,11818,5539],{"class":805},[138,11820,363],{"class":809},[138,11822,2030],{"class":801},[138,11824,2796],{"class":815},[138,11826,11827,11829,11832,11834,11837,11839],{"class":140,"line":281},[138,11828,2863],{"class":809},[138,11830,11831],{"class":794}," catch",[138,11833,1084],{"class":815},[138,11835,11836],{"class":805},"err",[138,11838,1109],{"class":815},[138,11840,810],{"class":809},[138,11842,11843,11846,11848,11850,11852,11855,11857],{"class":140,"line":287},[138,11844,11845],{"class":805},"    error",[138,11847,363],{"class":809},[138,11849,3102],{"class":805},[138,11851,1146],{"class":1112},[138,11853,11854],{"class":805}," err",[138,11856,363],{"class":809},[138,11858,11859],{"class":805},"message\n",[138,11861,11862,11864,11867],{"class":140,"line":293},[138,11863,2863],{"class":809},[138,11865,11866],{"class":794}," finally",[138,11868,934],{"class":809},[138,11870,11871,11874,11876,11878,11880],{"class":140,"line":299},[138,11872,11873],{"class":805},"    loading",[138,11875,363],{"class":809},[138,11877,3102],{"class":805},[138,11879,1146],{"class":1112},[138,11881,3863],{"class":3862},[138,11883,11884],{"class":140,"line":305},[138,11885,1184],{"class":809},[138,11887,11888],{"class":140,"line":311},[138,11889,2082],{"class":809},[138,11891,11892],{"class":140,"line":317},[138,11893,649],{"emptyLinePlaceholder":86},[138,11895,11896,11898],{"class":140,"line":323},[138,11897,10225],{"class":801},[138,11899,11900],{"class":805},"(fetchUsers)\n",[138,11902,11903,11905,11907],{"class":140,"line":329},[138,11904,1505],{"class":809},[138,11906,1420],{"class":1419},[138,11908,1440],{"class":809},[12,11910,11911],{},"Agora imagine que essa lógica de fetch (loading, error, dados) precisa ser reutilizada em vários lugares. Com a Composition API, você extrai para um composable:",[129,11913,11915],{"className":5084,"code":11914,"language":5086,"meta":75,"style":75},"\u002F\u002F useFetch.js\nimport { ref } from 'vue'\n\nexport function useFetch(url) {\n  const data = ref(null)\n  const loading = ref(false)\n  const error = ref(null)\n\n  async function execute() {\n    loading.value = true\n    error.value = null\n\n    try {\n      const response = await fetch(url)\n      data.value = await response.json()\n    } catch (err) {\n      error.value = err.message\n    } finally {\n      loading.value = false\n    }\n  }\n\n  return { data, loading, error, execute }\n}\n",[135,11916,11917,11922,11940,11944,11962,11978,11994,12010,12014,12028,12040,12052,12056,12063,12081,12102,12117,12134,12142,12155,12159,12163,12167,12189],{"__ignoreMap":75},[138,11918,11919],{"class":140,"line":141},[138,11920,11921],{"class":911},"\u002F\u002F useFetch.js\n",[138,11923,11924,11926,11928,11930,11932,11934,11936,11938],{"class":140,"line":76},[138,11925,3735],{"class":794},[138,11927,2812],{"class":809},[138,11929,3050],{"class":805},[138,11931,2823],{"class":809},[138,11933,3748],{"class":794},[138,11935,957],{"class":826},[138,11937,1409],{"class":830},[138,11939,2361],{"class":826},[138,11941,11942],{"class":140,"line":152},[138,11943,649],{"emptyLinePlaceholder":86},[138,11945,11946,11948,11950,11953,11955,11958,11960],{"class":140,"line":158},[138,11947,795],{"class":794},[138,11949,3931],{"class":1073},[138,11951,11952],{"class":801}," useFetch",[138,11954,806],{"class":809},[138,11956,11957],{"class":1045},"url",[138,11959,1049],{"class":809},[138,11961,934],{"class":809},[138,11963,11964,11966,11968,11970,11972,11974,11976],{"class":140,"line":164},[138,11965,2607],{"class":1073},[138,11967,2815],{"class":2521},[138,11969,1146],{"class":1112},[138,11971,3050],{"class":801},[138,11973,806],{"class":815},[138,11975,3070],{"class":2041},[138,11977,872],{"class":815},[138,11979,11980,11982,11984,11986,11988,11990,11992],{"class":140,"line":170},[138,11981,2607],{"class":1073},[138,11983,11698],{"class":2521},[138,11985,1146],{"class":1112},[138,11987,3050],{"class":801},[138,11989,806],{"class":815},[138,11991,11707],{"class":3862},[138,11993,872],{"class":815},[138,11995,11996,11998,12000,12002,12004,12006,12008],{"class":140,"line":176},[138,11997,2607],{"class":1073},[138,11999,2820],{"class":2521},[138,12001,1146],{"class":1112},[138,12003,3050],{"class":801},[138,12005,806],{"class":815},[138,12007,3070],{"class":2041},[138,12009,872],{"class":815},[138,12011,12012],{"class":140,"line":182},[138,12013,649],{"emptyLinePlaceholder":86},[138,12015,12016,12019,12021,12024,12026],{"class":140,"line":188},[138,12017,12018],{"class":1073},"  async",[138,12020,3931],{"class":1073},[138,12022,12023],{"class":801}," execute",[138,12025,3093],{"class":809},[138,12027,934],{"class":809},[138,12029,12030,12032,12034,12036,12038],{"class":140,"line":194},[138,12031,11873],{"class":805},[138,12033,363],{"class":809},[138,12035,3102],{"class":805},[138,12037,1146],{"class":1112},[138,12039,11756],{"class":3862},[138,12041,12042,12044,12046,12048,12050],{"class":140,"line":199},[138,12043,11845],{"class":805},[138,12045,363],{"class":809},[138,12047,3102],{"class":805},[138,12049,1146],{"class":1112},[138,12051,3186],{"class":2041},[138,12053,12054],{"class":140,"line":204},[138,12055,649],{"emptyLinePlaceholder":86},[138,12057,12058,12061],{"class":140,"line":209},[138,12059,12060],{"class":794},"    try",[138,12062,934],{"class":809},[138,12064,12065,12067,12069,12071,12073,12075,12077,12079],{"class":140,"line":215},[138,12066,5536],{"class":1073},[138,12068,5539],{"class":2521},[138,12070,1146],{"class":1112},[138,12072,2828],{"class":794},[138,12074,5546],{"class":801},[138,12076,806],{"class":815},[138,12078,11957],{"class":805},[138,12080,872],{"class":815},[138,12082,12083,12086,12088,12090,12092,12094,12096,12098,12100],{"class":140,"line":221},[138,12084,12085],{"class":805},"      data",[138,12087,363],{"class":809},[138,12089,3102],{"class":805},[138,12091,1146],{"class":1112},[138,12093,2828],{"class":794},[138,12095,5539],{"class":805},[138,12097,363],{"class":809},[138,12099,2030],{"class":801},[138,12101,2796],{"class":815},[138,12103,12104,12107,12109,12111,12113,12115],{"class":140,"line":227},[138,12105,12106],{"class":809},"    }",[138,12108,11831],{"class":794},[138,12110,1084],{"class":815},[138,12112,11836],{"class":805},[138,12114,1109],{"class":815},[138,12116,810],{"class":809},[138,12118,12119,12122,12124,12126,12128,12130,12132],{"class":140,"line":233},[138,12120,12121],{"class":805},"      error",[138,12123,363],{"class":809},[138,12125,3102],{"class":805},[138,12127,1146],{"class":1112},[138,12129,11854],{"class":805},[138,12131,363],{"class":809},[138,12133,11859],{"class":805},[138,12135,12136,12138,12140],{"class":140,"line":239},[138,12137,12106],{"class":809},[138,12139,11866],{"class":794},[138,12141,934],{"class":809},[138,12143,12144,12147,12149,12151,12153],{"class":140,"line":245},[138,12145,12146],{"class":805},"      loading",[138,12148,363],{"class":809},[138,12150,3102],{"class":805},[138,12152,1146],{"class":1112},[138,12154,3863],{"class":3862},[138,12156,12157],{"class":140,"line":251},[138,12158,1179],{"class":809},[138,12160,12161],{"class":140,"line":257},[138,12162,1184],{"class":809},[138,12164,12165],{"class":140,"line":263},[138,12166,649],{"emptyLinePlaceholder":86},[138,12168,12169,12171,12173,12175,12177,12179,12181,12183,12185,12187],{"class":140,"line":269},[138,12170,3199],{"class":794},[138,12172,2812],{"class":809},[138,12174,2815],{"class":805},[138,12176,954],{"class":809},[138,12178,11698],{"class":805},[138,12180,954],{"class":809},[138,12182,2820],{"class":805},[138,12184,954],{"class":809},[138,12186,12023],{"class":805},[138,12188,1015],{"class":809},[138,12190,12191],{"class":140,"line":275},[138,12192,2082],{"class":809},[12,12194,12195],{},"E o componente fica limpo e expressivo:",[129,12197,12199],{"className":1407,"code":12198,"language":1409,"meta":75,"style":75},"\u003Cscript setup>\nimport { onMounted } from 'vue'\nimport { useFetch } from '.\u002Fcomposables\u002FuseFetch'\n\nconst {\n  data: users,\n  loading,\n  error,\n  execute\n} = useFetch('https:\u002F\u002Fjsonplaceholder.typicode.com\u002Fusers')\n\nonMounted(execute)\n\u003C\u002Fscript>\n",[135,12200,12201,12211,12229,12248,12252,12258,12268,12274,12280,12285,12303,12307,12314],{"__ignoreMap":75},[138,12202,12203,12205,12207,12209],{"class":140,"line":141},[138,12204,1416],{"class":809},[138,12206,1420],{"class":1419},[138,12208,1424],{"class":1423},[138,12210,1440],{"class":809},[138,12212,12213,12215,12217,12219,12221,12223,12225,12227],{"class":140,"line":76},[138,12214,3735],{"class":794},[138,12216,2812],{"class":809},[138,12218,10046],{"class":805},[138,12220,2823],{"class":809},[138,12222,3748],{"class":794},[138,12224,957],{"class":826},[138,12226,1409],{"class":830},[138,12228,2361],{"class":826},[138,12230,12231,12233,12235,12237,12239,12241,12243,12246],{"class":140,"line":152},[138,12232,3735],{"class":794},[138,12234,2812],{"class":809},[138,12236,11952],{"class":805},[138,12238,2823],{"class":809},[138,12240,3748],{"class":794},[138,12242,957],{"class":826},[138,12244,12245],{"class":830},".\u002Fcomposables\u002FuseFetch",[138,12247,2361],{"class":826},[138,12249,12250],{"class":140,"line":158},[138,12251,649],{"emptyLinePlaceholder":86},[138,12253,12254,12256],{"class":140,"line":164},[138,12255,2518],{"class":1073},[138,12257,934],{"class":809},[138,12259,12260,12262,12264,12266],{"class":140,"line":170},[138,12261,9528],{"class":7548},[138,12263,782],{"class":809},[138,12265,11685],{"class":2521},[138,12267,837],{"class":809},[138,12269,12270,12272],{"class":140,"line":176},[138,12271,11747],{"class":2521},[138,12273,837],{"class":809},[138,12275,12276,12278],{"class":140,"line":182},[138,12277,11761],{"class":2521},[138,12279,837],{"class":809},[138,12281,12282],{"class":140,"line":188},[138,12283,12284],{"class":2521},"  execute\n",[138,12286,12287,12289,12291,12293,12295,12297,12299,12301],{"class":140,"line":194},[138,12288,869],{"class":809},[138,12290,1146],{"class":1112},[138,12292,11952],{"class":801},[138,12294,806],{"class":805},[138,12296,834],{"class":826},[138,12298,11799],{"class":830},[138,12300,834],{"class":826},[138,12302,872],{"class":805},[138,12304,12305],{"class":140,"line":199},[138,12306,649],{"emptyLinePlaceholder":86},[138,12308,12309,12311],{"class":140,"line":204},[138,12310,10225],{"class":801},[138,12312,12313],{"class":805},"(execute)\n",[138,12315,12316,12318,12320],{"class":140,"line":209},[138,12317,1505],{"class":809},[138,12319,1420],{"class":1419},[138,12321,1440],{"class":809},[27,12323],{},[30,12325,4866],{"id":4865},[12,12327,12328],{},"A Options API e a Composition API não são inimigas: são ferramentas diferentes, cada uma com seu contexto ideal. A Options API continua sendo uma escolha válida e não vai desaparecer. Mas a Composition API representa a evolução natural do Vue, trazendo mais flexibilidade, melhor suporte a TypeScript e uma forma muito mais elegante de compartilhar lógica entre componentes.",[12,12330,12331,12332,370,12334,370,12336,12338],{},"Se você ainda não mergulhou de cabeça na Composition API, esse é o momento. A curva de aprendizado existe, especialmente se você vem de anos de Options API, mas uma vez que você entende ",[135,12333,7108],{},[135,12335,7111],{},[135,12337,10225],{}," e a ideia de composables, o código começa a fluir de forma muito mais natural e organizada.",[12,12340,12341],{},"E a melhor parte: você pode começar devagar, introduzindo a Composition API gradualmente nos seus projetos, sem precisar jogar tudo fora.",[27,12343],{},[30,12345,9153],{"id":9152},[1643,12347,12349],{"id":12348},"documentação-oficial","Documentação oficial",[402,12351,12352,12358],{},[405,12353,12354],{},[105,12355,12356],{"href":12356,"rel":12357},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fextras\u002Fcomposition-api-faq",[109],[405,12359,12360],{},[105,12361,12362],{"href":12362,"rel":12363},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Ftypescript\u002Foptions-api",[109],[1643,12365,12367],{"id":12366},"artigos-e-conteúdos-complementares","Artigos e conteúdos complementares",[402,12369,12370,12376],{},[405,12371,12372],{},[105,12373,12374],{"href":12374,"rel":12375},"https:\u002F\u002Fdev.to\u002Fsucodelarangela\u002Fvue3-options-api-vs-composition-api-1j09",[109],[405,12377,12378],{},[105,12379,12380],{"href":12380,"rel":12381},"https:\u002F\u002Fmedium.com\u002F@victor.souza2210\u002Fvue-js-composition-api-vs-options-api-qual-abordagem-escolher-a50a2f2f932b",[109],[1744,12383,12384],{},"html pre.shiki code .sG-J9, html code.shiki .sG-J9{--shiki-light:#39ADB5;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sqIbZ, html code.shiki .sqIbZ{--shiki-light:#E53935;--shiki-default:#85E89D;--shiki-dark:#85E89D}html pre.shiki code .sMo7A, html code.shiki .sMo7A{--shiki-light:#90A4AE;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s7047, html code.shiki .s7047{--shiki-light:#9C3EDA;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sF_wb, html code.shiki .sF_wb{--shiki-light:#39ADB5;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .s0vBq, html code.shiki .s0vBq{--shiki-light:#91B859;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .s3Er8, html code.shiki .s3Er8{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sdv8B, html code.shiki .sdv8B{--shiki-light:#E53935;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s0u7J, html code.shiki .s0u7J{--shiki-light:#E53935;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .s_k96, html code.shiki .s_k96{--shiki-light:#F76D47;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .swu5b, html code.shiki .swu5b{--shiki-light:#39ADB5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sFfmW, html code.shiki .sFfmW{--shiki-light:#39ADB5;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sK_r7, html code.shiki .sK_r7{--shiki-light:#6182B8;--shiki-default:#B392F0;--shiki-dark:#B392F0}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sFsEu, html code.shiki .sFsEu{--shiki-light:#9C3EDA;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sVPC0, html code.shiki .sVPC0{--shiki-light:#90A4AE;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sk1zL, html code.shiki .sk1zL{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#FFAB70;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .s3afY, html code.shiki .s3afY{--shiki-light:#E2931D;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sMrrN, html code.shiki .sMrrN{--shiki-light:#FF5370;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sTbKH, html code.shiki .sTbKH{--shiki-light:#E53935;--shiki-default:#FFAB70;--shiki-dark:#FFAB70}",{"title":75,"searchDepth":76,"depth":76,"links":12386},[12387,12388,12391,12392,12395,12397,12398,12399,12400,12401,12402,12403,12404,12405],{"id":9266,"depth":76,"text":9267},{"id":9286,"depth":76,"text":9287,"children":12389},[12390],{"id":9351,"depth":152,"text":9352},{"id":9770,"depth":76,"text":9771},{"id":9819,"depth":76,"text":9820,"children":12393},[12394],{"id":9888,"depth":152,"text":9889},{"id":10282,"depth":76,"text":12396},"Uma diferença importante: ref e .value",{"id":10478,"depth":76,"text":10479},{"id":10675,"depth":76,"text":10676},{"id":11051,"depth":76,"text":11052},{"id":11220,"depth":76,"text":11221},{"id":11244,"depth":76,"text":11245},{"id":11414,"depth":76,"text":11415},{"id":11473,"depth":76,"text":11474},{"id":4865,"depth":76,"text":4866},{"id":9152,"depth":76,"text":9153,"children":12406},[12407,12408],{"id":12348,"depth":152,"text":12349},{"id":12366,"depth":152,"text":12367},"2026-04-16T21:30:00-03:00","Entenda as diferenças entre Options API e Composition API no Vue 3 com exemplos práticos, reatividade, reutilização de código e quando usar cada abordagem.","\u002Fimages\u002Fblog\u002Fvue-options-vs-composition.png",{},"\u002Fblog\u002Fvue-composition-vs-options-api",{"title":9247,"description":12410},"blog\u002Fvue-composition-vs-options-api",[9239,9240,9241,9242,9280,9273],"J5SE8TqYd6qi9usKAScI9E67YLW6bzzkLHtfTk3AO9Q",{"id":12419,"title":12420,"author":7,"body":12421,"date":13329,"description":13330,"extension":83,"image":13331,"meta":13332,"navigation":86,"path":13333,"seo":13334,"stem":13335,"tags":13336,"__hash__":13340},"blog\u002Fblog\u002Fhoisting-javascript-scope.md","Hoisting e Scope: como o JavaScript decide onde as coisas existem",{"type":9,"value":12422,"toc":13321},[12423,12434,12438,12441,12454,12458,12461,12464,12471,12520,12523,12573,12584,12595,12626,12632,12638,12691,12697,12755,12779,12783,12786,12792,12801,12851,12866,12941,12946,12997,13010,13014,13017,13083,13089,13094,13174,13189,13194,13268,13270,13273,13290,13293,13295,13318],[12,12424,12425,12426,12429,12430,12433],{},"Se você já tomou um ",[135,12427,12428],{},"undefined"," inesperado, ou um ",[135,12431,12432],{},"ReferenceError"," em um lugar que parecia impossível, hoisting ou scope estavam envolvidos. Esses dois conceitos definem como o JavaScript enxerga o seu código antes e durante a execução.",[30,12435,12437],{"id":12436},"por-que-isso-importa","Por que isso importa",[12,12439,12440],{},"O JavaScript não lê o código de cima para baixo da forma que parece. Antes de executar qualquer linha, o engine passa por uma fase de compilação onde registra todas as declarações. É nessa fase que o hoisting acontece.",[12,12442,12443,12444,12447,12448,377,12451,12453],{},"Entender isso muda como você interpreta erros, como você organiza funções e por que ",[135,12445,12446],{},"var"," se comporta de forma diferente de ",[135,12449,12450],{},"let",[135,12452,2518],{}," em contextos que parecem idênticos.",[30,12455,12457],{"id":12456},"o-que-é-hoisting","O que é hoisting",[12,12459,12460],{},"Hoisting é o comportamento do engine de registrar declarações antes da execução começar. Popularmente se diz que as declarações \"sobem para o topo do código\", mas o que acontece de verdade é que elas são processadas na fase de compilação, antes de qualquer atribuição de valor.",[12,12462,12463],{},"O que sobe e o que fica no lugar depende do tipo da declaração.",[12,12465,12466,12470],{},[19,12467,6225,12468],{},[135,12469,12446],{},", a declaração sobe, mas o valor não:",[129,12472,12474],{"className":5084,"code":12473,"language":5086,"meta":75,"style":75},"console.log(nome) \u002F\u002F undefined\nvar nome = 'Ana'\nconsole.log(nome) \u002F\u002F \"Ana\"\n",[135,12475,12476,12491,12507],{"__ignoreMap":75},[138,12477,12478,12481,12483,12485,12488],{"class":140,"line":141},[138,12479,12480],{"class":805},"console",[138,12482,363],{"class":809},[138,12484,9727],{"class":801},[138,12486,12487],{"class":805},"(nome) ",[138,12489,12490],{"class":911},"\u002F\u002F undefined\n",[138,12492,12493,12495,12498,12500,12502,12505],{"class":140,"line":76},[138,12494,12446],{"class":1073},[138,12496,12497],{"class":805}," nome ",[138,12499,1430],{"class":1112},[138,12501,957],{"class":826},[138,12503,12504],{"class":830},"Ana",[138,12506,2361],{"class":826},[138,12508,12509,12511,12513,12515,12517],{"class":140,"line":152},[138,12510,12480],{"class":805},[138,12512,363],{"class":809},[138,12514,9727],{"class":801},[138,12516,12487],{"class":805},[138,12518,12519],{"class":911},"\u002F\u002F \"Ana\"\n",[12,12521,12522],{},"O que o engine processa é algo como:",[129,12524,12526],{"className":5084,"code":12525,"language":5086,"meta":75,"style":75},"var nome \u002F\u002F içado, valor = undefined\nconsole.log(nome) \u002F\u002F undefined\nnome = 'Ana'\nconsole.log(nome) \u002F\u002F \"Ana\"\n",[135,12527,12528,12537,12549,12561],{"__ignoreMap":75},[138,12529,12530,12532,12534],{"class":140,"line":141},[138,12531,12446],{"class":1073},[138,12533,12497],{"class":805},[138,12535,12536],{"class":911},"\u002F\u002F içado, valor = undefined\n",[138,12538,12539,12541,12543,12545,12547],{"class":140,"line":76},[138,12540,12480],{"class":805},[138,12542,363],{"class":809},[138,12544,9727],{"class":801},[138,12546,12487],{"class":805},[138,12548,12490],{"class":911},[138,12550,12551,12553,12555,12557,12559],{"class":140,"line":152},[138,12552,3388],{"class":805},[138,12554,1430],{"class":1112},[138,12556,957],{"class":826},[138,12558,12504],{"class":830},[138,12560,2361],{"class":826},[138,12562,12563,12565,12567,12569,12571],{"class":140,"line":158},[138,12564,12480],{"class":805},[138,12566,363],{"class":809},[138,12568,9727],{"class":801},[138,12570,12487],{"class":805},[138,12572,12519],{"class":911},[12,12574,12575,12576,12579,12580,12583],{},"A linha ",[135,12577,12578],{},"var nome"," vai ao topo. A atribuição ",[135,12581,12582],{},"= \"Ana\""," permanece exatamente onde foi escrita.",[12,12585,12586,12592,12593,782],{},[19,12587,6225,12588,377,12590],{},[135,12589,12450],{},[135,12591,2518],{},", o comportamento muda. As declarações também são içadas, mas ficam em uma zona morta temporal, a Temporal Dead Zone, até que a linha de declaração seja atingida em execução. Qualquer acesso antes disso gera um ",[135,12594,12432],{},[129,12596,12598],{"className":5084,"code":12597,"language":5086,"meta":75,"style":75},"console.log(idade) \u002F\u002F ReferenceError\nlet idade = 25\n",[135,12599,12600,12614],{"__ignoreMap":75},[138,12601,12602,12604,12606,12608,12611],{"class":140,"line":141},[138,12603,12480],{"class":805},[138,12605,363],{"class":809},[138,12607,9727],{"class":801},[138,12609,12610],{"class":805},"(idade) ",[138,12612,12613],{"class":911},"\u002F\u002F ReferenceError\n",[138,12615,12616,12618,12621,12623],{"class":140,"line":76},[138,12617,12450],{"class":1073},[138,12619,12620],{"class":805}," idade ",[138,12622,1430],{"class":1112},[138,12624,12625],{"class":5295}," 25\n",[12,12627,12628,12629,12631],{},"Isso não é um bug. É intencional. O comportamento foi projetado exatamente para evitar leitura de variáveis em estado indefinido, um problema silencioso que ",[135,12630,12446],{}," carrega consigo.",[12,12633,12634,12637],{},[19,12635,12636],{},"Com function declarations",", o hoisting é completo. Declaração e corpo sobem juntos:",[129,12639,12641],{"className":5084,"code":12640,"language":5086,"meta":75,"style":75},"saudar() \u002F\u002F \"Olá!\"\n\nfunction saudar() {\n  console.log('Olá!')\n}\n",[135,12642,12643,12653,12657,12668,12687],{"__ignoreMap":75},[138,12644,12645,12648,12650],{"class":140,"line":141},[138,12646,12647],{"class":801},"saudar",[138,12649,2732],{"class":805},[138,12651,12652],{"class":911},"\u002F\u002F \"Olá!\"\n",[138,12654,12655],{"class":140,"line":76},[138,12656,649],{"emptyLinePlaceholder":86},[138,12658,12659,12661,12664,12666],{"class":140,"line":152},[138,12660,3824],{"class":1073},[138,12662,12663],{"class":801}," saudar",[138,12665,3093],{"class":809},[138,12667,934],{"class":809},[138,12669,12670,12672,12674,12676,12678,12680,12683,12685],{"class":140,"line":158},[138,12671,10238],{"class":805},[138,12673,363],{"class":809},[138,12675,9727],{"class":801},[138,12677,806],{"class":815},[138,12679,834],{"class":826},[138,12681,12682],{"class":830},"Olá!",[138,12684,834],{"class":826},[138,12686,872],{"class":815},[138,12688,12689],{"class":140,"line":164},[138,12690,2082],{"class":809},[12,12692,12693,12696],{},[19,12694,12695],{},"Com function expressions e arrow functions",", a função é apenas um valor atribuído a uma variável. O hoisting se aplica à variável, não à função:",[129,12698,12700],{"className":5084,"code":12699,"language":5086,"meta":75,"style":75},"cumprimentar() \u002F\u002F TypeError: cumprimentar is not a function\n\nvar cumprimentar = function () {\n  console.log('Oi!')\n}\n",[135,12701,12702,12712,12716,12732,12751],{"__ignoreMap":75},[138,12703,12704,12707,12709],{"class":140,"line":141},[138,12705,12706],{"class":801},"cumprimentar",[138,12708,2732],{"class":805},[138,12710,12711],{"class":911},"\u002F\u002F TypeError: cumprimentar is not a function\n",[138,12713,12714],{"class":140,"line":76},[138,12715,649],{"emptyLinePlaceholder":86},[138,12717,12718,12720,12724,12726,12728,12730],{"class":140,"line":152},[138,12719,12446],{"class":1073},[138,12721,12723],{"class":12722},"sqHAQ"," cumprimentar",[138,12725,1146],{"class":1112},[138,12727,3931],{"class":1073},[138,12729,3034],{"class":809},[138,12731,934],{"class":809},[138,12733,12734,12736,12738,12740,12742,12744,12747,12749],{"class":140,"line":158},[138,12735,10238],{"class":805},[138,12737,363],{"class":809},[138,12739,9727],{"class":801},[138,12741,806],{"class":815},[138,12743,834],{"class":826},[138,12745,12746],{"class":830},"Oi!",[138,12748,834],{"class":826},[138,12750,872],{"class":815},[138,12752,12753],{"class":140,"line":164},[138,12754,2082],{"class":809},[12,12756,12757,12760,12761,12763,12764,12766,12767,12770,12771,1207,12773,12775,12776,12778],{},[135,12758,12759],{},"var cumprimentar"," é içado como ",[135,12762,12428],{},". Chamar ",[135,12765,12428],{}," como função gera um ",[135,12768,12769],{},"TypeError",". Se fosse ",[135,12772,2518],{},[135,12774,12450],{},", seria um ",[135,12777,12432],{}," pela TDZ.",[30,12780,12782],{"id":12781},"o-que-é-scope","O que é scope",[12,12784,12785],{},"Scope define onde uma variável pode ser acessada. O JavaScript trabalha com três níveis.",[12,12787,12788,12791],{},[19,12789,12790],{},"Scope global"," é o nível mais externo. Variáveis declaradas aqui estão disponíveis em qualquer parte do código. Em aplicações maiores, isso vira um problema rápido: qualquer coisa pode ler e sobrescrever essas variáveis.",[12,12793,12794,12797,12798,12800],{},[19,12795,12796],{},"Function scope"," isola variáveis dentro de funções. Uma variável declarada com ",[135,12799,12446],{}," dentro de uma função não existe fora dela:",[129,12802,12804],{"className":5084,"code":12803,"language":5086,"meta":75,"style":75},"function calcular() {\n  var resultado = 42\n}\n\nconsole.log(resultado) \u002F\u002F ReferenceError\n",[135,12805,12806,12817,12830,12834,12838],{"__ignoreMap":75},[138,12807,12808,12810,12813,12815],{"class":140,"line":141},[138,12809,3824],{"class":1073},[138,12811,12812],{"class":801}," calcular",[138,12814,3093],{"class":809},[138,12816,934],{"class":809},[138,12818,12819,12822,12825,12827],{"class":140,"line":76},[138,12820,12821],{"class":1073},"  var",[138,12823,12824],{"class":805}," resultado",[138,12826,1146],{"class":1112},[138,12828,12829],{"class":5295}," 42\n",[138,12831,12832],{"class":140,"line":152},[138,12833,2082],{"class":809},[138,12835,12836],{"class":140,"line":158},[138,12837,649],{"emptyLinePlaceholder":86},[138,12839,12840,12842,12844,12846,12849],{"class":140,"line":164},[138,12841,12480],{"class":805},[138,12843,363],{"class":809},[138,12845,9727],{"class":801},[138,12847,12848],{"class":805},"(resultado) ",[138,12850,12613],{"class":911},[12,12852,12853,12856,12857,377,12859,12861,12862,12865],{},[19,12854,12855],{},"Block scope"," é o comportamento de ",[135,12858,12450],{},[135,12860,2518],{},". Qualquer bloco delimitado por ",[135,12863,12864],{},"{}"," cria um escopo próprio:",[129,12867,12869],{"className":5084,"code":12868,"language":5086,"meta":75,"style":75},"if (true) {\n  let mensagem = 'dentro do bloco'\n  console.log(mensagem) \u002F\u002F \"dentro do bloco\"\n}\n\nconsole.log(mensagem) \u002F\u002F ReferenceError\n",[135,12870,12871,12885,12902,12920,12924,12928],{"__ignoreMap":75},[138,12872,12873,12876,12878,12881,12883],{"class":140,"line":141},[138,12874,12875],{"class":794},"if",[138,12877,1084],{"class":805},[138,12879,12880],{"class":3862},"true",[138,12882,1109],{"class":805},[138,12884,810],{"class":809},[138,12886,12887,12890,12893,12895,12897,12900],{"class":140,"line":76},[138,12888,12889],{"class":1073},"  let",[138,12891,12892],{"class":805}," mensagem",[138,12894,1146],{"class":1112},[138,12896,957],{"class":826},[138,12898,12899],{"class":830},"dentro do bloco",[138,12901,2361],{"class":826},[138,12903,12904,12906,12908,12910,12912,12915,12917],{"class":140,"line":152},[138,12905,10238],{"class":805},[138,12907,363],{"class":809},[138,12909,9727],{"class":801},[138,12911,806],{"class":815},[138,12913,12914],{"class":805},"mensagem",[138,12916,1109],{"class":815},[138,12918,12919],{"class":911},"\u002F\u002F \"dentro do bloco\"\n",[138,12921,12922],{"class":140,"line":158},[138,12923,2082],{"class":809},[138,12925,12926],{"class":140,"line":164},[138,12927,649],{"emptyLinePlaceholder":86},[138,12929,12930,12932,12934,12936,12939],{"class":140,"line":170},[138,12931,12480],{"class":805},[138,12933,363],{"class":809},[138,12935,9727],{"class":801},[138,12937,12938],{"class":805},"(mensagem) ",[138,12940,12613],{"class":911},[12,12942,12943,12945],{},[135,12944,12446],{}," não respeita block scope. Ele respeita apenas o escopo da função mais próxima:",[129,12947,12949],{"className":5084,"code":12948,"language":5086,"meta":75,"style":75},"if (true) {\n  var valor = 10\n}\n\nconsole.log(valor) \u002F\u002F 10\n",[135,12950,12951,12963,12975,12979,12983],{"__ignoreMap":75},[138,12952,12953,12955,12957,12959,12961],{"class":140,"line":141},[138,12954,12875],{"class":794},[138,12956,1084],{"class":805},[138,12958,12880],{"class":3862},[138,12960,1109],{"class":805},[138,12962,810],{"class":809},[138,12964,12965,12967,12970,12972],{"class":140,"line":76},[138,12966,12821],{"class":1073},[138,12968,12969],{"class":805}," valor",[138,12971,1146],{"class":1112},[138,12973,12974],{"class":5295}," 10\n",[138,12976,12977],{"class":140,"line":152},[138,12978,2082],{"class":809},[138,12980,12981],{"class":140,"line":158},[138,12982,649],{"emptyLinePlaceholder":86},[138,12984,12985,12987,12989,12991,12994],{"class":140,"line":164},[138,12986,12480],{"class":805},[138,12988,363],{"class":809},[138,12990,9727],{"class":801},[138,12992,12993],{"class":805},"(valor) ",[138,12995,12996],{"class":911},"\u002F\u002F 10\n",[12,12998,12999,13000,13002,13003,13005,13006,13009],{},"Esse é um dos motivos pelos quais ",[135,13001,12446],{}," costuma ser fonte de bugs difíceis de rastrear. Você declara dentro de um ",[135,13004,12875],{},", de um ",[135,13007,13008],{},"for",", de qualquer bloco, e a variável vaza para o escopo da função inteira.",[30,13011,13013],{"id":13012},"como-os-dois-se-conectam","Como os dois se conectam",[12,13015,13016],{},"Hoisting e scope não operam de forma isolada. O hoisting sempre acontece dentro de um escopo. Uma variável é içada para o topo do escopo em que foi declarada, não para o topo do arquivo.",[129,13018,13020],{"className":5084,"code":13019,"language":5086,"meta":75,"style":75},"function exemplo() {\n  console.log(x) \u002F\u002F undefined, não ReferenceError\n  var x = 5\n}\n\nconsole.log(x) \u002F\u002F ReferenceError\n",[135,13021,13022,13033,13051,13062,13066,13070],{"__ignoreMap":75},[138,13023,13024,13026,13029,13031],{"class":140,"line":141},[138,13025,3824],{"class":1073},[138,13027,13028],{"class":801}," exemplo",[138,13030,3093],{"class":809},[138,13032,934],{"class":809},[138,13034,13035,13037,13039,13041,13043,13046,13048],{"class":140,"line":76},[138,13036,10238],{"class":805},[138,13038,363],{"class":809},[138,13040,9727],{"class":801},[138,13042,806],{"class":815},[138,13044,13045],{"class":805},"x",[138,13047,1109],{"class":815},[138,13049,13050],{"class":911},"\u002F\u002F undefined, não ReferenceError\n",[138,13052,13053,13055,13057,13059],{"class":140,"line":152},[138,13054,12821],{"class":1073},[138,13056,10750],{"class":805},[138,13058,1146],{"class":1112},[138,13060,13061],{"class":5295}," 5\n",[138,13063,13064],{"class":140,"line":158},[138,13065,2082],{"class":809},[138,13067,13068],{"class":140,"line":164},[138,13069,649],{"emptyLinePlaceholder":86},[138,13071,13072,13074,13076,13078,13081],{"class":140,"line":170},[138,13073,12480],{"class":805},[138,13075,363],{"class":809},[138,13077,9727],{"class":801},[138,13079,13080],{"class":805},"(x) ",[138,13082,12613],{"class":911},[12,13084,13085,13086,13088],{},"Dentro da função, ",[135,13087,13045],{}," é içado para o topo do function scope. Fora da função, ela simplesmente não existe.",[12,13090,13091,13092,782],{},"Isso explica um comportamento que confunde bastante em loops com ",[135,13093,12446],{},[129,13095,13097],{"className":5084,"code":13096,"language":5086,"meta":75,"style":75},"for (var i = 0; i \u003C 3; i++) {\n  setTimeout(() => console.log(i), 100)\n}\n\u002F\u002F 3, 3, 3\n",[135,13098,13099,13134,13165,13169],{"__ignoreMap":75},[138,13100,13101,13103,13105,13107,13110,13112,13114,13116,13118,13120,13123,13125,13128,13130,13132],{"class":140,"line":141},[138,13102,13008],{"class":794},[138,13104,1084],{"class":805},[138,13106,12446],{"class":1073},[138,13108,13109],{"class":805}," i ",[138,13111,1430],{"class":1112},[138,13113,5296],{"class":5295},[138,13115,1829],{"class":809},[138,13117,13109],{"class":805},[138,13119,1416],{"class":1112},[138,13121,13122],{"class":5295}," 3",[138,13124,1829],{"class":809},[138,13126,13127],{"class":805}," i",[138,13129,10352],{"class":1112},[138,13131,1109],{"class":805},[138,13133,810],{"class":809},[138,13135,13136,13139,13141,13143,13145,13148,13150,13152,13154,13156,13158,13160,13163],{"class":140,"line":76},[138,13137,13138],{"class":801},"  setTimeout",[138,13140,806],{"class":815},[138,13142,3093],{"class":809},[138,13144,1074],{"class":1073},[138,13146,13147],{"class":805}," console",[138,13149,363],{"class":809},[138,13151,9727],{"class":801},[138,13153,806],{"class":815},[138,13155,3905],{"class":805},[138,13157,1049],{"class":815},[138,13159,954],{"class":809},[138,13161,13162],{"class":5295}," 100",[138,13164,872],{"class":815},[138,13166,13167],{"class":140,"line":152},[138,13168,2082],{"class":809},[138,13170,13171],{"class":140,"line":158},[138,13172,13173],{"class":911},"\u002F\u002F 3, 3, 3\n",[12,13175,13176,13179,13180,13182,13183,13185,13186,363],{},[135,13177,13178],{},"var i"," não fica presa no bloco do ",[135,13181,13008],{},". Ela é içada para o escopo da função (ou global) e existe como uma única variável. Quando os callbacks executam, o loop já terminou e ",[135,13184,3905],{}," é ",[135,13187,13188],{},"3",[12,13190,6225,13191,13193],{},[135,13192,12450],{},", cada iteração cria um escopo próprio:",[129,13195,13197],{"className":5084,"code":13196,"language":5086,"meta":75,"style":75},"for (let i = 0; i \u003C 3; i++) {\n  setTimeout(() => console.log(i), 100)\n}\n\u002F\u002F 0, 1, 2\n",[135,13198,13199,13231,13259,13263],{"__ignoreMap":75},[138,13200,13201,13203,13205,13207,13209,13211,13213,13215,13217,13219,13221,13223,13225,13227,13229],{"class":140,"line":141},[138,13202,13008],{"class":794},[138,13204,1084],{"class":805},[138,13206,12450],{"class":1073},[138,13208,13109],{"class":805},[138,13210,1430],{"class":1112},[138,13212,5296],{"class":5295},[138,13214,1829],{"class":809},[138,13216,13109],{"class":805},[138,13218,1416],{"class":1112},[138,13220,13122],{"class":5295},[138,13222,1829],{"class":809},[138,13224,13127],{"class":805},[138,13226,10352],{"class":1112},[138,13228,1109],{"class":805},[138,13230,810],{"class":809},[138,13232,13233,13235,13237,13239,13241,13243,13245,13247,13249,13251,13253,13255,13257],{"class":140,"line":76},[138,13234,13138],{"class":801},[138,13236,806],{"class":815},[138,13238,3093],{"class":809},[138,13240,1074],{"class":1073},[138,13242,13147],{"class":805},[138,13244,363],{"class":809},[138,13246,9727],{"class":801},[138,13248,806],{"class":815},[138,13250,3905],{"class":805},[138,13252,1049],{"class":815},[138,13254,954],{"class":809},[138,13256,13162],{"class":5295},[138,13258,872],{"class":815},[138,13260,13261],{"class":140,"line":152},[138,13262,2082],{"class":809},[138,13264,13265],{"class":140,"line":158},[138,13266,13267],{"class":911},"\u002F\u002F 0, 1, 2\n",[30,13269,4866],{"id":4865},[12,13271,13272],{},"Hoisting e scope são a base de como o JavaScript organiza o seu código antes de executá-lo. Quando você entende que o engine processa declarações antes de valores, e que cada tipo de declaração tem um comportamento diferente nesse processo, os erros que antes pareciam aleatórios começam a fazer sentido.",[12,13274,13275,13277,13278,13280,13281,13283,13284,13286,13287,13289],{},[135,13276,12446],{}," em loop vaza para fora do bloco porque ",[135,13279,12446],{}," não respeita block scope. ",[135,13282,12428],{}," antes da declaração acontece porque a declaração subiu mas o valor não. ",[135,13285,12432],{}," em ",[135,13288,12450],{}," acontece porque a TDZ protege você de ler uma variável que ainda não foi inicializada.",[12,13291,13292],{},"O JavaScript não está sendo imprevisível. Ele está seguindo regras bem definidas de onde as coisas vivem e quando ficam disponíveis.",[30,13294,9153],{"id":9152},[402,13296,13297,13304,13311],{},[405,13298,13299],{},[105,13300,13303],{"href":13301,"rel":13302},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fpt-BR\u002Fdocs\u002FGlossary\u002FHoisting",[109],"Hoisting - MDN Web Docs",[405,13305,13306],{},[105,13307,13310],{"href":13308,"rel":13309},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fpt-BR\u002Fdocs\u002FWeb\u002FJavaScript\u002FClosures",[109],"Closures - MDN Web Docs",[405,13312,13313],{},[105,13314,13317],{"href":13315,"rel":13316},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fpt-BR\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FStatements\u002Flet",[109],"let, const e block scope - MDN Web Docs",[1744,13319,13320],{},"html pre.shiki code .sMo7A, html code.shiki .sMo7A{--shiki-light:#90A4AE;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sG-J9, html code.shiki .sG-J9{--shiki-light:#39ADB5;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sK_r7, html code.shiki .sK_r7{--shiki-light:#6182B8;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sFsEu, html code.shiki .sFsEu{--shiki-light:#9C3EDA;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sFfmW, html code.shiki .sFfmW{--shiki-light:#39ADB5;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sF_wb, html code.shiki .sF_wb{--shiki-light:#39ADB5;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .s0vBq, html code.shiki .s0vBq{--shiki-light:#91B859;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s_k96, html code.shiki .s_k96{--shiki-light:#F76D47;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sdv8B, html code.shiki .sdv8B{--shiki-light:#E53935;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sqHAQ, html code.shiki .sqHAQ{--shiki-light:#90A4AE;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .s3Er8, html code.shiki .s3Er8{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sMrrN, html code.shiki .sMrrN{--shiki-light:#FF5370;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}",{"title":75,"searchDepth":76,"depth":76,"links":13322},[13323,13324,13325,13326,13327,13328],{"id":12436,"depth":76,"text":12437},{"id":12456,"depth":76,"text":12457},{"id":12781,"depth":76,"text":12782},{"id":13012,"depth":76,"text":13013},{"id":4865,"depth":76,"text":4866},{"id":9152,"depth":76,"text":9153},"2026-04-11T10:18:00-03:00","Se você já tomou um undefined inesperado, ou um ReferenceError em um lugar que parecia impossível, hoisting ou scope estavam envolvidos. Entender esses dois conceitos muda como você lê e escreve JavaScript.","\u002Fimages\u002Fblog\u002Fjs-scope.png",{},"\u002Fblog\u002Fhoisting-javascript-scope",{"title":12420,"description":13330},"blog\u002Fhoisting-javascript-scope",[13337,13338,13339],"javascript","frontend","fundamentos","_f9mZSFcdoxE-shkhF_P8C6FKoZWWSOOOY6EAHEbvIs",{"id":13342,"title":13343,"author":7,"body":13344,"date":14629,"description":14630,"extension":83,"image":14631,"meta":14632,"navigation":86,"path":14633,"seo":14634,"stem":14635,"tags":14636,"__hash__":14637},"blog\u002Fblog\u002Fjavascript-prototype-e-heranca-com.md","Prototype e herança: como o JavaScript compartilha comportamento",{"type":9,"value":13345,"toc":14613},[13346,13349,13353,13360,13520,13535,13547,13555,13558,13565,13568,13756,13778,13782,13785,13880,13891,13895,13901,14120,14123,14129,14147,14152,14155,14411,14429,14433,14442,14445,14462,14472,14476,14483,14487,14553,14562,14564,14573,14582,14585,14587,14610],[12,13347,13348],{},"JavaScript não tem herança clássica como Java ou C#. Tem algo diferente: um sistema de protótipos onde objetos herdam de outros objetos. Entender essa diferença muda como você lê e escreve orientação a objetos em JS, e explica comportamentos que parecem estranhos à primeira vista.",[30,13350,13352],{"id":13351},"o-que-é-prototype","O que é prototype",[12,13354,13355,13356,13359],{},"Todo objeto em JavaScript tem uma propriedade interna chamada ",[135,13357,13358],{},"[[Prototype]]",". Ela aponta para outro objeto, e é por meio dessa referência que o JavaScript busca propriedades e métodos que não existem diretamente no objeto.",[129,13361,13363],{"className":5084,"code":13362,"language":5086,"meta":75,"style":75},"const animal = {\n  respirar() {\n    console.log(\"respirando...\");\n  }\n};\n\nconst cachorro = Object.create(animal);\ncachorro.latir = function() {\n  console.log(\"au!\");\n};\n\ncachorro.latir();    \u002F\u002F \"au!\" — próprio do cachorro\ncachorro.respirar(); \u002F\u002F \"respirando...\" — veio do prototype\n",[135,13364,13365,13376,13385,13407,13411,13416,13420,13442,13460,13481,13485,13489,13504],{"__ignoreMap":75},[138,13366,13367,13369,13372,13374],{"class":140,"line":141},[138,13368,2518],{"class":1073},[138,13370,13371],{"class":2521}," animal",[138,13373,1146],{"class":1112},[138,13375,934],{"class":809},[138,13377,13378,13381,13383],{"class":140,"line":76},[138,13379,13380],{"class":5196},"  respirar",[138,13382,3093],{"class":809},[138,13384,934],{"class":809},[138,13386,13387,13389,13391,13393,13395,13397,13400,13402,13404],{"class":140,"line":152},[138,13388,9722],{"class":805},[138,13390,363],{"class":809},[138,13392,9727],{"class":801},[138,13394,806],{"class":815},[138,13396,1433],{"class":826},[138,13398,13399],{"class":830},"respirando...",[138,13401,1433],{"class":826},[138,13403,1049],{"class":815},[138,13405,13406],{"class":809},";\n",[138,13408,13409],{"class":140,"line":158},[138,13410,1184],{"class":809},[138,13412,13413],{"class":140,"line":164},[138,13414,13415],{"class":809},"};\n",[138,13417,13418],{"class":140,"line":170},[138,13419,649],{"emptyLinePlaceholder":86},[138,13421,13422,13424,13427,13429,13432,13434,13437,13440],{"class":140,"line":176},[138,13423,2518],{"class":1073},[138,13425,13426],{"class":2521}," cachorro",[138,13428,1146],{"class":1112},[138,13430,13431],{"class":805}," Object",[138,13433,363],{"class":809},[138,13435,13436],{"class":801},"create",[138,13438,13439],{"class":805},"(animal)",[138,13441,13406],{"class":809},[138,13443,13444,13447,13449,13452,13454,13456,13458],{"class":140,"line":182},[138,13445,13446],{"class":805},"cachorro",[138,13448,363],{"class":809},[138,13450,13451],{"class":801},"latir",[138,13453,1146],{"class":1112},[138,13455,3931],{"class":1073},[138,13457,3093],{"class":809},[138,13459,934],{"class":809},[138,13461,13462,13464,13466,13468,13470,13472,13475,13477,13479],{"class":140,"line":188},[138,13463,10238],{"class":805},[138,13465,363],{"class":809},[138,13467,9727],{"class":801},[138,13469,806],{"class":815},[138,13471,1433],{"class":826},[138,13473,13474],{"class":830},"au!",[138,13476,1433],{"class":826},[138,13478,1049],{"class":815},[138,13480,13406],{"class":809},[138,13482,13483],{"class":140,"line":194},[138,13484,13415],{"class":809},[138,13486,13487],{"class":140,"line":199},[138,13488,649],{"emptyLinePlaceholder":86},[138,13490,13491,13493,13495,13497,13499,13501],{"class":140,"line":204},[138,13492,13446],{"class":805},[138,13494,363],{"class":809},[138,13496,13451],{"class":801},[138,13498,3093],{"class":805},[138,13500,1829],{"class":809},[138,13502,13503],{"class":911},"    \u002F\u002F \"au!\" — próprio do cachorro\n",[138,13505,13506,13508,13510,13513,13515,13517],{"class":140,"line":209},[138,13507,13446],{"class":805},[138,13509,363],{"class":809},[138,13511,13512],{"class":801},"respirar",[138,13514,3093],{"class":805},[138,13516,1829],{"class":809},[138,13518,13519],{"class":911}," \u002F\u002F \"respirando...\" — veio do prototype\n",[12,13521,13522,13524,13525,13527,13528,13531,13532,363],{},[135,13523,13446],{}," não tem ",[135,13526,13512],{},". O JavaScript sobe pela cadeia de protótipos até encontrar em ",[135,13529,13530],{},"animal",". Isso se chama ",[19,13533,13534],{},"prototype chain",[12,13536,13537,13538,13540,13541,13544,13545,782],{},"Se não encontrar em nenhum nível, retorna ",[135,13539,12428],{},". O topo da cadeia é sempre ",[135,13542,13543],{},"Object.prototype",", e acima dele é ",[135,13546,3070],{},[129,13548,13553],{"className":13549,"code":13551,"language":13552},[13550],"language-text","cachorro → animal → Object.prototype → null\n","text",[135,13554,13551],{"__ignoreMap":75},[12,13556,13557],{},"É essa busca encadeada que permite compartilhar comportamento entre objetos sem copiar nada.",[30,13559,13561,13562],{"id":13560},"constructor-functions-e-o-prototype","Constructor functions e o ",[135,13563,13564],{},"prototype",[12,13566,13567],{},"Antes do ES6, a forma padrão de criar objetos com comportamento compartilhado era via constructor functions:",[129,13569,13571],{"className":5084,"code":13570,"language":5086,"meta":75,"style":75},"function Pessoa(nome) {\n  this.nome = nome;\n}\n\nPessoa.prototype.saudar = function() {\n  console.log(`Olá, sou ${this.nome}`);\n};\n\nconst p1 = new Pessoa(\"Ana\");\nconst p2 = new Pessoa(\"João\");\n\np1.saudar(); \u002F\u002F \"Olá, sou Ana\"\np2.saudar(); \u002F\u002F \"Olá, sou João\"\n",[135,13572,13573,13588,13603,13607,13611,13632,13661,13665,13669,13694,13720,13724,13740],{"__ignoreMap":75},[138,13574,13575,13577,13580,13582,13584,13586],{"class":140,"line":141},[138,13576,3824],{"class":1073},[138,13578,13579],{"class":801}," Pessoa",[138,13581,806],{"class":809},[138,13583,3107],{"class":1045},[138,13585,1049],{"class":809},[138,13587,934],{"class":809},[138,13589,13590,13593,13595,13597,13599,13601],{"class":140,"line":76},[138,13591,13592],{"class":2041},"  this",[138,13594,363],{"class":809},[138,13596,3107],{"class":805},[138,13598,1146],{"class":1112},[138,13600,6974],{"class":805},[138,13602,13406],{"class":809},[138,13604,13605],{"class":140,"line":152},[138,13606,2082],{"class":809},[138,13608,13609],{"class":140,"line":158},[138,13610,649],{"emptyLinePlaceholder":86},[138,13612,13613,13616,13618,13620,13622,13624,13626,13628,13630],{"class":140,"line":164},[138,13614,13615],{"class":3061},"Pessoa",[138,13617,363],{"class":809},[138,13619,13564],{"class":2521},[138,13621,363],{"class":809},[138,13623,12647],{"class":801},[138,13625,1146],{"class":1112},[138,13627,3931],{"class":1073},[138,13629,3093],{"class":809},[138,13631,934],{"class":809},[138,13633,13634,13636,13638,13640,13642,13644,13647,13649,13651,13653,13655,13657,13659],{"class":140,"line":170},[138,13635,10238],{"class":805},[138,13637,363],{"class":809},[138,13639,9727],{"class":801},[138,13641,806],{"class":815},[138,13643,3791],{"class":826},[138,13645,13646],{"class":830},"Olá, sou ",[138,13648,3797],{"class":826},[138,13650,8821],{"class":2041},[138,13652,363],{"class":826},[138,13654,3107],{"class":805},[138,13656,3803],{"class":826},[138,13658,1049],{"class":815},[138,13660,13406],{"class":809},[138,13662,13663],{"class":140,"line":176},[138,13664,13415],{"class":809},[138,13666,13667],{"class":140,"line":182},[138,13668,649],{"emptyLinePlaceholder":86},[138,13670,13671,13673,13676,13678,13680,13682,13684,13686,13688,13690,13692],{"class":140,"line":188},[138,13672,2518],{"class":1073},[138,13674,13675],{"class":2521}," p1",[138,13677,1146],{"class":1112},[138,13679,8662],{"class":1112},[138,13681,13579],{"class":801},[138,13683,806],{"class":805},[138,13685,1433],{"class":826},[138,13687,12504],{"class":830},[138,13689,1433],{"class":826},[138,13691,1049],{"class":805},[138,13693,13406],{"class":809},[138,13695,13696,13698,13701,13703,13705,13707,13709,13711,13714,13716,13718],{"class":140,"line":194},[138,13697,2518],{"class":1073},[138,13699,13700],{"class":2521}," p2",[138,13702,1146],{"class":1112},[138,13704,8662],{"class":1112},[138,13706,13579],{"class":801},[138,13708,806],{"class":805},[138,13710,1433],{"class":826},[138,13712,13713],{"class":830},"João",[138,13715,1433],{"class":826},[138,13717,1049],{"class":805},[138,13719,13406],{"class":809},[138,13721,13722],{"class":140,"line":199},[138,13723,649],{"emptyLinePlaceholder":86},[138,13725,13726,13729,13731,13733,13735,13737],{"class":140,"line":204},[138,13727,13728],{"class":805},"p1",[138,13730,363],{"class":809},[138,13732,12647],{"class":801},[138,13734,3093],{"class":805},[138,13736,1829],{"class":809},[138,13738,13739],{"class":911}," \u002F\u002F \"Olá, sou Ana\"\n",[138,13741,13742,13745,13747,13749,13751,13753],{"class":140,"line":209},[138,13743,13744],{"class":805},"p2",[138,13746,363],{"class":809},[138,13748,12647],{"class":801},[138,13750,3093],{"class":805},[138,13752,1829],{"class":809},[138,13754,13755],{"class":911}," \u002F\u002F \"Olá, sou João\"\n",[12,13757,13758,13760,13761,13764,13765,13768,13769,13771,13772,13774,13775,13777],{},[135,13759,12647],{}," não está em cada instância. Está em ",[135,13762,13763],{},"Pessoa.prototype",", compartilhado entre todas. Quando você usa ",[135,13766,13767],{},"new",", o JavaScript faz quatro coisas em sequência: cria um objeto vazio, aponta o ",[135,13770,13358],{}," dele para ",[135,13773,13763],{},", executa a função com ",[135,13776,8821],{}," apontando para esse objeto e retorna o objeto.",[30,13779,13781],{"id":13780},"classes-es6","Classes (ES6)",[12,13783,13784],{},"As classes introduzidas no ES6 são uma sintaxe mais limpa em cima do mesmo mecanismo. Não é um sistema novo, é açúcar sintático sobre protótipos.",[129,13786,13788],{"className":5084,"code":13787,"language":5086,"meta":75,"style":75},"class Pessoa {\n  constructor(nome) {\n    this.nome = nome;\n  }\n\n  saudar() {\n    console.log(`Olá, sou ${this.nome}`);\n  }\n}\n",[135,13789,13790,13799,13812,13827,13831,13835,13844,13872,13876],{"__ignoreMap":75},[138,13791,13792,13795,13797],{"class":140,"line":141},[138,13793,13794],{"class":1073},"class",[138,13796,13579],{"class":1556},[138,13798,934],{"class":809},[138,13800,13801,13804,13806,13808,13810],{"class":140,"line":76},[138,13802,13803],{"class":1073},"  constructor",[138,13805,806],{"class":809},[138,13807,3107],{"class":1045},[138,13809,1049],{"class":809},[138,13811,934],{"class":809},[138,13813,13814,13817,13819,13821,13823,13825],{"class":140,"line":152},[138,13815,13816],{"class":2041},"    this",[138,13818,363],{"class":809},[138,13820,3107],{"class":805},[138,13822,1146],{"class":1112},[138,13824,6974],{"class":805},[138,13826,13406],{"class":809},[138,13828,13829],{"class":140,"line":158},[138,13830,1184],{"class":809},[138,13832,13833],{"class":140,"line":164},[138,13834,649],{"emptyLinePlaceholder":86},[138,13836,13837,13840,13842],{"class":140,"line":170},[138,13838,13839],{"class":5196},"  saudar",[138,13841,3093],{"class":809},[138,13843,934],{"class":809},[138,13845,13846,13848,13850,13852,13854,13856,13858,13860,13862,13864,13866,13868,13870],{"class":140,"line":176},[138,13847,9722],{"class":805},[138,13849,363],{"class":809},[138,13851,9727],{"class":801},[138,13853,806],{"class":815},[138,13855,3791],{"class":826},[138,13857,13646],{"class":830},[138,13859,3797],{"class":826},[138,13861,8821],{"class":2041},[138,13863,363],{"class":826},[138,13865,3107],{"class":805},[138,13867,3803],{"class":826},[138,13869,1049],{"class":815},[138,13871,13406],{"class":809},[138,13873,13874],{"class":140,"line":182},[138,13875,1184],{"class":809},[138,13877,13878],{"class":140,"line":188},[138,13879,2082],{"class":809},[12,13881,13882,13883,13885,13886,13888,13889,363],{},"Por baixo, ",[135,13884,12647],{}," ainda vai para ",[135,13887,13763],{},". O mecanismo é idêntico ao das constructor functions. A diferença é legibilidade e algumas proteções extras, como não poder chamar a classe sem ",[135,13890,13767],{},[30,13892,13894],{"id":13893},"herança","Herança",[12,13896,13897,13898,13900],{},"Herança em JavaScript é prototípica: um objeto herda de outro objeto. O ",[135,13899,781],{}," configura essa cadeia.",[129,13902,13904],{"className":5084,"code":13903,"language":5086,"meta":75,"style":75},"class Animal {\n  constructor(nome) {\n    this.nome = nome;\n  }\n\n  respirar() {\n    console.log(`${this.nome} está respirando`);\n  }\n}\n\nclass Cachorro extends Animal {\n  latir() {\n    console.log(`${this.nome} está latindo`);\n  }\n}\n\nconst rex = new Cachorro(\"Rex\");\nrex.respirar(); \u002F\u002F herdado de Animal\nrex.latir();    \u002F\u002F próprio de Cachorro\n",[135,13905,13906,13915,13927,13941,13945,13949,13957,13987,13991,13995,13999,14013,14022,14051,14055,14059,14063,14089,14105],{"__ignoreMap":75},[138,13907,13908,13910,13913],{"class":140,"line":141},[138,13909,13794],{"class":1073},[138,13911,13912],{"class":1556}," Animal",[138,13914,934],{"class":809},[138,13916,13917,13919,13921,13923,13925],{"class":140,"line":76},[138,13918,13803],{"class":1073},[138,13920,806],{"class":809},[138,13922,3107],{"class":1045},[138,13924,1049],{"class":809},[138,13926,934],{"class":809},[138,13928,13929,13931,13933,13935,13937,13939],{"class":140,"line":152},[138,13930,13816],{"class":2041},[138,13932,363],{"class":809},[138,13934,3107],{"class":805},[138,13936,1146],{"class":1112},[138,13938,6974],{"class":805},[138,13940,13406],{"class":809},[138,13942,13943],{"class":140,"line":158},[138,13944,1184],{"class":809},[138,13946,13947],{"class":140,"line":164},[138,13948,649],{"emptyLinePlaceholder":86},[138,13950,13951,13953,13955],{"class":140,"line":170},[138,13952,13380],{"class":5196},[138,13954,3093],{"class":809},[138,13956,934],{"class":809},[138,13958,13959,13961,13963,13965,13967,13970,13972,13974,13976,13978,13981,13983,13985],{"class":140,"line":176},[138,13960,9722],{"class":805},[138,13962,363],{"class":809},[138,13964,9727],{"class":801},[138,13966,806],{"class":815},[138,13968,13969],{"class":826},"`${",[138,13971,8821],{"class":2041},[138,13973,363],{"class":826},[138,13975,3107],{"class":805},[138,13977,869],{"class":826},[138,13979,13980],{"class":830}," está respirando",[138,13982,3791],{"class":826},[138,13984,1049],{"class":815},[138,13986,13406],{"class":809},[138,13988,13989],{"class":140,"line":182},[138,13990,1184],{"class":809},[138,13992,13993],{"class":140,"line":188},[138,13994,2082],{"class":809},[138,13996,13997],{"class":140,"line":194},[138,13998,649],{"emptyLinePlaceholder":86},[138,14000,14001,14003,14006,14009,14011],{"class":140,"line":199},[138,14002,13794],{"class":1073},[138,14004,14005],{"class":1556}," Cachorro",[138,14007,14008],{"class":1073}," extends",[138,14010,13912],{"class":1556},[138,14012,934],{"class":809},[138,14014,14015,14018,14020],{"class":140,"line":204},[138,14016,14017],{"class":5196},"  latir",[138,14019,3093],{"class":809},[138,14021,934],{"class":809},[138,14023,14024,14026,14028,14030,14032,14034,14036,14038,14040,14042,14045,14047,14049],{"class":140,"line":209},[138,14025,9722],{"class":805},[138,14027,363],{"class":809},[138,14029,9727],{"class":801},[138,14031,806],{"class":815},[138,14033,13969],{"class":826},[138,14035,8821],{"class":2041},[138,14037,363],{"class":826},[138,14039,3107],{"class":805},[138,14041,869],{"class":826},[138,14043,14044],{"class":830}," está latindo",[138,14046,3791],{"class":826},[138,14048,1049],{"class":815},[138,14050,13406],{"class":809},[138,14052,14053],{"class":140,"line":215},[138,14054,1184],{"class":809},[138,14056,14057],{"class":140,"line":221},[138,14058,2082],{"class":809},[138,14060,14061],{"class":140,"line":227},[138,14062,649],{"emptyLinePlaceholder":86},[138,14064,14065,14067,14070,14072,14074,14076,14078,14080,14083,14085,14087],{"class":140,"line":233},[138,14066,2518],{"class":1073},[138,14068,14069],{"class":2521}," rex",[138,14071,1146],{"class":1112},[138,14073,8662],{"class":1112},[138,14075,14005],{"class":801},[138,14077,806],{"class":805},[138,14079,1433],{"class":826},[138,14081,14082],{"class":830},"Rex",[138,14084,1433],{"class":826},[138,14086,1049],{"class":805},[138,14088,13406],{"class":809},[138,14090,14091,14094,14096,14098,14100,14102],{"class":140,"line":239},[138,14092,14093],{"class":805},"rex",[138,14095,363],{"class":809},[138,14097,13512],{"class":801},[138,14099,3093],{"class":805},[138,14101,1829],{"class":809},[138,14103,14104],{"class":911}," \u002F\u002F herdado de Animal\n",[138,14106,14107,14109,14111,14113,14115,14117],{"class":140,"line":245},[138,14108,14093],{"class":805},[138,14110,363],{"class":809},[138,14112,13451],{"class":801},[138,14114,3093],{"class":805},[138,14116,1829],{"class":809},[138,14118,14119],{"class":911},"    \u002F\u002F próprio de Cachorro\n",[12,14121,14122],{},"A cadeia de protótipos fica assim:",[129,14124,14127],{"className":14125,"code":14126,"language":13552},[13550],"rex → Cachorro.prototype → Animal.prototype → Object.prototype → null\n",[135,14128,14126],{"__ignoreMap":75},[12,14130,14131,14132,14135,14136,14138,14139,14142,14143,14146],{},"Quando você chama ",[135,14133,14134],{},"rex.respirar()",", o JavaScript não encontra em ",[135,14137,14093],{},", não encontra em ",[135,14140,14141],{},"Cachorro.prototype",", encontra em ",[135,14144,14145],{},"Animal.prototype"," e executa.",[30,14148,14150],{"id":14149},"super",[135,14151,14149],{},[12,14153,14154],{},"Usado para acessar o constructor ou métodos da classe pai:",[129,14156,14158],{"className":5084,"code":14157,"language":5086,"meta":75,"style":75},"class Animal {\n  constructor(nome) {\n    this.nome = nome;\n  }\n\n  descrever() {\n    return `Sou ${this.nome}`;\n  }\n}\n\nclass Cachorro extends Animal {\n  constructor(nome, raca) {\n    super(nome);\n    this.raca = raca;\n  }\n\n  descrever() {\n    return `${super.descrever()}, da raça ${this.raca}`;\n  }\n}\n\nconst rex = new Cachorro(\"Rex\", \"Labrador\");\nrex.descrever(); \u002F\u002F \"Sou Rex, da raça Labrador\"\n",[135,14159,14160,14168,14180,14194,14198,14202,14211,14232,14236,14240,14244,14256,14273,14286,14301,14305,14309,14317,14351,14355,14359,14363,14396],{"__ignoreMap":75},[138,14161,14162,14164,14166],{"class":140,"line":141},[138,14163,13794],{"class":1073},[138,14165,13912],{"class":1556},[138,14167,934],{"class":809},[138,14169,14170,14172,14174,14176,14178],{"class":140,"line":76},[138,14171,13803],{"class":1073},[138,14173,806],{"class":809},[138,14175,3107],{"class":1045},[138,14177,1049],{"class":809},[138,14179,934],{"class":809},[138,14181,14182,14184,14186,14188,14190,14192],{"class":140,"line":152},[138,14183,13816],{"class":2041},[138,14185,363],{"class":809},[138,14187,3107],{"class":805},[138,14189,1146],{"class":1112},[138,14191,6974],{"class":805},[138,14193,13406],{"class":809},[138,14195,14196],{"class":140,"line":158},[138,14197,1184],{"class":809},[138,14199,14200],{"class":140,"line":164},[138,14201,649],{"emptyLinePlaceholder":86},[138,14203,14204,14207,14209],{"class":140,"line":170},[138,14205,14206],{"class":5196},"  descrever",[138,14208,3093],{"class":809},[138,14210,934],{"class":809},[138,14212,14213,14215,14217,14220,14222,14224,14226,14228,14230],{"class":140,"line":176},[138,14214,2900],{"class":794},[138,14216,9612],{"class":826},[138,14218,14219],{"class":830},"Sou ",[138,14221,3797],{"class":826},[138,14223,8821],{"class":2041},[138,14225,363],{"class":826},[138,14227,3107],{"class":805},[138,14229,3803],{"class":826},[138,14231,13406],{"class":809},[138,14233,14234],{"class":140,"line":182},[138,14235,1184],{"class":809},[138,14237,14238],{"class":140,"line":188},[138,14239,2082],{"class":809},[138,14241,14242],{"class":140,"line":194},[138,14243,649],{"emptyLinePlaceholder":86},[138,14245,14246,14248,14250,14252,14254],{"class":140,"line":199},[138,14247,13794],{"class":1073},[138,14249,14005],{"class":1556},[138,14251,14008],{"class":1073},[138,14253,13912],{"class":1556},[138,14255,934],{"class":809},[138,14257,14258,14260,14262,14264,14266,14269,14271],{"class":140,"line":204},[138,14259,13803],{"class":1073},[138,14261,806],{"class":809},[138,14263,3107],{"class":1045},[138,14265,954],{"class":809},[138,14267,14268],{"class":1045}," raca",[138,14270,1049],{"class":809},[138,14272,934],{"class":809},[138,14274,14275,14278,14280,14282,14284],{"class":140,"line":209},[138,14276,14277],{"class":2521},"    super",[138,14279,806],{"class":815},[138,14281,3107],{"class":805},[138,14283,1049],{"class":815},[138,14285,13406],{"class":809},[138,14287,14288,14290,14292,14295,14297,14299],{"class":140,"line":215},[138,14289,13816],{"class":2041},[138,14291,363],{"class":809},[138,14293,14294],{"class":805},"raca",[138,14296,1146],{"class":1112},[138,14298,14268],{"class":805},[138,14300,13406],{"class":809},[138,14302,14303],{"class":140,"line":221},[138,14304,1184],{"class":809},[138,14306,14307],{"class":140,"line":227},[138,14308,649],{"emptyLinePlaceholder":86},[138,14310,14311,14313,14315],{"class":140,"line":233},[138,14312,14206],{"class":5196},[138,14314,3093],{"class":809},[138,14316,934],{"class":809},[138,14318,14319,14321,14324,14326,14328,14331,14334,14336,14339,14341,14343,14345,14347,14349],{"class":140,"line":239},[138,14320,2900],{"class":794},[138,14322,14323],{"class":826}," `${",[138,14325,14149],{"class":2521},[138,14327,363],{"class":826},[138,14329,14330],{"class":801},"descrever",[138,14332,3093],{"class":14333},"ssI5v",[138,14335,869],{"class":826},[138,14337,14338],{"class":830},", da raça ",[138,14340,3797],{"class":826},[138,14342,8821],{"class":2041},[138,14344,363],{"class":826},[138,14346,14294],{"class":805},[138,14348,3803],{"class":826},[138,14350,13406],{"class":809},[138,14352,14353],{"class":140,"line":245},[138,14354,1184],{"class":809},[138,14356,14357],{"class":140,"line":251},[138,14358,2082],{"class":809},[138,14360,14361],{"class":140,"line":257},[138,14362,649],{"emptyLinePlaceholder":86},[138,14364,14365,14367,14369,14371,14373,14375,14377,14379,14381,14383,14385,14387,14390,14392,14394],{"class":140,"line":263},[138,14366,2518],{"class":1073},[138,14368,14069],{"class":2521},[138,14370,1146],{"class":1112},[138,14372,8662],{"class":1112},[138,14374,14005],{"class":801},[138,14376,806],{"class":805},[138,14378,1433],{"class":826},[138,14380,14082],{"class":830},[138,14382,1433],{"class":826},[138,14384,954],{"class":809},[138,14386,1567],{"class":826},[138,14388,14389],{"class":830},"Labrador",[138,14391,1433],{"class":826},[138,14393,1049],{"class":805},[138,14395,13406],{"class":809},[138,14397,14398,14400,14402,14404,14406,14408],{"class":140,"line":269},[138,14399,14093],{"class":805},[138,14401,363],{"class":809},[138,14403,14330],{"class":801},[138,14405,3093],{"class":805},[138,14407,1829],{"class":809},[138,14409,14410],{"class":911}," \u002F\u002F \"Sou Rex, da raça Labrador\"\n",[12,14412,14413,14414,14417,14418,14420,14421,14423,14424,14426,14427,363],{},"Sem o ",[135,14415,14416],{},"super()"," no constructor de uma classe filha, o JavaScript lança um ",[135,14419,12432],{},". Ele precisa ser chamado antes de qualquer uso de ",[135,14422,8821],{},", porque é o ",[135,14425,14149],{}," que cria o objeto e vincula o ",[135,14428,8821],{},[30,14430,14432],{"id":14431},"pontos-que-costumam-confundir","Pontos que costumam confundir",[1643,14434,14436,14438,14439],{"id":14435},"prototype-vs-__proto__",[135,14437,13564],{}," vs ",[135,14440,14441],{},"__proto__",[12,14443,14444],{},"São coisas diferentes com nomes parecidos, o que gera bastante confusão.",[12,14446,14447,14449,14450,14453,14454,14456,14457,13185,14460,363],{},[135,14448,13763],{}," é a propriedade da função construtora, o objeto onde os métodos compartilhados ficam. ",[135,14451,14452],{},"instancia.__proto__"," é a referência interna do objeto criado, que aponta para ",[135,14455,13763],{},". Na prática, ",[135,14458,14459],{},"p1.__proto__ === Pessoa.prototype",[135,14461,12880],{},[12,14463,14464,14465,14468,14469,14471],{},"A forma oficial de acessar o prototype de um objeto é ",[135,14466,14467],{},"Object.getPrototypeOf(instancia)",". O ",[135,14470,14441],{}," funciona nos browsers modernos, mas é considerado legado.",[1643,14473,14475],{"id":14474},"herança-não-copia-referencia","Herança não copia, referencia",[12,14477,14478,14479,14482],{},"Diferente de algumas linguagens, JavaScript não copia os métodos para cada instância. Todas as instâncias referenciam o mesmo objeto no prototype. Isso é eficiente em memória, mas significa que se você alterar ",[135,14480,14481],{},"Pessoa.prototype.saudar"," em runtime, todas as instâncias já existentes são afetadas imediatamente.",[1643,14484,14486],{"id":14485},"propriedades-no-objeto-vs-no-prototype","Propriedades no objeto vs no prototype",[129,14488,14490],{"className":5084,"code":14489,"language":5086,"meta":75,"style":75},"class Pessoa {\n  constructor(nome) {\n    this.nome = nome; \u002F\u002F cada instância tem a sua cópia\n  }\n\n  saudar() {}        \u002F\u002F compartilhado no prototype\n}\n",[135,14491,14492,14500,14512,14529,14533,14537,14549],{"__ignoreMap":75},[138,14493,14494,14496,14498],{"class":140,"line":141},[138,14495,13794],{"class":1073},[138,14497,13579],{"class":1556},[138,14499,934],{"class":809},[138,14501,14502,14504,14506,14508,14510],{"class":140,"line":76},[138,14503,13803],{"class":1073},[138,14505,806],{"class":809},[138,14507,3107],{"class":1045},[138,14509,1049],{"class":809},[138,14511,934],{"class":809},[138,14513,14514,14516,14518,14520,14522,14524,14526],{"class":140,"line":152},[138,14515,13816],{"class":2041},[138,14517,363],{"class":809},[138,14519,3107],{"class":805},[138,14521,1146],{"class":1112},[138,14523,6974],{"class":805},[138,14525,1829],{"class":809},[138,14527,14528],{"class":911}," \u002F\u002F cada instância tem a sua cópia\n",[138,14530,14531],{"class":140,"line":158},[138,14532,1184],{"class":809},[138,14534,14535],{"class":140,"line":164},[138,14536,649],{"emptyLinePlaceholder":86},[138,14538,14539,14541,14543,14546],{"class":140,"line":170},[138,14540,13839],{"class":5196},[138,14542,3093],{"class":809},[138,14544,14545],{"class":809}," {}",[138,14547,14548],{"class":911},"        \u002F\u002F compartilhado no prototype\n",[138,14550,14551],{"class":140,"line":176},[138,14552,2082],{"class":809},[12,14554,14555,14556,14558,14559,363],{},"Propriedades definidas no constructor com ",[135,14557,8821],{}," ficam no próprio objeto. Métodos definidos no corpo da classe ficam no prototype. Essa distinção importa quando você inspeciona objetos no DevTools ou usa ",[135,14560,14561],{},"hasOwnProperty",[30,14563,4866],{"id":4865},[12,14565,14566,14567,14569,14570,14572],{},"O sistema de protótipos é o que está por baixo de toda orientação a objetos em JavaScript, incluindo as classes do ES6. Quando você chama um método, o JavaScript percorre a cadeia de protótipos até encontrá-lo ou retornar ",[135,14568,12428],{},". Quando você usa ",[135,14571,781],{},", está configurando essa cadeia.",[12,14574,14575,14576,13185,14579,14581],{},"Entender isso explica por que ",[135,14577,14578],{},"instancia.__proto__ === Classe.prototype",[135,14580,12880],{},", por que métodos são compartilhados em memória entre instâncias e por que alterar o prototype em runtime afeta todos os objetos que o referenciam.",[12,14583,14584],{},"As classes não escondem os protótipos. Elas só tornam a sintaxe mais legível.",[30,14586,9153],{"id":9152},[402,14588,14589,14596,14603],{},[405,14590,14591],{},[105,14592,14595],{"href":14593,"rel":14594},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fpt-BR\u002Fdocs\u002FWeb\u002FJavaScript\u002FInheritance_and_the_prototype_chain",[109],"Herança e cadeia de protótipos - MDN Web Docs",[405,14597,14598],{},[105,14599,14602],{"href":14600,"rel":14601},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fpt-BR\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FClasses",[109],"Classes - MDN Web Docs",[405,14604,14605],{},[105,14606,14609],{"href":14607,"rel":14608},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fpt-BR\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FObject\u002Fcreate",[109],"Object.create() - MDN Web Docs",[1744,14611,14612],{},"html pre.shiki code .sFsEu, html code.shiki .sFsEu{--shiki-light:#9C3EDA;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sVPC0, html code.shiki .sVPC0{--shiki-light:#90A4AE;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sFfmW, html code.shiki .sFfmW{--shiki-light:#39ADB5;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sG-J9, html code.shiki .sG-J9{--shiki-light:#39ADB5;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s0u7J, html code.shiki .s0u7J{--shiki-light:#E53935;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sMo7A, html code.shiki .sMo7A{--shiki-light:#90A4AE;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sK_r7, html code.shiki .sK_r7{--shiki-light:#6182B8;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sdv8B, html code.shiki .sdv8B{--shiki-light:#E53935;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sF_wb, html code.shiki .sF_wb{--shiki-light:#39ADB5;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .s0vBq, html code.shiki .s0vBq{--shiki-light:#91B859;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sk1zL, html code.shiki .sk1zL{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#FFAB70;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .swu5b, html code.shiki .swu5b{--shiki-light:#39ADB5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s3afY, html code.shiki .s3afY{--shiki-light:#E2931D;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .soiBB, html code.shiki .soiBB{--shiki-light:#E2931D;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .s3Er8, html code.shiki .s3Er8{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .ssI5v, html code.shiki .ssI5v{--shiki-light:#90A4AE;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}",{"title":75,"searchDepth":76,"depth":76,"links":14614},[14615,14616,14618,14619,14620,14621,14627,14628],{"id":13351,"depth":76,"text":13352},{"id":13560,"depth":76,"text":14617},"Constructor functions e o prototype",{"id":13780,"depth":76,"text":13781},{"id":13893,"depth":76,"text":13894},{"id":14149,"depth":76,"text":14149},{"id":14431,"depth":76,"text":14432,"children":14622},[14623,14625,14626],{"id":14435,"depth":152,"text":14624},"prototype vs __proto__",{"id":14474,"depth":152,"text":14475},{"id":14485,"depth":152,"text":14486},{"id":4865,"depth":76,"text":4866},{"id":9152,"depth":76,"text":9153},"2026-04-08T20:12:00-03:00","JavaScript não tem herança clássica como Java ou C#. Tem algo diferente: um sistema de protótipos onde objetos herdam de outros objetos. Entender essa diferença muda como você lê e escreve orientação a objetos em JS.","\u002Fimages\u002Fblog\u002Fprototype-heranca-js.png",{},"\u002Fblog\u002Fjavascript-prototype-e-heranca-com",{"title":13343,"description":14630},"blog\u002Fjavascript-prototype-e-heranca-com",[13337,13338,13339],"QV_sS7UL3WCvltECiAWEpLEQVSZIzUrNZjzeD058c6w",{"id":14639,"title":14640,"author":7,"body":14641,"date":15480,"description":15481,"extension":83,"image":15482,"meta":15483,"navigation":86,"path":15484,"seo":15485,"stem":15486,"tags":15487,"__hash__":15491},"blog\u002Fblog\u002Fjavascript-eventloop.md","Event Loop no JavaScript: por que o código não roda na ordem que você escreveu",{"type":9,"value":14642,"toc":15467},[14643,14646,14722,14725,14745,14751,14758,14760,14764,14770,14777,14780,14783,14785,14789,14793,14796,14799,14885,14888,14908,14918,14920,14924,14935,14941,15013,15016,15038,15040,15044,15050,15053,15068,15074,15076,15080,15083,15089,15092,15095,15147,15149,15153,15159,15162,15164,15168,15171,15183,15195,15198,15207,15210,15336,15339,15362,15365,15402,15404,15408,15446,15448,15450,15461,15464],[12,14644,14645],{},"Você já sabe o resultado desse código abaixo? Qual a ordem que ele será executado?",[129,14647,14649],{"className":5084,"code":14648,"language":5086,"meta":75,"style":75},"console.log('1')\nsetTimeout(() => console.log('2'), 0)\nconsole.log('3')\n",[135,14650,14651,14670,14704],{"__ignoreMap":75},[138,14652,14653,14655,14657,14659,14661,14663,14666,14668],{"class":140,"line":141},[138,14654,12480],{"class":805},[138,14656,363],{"class":809},[138,14658,9727],{"class":801},[138,14660,806],{"class":805},[138,14662,834],{"class":826},[138,14664,14665],{"class":830},"1",[138,14667,834],{"class":826},[138,14669,872],{"class":805},[138,14671,14672,14675,14677,14679,14681,14683,14685,14687,14689,14691,14694,14696,14698,14700,14702],{"class":140,"line":76},[138,14673,14674],{"class":801},"setTimeout",[138,14676,806],{"class":805},[138,14678,3093],{"class":809},[138,14680,1074],{"class":1073},[138,14682,13147],{"class":805},[138,14684,363],{"class":809},[138,14686,9727],{"class":801},[138,14688,806],{"class":805},[138,14690,834],{"class":826},[138,14692,14693],{"class":830},"2",[138,14695,834],{"class":826},[138,14697,1049],{"class":805},[138,14699,954],{"class":809},[138,14701,5296],{"class":5295},[138,14703,872],{"class":805},[138,14705,14706,14708,14710,14712,14714,14716,14718,14720],{"class":140,"line":152},[138,14707,12480],{"class":805},[138,14709,363],{"class":809},[138,14711,9727],{"class":801},[138,14713,806],{"class":805},[138,14715,834],{"class":826},[138,14717,13188],{"class":830},[138,14719,834],{"class":826},[138,14721,872],{"class":805},[12,14723,14724],{},"O timeout é zero milissegundos. Zero. E mesmo assim o resultado é:",[129,14726,14728],{"className":131,"code":14727,"language":133,"meta":75,"style":75},"1\n3\n2\n",[135,14729,14730,14735,14740],{"__ignoreMap":75},[138,14731,14732],{"class":140,"line":141},[138,14733,14734],{},"1\n",[138,14736,14737],{"class":140,"line":76},[138,14738,14739],{},"3\n",[138,14741,14742],{"class":140,"line":152},[138,14743,14744],{},"2\n",[12,14746,14747,14748,14750],{},"Por que o ",[135,14749,14693],{}," sai por último se o delay é zero?",[12,14752,14753,14754,14757],{},"A resposta está no ",[19,14755,14756],{},"Event Loop",", e entender isso muda a forma como você lê e escreve JavaScript.",[27,14759],{},[30,14761,14763],{"id":14762},"javascript-é-single-thread","JavaScript é single-thread",[12,14765,14766,14767,363],{},"Antes de qualquer coisa, é importante entender isso: ",[19,14768,14769],{},"JavaScript executa uma coisa por vez",[12,14771,14772,14773,14776],{},"Não existe paralelismo real dentro do JavaScript. Há apenas uma linha de execução, chamada de ",[19,14774,14775],{},"thread",". Isso significa que, enquanto uma função está rodando, nenhuma outra pode rodar ao mesmo tempo.",[12,14778,14779],{},"Então como o JavaScript consegue fazer coisas assíncronas, como requisições HTTP, timers e eventos de clique, sem travar tudo?",[12,14781,14782],{},"É aí que entram as peças do Event Loop.",[27,14784],{},[30,14786,14788],{"id":14787},"as-três-peças","As três peças",[1643,14790,14792],{"id":14791},"_1-call-stack","1. Call Stack",[12,14794,14795],{},"A Call Stack é onde o código é executado. Funciona como uma pilha: o que entra por último, sai primeiro.",[12,14797,14798],{},"Quando você chama uma função, ela entra na stack. Quando ela termina, sai da stack. Simples assim.",[129,14800,14802],{"className":5084,"code":14801,"language":5086,"meta":75,"style":75},"function soma(a, b) {\n  return a + b\n}\n\nfunction calcular() {\n  return soma(2, 3)\n}\n\ncalcular()\n",[135,14803,14804,14824,14836,14840,14844,14854,14870,14874,14878],{"__ignoreMap":75},[138,14805,14806,14808,14811,14813,14815,14817,14820,14822],{"class":140,"line":141},[138,14807,3824],{"class":1073},[138,14809,14810],{"class":801}," soma",[138,14812,806],{"class":809},[138,14814,105],{"class":1045},[138,14816,954],{"class":809},[138,14818,14819],{"class":1045}," b",[138,14821,1049],{"class":809},[138,14823,934],{"class":809},[138,14825,14826,14828,14831,14833],{"class":140,"line":76},[138,14827,3199],{"class":794},[138,14829,14830],{"class":805}," a",[138,14832,1155],{"class":1112},[138,14834,14835],{"class":805}," b\n",[138,14837,14838],{"class":140,"line":152},[138,14839,2082],{"class":809},[138,14841,14842],{"class":140,"line":158},[138,14843,649],{"emptyLinePlaceholder":86},[138,14845,14846,14848,14850,14852],{"class":140,"line":164},[138,14847,3824],{"class":1073},[138,14849,12812],{"class":801},[138,14851,3093],{"class":809},[138,14853,934],{"class":809},[138,14855,14856,14858,14860,14862,14864,14866,14868],{"class":140,"line":170},[138,14857,3199],{"class":794},[138,14859,14810],{"class":801},[138,14861,806],{"class":815},[138,14863,14693],{"class":5295},[138,14865,954],{"class":809},[138,14867,13122],{"class":5295},[138,14869,872],{"class":815},[138,14871,14872],{"class":140,"line":176},[138,14873,2082],{"class":809},[138,14875,14876],{"class":140,"line":182},[138,14877,649],{"emptyLinePlaceholder":86},[138,14879,14880,14883],{"class":140,"line":188},[138,14881,14882],{"class":801},"calcular",[138,14884,2796],{"class":805},[12,14886,14887],{},"A stack nesse momento ficaria assim:",[129,14889,14891],{"className":131,"code":14890,"language":133,"meta":75,"style":75},"[ soma ]\n[ calcular ]\n[ main ]\n",[135,14892,14893,14898,14903],{"__ignoreMap":75},[138,14894,14895],{"class":140,"line":141},[138,14896,14897],{},"[ soma ]\n",[138,14899,14900],{"class":140,"line":76},[138,14901,14902],{},"[ calcular ]\n",[138,14904,14905],{"class":140,"line":152},[138,14906,14907],{},"[ main ]\n",[12,14909,14910,14911,14914,14915,14917],{},"Quando ",[135,14912,14913],{},"soma"," termina, sai. Quando ",[135,14916,14882],{}," termina, sai. Quando o script termina, sai.",[27,14919],{},[1643,14921,14923],{"id":14922},"_2-web-apis","2. Web APIs",[12,14925,14131,14926,370,14928,14931,14932,363],{},[135,14927,14674],{},[135,14929,14930],{},"fetch",", ou adiciona um event listener, o ",[19,14933,14934],{},"browser assume o controle",[12,14936,14937,14938,363],{},"Isso é importante: essas funcionalidades não são JavaScript puro. São APIs fornecidas pelo ambiente (browser ou Node.js). O JavaScript entrega a tarefa para elas e ",[19,14939,14940],{},"segue em frente sem esperar",[129,14942,14943],{"className":5084,"code":14648,"language":5086,"meta":75,"style":75},[135,14944,14945,14963,14995],{"__ignoreMap":75},[138,14946,14947,14949,14951,14953,14955,14957,14959,14961],{"class":140,"line":141},[138,14948,12480],{"class":805},[138,14950,363],{"class":809},[138,14952,9727],{"class":801},[138,14954,806],{"class":805},[138,14956,834],{"class":826},[138,14958,14665],{"class":830},[138,14960,834],{"class":826},[138,14962,872],{"class":805},[138,14964,14965,14967,14969,14971,14973,14975,14977,14979,14981,14983,14985,14987,14989,14991,14993],{"class":140,"line":76},[138,14966,14674],{"class":801},[138,14968,806],{"class":805},[138,14970,3093],{"class":809},[138,14972,1074],{"class":1073},[138,14974,13147],{"class":805},[138,14976,363],{"class":809},[138,14978,9727],{"class":801},[138,14980,806],{"class":805},[138,14982,834],{"class":826},[138,14984,14693],{"class":830},[138,14986,834],{"class":826},[138,14988,1049],{"class":805},[138,14990,954],{"class":809},[138,14992,5296],{"class":5295},[138,14994,872],{"class":805},[138,14996,14997,14999,15001,15003,15005,15007,15009,15011],{"class":140,"line":152},[138,14998,12480],{"class":805},[138,15000,363],{"class":809},[138,15002,9727],{"class":801},[138,15004,806],{"class":805},[138,15006,834],{"class":826},[138,15008,13188],{"class":830},[138,15010,834],{"class":826},[138,15012,872],{"class":805},[12,15014,15015],{},"O que acontece aqui:",[15017,15018,15019,15025,15030,15035],"ol",{},[405,15020,15021,15024],{},[135,15022,15023],{},"console.log('1')"," entra na stack, executa, sai",[405,15026,15027,15029],{},[135,15028,14674],{}," entra na stack, entrega o callback para a Web API, sai",[405,15031,15032,15024],{},[135,15033,15034],{},"console.log('3')",[405,15036,15037],{},"A Web API termina o timer e empurra o callback para a fila",[27,15039],{},[1643,15041,15043],{"id":15042},"_3-a-fila-queue","3. A Fila (Queue)",[12,15045,15046,15047,363],{},"Quando uma Web API termina seu trabalho, o callback não vai direto para a stack. Ele vai para uma ",[19,15048,15049],{},"fila de espera",[12,15051,15052],{},"E ele só sai dessa fila quando a stack estiver completamente vazia.",[129,15054,15056],{"className":131,"code":15055,"language":133,"meta":75,"style":75},"Stack: []\nQueue: [ () => console.log('2') ]\n",[135,15057,15058,15063],{"__ignoreMap":75},[138,15059,15060],{"class":140,"line":141},[138,15061,15062],{},"Stack: []\n",[138,15064,15065],{"class":140,"line":76},[138,15066,15067],{},"Queue: [ () => console.log('2') ]\n",[12,15069,15070,15071,15073],{},"Agora a stack está vazia. O Event Loop pega o callback da fila e coloca na stack. Ele executa. O ",[135,15072,14693],{}," aparece.",[27,15075],{},[30,15077,15079],{"id":15078},"o-event-loop-em-si","O Event Loop em si",[12,15081,15082],{},"O Event Loop é o mecanismo que conecta tudo isso. Ele fica em um loop contínuo fazendo uma pergunta:",[15084,15085,15086],"blockquote",{},[12,15087,15088],{},"A stack está vazia? Se sim, pega o próximo item da fila.",[12,15090,15091],{},"É só isso. Simples assim na teoria, mas é o que permite que o JavaScript seja assíncrono mesmo sendo single-thread.",[12,15093,15094],{},"Visualizando o fluxo completo:",[129,15096,15098],{"className":131,"code":15097,"language":133,"meta":75,"style":75},"console.log('1')     → Stack → executa → sai\nsetTimeout(fn, 0)    → Stack → entrega para Web API → sai\nconsole.log('3')     → Stack → executa → sai\n\nWeb API termina      → fn vai para a Queue\n\nEvent Loop verifica  → Stack está vazia? Sim\n                     → pega fn da Queue → coloca na Stack\n\nfn executa           → console.log('2') → sai\n",[135,15099,15100,15105,15110,15115,15119,15124,15128,15133,15138,15142],{"__ignoreMap":75},[138,15101,15102],{"class":140,"line":141},[138,15103,15104],{},"console.log('1')     → Stack → executa → sai\n",[138,15106,15107],{"class":140,"line":76},[138,15108,15109],{},"setTimeout(fn, 0)    → Stack → entrega para Web API → sai\n",[138,15111,15112],{"class":140,"line":152},[138,15113,15114],{},"console.log('3')     → Stack → executa → sai\n",[138,15116,15117],{"class":140,"line":158},[138,15118,649],{"emptyLinePlaceholder":86},[138,15120,15121],{"class":140,"line":164},[138,15122,15123],{},"Web API termina      → fn vai para a Queue\n",[138,15125,15126],{"class":140,"line":170},[138,15127,649],{"emptyLinePlaceholder":86},[138,15129,15130],{"class":140,"line":176},[138,15131,15132],{},"Event Loop verifica  → Stack está vazia? Sim\n",[138,15134,15135],{"class":140,"line":182},[138,15136,15137],{},"                     → pega fn da Queue → coloca na Stack\n",[138,15139,15140],{"class":140,"line":188},[138,15141,649],{"emptyLinePlaceholder":86},[138,15143,15144],{"class":140,"line":194},[138,15145,15146],{},"fn executa           → console.log('2') → sai\n",[27,15148],{},[30,15150,15152],{"id":15151},"por-que-isso-responde-a-pergunta-inicial","Por que isso responde a pergunta inicial?",[12,15154,15155,15156,15158],{},"O timeout era zero, mas o callback só pôde entrar na stack depois que ela esvaziou. E ela só esvaziou depois que o ",[135,15157,15034],{}," executou.",[12,15160,15161],{},"Mesmo com delay zero, o callback nunca pula a fila.",[27,15163],{},[30,15165,15167],{"id":15166},"microtasks-e-macrotasks","Microtasks e Macrotasks",[12,15169,15170],{},"Até aqui falamos de uma única fila. Na prática, existem duas, com prioridades diferentes.",[12,15172,15173,15176,15177,370,15179,15182],{},[19,15174,15175],{},"Macrotasks"," — ",[135,15178,14674],{},[135,15180,15181],{},"setInterval",", eventos de DOM. Essa é a fila que vimos até agora.",[12,15184,15185,15176,15188,370,15191,15194],{},[19,15186,15187],{},"Microtasks",[135,15189,15190],{},"Promise.then",[135,15192,15193],{},"queueMicrotask",". Essa fila tem prioridade maior.",[12,15196,15197],{},"A regra é:",[15084,15199,15200],{},[12,15201,15202,15203,15206],{},"Depois que a stack esvazia, o Event Loop processa ",[19,15204,15205],{},"todas"," as microtasks antes de pegar a próxima macrotask.",[12,15208,15209],{},"Veja o que acontece aqui:",[129,15211,15213],{"className":5084,"code":15212,"language":5086,"meta":75,"style":75},"console.log('1')\n\nsetTimeout(() => console.log('timeout'), 0)\n\nPromise.resolve().then(() => console.log('promise'))\n\nconsole.log('2')\n",[135,15214,15215,15233,15237,15270,15274,15314,15318],{"__ignoreMap":75},[138,15216,15217,15219,15221,15223,15225,15227,15229,15231],{"class":140,"line":141},[138,15218,12480],{"class":805},[138,15220,363],{"class":809},[138,15222,9727],{"class":801},[138,15224,806],{"class":805},[138,15226,834],{"class":826},[138,15228,14665],{"class":830},[138,15230,834],{"class":826},[138,15232,872],{"class":805},[138,15234,15235],{"class":140,"line":76},[138,15236,649],{"emptyLinePlaceholder":86},[138,15238,15239,15241,15243,15245,15247,15249,15251,15253,15255,15257,15260,15262,15264,15266,15268],{"class":140,"line":152},[138,15240,14674],{"class":801},[138,15242,806],{"class":805},[138,15244,3093],{"class":809},[138,15246,1074],{"class":1073},[138,15248,13147],{"class":805},[138,15250,363],{"class":809},[138,15252,9727],{"class":801},[138,15254,806],{"class":805},[138,15256,834],{"class":826},[138,15258,15259],{"class":830},"timeout",[138,15261,834],{"class":826},[138,15263,1049],{"class":805},[138,15265,954],{"class":809},[138,15267,5296],{"class":5295},[138,15269,872],{"class":805},[138,15271,15272],{"class":140,"line":158},[138,15273,649],{"emptyLinePlaceholder":86},[138,15275,15276,15279,15281,15284,15286,15288,15291,15293,15295,15297,15299,15301,15303,15305,15307,15310,15312],{"class":140,"line":164},[138,15277,15278],{"class":3061},"Promise",[138,15280,363],{"class":809},[138,15282,15283],{"class":801},"resolve",[138,15285,3093],{"class":805},[138,15287,363],{"class":809},[138,15289,15290],{"class":801},"then",[138,15292,806],{"class":805},[138,15294,3093],{"class":809},[138,15296,1074],{"class":1073},[138,15298,13147],{"class":805},[138,15300,363],{"class":809},[138,15302,9727],{"class":801},[138,15304,806],{"class":805},[138,15306,834],{"class":826},[138,15308,15309],{"class":830},"promise",[138,15311,834],{"class":826},[138,15313,4205],{"class":805},[138,15315,15316],{"class":140,"line":170},[138,15317,649],{"emptyLinePlaceholder":86},[138,15319,15320,15322,15324,15326,15328,15330,15332,15334],{"class":140,"line":176},[138,15321,12480],{"class":805},[138,15323,363],{"class":809},[138,15325,9727],{"class":801},[138,15327,806],{"class":805},[138,15329,834],{"class":826},[138,15331,14693],{"class":830},[138,15333,834],{"class":826},[138,15335,872],{"class":805},[12,15337,15338],{},"Resultado:",[129,15340,15342],{"className":131,"code":15341,"language":133,"meta":75,"style":75},"1\n2\npromise\ntimeout\n",[135,15343,15344,15348,15352,15357],{"__ignoreMap":75},[138,15345,15346],{"class":140,"line":141},[138,15347,14734],{},[138,15349,15350],{"class":140,"line":76},[138,15351,14744],{},[138,15353,15354],{"class":140,"line":152},[138,15355,15356],{},"promise\n",[138,15358,15359],{"class":140,"line":158},[138,15360,15361],{},"timeout\n",[12,15363,15364],{},"O fluxo:",[15017,15366,15367,15372,15377,15383,15388,15391,15397],{},[405,15368,15369,15371],{},[135,15370,15023],{}," executa",[405,15373,15374,15376],{},[135,15375,14674],{}," entrega o callback para a Web API",[405,15378,15379,15382],{},[135,15380,15381],{},"Promise.resolve().then(...)"," coloca o callback na fila de microtasks",[405,15384,15385,15371],{},[135,15386,15387],{},"console.log('2')",[405,15389,15390],{},"Stack esvazia",[405,15392,15393,15394,15396],{},"Event Loop processa microtasks primeiro: ",[135,15395,15309],{}," aparece",[405,15398,15399,15400,15396],{},"Event Loop pega a próxima macrotask: ",[135,15401,15259],{},[27,15403],{},[30,15405,15407],{"id":15406},"resumo","Resumo",[402,15409,15410,15413,15420,15426,15433,15438],{},[405,15411,15412],{},"JavaScript é single-thread: executa uma coisa por vez",[405,15414,15415,15416,15419],{},"A ",[19,15417,15418],{},"Call Stack"," é onde o código roda",[405,15421,15422,15425],{},[19,15423,15424],{},"Web APIs"," assumem tarefas assíncronas enquanto a stack segue em frente",[405,15427,15428,15429,15432],{},"Os callbacks vão para uma ",[19,15430,15431],{},"fila"," e só entram na stack quando ela estiver vazia",[405,15434,1193,15435,15437],{},[19,15436,14756],{}," é o mecanismo que faz essa verificação continuamente",[405,15439,15440,15442,15443,15445],{},[19,15441,15187],{}," (Promises) têm prioridade sobre ",[19,15444,15175],{}," (setTimeout)",[27,15447],{},[30,15449,4866],{"id":4865},[12,15451,15452,15453,15456,15457,15460],{},"Event Loop não é um recurso avançado reservado para casos específicos. É o modelo de execução do JavaScript, e ele está presente em cada ",[135,15454,15455],{},"await",", cada ",[135,15458,15459],{},".then"," e cada evento que você escuta.",[12,15462,15463],{},"Quanto mais claro estiver esse modelo na sua cabeça, mais previsível o seu código assíncrono vai se tornar.",[1744,15465,15466],{},"html pre.shiki code .sMo7A, html code.shiki .sMo7A{--shiki-light:#90A4AE;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sG-J9, html code.shiki .sG-J9{--shiki-light:#39ADB5;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sK_r7, html code.shiki .sK_r7{--shiki-light:#6182B8;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sF_wb, html code.shiki .sF_wb{--shiki-light:#39ADB5;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .s0vBq, html code.shiki .s0vBq{--shiki-light:#91B859;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sFsEu, html code.shiki .sFsEu{--shiki-light:#9C3EDA;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .s_k96, html code.shiki .s_k96{--shiki-light:#F76D47;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sk1zL, html code.shiki .sk1zL{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#FFAB70;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .s3Er8, html code.shiki .s3Er8{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sFfmW, html code.shiki .sFfmW{--shiki-light:#39ADB5;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sdv8B, html code.shiki .sdv8B{--shiki-light:#E53935;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s3afY, html code.shiki .s3afY{--shiki-light:#E2931D;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}",{"title":75,"searchDepth":76,"depth":76,"links":15468},[15469,15470,15475,15476,15477,15478,15479],{"id":14762,"depth":76,"text":14763},{"id":14787,"depth":76,"text":14788,"children":15471},[15472,15473,15474],{"id":14791,"depth":152,"text":14792},{"id":14922,"depth":152,"text":14923},{"id":15042,"depth":152,"text":15043},{"id":15078,"depth":76,"text":15079},{"id":15151,"depth":76,"text":15152},{"id":15166,"depth":76,"text":15167},{"id":15406,"depth":76,"text":15407},{"id":4865,"depth":76,"text":4866},"2026-04-02T17:23:00-03:00","Entenda como o JavaScript executa código assíncrono e por que o resultado nem sempre é o que você espera.","\u002Fimages\u002Fblog\u002Feventloop.png",{},"\u002Fblog\u002Fjavascript-eventloop",{"title":14640,"description":15481},"blog\u002Fjavascript-eventloop",[15488,9241,15489,15490],"event loop","assíncrono","desenvolvimento","8DnJQ1W4xoO2KreDeyXnQuFOEyCetT0d8uiIOdskLzI",{"id":15493,"title":15494,"author":7,"body":15495,"date":16433,"description":16434,"extension":83,"image":16435,"meta":16436,"navigation":86,"path":16437,"seo":16438,"stem":16439,"tags":16440,"__hash__":16442},"blog\u002Fblog\u002Fclosures-javascript.md","Closure no JavaScript: explicação simplificada",{"type":9,"value":15496,"toc":16407},[15497,15500,15507,15509,15513,15516,15521,15525,15636,15639,15659,15661,15667,15676,15688,15705,15712,15714,15721,15736,15738,15745,15748,15756,15759,15775,15778,15801,15804,15813,15815,15819,15823,15826,15862,15866,15901,15905,15940,15942,15946,15951,15954,15971,15974,15976,15980,15984,16053,16059,16063,16108,16111,16113,16117,16123,16188,16191,16206,16214,16216,16220,16240,16245,16247,16251,16257,16260,16353,16362,16364,16366,16380,16382,16384,16387,16390,16404],[12,15498,15499],{},"Imagine uma função que \"lembra\" de uma variável mesmo depois de ter encerrado. Parece estranho, mas é exatamente isso que closure faz, e provavelmente você já usou sem perceber.",[12,15501,15502,15503,15506],{},"Neste post, vamos entender ",[19,15504,15505],{},"passo a passo",", respondendo exatamente as dúvidas mais comuns que surgem ao ver esse código pela primeira vez.",[27,15508],{},[30,15510,15512],{"id":15511},"o-que-é-closure","O que é closure?",[12,15514,15515],{},"Closure é quando uma função \"lembra\" do ambiente onde foi criada, mesmo depois que esse ambiente já deveria ter acabado.\nEm JavaScript, funções não carregam só o código. Elas também carregam as variáveis que estavam ao redor delas no momento da criação.",[15084,15517,15518],{},[12,15519,15520],{},"Closure é uma função que consegue acessar variáveis do escopo externo mesmo depois que esse escopo já foi encerrado.",[1643,15522,15524],{"id":15523},"exemplo-base","Exemplo base",[129,15526,15528],{"className":5084,"code":15527,"language":5086,"meta":75,"style":75},"function criarContador() {\n  let count = 0\n\n  return function () {\n    count++\n    return count\n  }\n}\n\nconst contador = criarContador()\n\ncontador() \u002F\u002F 1\ncontador() \u002F\u002F 2\ncontador() \u002F\u002F 3\n",[135,15529,15530,15541,15551,15555,15565,15572,15579,15583,15587,15591,15604,15608,15618,15627],{"__ignoreMap":75},[138,15531,15532,15534,15537,15539],{"class":140,"line":141},[138,15533,3824],{"class":1073},[138,15535,15536],{"class":801}," criarContador",[138,15538,3093],{"class":809},[138,15540,934],{"class":809},[138,15542,15543,15545,15547,15549],{"class":140,"line":76},[138,15544,12889],{"class":1073},[138,15546,10067],{"class":805},[138,15548,1146],{"class":1112},[138,15550,9548],{"class":5295},[138,15552,15553],{"class":140,"line":152},[138,15554,649],{"emptyLinePlaceholder":86},[138,15556,15557,15559,15561,15563],{"class":140,"line":158},[138,15558,3199],{"class":794},[138,15560,3931],{"class":1073},[138,15562,3034],{"class":809},[138,15564,934],{"class":809},[138,15566,15567,15570],{"class":140,"line":164},[138,15568,15569],{"class":805},"    count",[138,15571,9671],{"class":1112},[138,15573,15574,15576],{"class":140,"line":170},[138,15575,2900],{"class":794},[138,15577,15578],{"class":805}," count\n",[138,15580,15581],{"class":140,"line":176},[138,15582,1184],{"class":809},[138,15584,15585],{"class":140,"line":182},[138,15586,2082],{"class":809},[138,15588,15589],{"class":140,"line":188},[138,15590,649],{"emptyLinePlaceholder":86},[138,15592,15593,15595,15598,15600,15602],{"class":140,"line":194},[138,15594,2518],{"class":1073},[138,15596,15597],{"class":2521}," contador",[138,15599,1146],{"class":1112},[138,15601,15536],{"class":801},[138,15603,2796],{"class":805},[138,15605,15606],{"class":140,"line":199},[138,15607,649],{"emptyLinePlaceholder":86},[138,15609,15610,15613,15615],{"class":140,"line":204},[138,15611,15612],{"class":801},"contador",[138,15614,2732],{"class":805},[138,15616,15617],{"class":911},"\u002F\u002F 1\n",[138,15619,15620,15622,15624],{"class":140,"line":209},[138,15621,15612],{"class":801},[138,15623,2732],{"class":805},[138,15625,15626],{"class":911},"\u002F\u002F 2\n",[138,15628,15629,15631,15633],{"class":140,"line":215},[138,15630,15612],{"class":801},[138,15632,2732],{"class":805},[138,15634,15635],{"class":911},"\u002F\u002F 3\n",[12,15637,15638],{},"O que está acontecendo aqui:",[402,15640,15641,15647,15652],{},[405,15642,15643,15646],{},[135,15644,15645],{},"criarContador()"," retorna uma função",[405,15648,15649,15650],{},"essa função é armazenada na variável ",[135,15651,15612],{},[405,15653,15654,15655,15658],{},"quando você chama ",[135,15656,15657],{},"contador()",", está executando essa função retornada",[27,15660],{},[30,15662,14747,15664,15666],{"id":15663},"por-que-o-count-não-reinicia",[135,15665,9588],{}," não reinicia?",[12,15668,15669,15670,15672,15673,15675],{},"Parece que ",[135,15671,9588],{}," deveria voltar para ",[135,15674,10076],{}," a cada chamada, mas não volta.",[12,15677,15678,15681,15682,15684,15685,363],{},[19,15679,15680],{},"Resposta direta:"," porque ",[135,15683,15645],{}," foi executada apenas ",[19,15686,15687],{},"uma vez",[129,15689,15691],{"className":5084,"code":15690,"language":5086,"meta":75,"style":75},"const contador = criarContador()\n",[135,15692,15693],{"__ignoreMap":75},[138,15694,15695,15697,15699,15701,15703],{"class":140,"line":141},[138,15696,2518],{"class":1073},[138,15698,15597],{"class":2521},[138,15700,1146],{"class":1112},[138,15702,15536],{"class":801},[138,15704,2796],{"class":805},[12,15706,15707,15708,15711],{},"Depois disso, você não chama mais ",[135,15709,15710],{},"criarContador",", apenas a função retornada.",[27,15713],{},[30,15715,15717,15718,15720],{"id":15716},"então-onde-o-count-fica-armazenado","Então onde o ",[135,15719,9588],{}," fica armazenado?",[12,15722,15723,15724,15726,15727,15729,15730,15733,15734,363],{},"Mesmo depois da execução de ",[135,15725,15710],{}," terminar, o ",[135,15728,9588],{}," continua existindo.\nIsso acontece porque a função retornada ",[19,15731,15732],{},"mantém acesso ao escopo onde foi criada.","\nEsse comportamento é o que chamamos de ",[19,15735,7116],{},[27,15737],{},[30,15739,15741,15742,15744],{"id":15740},"dúvida-comum-mas-count-não-deveria-ser-global","Dúvida comum: mas ",[135,15743,9588],{}," não deveria ser global?",[12,15746,15747],{},"Não, e aqui está o ponto mais importante.",[12,15749,1193,15750,15752,15753,363],{},[135,15751,9588],{}," não é global. Ele fica em um ",[19,15754,15755],{},"ambiente em memória criado quando a função roda",[12,15757,15758],{},"Quando você executa:",[129,15760,15761],{"className":5084,"code":15690,"language":5086,"meta":75,"style":75},[135,15762,15763],{"__ignoreMap":75},[138,15764,15765,15767,15769,15771,15773],{"class":140,"line":141},[138,15766,2518],{"class":1073},[138,15768,15597],{"class":2521},[138,15770,1146],{"class":1112},[138,15772,15536],{"class":801},[138,15774,2796],{"class":805},[12,15776,15777],{},"O JavaScript cria um \"ambiente\" na memória:",[129,15779,15781],{"className":131,"code":15780,"language":133,"meta":75,"style":75},"Ambiente da função criarContador:\n{\n  count: 0\n}\n",[135,15782,15783,15788,15792,15797],{"__ignoreMap":75},[138,15784,15785],{"class":140,"line":141},[138,15786,15787],{},"Ambiente da função criarContador:\n",[138,15789,15790],{"class":140,"line":76},[138,15791,810],{},[138,15793,15794],{"class":140,"line":152},[138,15795,15796],{},"  count: 0\n",[138,15798,15799],{"class":140,"line":158},[138,15800,2082],{},[12,15802,15803],{},"E a função retornada fica ligada a esse ambiente:",[129,15805,15807],{"className":131,"code":15806,"language":133,"meta":75,"style":75},"contador → função que acessa { count }\n",[135,15808,15809],{"__ignoreMap":75},[138,15810,15811],{"class":140,"line":141},[138,15812,15806],{},[27,15814],{},[30,15816,15818],{"id":15817},"o-que-acontece-em-cada-chamada","O que acontece em cada chamada",[1643,15820,15822],{"id":15821},"primeira-chamada","Primeira chamada",[12,15824,15825],{},"Atualiza o ambiente:",[129,15827,15829],{"className":5084,"code":15828,"language":5086,"meta":75,"style":75},"contador()\n{ count: 0 } → { count: 1 }\n",[135,15830,15831,15837],{"__ignoreMap":75},[138,15832,15833,15835],{"class":140,"line":141},[138,15834,15612],{"class":801},[138,15836,2796],{"class":805},[138,15838,15839,15841,15843,15845,15847,15849,15852,15854,15856,15858,15860],{"class":140,"line":76},[138,15840,3284],{"class":809},[138,15842,10067],{"class":1556},[138,15844,782],{"class":809},[138,15846,5296],{"class":5295},[138,15848,2823],{"class":809},[138,15850,15851],{"class":805}," → ",[138,15853,3284],{"class":809},[138,15855,10067],{"class":1556},[138,15857,782],{"class":809},[138,15859,6969],{"class":5295},[138,15861,1015],{"class":809},[1643,15863,15865],{"id":15864},"segunda-chamada","Segunda chamada",[129,15867,15869],{"className":5084,"code":15868,"language":5086,"meta":75,"style":75},"contador()\n{ count: 1 } → { count: 2 }\n",[135,15870,15871,15877],{"__ignoreMap":75},[138,15872,15873,15875],{"class":140,"line":141},[138,15874,15612],{"class":801},[138,15876,2796],{"class":805},[138,15878,15879,15881,15883,15885,15887,15889,15891,15893,15895,15897,15899],{"class":140,"line":76},[138,15880,3284],{"class":809},[138,15882,10067],{"class":1556},[138,15884,782],{"class":809},[138,15886,6969],{"class":5295},[138,15888,2823],{"class":809},[138,15890,15851],{"class":805},[138,15892,3284],{"class":809},[138,15894,10067],{"class":1556},[138,15896,782],{"class":809},[138,15898,11162],{"class":5295},[138,15900,1015],{"class":809},[1643,15902,15904],{"id":15903},"terceira-chamada","Terceira chamada",[129,15906,15908],{"className":5084,"code":15907,"language":5086,"meta":75,"style":75},"contador()\n{ count: 2 } → { count: 3 }\n",[135,15909,15910,15916],{"__ignoreMap":75},[138,15911,15912,15914],{"class":140,"line":141},[138,15913,15612],{"class":801},[138,15915,2796],{"class":805},[138,15917,15918,15920,15922,15924,15926,15928,15930,15932,15934,15936,15938],{"class":140,"line":76},[138,15919,3284],{"class":809},[138,15921,10067],{"class":1556},[138,15923,782],{"class":809},[138,15925,11162],{"class":5295},[138,15927,2823],{"class":809},[138,15929,15851],{"class":805},[138,15931,3284],{"class":809},[138,15933,10067],{"class":1556},[138,15935,782],{"class":809},[138,15937,13122],{"class":5295},[138,15939,1015],{"class":809},[27,15941],{},[30,15943,15945],{"id":15944},"por-que-isso-funciona","Por que isso funciona?",[15084,15947,15948],{},[12,15949,15950],{},"Enquanto existir uma função usando esse ambiente, o JavaScript mantém ele na memória.",[12,15952,15953],{},"Ou seja:",[402,15955,15956,15962,15965],{},[405,15957,15958,15959,15961],{},"o ",[135,15960,9588],{}," não é global",[405,15963,15964],{},"não é recriado a cada chamada",[405,15966,15967,15968],{},"ele é ",[19,15969,15970],{},"preservado porque ainda está sendo usado",[12,15972,15973],{},"Esse é o ponto central de closure.",[27,15975],{},[30,15977,15979],{"id":15978},"comparação-com-e-sem-closure","Comparação: com e sem closure",[1643,15981,15983],{"id":15982},"sem-persistência","Sem persistência",[129,15985,15987],{"className":5084,"code":15986,"language":5086,"meta":75,"style":75},"function contador() {\n  let count = 0\n  count++\n  return count\n}\n\ncontador() \u002F\u002F 1\ncontador() \u002F\u002F 1\ncontador() \u002F\u002F 1\n",[135,15988,15989,15999,16009,16015,16021,16025,16029,16037,16045],{"__ignoreMap":75},[138,15990,15991,15993,15995,15997],{"class":140,"line":141},[138,15992,3824],{"class":1073},[138,15994,15597],{"class":801},[138,15996,3093],{"class":809},[138,15998,934],{"class":809},[138,16000,16001,16003,16005,16007],{"class":140,"line":76},[138,16002,12889],{"class":1073},[138,16004,10067],{"class":805},[138,16006,1146],{"class":1112},[138,16008,9548],{"class":5295},[138,16010,16011,16013],{"class":140,"line":152},[138,16012,10175],{"class":805},[138,16014,9671],{"class":1112},[138,16016,16017,16019],{"class":140,"line":158},[138,16018,3199],{"class":794},[138,16020,15578],{"class":805},[138,16022,16023],{"class":140,"line":164},[138,16024,2082],{"class":809},[138,16026,16027],{"class":140,"line":170},[138,16028,649],{"emptyLinePlaceholder":86},[138,16030,16031,16033,16035],{"class":140,"line":176},[138,16032,15612],{"class":801},[138,16034,2732],{"class":805},[138,16036,15617],{"class":911},[138,16038,16039,16041,16043],{"class":140,"line":182},[138,16040,15612],{"class":801},[138,16042,2732],{"class":805},[138,16044,15617],{"class":911},[138,16046,16047,16049,16051],{"class":140,"line":188},[138,16048,15612],{"class":801},[138,16050,2732],{"class":805},[138,16052,15617],{"class":911},[12,16054,16055,16056,16058],{},"Aqui o ",[135,16057,9588],{}," é recriado toda vez que a função é chamada.",[1643,16060,16062],{"id":16061},"com-closure","Com closure",[129,16064,16066],{"className":5084,"code":16065,"language":5086,"meta":75,"style":75},"const contador = criarContador()\n\ncontador() \u002F\u002F 1\ncontador() \u002F\u002F 2\ncontador() \u002F\u002F 3\n",[135,16067,16068,16080,16084,16092,16100],{"__ignoreMap":75},[138,16069,16070,16072,16074,16076,16078],{"class":140,"line":141},[138,16071,2518],{"class":1073},[138,16073,15597],{"class":2521},[138,16075,1146],{"class":1112},[138,16077,15536],{"class":801},[138,16079,2796],{"class":805},[138,16081,16082],{"class":140,"line":76},[138,16083,649],{"emptyLinePlaceholder":86},[138,16085,16086,16088,16090],{"class":140,"line":152},[138,16087,15612],{"class":801},[138,16089,2732],{"class":805},[138,16091,15617],{"class":911},[138,16093,16094,16096,16098],{"class":140,"line":158},[138,16095,15612],{"class":801},[138,16097,2732],{"class":805},[138,16099,15626],{"class":911},[138,16101,16102,16104,16106],{"class":140,"line":164},[138,16103,15612],{"class":801},[138,16105,2732],{"class":805},[138,16107,15635],{"class":911},[12,16109,16110],{},"O valor é mantido entre chamadas porque o ambiente persiste.",[27,16112],{},[30,16114,16116],{"id":16115},"múltiplos-estados-independentes","Múltiplos estados independentes",[12,16118,16119,16120,16122],{},"Cada chamada a ",[135,16121,15710],{}," cria um ambiente isolado:",[129,16124,16126],{"className":5084,"code":16125,"language":5086,"meta":75,"style":75},"const c1 = criarContador()\nconst c2 = criarContador()\n\nc1() \u002F\u002F 1\nc1() \u002F\u002F 2\n\nc2() \u002F\u002F 1\n",[135,16127,16128,16141,16154,16158,16167,16175,16179],{"__ignoreMap":75},[138,16129,16130,16132,16135,16137,16139],{"class":140,"line":141},[138,16131,2518],{"class":1073},[138,16133,16134],{"class":2521}," c1",[138,16136,1146],{"class":1112},[138,16138,15536],{"class":801},[138,16140,2796],{"class":805},[138,16142,16143,16145,16148,16150,16152],{"class":140,"line":76},[138,16144,2518],{"class":1073},[138,16146,16147],{"class":2521}," c2",[138,16149,1146],{"class":1112},[138,16151,15536],{"class":801},[138,16153,2796],{"class":805},[138,16155,16156],{"class":140,"line":152},[138,16157,649],{"emptyLinePlaceholder":86},[138,16159,16160,16163,16165],{"class":140,"line":158},[138,16161,16162],{"class":801},"c1",[138,16164,2732],{"class":805},[138,16166,15617],{"class":911},[138,16168,16169,16171,16173],{"class":140,"line":164},[138,16170,16162],{"class":801},[138,16172,2732],{"class":805},[138,16174,15626],{"class":911},[138,16176,16177],{"class":140,"line":170},[138,16178,649],{"emptyLinePlaceholder":86},[138,16180,16181,16184,16186],{"class":140,"line":176},[138,16182,16183],{"class":801},"c2",[138,16185,2732],{"class":805},[138,16187,15617],{"class":911},[12,16189,16190],{},"Visualizando:",[129,16192,16194],{"className":131,"code":16193,"language":133,"meta":75,"style":75},"c1 → { count: 2 }\nc2 → { count: 1 }\n",[135,16195,16196,16201],{"__ignoreMap":75},[138,16197,16198],{"class":140,"line":141},[138,16199,16200],{},"c1 → { count: 2 }\n",[138,16202,16203],{"class":140,"line":76},[138,16204,16205],{},"c2 → { count: 1 }\n",[12,16207,16208,16209,377,16211,16213],{},"Isso significa que ",[135,16210,16162],{},[135,16212,16183],{}," são completamente independentes.",[27,16215],{},[30,16217,16219],{"id":16218},"o-que-closure-permite","O que closure permite",[402,16221,16222,16228,16234],{},[405,16223,16224,16227],{},[19,16225,16226],{},"Estado persistente"," entre chamadas",[405,16229,16230,16233],{},[19,16231,16232],{},"Encapsulamento"," de dados, sem expô-los globalmente",[405,16235,16236,16239],{},[19,16237,16238],{},"Isolamento"," entre instâncias diferentes",[15084,16241,16242],{},[12,16243,16244],{},"Variáveis só são recriadas quando a função que as define é executada novamente.",[27,16246],{},[30,16248,16250],{"id":16249},"conexão-com-o-mundo-real","Conexão com o mundo real",[12,16252,16253,16254,16256],{},"Esse padrão é a base de várias coisas que você já usa no dia a dia. O ",[135,16255,5202],{}," em frameworks como Vue e React, por exemplo, funciona com o mesmo princípio.",[12,16258,16259],{},"Veja como isso se parece em um gerenciamento de estado simples:",[129,16261,16263],{"className":5084,"code":16262,"language":5086,"meta":75,"style":75},"function createStore() {\n  let state = { count: 0 }\n\n  return {\n    increment() {\n      state.count++\n    },\n    getState() {\n      return state\n    }\n  }\n}\n",[135,16264,16265,16275,16293,16297,16303,16311,16321,16325,16334,16341,16345,16349],{"__ignoreMap":75},[138,16266,16267,16269,16271,16273],{"class":140,"line":141},[138,16268,3824],{"class":1073},[138,16270,5097],{"class":801},[138,16272,3093],{"class":809},[138,16274,934],{"class":809},[138,16276,16277,16279,16281,16283,16285,16287,16289,16291],{"class":140,"line":76},[138,16278,12889],{"class":1073},[138,16280,5214],{"class":805},[138,16282,1146],{"class":1112},[138,16284,2812],{"class":809},[138,16286,10067],{"class":815},[138,16288,782],{"class":809},[138,16290,5296],{"class":5295},[138,16292,1015],{"class":809},[138,16294,16295],{"class":140,"line":152},[138,16296,649],{"emptyLinePlaceholder":86},[138,16298,16299,16301],{"class":140,"line":158},[138,16300,3199],{"class":794},[138,16302,934],{"class":809},[138,16304,16305,16307,16309],{"class":140,"line":164},[138,16306,9656],{"class":5196},[138,16308,3093],{"class":809},[138,16310,934],{"class":809},[138,16312,16313,16315,16317,16319],{"class":140,"line":170},[138,16314,5357],{"class":805},[138,16316,363],{"class":809},[138,16318,9588],{"class":805},[138,16320,9671],{"class":1112},[138,16322,16323],{"class":140,"line":176},[138,16324,5229],{"class":809},[138,16326,16327,16330,16332],{"class":140,"line":182},[138,16328,16329],{"class":5196},"    getState",[138,16331,3093],{"class":809},[138,16333,934],{"class":809},[138,16335,16336,16338],{"class":140,"line":188},[138,16337,5211],{"class":794},[138,16339,16340],{"class":805}," state\n",[138,16342,16343],{"class":140,"line":194},[138,16344,1179],{"class":809},[138,16346,16347],{"class":140,"line":199},[138,16348,1184],{"class":809},[138,16350,16351],{"class":140,"line":204},[138,16352,2082],{"class":809},[12,16354,1193,16355,16357,16358,16361],{},[135,16356,5202],{}," fica protegido dentro do ambiente de ",[135,16359,16360],{},"createStore",", acessível apenas pelas funções retornadas. Isso já é encapsulamento real, sem nenhuma biblioteca.",[27,16363],{},[30,16365,15407],{"id":15406},[402,16367,16368,16371,16374,16377],{},[405,16369,16370],{},"Closure acontece quando uma função mantém acesso ao seu escopo externo após a execução",[405,16372,16373],{},"Esse acesso mantém variáveis vivas na memória",[405,16375,16376],{},"Isso permite criar estados persistentes sem usar variáveis globais",[405,16378,16379],{},"Cada instância criada tem seu próprio ambiente isolado",[27,16381],{},[30,16383,4866],{"id":4865},[12,16385,16386],{},"Closure não é um recurso avançado reservado para casos especiais. É simplesmente como o JavaScript lida com funções, escopo e memória.",[12,16388,16389],{},"Entender isso muda a forma como você escreve código com:",[402,16391,16392,16395,16398,16401],{},[405,16393,16394],{},"estado",[405,16396,16397],{},"funções assíncronas",[405,16399,16400],{},"hooks e composables",[405,16402,16403],{},"arquitetura de aplicações",[1744,16405,16406],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sFsEu, html code.shiki .sFsEu{--shiki-light:#9C3EDA;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sK_r7, html code.shiki .sK_r7{--shiki-light:#6182B8;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sG-J9, html code.shiki .sG-J9{--shiki-light:#39ADB5;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sMo7A, html code.shiki .sMo7A{--shiki-light:#90A4AE;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sFfmW, html code.shiki .sFfmW{--shiki-light:#39ADB5;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .s_k96, html code.shiki .s_k96{--shiki-light:#F76D47;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s3Er8, html code.shiki .s3Er8{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sVPC0, html code.shiki .sVPC0{--shiki-light:#90A4AE;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .soiBB, html code.shiki .soiBB{--shiki-light:#E2931D;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sdv8B, html code.shiki .sdv8B{--shiki-light:#E53935;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s0u7J, html code.shiki .s0u7J{--shiki-light:#E53935;--shiki-default:#B392F0;--shiki-dark:#B392F0}",{"title":75,"searchDepth":76,"depth":76,"links":16408},[16409,16412,16414,16416,16418,16423,16424,16428,16429,16430,16431,16432],{"id":15511,"depth":76,"text":15512,"children":16410},[16411],{"id":15523,"depth":152,"text":15524},{"id":15663,"depth":76,"text":16413},"Por que o count não reinicia?",{"id":15716,"depth":76,"text":16415},"Então onde o count fica armazenado?",{"id":15740,"depth":76,"text":16417},"Dúvida comum: mas count não deveria ser global?",{"id":15817,"depth":76,"text":15818,"children":16419},[16420,16421,16422],{"id":15821,"depth":152,"text":15822},{"id":15864,"depth":152,"text":15865},{"id":15903,"depth":152,"text":15904},{"id":15944,"depth":76,"text":15945},{"id":15978,"depth":76,"text":15979,"children":16425},[16426,16427],{"id":15982,"depth":152,"text":15983},{"id":16061,"depth":152,"text":16062},{"id":16115,"depth":76,"text":16116},{"id":16218,"depth":76,"text":16219},{"id":16249,"depth":76,"text":16250},{"id":15406,"depth":76,"text":15407},{"id":4865,"depth":76,"text":4866},"2026-03-21T15:32:00-03:00","Entenda um dos conceitos mais importantes do JavaScript, e também um dos mais confusos à primeira vista.","\u002Fimages\u002Fblog\u002Fentendendo-closures.png",{},"\u002Fblog\u002Fclosures-javascript",{"title":15494,"description":16434},"blog\u002Fclosures-javascript",[7116,9241,16441,1770,15490],"Clean Code","bfvpOxnMUdXx8mbMPpENdb8aGx3EmVqXgN53rp6ktzE",{"id":16444,"title":16445,"author":7,"body":16446,"date":18878,"description":18879,"extension":83,"image":18880,"meta":18881,"navigation":86,"path":18922,"seo":18923,"stem":18924,"tags":18925,"__hash__":18930},"blog\u002Fblog\u002Fapis-e-ferramentas-para-devs.md","APIs e Ferramentas Úteis para Desenvolvedores",{"type":9,"value":16447,"toc":18859},[16448,16454,16457,16462,16464,16468,16585,16587,16591,16758,16760,16764,17071,17073,17077,17245,17247,17251,17502,17504,17508,17704,17706,17710,17822,17824,17828,17968,17970,17974,18030,18034,18132,18134,18138,18278,18282,18480,18484,18488,18572,18574,18578,18724,18726,18730],[12,16449,16450,16451,363],{},"Ao longo do tempo fui salvando ",[19,16452,16453],{},"APIs interessantes, ferramentas úteis, jogos para aprender programação e recursos para projetos",[12,16455,16456],{},"Resolvi organizar tudo aqui para consultar depois e talvez ajudar outros devs também.",[15084,16458,16459],{},[12,16460,16461],{},"💡 Este post é atualizado conforme descubro novas ferramentas e recursos interessantes.",[27,16463],{},[30,16465,16467],{"id":16466},"projetos-e-recursos-do-github","📦 Projetos e recursos do GitHub",[1915,16469,16470,16483],{},[1918,16471,16472],{},[1921,16473,16474,16477,16480],{},[1924,16475,16476],{},"Nome",[1924,16478,16479],{},"Descrição",[1924,16481,16482],{},"Link",[1931,16484,16485,16500,16515,16529,16543,16557,16571],{},[1921,16486,16487,16490,16493],{},[1936,16488,16489],{},"Apresentação no GitHub",[1936,16491,16492],{},"Como criar um README para customizar sua pagina de perfil",[1936,16494,16495],{},[105,16496,16499],{"href":16497,"rel":16498},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=TsaLQAetPLU",[109],"Assistir",[1921,16501,16502,16505,16508],{},[1936,16503,16504],{},"Modelo de README",[1936,16506,16507],{},"Template estruturado para montar README de projetos.",[1936,16509,16510],{},[105,16511,16514],{"href":16512,"rel":16513},"https:\u002F\u002Fgithub.com\u002Fengenheiracoelho\u002Fengenheiracoelho\u002Fblob\u002Fmaster\u002FprojectModel.md",[109],"Acessar",[1921,16516,16517,16520,16523],{},[1936,16518,16519],{},"readme.so",[1936,16521,16522],{},"Editor visual para montar README online.",[1936,16524,16525],{},[105,16526,16514],{"href":16527,"rel":16528},"https:\u002F\u002Freadme.so",[109],[1921,16530,16531,16534,16537],{},[1936,16532,16533],{},"Shields.io",[1936,16535,16536],{},"Gera badges para README de projetos.",[1936,16538,16539],{},[105,16540,16514],{"href":16541,"rel":16542},"https:\u002F\u002Fshields.io\u002F",[109],[1921,16544,16545,16548,16551],{},[1936,16546,16547],{},"Icones Markdown (README)",[1936,16549,16550],{},"Lista de todos os icones para arquivos .md (GitHub)",[1936,16552,16553],{},[105,16554,16514],{"href":16555,"rel":16556},"https:\u002F\u002Fgist.github.com\u002Frxaviers\u002F7360908",[109],[1921,16558,16559,16562,16565],{},[1936,16560,16561],{},"Medalhas",[1936,16563,16564],{},"Coleção de medalhas do github e como consegui-las.",[1936,16566,16567],{},[105,16568,16514],{"href":16569,"rel":16570},"https:\u002F\u002Fgithub.com\u002Fengenheiracoelho\u002FGitHub\u002Fblob\u002Fmaster\u002FMedalhas_GitHub.md",[109],[1921,16572,16573,16576,16579],{},[1936,16574,16575],{},"Currículo no GitHub",[1936,16577,16578],{},"Exemplo de currículo hospedado no GitHub.",[1936,16580,16581],{},[105,16582,16514],{"href":16583,"rel":16584},"https:\u002F\u002Fgithub.com\u002Fengenheiracoelho\u002Fcurriculo",[109],[27,16586],{},[30,16588,16590],{"id":16589},"recursos-visuais","🎨 Recursos visuais",[1915,16592,16593,16603],{},[1918,16594,16595],{},[1921,16596,16597,16599,16601],{},[1924,16598,16476],{},[1924,16600,16479],{},[1924,16602,16482],{},[1931,16604,16605,16619,16633,16647,16660,16674,16688,16702,16716,16730,16744],{},[1921,16606,16607,16610,16613],{},[1936,16608,16609],{},"Favicon.io",[1936,16611,16612],{},"Gerador de favicons a partir de texto, imagem ou emoji.",[1936,16614,16615],{},[105,16616,16514],{"href":16617,"rel":16618},"https:\u002F\u002Ffavicon.io\u002F",[109],[1921,16620,16621,16624,16627],{},[1936,16622,16623],{},"Devicon",[1936,16625,16626],{},"Ícones de linguagens e tecnologias para portfólios e READMEs.",[1936,16628,16629],{},[105,16630,16514],{"href":16631,"rel":16632},"https:\u002F\u002Fdevicon.dev\u002F",[109],[1921,16634,16635,16638,16641],{},[1936,16636,16637],{},"Ray.so",[1936,16639,16640],{},"Gera imagens bonitas do seu código para compartilhar.",[1936,16642,16643],{},[105,16644,16514],{"href":16645,"rel":16646},"https:\u002F\u002Fray.so\u002F",[109],[1921,16648,16649,16652,16654],{},[1936,16650,16651],{},"Carbon",[1936,16653,16640],{},[1936,16655,16656],{},[105,16657,16514],{"href":16658,"rel":16659},"https:\u002F\u002Fcarbon.now.sh",[109],[1921,16661,16662,16665,16668],{},[1936,16663,16664],{},"Coolors",[1936,16666,16667],{},"Gerador de paletas de cores para projetos.",[1936,16669,16670],{},[105,16671,16514],{"href":16672,"rel":16673},"https:\u002F\u002Fcoolors.co",[109],[1921,16675,16676,16679,16682],{},[1936,16677,16678],{},"Humaaans",[1936,16680,16681],{},"Ilustrações para landing pages e interfaces.",[1936,16683,16684],{},[105,16685,16514],{"href":16686,"rel":16687},"https:\u002F\u002Fhumaaans.com",[109],[1921,16689,16690,16693,16696],{},[1936,16691,16692],{},"Open Doodles",[1936,16694,16695],{},"Biblioteca de ilustrações open source.",[1936,16697,16698],{},[105,16699,16514],{"href":16700,"rel":16701},"https:\u002F\u002Fopendoodles.com",[109],[1921,16703,16704,16707,16710],{},[1936,16705,16706],{},"unDraw",[1936,16708,16709],{},"Ilustrações para projetos frontend.",[1936,16711,16712],{},[105,16713,16514],{"href":16714,"rel":16715},"https:\u002F\u002Fundraw.co",[109],[1921,16717,16718,16721,16724],{},[1936,16719,16720],{},"Picsum",[1936,16722,16723],{},"API que retorna imagens aleatórias para placeholders.",[1936,16725,16726],{},[105,16727,16514],{"href":16728,"rel":16729},"https:\u002F\u002Fpicsum.photos\u002F",[109],[1921,16731,16732,16735,16738],{},[1936,16733,16734],{},"Iconify",[1936,16736,16737],{},"Coleção gigante de ícones para projetos web.",[1936,16739,16740],{},[105,16741,16514],{"href":16742,"rel":16743},"https:\u002F\u002Ficonify.design\u002F",[109],[1921,16745,16746,16749,16752],{},[1936,16747,16748],{},"Glassmorphism CSS",[1936,16750,16751],{},"Gerador de efeito de vidro com css.",[1936,16753,16754],{},[105,16755,16514],{"href":16756,"rel":16757},"https:\u002F\u002Fcss.glass\u002F",[109],[27,16759],{},[30,16761,16763],{"id":16762},"ferramentas-úteis-para-desenvolvedores","🛠 Ferramentas úteis para desenvolvedores",[1915,16765,16766,16776],{},[1918,16767,16768],{},[1921,16769,16770,16772,16774],{},[1924,16771,16476],{},[1924,16773,16479],{},[1924,16775,16482],{},[1931,16777,16778,16792,16806,16820,16834,16848,16862,16876,16890,16904,16918,16931,16945,16959,16973,16987,17001,17015,17029,17043,17057],{},[1921,16779,16780,16783,16786],{},[1936,16781,16782],{},"Figma",[1936,16784,16785],{},"Ferramenta de design colaborativo para interfaces.",[1936,16787,16788],{},[105,16789,16514],{"href":16790,"rel":16791},"https:\u002F\u002Fwww.figma.com\u002F",[109],[1921,16793,16794,16797,16800],{},[1936,16795,16796],{},"Excalidraw",[1936,16798,16799],{},"Quadro branco colaborativo para rascunhos e diagramas rápidos.",[1936,16801,16802],{},[105,16803,16514],{"href":16804,"rel":16805},"https:\u002F\u002Fexcalidraw.com\u002F",[109],[1921,16807,16808,16811,16814],{},[1936,16809,16810],{},"JSON Formatter",[1936,16812,16813],{},"Formata e valida arquivos JSON online.",[1936,16815,16816],{},[105,16817,16514],{"href":16818,"rel":16819},"https:\u002F\u002Fjsonformatter.org\u002F",[109],[1921,16821,16822,16825,16828],{},[1936,16823,16824],{},"Awesome Screenshot",[1936,16826,16827],{},"Ferramenta para capturar, gravar e anotar telas com facilidade. (Possui extensão no navegador também)",[1936,16829,16830],{},[105,16831,16514],{"href":16832,"rel":16833},"https:\u002F\u002Fwww.awesomescreenshot.com\u002F",[109],[1921,16835,16836,16839,16842],{},[1936,16837,16838],{},"Google Search Console",[1936,16840,16841],{},"Indexa site, analisa tráfego, comportamento de usuários e métricas do site.",[1936,16843,16844],{},[105,16845,16514],{"href":16846,"rel":16847},"https:\u002F\u002Fsearch.google.com\u002Fsearch-console\u002Fabout",[109],[1921,16849,16850,16853,16856],{},[1936,16851,16852],{},"PageSpeed Insights",[1936,16854,16855],{},"Analisa performance, acessibilidade e SEO de páginas web.",[1936,16857,16858],{},[105,16859,16514],{"href":16860,"rel":16861},"https:\u002F\u002Fpagespeed.web.dev\u002F",[109],[1921,16863,16864,16867,16870],{},[1936,16865,16866],{},"OpenGraph.xyz",[1936,16868,16869],{},"Visualiza e testa como links aparecem em redes sociais (Open Graph).",[1936,16871,16872],{},[105,16873,16514],{"href":16874,"rel":16875},"https:\u002F\u002Fwww.opengraph.xyz",[109],[1921,16877,16878,16881,16884],{},[1936,16879,16880],{},"LinkedIn Post Inspector",[1936,16882,16883],{},"Ferramenta para visualizar e atualizar o preview de links compartilhados no LinkedIn.",[1936,16885,16886],{},[105,16887,16514],{"href":16888,"rel":16889},"https:\u002F\u002Fwww.linkedin.com\u002Fpost-inspector\u002F",[109],[1921,16891,16892,16895,16898],{},[1936,16893,16894],{},"Facebook Sharing Debugger",[1936,16896,16897],{},"Ferramenta para debug de preview no Facebook",[1936,16899,16900],{},[105,16901,16514],{"href":16902,"rel":16903},"https:\u002F\u002Fdevelopers.facebook.com\u002Ftools\u002Fdebug\u002F",[109],[1921,16905,16906,16909,16912],{},[1936,16907,16908],{},"Google Lighthouse",[1936,16910,16911],{},"Ferramenta do Google para avaliar desempenho e qualidade de sites.",[1936,16913,16914],{},[105,16915,16514],{"href":16916,"rel":16917},"https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Flighthouse\u002Foverview?hl=pt-br",[109],[1921,16919,16920,16922,16925],{},[1936,16921,1804],{},[1936,16923,16924],{},"Plataforma backend open source com auth, base de dados, storage e mais.",[1936,16926,16927],{},[105,16928,16514],{"href":16929,"rel":16930},"https:\u002F\u002Fsupabase.com\u002F",[109],[1921,16932,16933,16936,16939],{},[1936,16934,16935],{},"CodePen",[1936,16937,16938],{},"Editor online para testar HTML, CSS e JavaScript.",[1936,16940,16941],{},[105,16942,16514],{"href":16943,"rel":16944},"https:\u002F\u002Fcodepen.io\u002F",[109],[1921,16946,16947,16950,16953],{},[1936,16948,16949],{},"JSFiddle",[1936,16951,16952],{},"Ambiente online para testes rápidos de código front-end.",[1936,16954,16955],{},[105,16956,16514],{"href":16957,"rel":16958},"https:\u002F\u002Fjsfiddle.net\u002F",[109],[1921,16960,16961,16964,16967],{},[1936,16962,16963],{},"Abstract API",[1936,16965,16966],{},"Conjunto de APIs para automação de tarefas comuns.",[1936,16968,16969],{},[105,16970,16514],{"href":16971,"rel":16972},"https:\u002F\u002Fwww.abstractapi.com\u002F",[109],[1921,16974,16975,16978,16981],{},[1936,16976,16977],{},"4Devs",[1936,16979,16980],{},"Geradores de CPF, CNPJ, hashes e dados fake.",[1936,16982,16983],{},[105,16984,16514],{"href":16985,"rel":16986},"https:\u002F\u002F4devs.com.br",[109],[1921,16988,16989,16992,16995],{},[1936,16990,16991],{},"Pantry",[1936,16993,16994],{},"Armazenamento JSON gratuito para pequenos projetos.",[1936,16996,16997],{},[105,16998,16514],{"href":16999,"rel":17000},"https:\u002F\u002Fgetpantry.cloud",[109],[1921,17002,17003,17006,17009],{},[1936,17004,17005],{},"File.io",[1936,17007,17008],{},"Compartilhamento rápido de arquivos temporários.",[1936,17010,17011],{},[105,17012,16514],{"href":17013,"rel":17014},"https:\u002F\u002Ffile.io",[109],[1921,17016,17017,17020,17023],{},[1936,17018,17019],{},"JSON Server GitHub",[1936,17021,17022],{},"Transforme um JSON hospedado no GitHub em API fake rapidamente.",[1936,17024,17025],{},[105,17026,16514],{"href":17027,"rel":17028},"https:\u002F\u002Fmy-json-server.typicode.com\u002F",[109],[1921,17030,17031,17034,17037],{},[1936,17032,17033],{},"Bundlephobia",[1936,17035,17036],{},"Analisa o tamanho de pacotes NPM antes de instalar.",[1936,17038,17039],{},[105,17040,16514],{"href":17041,"rel":17042},"https:\u002F\u002Fbundlephobia.com\u002F",[109],[1921,17044,17045,17048,17051],{},[1936,17046,17047],{},"Regex101",[1936,17049,17050],{},"Teste e validação de expressões regulares.",[1936,17052,17053],{},[105,17054,16514],{"href":17055,"rel":17056},"https:\u002F\u002Fregex101.com\u002F",[109],[1921,17058,17059,17062,17065],{},[1936,17060,17061],{},"WhatFont",[1936,17063,17064],{},"Extensão para identificar fontes em qualquer site.",[1936,17066,17067],{},[105,17068,16514],{"href":17069,"rel":17070},"https:\u002F\u002Fwww.whatfontis.com\u002F",[109],[27,17072],{},[30,17074,17076],{"id":17075},"bibliotecas-úteis","📁 Bibliotecas úteis",[1915,17078,17079,17089],{},[1918,17080,17081],{},[1921,17082,17083,17085,17087],{},[1924,17084,16476],{},[1924,17086,16479],{},[1924,17088,16482],{},[1931,17090,17091,17105,17119,17133,17147,17161,17175,17189,17203,17217,17231],{},[1921,17092,17093,17096,17099],{},[1936,17094,17095],{},"Gulp",[1936,17097,17098],{},"Ferramenta de automação de tarefas para JavaScript, com foco em desempenho e simplicidade",[1936,17100,17101],{},[105,17102,16514],{"href":17103,"rel":17104},"https:\u002F\u002Fgulpjs.com\u002F",[109],[1921,17106,17107,17110,17113],{},[1936,17108,17109],{},"Faker.js",[1936,17111,17112],{},"Biblioteca para gerar dados fake realistas.",[1936,17114,17115],{},[105,17116,16514],{"href":17117,"rel":17118},"https:\u002F\u002Ffakerjs.dev",[109],[1921,17120,17121,17124,17127],{},[1936,17122,17123],{},"Moment.js",[1936,17125,17126],{},"Biblioteca clássica para manipulação e formatação de datas em JavaScript.",[1936,17128,17129],{},[105,17130,16514],{"href":17131,"rel":17132},"https:\u002F\u002Fmomentjs.com\u002F",[109],[1921,17134,17135,17138,17141],{},[1936,17136,17137],{},"DataTables",[1936,17139,17140],{},"Biblioteca para criação de tabelas dinâmicas com paginação e busca.",[1936,17142,17143],{},[105,17144,16514],{"href":17145,"rel":17146},"https:\u002F\u002Fdatatables.net\u002F",[109],[1921,17148,17149,17152,17155],{},[1936,17150,17151],{},"ApexCharts",[1936,17153,17154],{},"Biblioteca moderna para criação de gráficos interativos em JavaScript\u002FVue\u002FReact.",[1936,17156,17157],{},[105,17158,16514],{"href":17159,"rel":17160},"https:\u002F\u002Fapexcharts.com\u002F",[109],[1921,17162,17163,17166,17169],{},[1936,17164,17165],{},"Highcharts",[1936,17167,17168],{},"Biblioteca poderosa para criação de gráficos interativos.",[1936,17170,17171],{},[105,17172,16514],{"href":17173,"rel":17174},"https:\u002F\u002Fwww.highcharts.com\u002F",[109],[1921,17176,17177,17180,17183],{},[1936,17178,17179],{},"PapaParse",[1936,17181,17182],{},"Biblioteca JavaScript para leitura e processamento de arquivos CSV.",[1936,17184,17185],{},[105,17186,16514],{"href":17187,"rel":17188},"https:\u002F\u002Fwww.papaparse.com\u002F",[109],[1921,17190,17191,17194,17197],{},[1936,17192,17193],{},"Animate.css",[1936,17195,17196],{},"Biblioteca de animações CSS prontas para uso em projetos web.",[1936,17198,17199],{},[105,17200,16514],{"href":17201,"rel":17202},"https:\u002F\u002Fanimate.style\u002F",[109],[1921,17204,17205,17208,17211],{},[1936,17206,17207],{},"Vis.js",[1936,17209,17210],{},"Biblioteca para visualização de dados interativos (gráficos, redes, arvores, timelines).",[1936,17212,17213],{},[105,17214,16514],{"href":17215,"rel":17216},"https:\u002F\u002Fvisjs.org\u002F",[109],[1921,17218,17219,17222,17225],{},[1936,17220,17221],{},"Three.js",[1936,17223,17224],{},"Criação de gráficos 3D no navegador com WebGL.",[1936,17226,17227],{},[105,17228,16514],{"href":17229,"rel":17230},"https:\u002F\u002Fthreejs.org\u002F",[109],[1921,17232,17233,17236,17239],{},[1936,17234,17235],{},"Swiper.js",[1936,17237,17238],{},"Slider mobile-first muito usado em projetos modernos.",[1936,17240,17241],{},[105,17242,16514],{"href":17243,"rel":17244},"https:\u002F\u002Fswiperjs.com\u002F",[109],[27,17246],{},[30,17248,17250],{"id":17249},"ferramentas-de-ia","🤖 Ferramentas de IA",[1915,17252,17253,17263],{},[1918,17254,17255],{},[1921,17256,17257,17259,17261],{},[1924,17258,16476],{},[1924,17260,16479],{},[1924,17262,16482],{},[1931,17264,17265,17279,17293,17307,17321,17335,17349,17363,17377,17391,17405,17419,17433,17447,17461,17475,17489],{},[1921,17266,17267,17270,17273],{},[1936,17268,17269],{},"Claude",[1936,17271,17272],{},"Assistente de IA avançado para escrita, análise e programação.",[1936,17274,17275],{},[105,17276,16514],{"href":17277,"rel":17278},"https:\u002F\u002Fclaude.ai\u002F",[109],[1921,17280,17281,17284,17287],{},[1936,17282,17283],{},"Cursor",[1936,17285,17286],{},"Editor de código com IA integrada para programação.",[1936,17288,17289],{},[105,17290,16514],{"href":17291,"rel":17292},"https:\u002F\u002Fcursor.sh\u002F",[109],[1921,17294,17295,17298,17301],{},[1936,17296,17297],{},"ChatGPT",[1936,17299,17300],{},"Assistente de IA para código, textos e automações.",[1936,17302,17303],{},[105,17304,16514],{"href":17305,"rel":17306},"https:\u002F\u002Fchat.openai.com\u002F",[109],[1921,17308,17309,17312,17315],{},[1936,17310,17311],{},"Gamma",[1936,17313,17314],{},"Cria apresentações, documentos e páginas com ajuda de IA.",[1936,17316,17317],{},[105,17318,16514],{"href":17319,"rel":17320},"https:\u002F\u002Fgamma.app\u002Fsignup?r=wpu03ixu9fqts15",[109],[1921,17322,17323,17326,17329],{},[1936,17324,17325],{},"Mapify",[1936,17327,17328],{},"Cria resumos e mapas mentais com base em conteúdo fornecido.",[1936,17330,17331],{},[105,17332,16514],{"href":17333,"rel":17334},"https:\u002F\u002Fmapify.so\u002Fpt",[109],[1921,17336,17337,17340,17343],{},[1936,17338,17339],{},"Leonardo AI",[1936,17341,17342],{},"Geração de imagens com IA para diversos fins.",[1936,17344,17345],{},[105,17346,16514],{"href":17347,"rel":17348},"https:\u002F\u002Fleonardo.ai",[109],[1921,17350,17351,17354,17357],{},[1936,17352,17353],{},"Remove.bg",[1936,17355,17356],{},"Remove fundo de imagens usando IA.",[1936,17358,17359],{},[105,17360,16514],{"href":17361,"rel":17362},"https:\u002F\u002Fwww.remove.bg",[109],[1921,17364,17365,17368,17371],{},[1936,17366,17367],{},"Let’s Enhance",[1936,17369,17370],{},"Plataforma online para aprimorar a qualidade de imagens com inteligência artificial",[1936,17372,17373],{},[105,17374,16514],{"href":17375,"rel":17376},"https:\u002F\u002Fletsenhance.io\u002Fref\u002F1ghriyvfk6i_cp",[109],[1921,17378,17379,17382,17385],{},[1936,17380,17381],{},"HeyGen",[1936,17383,17384],{},"Cria videos com avatares de IA apartir de texto.",[1936,17386,17387],{},[105,17388,16514],{"href":17389,"rel":17390},"https:\u002F\u002Fwww.heygen.com",[109],[1921,17392,17393,17396,17399],{},[1936,17394,17395],{},"Suno Music",[1936,17397,17398],{},"Cria musicas apartir de descrição ou letra da musica.",[1936,17400,17401],{},[105,17402,16514],{"href":17403,"rel":17404},"https:\u002F\u002Fsuno.com\u002Fcreate",[109],[1921,17406,17407,17410,17413],{},[1936,17408,17409],{},"Google Opal",[1936,17411,17412],{},"Plataforma experimental de IA generativa para criação e exploração de conteúdo",[1936,17414,17415],{},[105,17416,16514],{"href":17417,"rel":17418},"https:\u002F\u002Fopal.google\u002Flanding\u002F",[109],[1921,17420,17421,17424,17427],{},[1936,17422,17423],{},"NotebookLM",[1936,17425,17426],{},"Ferramenta de IA do Google para organizar, resumir e estudar conteúdos com base em documentos",[1936,17428,17429],{},[105,17430,16514],{"href":17431,"rel":17432},"https:\u002F\u002Fnotebooklm.google.com\u002F",[109],[1921,17434,17435,17438,17441],{},[1936,17436,17437],{},"Perplexity",[1936,17439,17440],{},"Motor de busca com IA que cita fontes em tempo real.",[1936,17442,17443],{},[105,17444,16514],{"href":17445,"rel":17446},"https:\u002F\u002Fwww.perplexity.ai",[109],[1921,17448,17449,17452,17455],{},[1936,17450,17451],{},"Poe",[1936,17453,17454],{},"Plataforma para testar vários modelos de IA em um só lugar.",[1936,17456,17457],{},[105,17458,16514],{"href":17459,"rel":17460},"https:\u002F\u002Fpoe.com",[109],[1921,17462,17463,17466,17469],{},[1936,17464,17465],{},"Phind",[1936,17467,17468],{},"Buscador focado em programação e desenvolvimento.",[1936,17470,17471],{},[105,17472,16514],{"href":17473,"rel":17474},"https:\u002F\u002Fwww.phind.com",[109],[1921,17476,17477,17480,17483],{},[1936,17478,17479],{},"Durable",[1936,17481,17482],{},"Cria sites completos automaticamente com IA.",[1936,17484,17485],{},[105,17486,16514],{"href":17487,"rel":17488},"https:\u002F\u002Fdurable.co",[109],[1921,17490,17491,17494,17496],{},[1936,17492,17493],{},"Lovable",[1936,17495,17482],{},[1936,17497,17498],{},[105,17499,16514],{"href":17500,"rel":17501},"https:\u002F\u002Flovable.dev",[109],[27,17503],{},[30,17505,17507],{"id":17506},"apis-públicas-para-projetos","🔌 APIs públicas para projetos",[1915,17509,17510,17520],{},[1918,17511,17512],{},[1921,17513,17514,17516,17518],{},[1924,17515,16476],{},[1924,17517,16479],{},[1924,17519,16482],{},[1931,17521,17522,17536,17550,17564,17578,17592,17606,17620,17634,17648,17662,17676,17690],{},[1921,17523,17524,17527,17530],{},[1936,17525,17526],{},"HamtaroAPI",[1936,17528,17529],{},"Minha api publica sobre a serie infantil Hamtaro.",[1936,17531,17532],{},[105,17533,16514],{"href":17534,"rel":17535},"https:\u002F\u002Fhamtaro-api-page.vercel.app\u002F",[109],[1921,17537,17538,17541,17544],{},[1936,17539,17540],{},"Rick and Morty API",[1936,17542,17543],{},"Informações sobre personagens, episódios e locais da série.",[1936,17545,17546],{},[105,17547,16514],{"href":17548,"rel":17549},"https:\u002F\u002Frickandmortyapi.com\u002Fdocumentation",[109],[1921,17551,17552,17555,17558],{},[1936,17553,17554],{},"Naruto API",[1936,17556,17557],{},"Dados do universo Naruto.",[1936,17559,17560],{},[105,17561,16514],{"href":17562,"rel":17563},"https:\u002F\u002Fgithub.com\u002FGustavonobreza\u002Fnaruto-api",[109],[1921,17565,17566,17569,17572],{},[1936,17567,17568],{},"Open Library API",[1936,17570,17571],{},"Informações sobre livros, autores e capas.",[1936,17573,17574],{},[105,17575,16514],{"href":17576,"rel":17577},"https:\u002F\u002Fopenlibrary.org\u002Fdevelopers\u002Fapi",[109],[1921,17579,17580,17583,17586],{},[1936,17581,17582],{},"OMDb API",[1936,17584,17585],{},"Informações sobre filmes e séries.",[1936,17587,17588],{},[105,17589,16514],{"href":17590,"rel":17591},"https:\u002F\u002Fomdbapi.com",[109],[1921,17593,17594,17597,17600],{},[1936,17595,17596],{},"NBA API",[1936,17598,17599],{},"Dados e estatísticas da NBA.",[1936,17601,17602],{},[105,17603,16514],{"href":17604,"rel":17605},"https:\u002F\u002Fballdontlie.io",[109],[1921,17607,17608,17611,17614],{},[1936,17609,17610],{},"REST Countries",[1936,17612,17613],{},"Informações sobre países do mundo.",[1936,17615,17616],{},[105,17617,16514],{"href":17618,"rel":17619},"https:\u002F\u002Frestcountries.com",[109],[1921,17621,17622,17625,17628],{},[1936,17623,17624],{},"TheDogApi",[1936,17626,17627],{},"Imagens aleatórias de cães.",[1936,17629,17630],{},[105,17631,16514],{"href":17632,"rel":17633},"https:\u002F\u002Fapi.thedogapi.com\u002Fv1\u002Fimages\u002Fsearch",[109],[1921,17635,17636,17639,17642],{},[1936,17637,17638],{},"TheCatApi",[1936,17640,17641],{},"Imagens aleatórias de gatos.",[1936,17643,17644],{},[105,17645,16514],{"href":17646,"rel":17647},"https:\u002F\u002Fapi.thecatapi.com\u002Fv1\u002Fimages\u002Fsearch",[109],[1921,17649,17650,17653,17656],{},[1936,17651,17652],{},"CocktailDB",[1936,17654,17655],{},"Receitas de drinks e coquetéis.",[1936,17657,17658],{},[105,17659,16514],{"href":17660,"rel":17661},"https:\u002F\u002Fthecocktaildb.com\u002Fapi.php",[109],[1921,17663,17664,17667,17670],{},[1936,17665,17666],{},"Deck of Cards API",[1936,17668,17669],{},"Simula um baralho completo para jogos e testes.",[1936,17671,17672],{},[105,17673,16514],{"href":17674,"rel":17675},"http:\u002F\u002Fdeckofcardsapi.com",[109],[1921,17677,17678,17681,17684],{},[1936,17679,17680],{},"Art Institute of Chicago API",[1936,17682,17683],{},"Acesso a obras de arte digitalizadas.",[1936,17685,17686],{},[105,17687,16514],{"href":17688,"rel":17689},"https:\u002F\u002Fapi.artic.edu\u002Fdocs\u002F",[109],[1921,17691,17692,17695,17698],{},[1936,17693,17694],{},"Bible API",[1936,17696,17697],{},"Versículos e passagens da bíblia.",[1936,17699,17700],{},[105,17701,16514],{"href":17702,"rel":17703},"https:\u002F\u002Fbible-api.com",[109],[27,17705],{},[30,17707,17709],{"id":17708},"apis-úteis-no-dia-a-dia","🌎 APIs úteis no dia a dia",[1915,17711,17712,17722],{},[1918,17713,17714],{},[1921,17715,17716,17718,17720],{},[1924,17717,16476],{},[1924,17719,16479],{},[1924,17721,16482],{},[1931,17723,17724,17738,17752,17766,17780,17794,17808],{},[1921,17725,17726,17729,17732],{},[1936,17727,17728],{},"Formata Texto Linkedin",[1936,17730,17731],{},"Formatador de texto para linkedin negrio\u002Fitalico\u002Fetc",[1936,17733,17734],{},[105,17735,16514],{"href":17736,"rel":17737},"https:\u002F\u002Fflowpost.io\u002Fpt\u002Fnegrito-linkedin",[109],[1921,17739,17740,17743,17746],{},[1936,17741,17742],{},"ViaCEP",[1936,17744,17745],{},"Consulta de CEP com endereço completo.",[1936,17747,17748],{},[105,17749,16514],{"href":17750,"rel":17751},"https:\u002F\u002Fviacep.com.br\u002Fws\u002F01001000\u002Fjson\u002F",[109],[1921,17753,17754,17757,17760],{},[1936,17755,17756],{},"HG Weather",[1936,17758,17759],{},"API de previsão do tempo por localidade.",[1936,17761,17762],{},[105,17763,16514],{"href":17764,"rel":17765},"https:\u002F\u002Fapi.hgbrasil.com\u002Fweather",[109],[1921,17767,17768,17771,17774],{},[1936,17769,17770],{},"GitHub REST API",[1936,17772,17773],{},"Dados públicos de usuários e repositórios do GitHub.",[1936,17775,17776],{},[105,17777,16514],{"href":17778,"rel":17779},"https:\u002F\u002Fdocs.github.com\u002Fpt\u002Frest",[109],[1921,17781,17782,17785,17788],{},[1936,17783,17784],{},"API Correios",[1936,17786,17787],{},"Rastreamento de encomendas via código de envio.",[1936,17789,17790],{},[105,17791,16514],{"href":17792,"rel":17793},"https:\u002F\u002Fproxyapp.correios.com.br\u002Fv1\u002Fsro-rastro\u002FNumeroEncomenda",[109],[1921,17795,17796,17799,17802],{},[1936,17797,17798],{},"IP API",[1936,17800,17801],{},"Retorna informações sobre um endereço IP.",[1936,17803,17804],{},[105,17805,16514],{"href":17806,"rel":17807},"https:\u002F\u002Fapiip.net\u002Fdocumentation",[109],[1921,17809,17810,17813,17816],{},[1936,17811,17812],{},"OnWater",[1936,17814,17815],{},"Verifica se coordenadas estão em terra ou água.",[1936,17817,17818],{},[105,17819,16514],{"href":17820,"rel":17821},"https:\u002F\u002Fonwater.io",[109],[27,17823],{},[30,17825,17827],{"id":17826},"aprendizado","📚 Aprendizado",[1915,17829,17830,17840],{},[1918,17831,17832],{},[1921,17833,17834,17836,17838],{},[1924,17835,16476],{},[1924,17837,16479],{},[1924,17839,16482],{},[1931,17841,17842,17856,17870,17884,17898,17912,17926,17940,17954],{},[1921,17843,17844,17847,17850],{},[1936,17845,17846],{},"TechGuide",[1936,17848,17849],{},"Mapa de carreira para desenvolvedores criado pela Alura.",[1936,17851,17852],{},[105,17853,16514],{"href":17854,"rel":17855},"https:\u002F\u002Ftechguide.sh\u002F",[109],[1921,17857,17858,17861,17864],{},[1936,17859,17860],{},"Codedex",[1936,17862,17863],{},"Aprenda programação com trilhas interativas, exercícios práticos e desafios em várias linguagens.",[1936,17865,17866],{},[105,17867,16514],{"href":17868,"rel":17869},"https:\u002F\u002Fwww.codedex.io\u002F",[109],[1921,17871,17872,17875,17878],{},[1936,17873,17874],{},"Elevator Saga",[1936,17876,17877],{},"Programe elevadores usando JavaScript.",[1936,17879,17880],{},[105,17881,16514],{"href":17882,"rel":17883},"http:\u002F\u002Fplay.elevatorsaga.com",[109],[1921,17885,17886,17889,17892],{},[1936,17887,17888],{},"Star Wars Coding Game",[1936,17890,17891],{},"Aprenda lógica de programação resolvendo desafios.",[1936,17893,17894],{},[105,17895,16514],{"href":17896,"rel":17897},"https:\u002F\u002Fstudio.code.org\u002Fcourses\u002Fstarwars\u002Funits\u002F1\u002Flessons\u002F1\u002Flevels\u002F1",[109],[1921,17899,17900,17903,17906],{},[1936,17901,17902],{},"CodeCombat",[1936,17904,17905],{},"Aprenda programação jogando RPG.",[1936,17907,17908],{},[105,17909,16514],{"href":17910,"rel":17911},"https:\u002F\u002Fcodecombat.com\u002F",[109],[1921,17913,17914,17917,17920],{},[1936,17915,17916],{},"Grid Garden",[1936,17918,17919],{},"Aprenda CSS Grid com fazendinha",[1936,17921,17922],{},[105,17923,16514],{"href":17924,"rel":17925},"https:\u002F\u002Fcssgridgarden.com\u002F",[109],[1921,17927,17928,17931,17934],{},[1936,17929,17930],{},"Flexbox Froggy",[1936,17932,17933],{},"Aprenda Flexbox ajudando sapinhos a chegarem às vitórias-régias.",[1936,17935,17936],{},[105,17937,16514],{"href":17938,"rel":17939},"https:\u002F\u002Fflexboxfroggy.com\u002F",[109],[1921,17941,17942,17945,17948],{},[1936,17943,17944],{},"SQL Murder Mystery",[1936,17946,17947],{},"Aprenda banco de dados resolvendo um assassinato usando consultas SQL.",[1936,17949,17950],{},[105,17951,16514],{"href":17952,"rel":17953},"https:\u002F\u002Fmystery.knightlab.com\u002F",[109],[1921,17955,17956,17959,17962],{},[1936,17957,17958],{},"Oh My Git",[1936,17960,17961],{},"Aprenda Git jogando, visualizando repositórios em tempo real e entendendo na prática comandos, branches e merges.",[1936,17963,17964],{},[105,17965,16514],{"href":17966,"rel":17967},"https:\u002F\u002Fohmygit.org\u002F",[109],[27,17969],{},[30,17971,17973],{"id":17972},"curiosidades","💡 Curiosidades",[1915,17975,17976,17986],{},[1918,17977,17978],{},[1921,17979,17980,17982,17984],{},[1924,17981,16476],{},[1924,17983,16479],{},[1924,17985,16482],{},[1931,17987,17988,18002,18016],{},[1921,17989,17990,17993,17996],{},[1936,17991,17992],{},"HTTP Cat",[1936,17994,17995],{},"Códigos HTTP explicados com gatos.",[1936,17997,17998],{},[105,17999,16514],{"href":18000,"rel":18001},"https:\u002F\u002Fhttp.cat",[109],[1921,18003,18004,18007,18010],{},[1936,18005,18006],{},"HTTP Dog",[1936,18008,18009],{},"Códigos HTTP explicados com cachorros.",[1936,18011,18012],{},[105,18013,16514],{"href":18014,"rel":18015},"https:\u002F\u002Fhttp.dog",[109],[1921,18017,18018,18021,18024],{},[1936,18019,18020],{},"12ft.io",[1936,18022,18023],{},"Tenta remover paywall de alguns portais de notícias.",[1936,18025,18026],{},[105,18027,16514],{"href":18028,"rel":18029},"http:\u002F\u002F12ft.io\u002F",[109],[30,18031,18033],{"id":18032},"repositorios-legais","🚀 Repositorios legais",[1915,18035,18036,18046],{},[1918,18037,18038],{},[1921,18039,18040,18042,18044],{},[1924,18041,16476],{},[1924,18043,16479],{},[1924,18045,16482],{},[1931,18047,18048,18062,18076,18090,18104,18118],{},[1921,18049,18050,18053,18056],{},[1936,18051,18052],{},"Pikachu Jump",[1936,18054,18055],{},"Jogo feito em JavaScript puro.",[1936,18057,18058],{},[105,18059,16514],{"href":18060,"rel":18061},"https:\u002F\u002Flucianoneo.github.io\u002Fpikachu-jump",[109],[1921,18063,18064,18067,18070],{},[1936,18065,18066],{},"Freeway",[1936,18068,18069],{},"Jogo simples em html\u002Fcss\u002Fjs",[1936,18071,18072],{},[105,18073,16514],{"href":18074,"rel":18075},"https:\u002F\u002Fgithub.com\u002Fleticiakremer\u002Ffreeway-jogo",[109],[1921,18077,18078,18081,18084],{},[1936,18079,18080],{},"ToyStory CSS",[1936,18082,18083],{},"Personagens do filme em PixelArt feitos com CSS",[1936,18085,18086],{},[105,18087,16514],{"href":18088,"rel":18089},"https:\u002F\u002Fgithub.com\u002Flaisfrigerio\u002Ftoy-story-pixel-art",[109],[1921,18091,18092,18095,18098],{},[1936,18093,18094],{},"RamdomWords",[1936,18096,18097],{},"Gerador de palavras aleatorias desenvolvido com Quasar",[1936,18099,18100],{},[105,18101,16514],{"href":18102,"rel":18103},"https:\u002F\u002Fgithub.com\u002FLariMoro20\u002FRandomWords",[109],[1921,18105,18106,18109,18112],{},[1936,18107,18108],{},"JogoDaForca",[1936,18110,18111],{},"Jogo da forca desenvolvido com Quasar",[1936,18113,18114],{},[105,18115,16514],{"href":18116,"rel":18117},"https:\u002F\u002Fgithub.com\u002FLariMoro20\u002Fjogo-forca",[109],[1921,18119,18120,18123,18126],{},[1936,18121,18122],{},"GetCardsByIllustrator",[1936,18124,18125],{},"Robô que captura cartas de pokemon existentes de um ilustrador",[1936,18127,18128],{},[105,18129,16514],{"href":18130,"rel":18131},"https:\u002F\u002Fgithub.com\u002FLariMoro20\u002FGetCardsByIllustrator",[109],[27,18133],{},[30,18135,18137],{"id":18136},"humor","😀 Humor",[1915,18139,18140,18150],{},[1918,18141,18142],{},[1921,18143,18144,18146,18148],{},[1924,18145,16476],{},[1924,18147,16479],{},[1924,18149,16482],{},[1931,18151,18152,18166,18180,18194,18208,18222,18236,18250,18264],{},[1921,18153,18154,18157,18160],{},[1936,18155,18156],{},"Techy API",[1936,18158,18159],{},"Frases aleatórias relacionadas à tecnologia.",[1936,18161,18162],{},[105,18163,16514],{"href":18164,"rel":18165},"https:\u002F\u002Ftechy-api.vercel.app",[109],[1921,18167,18168,18171,18174],{},[1936,18169,18170],{},"ApiMeme",[1936,18172,18173],{},"API que gera memes automaticamente.",[1936,18175,18176],{},[105,18177,16514],{"href":18178,"rel":18179},"http:\u002F\u002Fapimeme.com",[109],[1921,18181,18182,18185,18188],{},[1936,18183,18184],{},"Should I Deploy Today",[1936,18186,18187],{},"Site divertido para decidir se hoje é dia de deploy.",[1936,18189,18190],{},[105,18191,16514],{"href":18192,"rel":18193},"https:\u002F\u002Fshouldideploy.today",[109],[1921,18195,18196,18199,18202],{},[1936,18197,18198],{},"FOAAS",[1936,18200,18201],{},"API que retorna xingamentos aleatórios em formato JSON.",[1936,18203,18204],{},[105,18205,16514],{"href":18206,"rel":18207},"https:\u002F\u002Ffoaas.io",[109],[1921,18209,18210,18213,18216],{},[1936,18211,18212],{},"Dad Jokes API",[1936,18214,18215],{},"API de piadas estilo \"tio do pavê\".",[1936,18217,18218],{},[105,18219,16514],{"href":18220,"rel":18221},"https:\u002F\u002Ficanhazdadjoke.com",[109],[1921,18223,18224,18227,18230],{},[1936,18225,18226],{},"Cat Facts API",[1936,18228,18229],{},"Fatos aleatórios sobre gatos.",[1936,18231,18232],{},[105,18233,16514],{"href":18234,"rel":18235},"https:\u002F\u002Fcatfact.ninja",[109],[1921,18237,18238,18241,18244],{},[1936,18239,18240],{},"Dog Facts API",[1936,18242,18243],{},"Curiosidades sobre cachorros.",[1936,18245,18246],{},[105,18247,16514],{"href":18248,"rel":18249},"https:\u002F\u002Fkinduff.github.io\u002Fdog-api\u002F",[109],[1921,18251,18252,18255,18258],{},[1936,18253,18254],{},"Yes \u002F No API",[1936,18256,18257],{},"Retorna respostas aleatórias de sim ou não com GIFs.",[1936,18259,18260],{},[105,18261,16514],{"href":18262,"rel":18263},"https:\u002F\u002Fyesno.wtf",[109],[1921,18265,18266,18269,18272],{},[1936,18267,18268],{},"Kanye Rest",[1936,18270,18271],{},"Retorna frases famosas do Kanye West.",[1936,18273,18274],{},[105,18275,16514],{"href":18276,"rel":18277},"https:\u002F\u002Fapi.kanye.rest",[109],[30,18279,18281],{"id":18280},"criadores-de-conteúdo","📺 Criadores de Conteúdo",[1915,18283,18284,18296],{},[1918,18285,18286],{},[1921,18287,18288,18291,18294],{},[1924,18289,18290],{},"Canal",[1924,18292,18293],{},"Conteúdo",[1924,18295,16482],{},[1931,18297,18298,18312,18326,18340,18354,18368,18382,18396,18410,18424,18438,18452,18466],{},[1921,18299,18300,18303,18306],{},[1936,18301,18302],{},"@PatrickMonteiro",[1936,18304,18305],{},"Cursos sobre programação Frontend e ecossistema Vue.js.",[1936,18307,18308],{},[105,18309,16514],{"href":18310,"rel":18311},"https:\u002F\u002Fwww.youtube.com\u002F@PatrickMonteiro",[109],[1921,18313,18314,18317,18320],{},[1936,18315,18316],{},"@rafaellaballerini",[1936,18318,18319],{},"Dicas de programação, carreira em tecnologia e conteúdo educativo.",[1936,18321,18322],{},[105,18323,16514],{"href":18324,"rel":18325},"https:\u002F\u002Fwww.youtube.com\u002F@rafaellaballerini",[109],[1921,18327,18328,18331,18334],{},[1936,18329,18330],{},"@codigofontetv",[1936,18332,18333],{},"Notícias, novidades e discussões sobre o mundo da programação.",[1936,18335,18336],{},[105,18337,16514],{"href":18338,"rel":18339},"https:\u002F\u002Fwww.youtube.com\u002F@codigofontetv",[109],[1921,18341,18342,18345,18348],{},[1936,18343,18344],{},"@FilipeDeschamps",[1936,18346,18347],{},"Conteúdos sobre programação, tecnologia, carreira e conceitos avançados de software.",[1936,18349,18350],{},[105,18351,16514],{"href":18352,"rel":18353},"https:\u002F\u002Fwww.youtube.com\u002F@FilipeDeschamps",[109],[1921,18355,18356,18359,18362],{},[1936,18357,18358],{},"@alura",[1936,18360,18361],{},"Conteúdos educacionais sobre programação, tecnologia e carreira em desenvolvimento.",[1936,18363,18364],{},[105,18365,16514],{"href":18366,"rel":18367},"https:\u002F\u002Fwww.youtube.com\u002F@alura",[109],[1921,18369,18370,18373,18376],{},[1936,18371,18372],{},"@rocketseat",[1936,18374,18375],{},"Conteúdos sobre desenvolvimento web, programação moderna e carreira dev.",[1936,18377,18378],{},[105,18379,16514],{"href":18380,"rel":18381},"https:\u002F\u002Fwww.youtube.com\u002F@rocketseat",[109],[1921,18383,18384,18387,18390],{},[1936,18385,18386],{},"@HashtagProgramacao",[1936,18388,18389],{},"Canal focado em programação, Python, automações e carreira em tecnologia.",[1936,18391,18392],{},[105,18393,16514],{"href":18394,"rel":18395},"https:\u002F\u002Fwww.youtube.com\u002F@HashtagProgramacao",[109],[1921,18397,18398,18401,18404],{},[1936,18399,18400],{},"@LucasMontano",[1936,18402,18403],{},"Notícias, reflexões e opiniões sobre o mercado de tecnologia.",[1936,18405,18406],{},[105,18407,16514],{"href":18408,"rel":18409},"https:\u002F\u002Fwww.youtube.com\u002F@LucasMontano",[109],[1921,18411,18412,18415,18418],{},[1936,18413,18414],{},"@cursoemvideo",[1936,18416,18417],{},"Cursos gratuitos de programação em formato de videoaulas.",[1936,18419,18420],{},[105,18421,16514],{"href":18422,"rel":18423},"https:\u002F\u002Fwww.youtube.com\u002F@cursoemvideo",[109],[1921,18425,18426,18429,18432],{},[1936,18427,18428],{},"@kipperdev",[1936,18430,18431],{},"Tutoriais de programação, backend, arquitetura de software e carreira dev.",[1936,18433,18434],{},[105,18435,16514],{"href":18436,"rel":18437},"https:\u002F\u002Fwww.youtube.com\u002F@kipperdev",[109],[1921,18439,18440,18443,18446],{},[1936,18441,18442],{},"@especializati",[1936,18444,18445],{},"Conteúdos sobre desenvolvimento backend, APIs e arquitetura de software.",[1936,18447,18448],{},[105,18449,16514],{"href":18450,"rel":18451},"https:\u002F\u002Fwww.youtube.com\u002F@especializati",[109],[1921,18453,18454,18457,18460],{},[1936,18455,18456],{},"@dicasparadevs",[1936,18458,18459],{},"Dicas de programação, carreira em tecnologia e desenvolvimento web.",[1936,18461,18462],{},[105,18463,16514],{"href":18464,"rel":18465},"https:\u002F\u002Fwww.youtube.com\u002F@dicasparadevs",[109],[1921,18467,18468,18471,18474],{},[1936,18469,18470],{},"@laisfrigerio",[1936,18472,18473],{},"Conteúdos sobre programação, carreira em tecnologia e aprendizado na área.",[1936,18475,18476],{},[105,18477,16514],{"href":18478,"rel":18479},"https:\u002F\u002Fwww.youtube.com\u002F@laisfrigerio",[109],[30,18481,18483],{"id":18482},"cursos-diversos","📺 Cursos diversos",[1643,18485,18487],{"id":18486},"front-end","💻 Front-end",[1915,18489,18490,18504],{},[1918,18491,18492],{},[1921,18493,18494,18497,18499,18502],{},[1924,18495,18496],{},"Curso",[1924,18498,18293],{},[1924,18500,18501],{},"Nível",[1924,18503,16482],{},[1931,18505,18506,18523,18540,18556],{},[1921,18507,18508,18511,18514,18517],{},[1936,18509,18510],{},"Vue.js Expert",[1936,18512,18513],{},"Cursos especializado em todo o ecosistema Vuejs com comunidade forte",[1936,18515,18516],{},"Intermediário\u002FAvançado",[1936,18518,18519],{},[105,18520,16514],{"href":18521,"rel":18522},"https:\u002F\u002Fcursovuejs.com.br\u002F",[109],[1921,18524,18525,18528,18531,18534],{},[1936,18526,18527],{},"Imersão Front-End",[1936,18529,18530],{},"Evento prático da Alura focado em desenvolvimento front-end",[1936,18532,18533],{},"Iniciante\u002FIntermediário",[1936,18535,18536],{},[105,18537,16514],{"href":18538,"rel":18539},"https:\u002F\u002Fwww.alura.com.br\u002Fimersao-front-end-iii",[109],[1921,18541,18542,18545,18548,18550],{},[1936,18543,18544],{},"7 Days of Code",[1936,18546,18547],{},"Desafios práticos de programação em várias tecnologias",[1936,18549,18533],{},[1936,18551,18552],{},[105,18553,16514],{"href":18554,"rel":18555},"https:\u002F\u002F7daysofcode.io\u002F",[109],[1921,18557,18558,18561,18564,18566],{},[1936,18559,18560],{},"Formação Front-end (Alura)",[1936,18562,18563],{},"HTML, CSS, JavaScript, frameworks e boas práticas modernas",[1936,18565,18533],{},[1936,18567,18568],{},[105,18569,16514],{"href":18570,"rel":18571},"https:\u002F\u002Fwww.alura.com.br\u002Fformacao-front-end",[109],[27,18573],{},[1643,18575,18577],{"id":18576},"inteligência-artificial","🤖 Inteligência Artificial",[1915,18579,18580,18592],{},[1918,18581,18582],{},[1921,18583,18584,18586,18588,18590],{},[1924,18585,18496],{},[1924,18587,18293],{},[1924,18589,18501],{},[1924,18591,16482],{},[1931,18593,18594,18611,18627,18644,18660,18676,18692,18708],{},[1921,18595,18596,18599,18602,18605],{},[1936,18597,18598],{},"Claude para Front-end (Vue.js Expert)",[1936,18600,18601],{},"Uso de inteligência artificial aplicada ao desenvolvimento front-end",[1936,18603,18604],{},"Intermediário",[1936,18606,18607],{},[105,18608,16514],{"href":18609,"rel":18610},"https:\u002F\u002Fcursovuejs.com.br\u002Fia-frontend\u002F",[109],[1921,18612,18613,18616,18619,18625],{},[1936,18614,18615],{},"AI Prompt Tester",[1936,18617,18618],{},"Ferramenta para testar, analisar e melhorar prompts de código com base em boas práticas de IA",[1936,18620,18621],{},[105,18622,16514],{"href":18623,"rel":18624},"https:\u002F\u002Fcoach.robinebers.com\u002F",[109],[1936,18626],{},[1921,18628,18629,18632,18635,18638],{},[1936,18630,18631],{},"Engenharia de Prompt",[1936,18633,18634],{},"Curso focado em criação de prompts eficientes para IA",[1936,18636,18637],{},"Iniciante",[1936,18639,18640],{},[105,18641,16514],{"href":18642,"rel":18643},"https:\u002F\u002Facademy.conversion.com.br\u002Fcurso\u002Fengenharia-de-prompt",[109],[1921,18645,18646,18649,18652,18654],{},[1936,18647,18648],{},"Fundamentos de IA",[1936,18650,18651],{},"Introdução aos conceitos de inteligência artificial e aplicações práticas",[1936,18653,18637],{},[1936,18655,18656],{},[105,18657,16514],{"href":18658,"rel":18659},"https:\u002F\u002Fwww.skills.google\u002Fcourse_templates\u002F536?locale=pt_BR",[109],[1921,18661,18662,18665,18668,18670],{},[1936,18663,18664],{},"Produtividade com IA",[1936,18666,18667],{},"Como utilizar IA para aumentar produtividade no dia a dia",[1936,18669,18637],{},[1936,18671,18672],{},[105,18673,16514],{"href":18674,"rel":18675},"https:\u002F\u002Fwww.skills.google\u002Fcourse_templates\u002F539?locale=pt_BR",[109],[1921,18677,18678,18681,18684,18686],{},[1936,18679,18680],{},"IA para Negócios",[1936,18682,18683],{},"Aplicação de inteligência artificial em estratégias e negócios",[1936,18685,18533],{},[1936,18687,18688],{},[105,18689,16514],{"href":18690,"rel":18691},"https:\u002F\u002Fwww.skills.google\u002Fcourse_templates\u002F552?locale=pt_BR",[109],[1921,18693,18694,18697,18700,18702],{},[1936,18695,18696],{},"Generative AI (Harvard)",[1936,18698,18699],{},"Uso prático de IA generativa para criação de conteúdo e aplicações",[1936,18701,18604],{},[1936,18703,18704],{},[105,18705,16514],{"href":18706,"rel":18707},"https:\u002F\u002Fgenerative-ai-course.hks.harvard.edu\u002F2-using-genai\u002Fclass-4",[109],[1921,18709,18710,18713,18716,18718],{},[1936,18711,18712],{},"Spec-Driven Development with Coding Agents",[1936,18714,18715],{},"Aulas sobre como usar SDD com agentes de IA",[1936,18717,18604],{},[1936,18719,18720],{},[105,18721,16514],{"href":18722,"rel":18723},"https:\u002F\u002Flearn.deeplearning.ai\u002Fcourses\u002Fspec-driven-development-with-coding-agents\u002Flesson\u002F8bhg0p\u002Fintroduction",[109],[27,18725],{},[1643,18727,18729],{"id":18728},"profissionalizantes-geral","🎓 Profissionalizantes \u002F Geral",[1915,18731,18732,18744],{},[1918,18733,18734],{},[1921,18735,18736,18738,18740,18742],{},[1924,18737,18496],{},[1924,18739,18293],{},[1924,18741,18501],{},[1924,18743,16482],{},[1931,18745,18746,18762,18778,18795,18811,18827,18843],{},[1921,18747,18748,18751,18754,18756],{},[1936,18749,18750],{},"Eu Capacito (FIAP)",[1936,18752,18753],{},"Plataforma com diversos cursos gratuitos em tecnologia e inovação",[1936,18755,18637],{},[1936,18757,18758],{},[105,18759,16514],{"href":18760,"rel":18761},"https:\u002F\u002Fwww.eucapacito.com.br\u002Fcursos\u002F?_sft_parceiro_ec=fiap",[109],[1921,18763,18764,18767,18770,18772],{},[1936,18765,18766],{},"Programa Eu Capacito",[1936,18768,18769],{},"Cursos profissionalizantes em parceria com FIAP",[1936,18771,18533],{},[1936,18773,18774],{},[105,18775,16514],{"href":18776,"rel":18777},"https:\u002F\u002Fon.fiap.com.br\u002Flocal\u002Fprogramaeucapacito\u002F",[109],[1921,18779,18780,18783,18786,18789],{},[1936,18781,18782],{},"Origamid",[1936,18784,18785],{},"Plataforma com cursos de Web Design, UX\u002FUI, HTML, CSS, JavaScript, TypeScript, Node e React",[1936,18787,18788],{},"Iniciante\u002FIntermediário\u002FAvançado",[1936,18790,18791],{},[105,18792,16514],{"href":18793,"rel":18794},"https:\u002F\u002Fwww.origamid.com\u002F",[109],[1921,18796,18797,18800,18803,18805],{},[1936,18798,18799],{},"DIO (Digital Innovation One)",[1936,18801,18802],{},"Cursos gratuitos com foco em mercado e carreira tech",[1936,18804,18533],{},[1936,18806,18807],{},[105,18808,16514],{"href":18809,"rel":18810},"https:\u002F\u002Fwww.dio.me\u002F",[109],[1921,18812,18813,18816,18819,18821],{},[1936,18814,18815],{},"Alura",[1936,18817,18818],{},"Plataforma completa com cursos de programação, design, dados e carreira",[1936,18820,18788],{},[1936,18822,18823],{},[105,18824,16514],{"href":18825,"rel":18826},"https:\u002F\u002Fwww.alura.com.br\u002F",[109],[1921,18828,18829,18832,18835,18837],{},[1936,18830,18831],{},"Rocketseat",[1936,18833,18834],{},"Cursos práticos focados em desenvolvimento web moderno e mercado",[1936,18836,18788],{},[1936,18838,18839],{},[105,18840,16514],{"href":18841,"rel":18842},"https:\u002F\u002Fwww.rocketseat.com.br\u002F",[109],[1921,18844,18845,18848,18851,18853],{},[1936,18846,18847],{},"FIAP",[1936,18849,18850],{},"Cursos e formações com foco em tecnologia e profissionalização",[1936,18852,18637],{},[1936,18854,18855],{},[105,18856,16514],{"href":18857,"rel":18858},"https:\u002F\u002Fwww.fiap.com.br\u002F",[109],{"title":75,"searchDepth":76,"depth":76,"links":18860},[18861,18862,18863,18864,18865,18866,18867,18868,18869,18870,18871,18872,18873],{"id":16466,"depth":76,"text":16467},{"id":16589,"depth":76,"text":16590},{"id":16762,"depth":76,"text":16763},{"id":17075,"depth":76,"text":17076},{"id":17249,"depth":76,"text":17250},{"id":17506,"depth":76,"text":17507},{"id":17708,"depth":76,"text":17709},{"id":17826,"depth":76,"text":17827},{"id":17972,"depth":76,"text":17973},{"id":18032,"depth":76,"text":18033},{"id":18136,"depth":76,"text":18137},{"id":18280,"depth":76,"text":18281},{"id":18482,"depth":76,"text":18483,"children":18874},[18875,18876,18877],{"id":18486,"depth":152,"text":18487},{"id":18576,"depth":152,"text":18577},{"id":18728,"depth":152,"text":18729},"2026-03-17T17:32:00-03:00","Uma coleção de APIs, ferramentas, jogos e recursos úteis para estudos, automações e projetos de desenvolvimento.","\u002Fimages\u002Fblog\u002Fcapa-dicas-ferramentas.png",{"sumary":18882},[18883,18886,18889,18892,18895,18898,18901,18904,18907,18910,18913,18916,18919],{"label":18884,"value":18885},"Projetos e recursos do GitHub","#projetos-e-recursos-do-github",{"label":18887,"value":18888},"Recursos visuais","#recursos-visuais",{"label":18890,"value":18891},"Ferramentas úteis para desenvolvedores","#ferramentas-úteis-para-desenvolvedores",{"label":18893,"value":18894},"Bibliotecas úteis","#bibliotecas-úteis",{"label":18896,"value":18897},"Ferramentas de IA","#ferramentas-de-ia",{"label":18899,"value":18900},"APIs públicas para projetos","#apis-públicas-para-projetos",{"label":18902,"value":18903},"APIs úteis no dia a dia","#apis-úteis-no-dia-a-dia",{"label":18905,"value":18906},"Aprendizado","#aprendizado",{"label":18908,"value":18909},"Curiosidades","#curiosidades",{"label":18911,"value":18912},"Repositórios legais","#repositorios-legais",{"label":18914,"value":18915},"Humor","#humor",{"label":18917,"value":18918},"Criadores de conteúdo","#criadores-de-conteúdo",{"label":18920,"value":18921},"Cursos diversos","#cursos-diversos","\u002Fblog\u002Fapis-e-ferramentas-para-devs",{"title":16445,"description":18879},"blog\u002Fapis-e-ferramentas-para-devs",[18926,18927,15490,18928,18929],"APIs","ferramentas","estudos","recursos","imvRRstGnKwSvk5uTXqMMngChwFoPDO68HWHzsEI5tI",{"id":18932,"title":18933,"author":7,"body":18934,"date":20944,"description":20945,"extension":83,"image":20946,"meta":20947,"navigation":86,"path":20948,"seo":20949,"stem":20950,"tags":20951,"__hash__":20953},"blog\u002Fblog\u002Fvisitcard-generator-nuxt-pdf-do-zero.md","VisitCardGenerator: como construí um gerador de cartões de visita em PDF do zero com Nuxt 3",{"type":9,"value":18935,"toc":20918},[18936,18939,18942,18945,18947,18949,18952,18955,18957,18960,18964,18978,18982,18985,18987,18990,18992,18996,18999,19002,19057,19060,19064,19070,19092,19095,19101,19104,19460,19477,19487,19501,19507,19672,19679,19683,19920,19923,19927,19935,20141,20144,20150,20180,20183,20187,20211,20214,20216,20223,20232,20240,20343,20351,20353,20357,20360,20533,20540,20608,20610,20614,20619,20726,20745,20747,20751,20757,20814,20821,20823,20827,20832,20883,20890,20892,20894,20901,20904,20912,20915],[12,18937,18938],{},"Criar um cartão de visita profissional deveria ser simples. Mas a realidade é que a maioria das ferramentas disponíveis exige conta, cobra pelo download, ou te coloca numa interface de drag-and-drop onde você passa mais tempo ajustando posição de elementos do que produzindo algo de valor.",[12,18940,18941],{},"A ideia do VisitCardGenerator surgiu exatamente daí: um gerador onde qualquer pessoa preenche campos, visualiza o resultado em tempo real e baixa o PDF pronto para gráfica, sem cadastro, sem custo, sem complicação.",[12,18943,18944],{},"Neste artigo vou detalhar as decisões técnicas, os desafios que enfrentei e o que aprendi no processo.",[27,18946],{},[30,18948,4799],{"id":4798},[1643,18950,1792],{"id":18951},"nuxt-3",[12,18953,18954],{},"O Nuxt foi escolhido por entregar SSR, SEO nativo e performance de carregamento, essenciais para um produto que precisa de visibilidade orgânica. O file-based routing elimina configuração manual de rotas, o sistema de módulos acelera integrações, e o ecossistema do NuxtUI com Tailwind v4 entregou velocidade de desenvolvimento sem abrir mão de qualidade visual. A escolha veio da experiência prévia com a tecnologia e da confiança no que ela entrega.",[1643,18956,4819],{"id":4975},[12,18958,18959],{},"O estado do editor tem uma característica importante: precisa ser acessado por pelo menos três camadas diferentes, a página que orquestra, o formulário que edita, e os cards que renderizam. Passar tudo via props e emits geraria um prop drilling desnecessário e acoplamento entre componentes que não precisam se conhecer. O Pinia resolve isso com uma store central que qualquer componente acessa diretamente, sem intermediários.",[1643,18961,18963],{"id":18962},"html-to-image-no-lugar-de-html2canvas","html-to-image no lugar de html2canvas",[12,18965,1193,18966,18969,18970,18973,18974,18977],{},[135,18967,18968],{},"html2canvas"," é a biblioteca mais popular para captura de DOM, mas não suporta o espaço de cor ",[135,18971,18972],{},"oklch()",", que é exatamente o que o Tailwind v4 usa para gerar suas cores. O resultado seria um card completamente branco no PDF. O ",[135,18975,18976],{},"html-to-image"," não tem essa limitação e entrega o mesmo resultado visual com uma API equivalente.",[1643,18979,18981],{"id":18980},"jspdf","jsPDF",[12,18983,18984],{},"Biblioteca consolidada para geração de PDF no browser, sem dependência de servidor. Permite definir o formato exato em milímetros, essencial para gerar o PDF no tamanho padrão de gráficas (88.9x50.8mm) sem distorção.",[1643,18986,4859],{"id":1773},[12,18988,18989],{},"Usado em todo o projeto. Com um formulário que tem ~15 campos tipados e componentes que recebem subconjuntos diferentes desses campos como props, o TypeScript eliminou uma classe inteira de bugs e tornou o autocomplete confiável durante o desenvolvimento.",[27,18991],{},[30,18993,18995],{"id":18994},"arquitetura-do-editor","Arquitetura do editor",[12,18997,18998],{},"O editor é a peça central do projeto. Visualmente é uma tela dividida em duas colunas: à esquerda o formulário de edição, à direita o preview do cartão em tempo real. No mobile, o preview ocupa a tela inteira e o formulário abre como um painel deslizante a partir de baixo.",[12,19000,19001],{},"A arquitetura foi pensada com separação clara de responsabilidades, seguindo o princípio da responsabilidade única: cada camada faz exatamente uma coisa e não sabe mais do que precisa.",[402,19003,19004,19012,19021,19033,19042],{},[405,19005,19006,19011],{},[19,19007,19008,19010],{},[135,19009,7483],{}," (Pinia):"," fonte da verdade do conteúdo do cartão. Gerencia o estado do formulário, as imagens, os getters de props dos cards, a persistência no localStorage e o reset.",[405,19013,19014,19020],{},[19,19015,19016,19019],{},[135,19017,19018],{},"useCardExport"," (composable):"," toda a lógica de captura dos elementos do DOM e geração do PDF. Não conhece a store nem o formulário, recebe os elementos e o nome do arquivo.",[405,19022,19023,19029,19030,19032],{},[19,19024,19025,19028],{},[135,19026,19027],{},"editor.vue"," (página):"," orquestra. Carrega o estado salvo no ",[135,19031,10225],{},", conecta o preview com os getters da store e delega a exportação ao composable.",[405,19034,19035,19041],{},[19,19036,19037,19040],{},[135,19038,19039],{},"Editor\u002FForm\u002Findex.vue"," (formulário):"," acessa a store diretamente e tem autonomia sobre sua própria apresentação: qual tab está ativa, validação de e-mail, upload de imagem, reset com modal de confirmação.",[405,19043,19044,19053,19054,19056],{},[19,19045,19046,377,19049,19052],{},[135,19047,19048],{},"BusinessFront.vue",[135,19050,19051],{},"BusinessBack.vue"," (cards):"," puramente apresentacionais. Recebem props, calculam estilos via ",[135,19055,7111],{}," e renderizam. Não têm estado próprio e podem ser usados em qualquer contexto sem adaptação.",[12,19058,19059],{},"Esse isolamento tem consequências práticas: mudar a lógica de exportação não afeta os cards, adicionar um campo no formulário não exige tocar no preview, e os cards são reutilizados sem modificação no editor, na landing page e na exportação do PDF.",[1643,19061,19063],{"id":19062},"estrutura-de-componentes","Estrutura de componentes",[129,19065,19068],{"className":19066,"code":19067,"language":13552},[13550],"components\u002F\n  Animations\u002F             ← animações reutilizáveis (ex: reveal on scroll)\n  Editor\u002F\n    Card\u002F\n      BusinessFront.vue   ← frente do cartão (apresentação pura)\n      BusinessBack.vue    ← verso do cartão (apresentação pura)\n      Preview.vue         ← preview escalado para a tela\n    Form\u002F\n      index.vue           ← formulário principal com tabs\n      ColorPicker.vue     ← seletor de cor customizado\n      PatternPicker.vue   ← seletor de padrão geométrico\n      Field.vue           ← wrapper de campo com label\n      Label.vue           ← label reutilizável\n    Header.vue            ← cabeçalho do editor\n    Footer.vue            ← rodapé do editor\n  LandingPage\u002F\n    Card.vue              ← exemplo interativo com flip 3D\n    \u002F\u002F demais seções da landing page...\n  OgImage\u002F                ← componente de OG Image customizado\n",[135,19069,19067],{"__ignoreMap":75},[12,19071,19072,19073,19076,19077,19080,19081,19084,19085,19088,19089,19091],{},"O formulário foi quebrado em componentes menores dentro de ",[135,19074,19075],{},"Form\u002F"," seguindo o SRP: ",[135,19078,19079],{},"ColorPicker"," só sabe selecionar cores, ",[135,19082,19083],{},"PatternPicker"," só sabe exibir e selecionar padrões, ",[135,19086,19087],{},"Field"," só sabe envolver um input com label e espaçamento consistente. O ",[135,19090,1385],{}," orquestra esses blocos nas tabs, mas não conhece os detalhes internos de nenhum deles.",[12,19093,19094],{},"Os cards são abertos para extensão via props, novos campos, alinhamentos e tamanhos de logo foram adicionados sem modificar a estrutura base, mas fechados para modificação de comportamento externo, já que não expõem estado interno nem emitem eventos. É o princípio aberto\u002Ffechado aplicado a componentes Vue.",[1643,19096,19098,19099],{"id":19097},"a-store-useeditorstore","A store ",[135,19100,7483],{},[12,19102,19103],{},"A store centraliza tudo que pertence ao domínio do cartão:",[129,19105,19107],{"className":785,"code":19106,"language":787,"meta":75,"style":75},"export const useEditorStore = defineStore('editor', () => {\n  const form        = reactive\u003CEditorForm>({ ...DEFAULT_FORM })\n  const logoPreview = ref\u003Cstring | null>(null)\n  const bgImages    = ref\u003CRecord\u003Cstring, string | null>>({ front: null, back: null })\n\n  const cardFrontProps = computed(() => ({ ... }))\n  const cardBackProps  = computed(() => ({ ... }))\n  const isFormValid    = computed(() => form.companyName.trim().length > 0)\n\n  function save()  { \u002F* persiste no localStorage *\u002F }\n  function load()  { \u002F* recupera do localStorage *\u002F }\n  function reset() { \u002F* limpa estado e storage   *\u002F }\n\n  return { form, logoPreview, bgImages, cardFrontProps, cardBackProps, isFormValid, save, load, reset }\n})\n",[135,19108,19109,19139,19172,19199,19255,19259,19286,19314,19356,19360,19377,19393,19408,19412,19454],{"__ignoreMap":75},[138,19110,19111,19113,19115,19118,19120,19122,19124,19126,19129,19131,19133,19135,19137],{"class":140,"line":141},[138,19112,795],{"class":794},[138,19114,3014],{"class":1073},[138,19116,19117],{"class":2521}," useEditorStore",[138,19119,1146],{"class":1112},[138,19121,3021],{"class":801},[138,19123,806],{"class":805},[138,19125,834],{"class":826},[138,19127,19128],{"class":830},"editor",[138,19130,834],{"class":826},[138,19132,954],{"class":809},[138,19134,3034],{"class":809},[138,19136,1074],{"class":1073},[138,19138,934],{"class":809},[138,19140,19141,19143,19146,19149,19151,19153,19156,19158,19160,19162,19165,19168,19170],{"class":140,"line":76},[138,19142,2607],{"class":1073},[138,19144,19145],{"class":2521}," form",[138,19147,19148],{"class":1112},"        =",[138,19150,10391],{"class":801},[138,19152,1416],{"class":809},[138,19154,19155],{"class":1556},"EditorForm",[138,19157,3065],{"class":809},[138,19159,806],{"class":815},[138,19161,3284],{"class":809},[138,19163,19164],{"class":1112}," ...",[138,19166,19167],{"class":2521},"DEFAULT_FORM",[138,19169,2823],{"class":809},[138,19171,872],{"class":815},[138,19173,19174,19176,19179,19181,19183,19185,19187,19189,19191,19193,19195,19197],{"class":140,"line":152},[138,19175,2607],{"class":1073},[138,19177,19178],{"class":2521}," logoPreview",[138,19180,1146],{"class":1112},[138,19182,3050],{"class":801},[138,19184,1416],{"class":809},[138,19186,3800],{"class":3061},[138,19188,3058],{"class":1112},[138,19190,3062],{"class":3061},[138,19192,3065],{"class":809},[138,19194,806],{"class":815},[138,19196,3070],{"class":2041},[138,19198,872],{"class":815},[138,19200,19201,19203,19206,19209,19211,19213,19216,19218,19220,19222,19224,19226,19228,19231,19233,19235,19238,19240,19242,19244,19247,19249,19251,19253],{"class":140,"line":158},[138,19202,2607],{"class":1073},[138,19204,19205],{"class":2521}," bgImages",[138,19207,19208],{"class":1112},"    =",[138,19210,3050],{"class":801},[138,19212,1416],{"class":809},[138,19214,19215],{"class":1556},"Record",[138,19217,1416],{"class":809},[138,19219,3800],{"class":3061},[138,19221,954],{"class":809},[138,19223,3808],{"class":3061},[138,19225,3058],{"class":1112},[138,19227,3062],{"class":3061},[138,19229,19230],{"class":809},">>",[138,19232,806],{"class":815},[138,19234,3284],{"class":809},[138,19236,19237],{"class":815}," front",[138,19239,782],{"class":809},[138,19241,3062],{"class":2041},[138,19243,954],{"class":809},[138,19245,19246],{"class":815}," back",[138,19248,782],{"class":809},[138,19250,3062],{"class":2041},[138,19252,2823],{"class":809},[138,19254,872],{"class":815},[138,19256,19257],{"class":140,"line":164},[138,19258,649],{"emptyLinePlaceholder":86},[138,19260,19261,19263,19266,19268,19270,19272,19274,19276,19278,19280,19282,19284],{"class":140,"line":170},[138,19262,2607],{"class":1073},[138,19264,19265],{"class":2521}," cardFrontProps",[138,19267,1146],{"class":1112},[138,19269,3088],{"class":801},[138,19271,806],{"class":815},[138,19273,3093],{"class":809},[138,19275,1074],{"class":1073},[138,19277,1084],{"class":815},[138,19279,3284],{"class":809},[138,19281,19164],{"class":1112},[138,19283,2823],{"class":809},[138,19285,4205],{"class":815},[138,19287,19288,19290,19293,19296,19298,19300,19302,19304,19306,19308,19310,19312],{"class":140,"line":176},[138,19289,2607],{"class":1073},[138,19291,19292],{"class":2521}," cardBackProps",[138,19294,19295],{"class":1112},"  =",[138,19297,3088],{"class":801},[138,19299,806],{"class":815},[138,19301,3093],{"class":809},[138,19303,1074],{"class":1073},[138,19305,1084],{"class":815},[138,19307,3284],{"class":809},[138,19309,19164],{"class":1112},[138,19311,2823],{"class":809},[138,19313,4205],{"class":815},[138,19315,19316,19318,19321,19323,19325,19327,19329,19331,19333,19335,19338,19340,19343,19345,19347,19349,19352,19354],{"class":140,"line":182},[138,19317,2607],{"class":1073},[138,19319,19320],{"class":2521}," isFormValid",[138,19322,19208],{"class":1112},[138,19324,3088],{"class":801},[138,19326,806],{"class":815},[138,19328,3093],{"class":809},[138,19330,1074],{"class":1073},[138,19332,19145],{"class":805},[138,19334,363],{"class":809},[138,19336,19337],{"class":805},"companyName",[138,19339,363],{"class":809},[138,19341,19342],{"class":801},"trim",[138,19344,3093],{"class":815},[138,19346,363],{"class":809},[138,19348,6484],{"class":2521},[138,19350,19351],{"class":1112}," >",[138,19353,5296],{"class":5295},[138,19355,872],{"class":815},[138,19357,19358],{"class":140,"line":188},[138,19359,649],{"emptyLinePlaceholder":86},[138,19361,19362,19364,19367,19369,19372,19375],{"class":140,"line":194},[138,19363,3124],{"class":1073},[138,19365,19366],{"class":801}," save",[138,19368,3093],{"class":809},[138,19370,19371],{"class":809},"  {",[138,19373,19374],{"class":911}," \u002F* persiste no localStorage *\u002F",[138,19376,1015],{"class":809},[138,19378,19379,19381,19384,19386,19388,19391],{"class":140,"line":199},[138,19380,3124],{"class":1073},[138,19382,19383],{"class":801}," load",[138,19385,3093],{"class":809},[138,19387,19371],{"class":809},[138,19389,19390],{"class":911}," \u002F* recupera do localStorage *\u002F",[138,19392,1015],{"class":809},[138,19394,19395,19397,19399,19401,19403,19406],{"class":140,"line":204},[138,19396,3124],{"class":1073},[138,19398,10196],{"class":801},[138,19400,3093],{"class":809},[138,19402,2812],{"class":809},[138,19404,19405],{"class":911}," \u002F* limpa estado e storage   *\u002F",[138,19407,1015],{"class":809},[138,19409,19410],{"class":140,"line":209},[138,19411,649],{"emptyLinePlaceholder":86},[138,19413,19414,19416,19418,19420,19422,19424,19426,19428,19430,19432,19434,19436,19438,19440,19442,19444,19446,19448,19450,19452],{"class":140,"line":215},[138,19415,3199],{"class":794},[138,19417,2812],{"class":809},[138,19419,19145],{"class":805},[138,19421,954],{"class":809},[138,19423,19178],{"class":805},[138,19425,954],{"class":809},[138,19427,19205],{"class":805},[138,19429,954],{"class":809},[138,19431,19265],{"class":805},[138,19433,954],{"class":809},[138,19435,19292],{"class":805},[138,19437,954],{"class":809},[138,19439,19320],{"class":805},[138,19441,954],{"class":809},[138,19443,19366],{"class":805},[138,19445,954],{"class":809},[138,19447,19383],{"class":805},[138,19449,954],{"class":809},[138,19451,10196],{"class":805},[138,19453,1015],{"class":809},[138,19455,19456,19458],{"class":140,"line":221},[138,19457,869],{"class":809},[138,19459,872],{"class":805},[12,19461,19462,19463,377,19466,19469,19470,14438,19473,19476],{},"Os ",[135,19464,19465],{},"cardFrontProps",[135,19467,19468],{},"cardBackProps"," são getters computados que encapsulam toda a lógica de composição, incluindo a regra de ",[135,19471,19472],{},"patternFront",[135,19474,19475],{},"pattern"," para frente e verso, e a intensidade do padrão compartilhada entre os dois lados. A página e os componentes consomem esses getters diretamente.",[12,19478,19479,19480,377,19483,19486],{},"O estado do formulário e as imagens ficam separados intencionalmente: ",[135,19481,19482],{},"logoPreview",[135,19484,19485],{},"bgImages"," armazenam strings base64 que podem ter centenas de KB. Misturá-los com os campos primitivos do formulário criaria um objeto pesado sendo monitorado desnecessariamente.",[12,19488,19489,19490,19493,19494,19497,19498,19500],{},"A persistência é explícita: ",[135,19491,19492],{},"save()"," é chamado pelo botão no formulário, e ",[135,19495,19496],{},"load()"," no ",[135,19499,10225],{}," da página. O usuário decide quando salvar, o que é mais previsível e evita gravar centenas de KB a cada keystroke.",[1643,19502,19504,19505],{"id":19503},"o-composable-usecardexport","O composable ",[135,19506,19018],{},[129,19508,19510],{"className":785,"code":19509,"language":787,"meta":75,"style":75},"export function useCardExport() {\n  const isGenerating = ref(false)\n\n  async function exportPDF(\n    frontEl: HTMLElement,\n    backEl: HTMLElement,\n    fileName: string\n  ) {\n    isGenerating.value = true\n    try {\n      \u002F\u002F captura com html-to-image, monta o PDF com jsPDF, faz o download\n    } finally {\n      isGenerating.value = false\n    }\n  }\n\n  return { isGenerating, exportPDF }\n}\n",[135,19511,19512,19525,19542,19546,19558,19570,19581,19590,19597,19610,19616,19621,19629,19642,19646,19650,19654,19668],{"__ignoreMap":75},[138,19513,19514,19516,19518,19521,19523],{"class":140,"line":141},[138,19515,795],{"class":794},[138,19517,3931],{"class":1073},[138,19519,19520],{"class":801}," useCardExport",[138,19522,3093],{"class":809},[138,19524,934],{"class":809},[138,19526,19527,19529,19532,19534,19536,19538,19540],{"class":140,"line":76},[138,19528,2607],{"class":1073},[138,19530,19531],{"class":2521}," isGenerating",[138,19533,1146],{"class":1112},[138,19535,3050],{"class":801},[138,19537,806],{"class":815},[138,19539,11707],{"class":3862},[138,19541,872],{"class":815},[138,19543,19544],{"class":140,"line":152},[138,19545,649],{"emptyLinePlaceholder":86},[138,19547,19548,19550,19552,19555],{"class":140,"line":158},[138,19549,12018],{"class":1073},[138,19551,3931],{"class":1073},[138,19553,19554],{"class":801}," exportPDF",[138,19556,19557],{"class":809},"(\n",[138,19559,19560,19563,19565,19568],{"class":140,"line":164},[138,19561,19562],{"class":1045},"    frontEl",[138,19564,782],{"class":1112},[138,19566,19567],{"class":1556}," HTMLElement",[138,19569,837],{"class":809},[138,19571,19572,19575,19577,19579],{"class":140,"line":170},[138,19573,19574],{"class":1045},"    backEl",[138,19576,782],{"class":1112},[138,19578,19567],{"class":1556},[138,19580,837],{"class":809},[138,19582,19583,19586,19588],{"class":140,"line":176},[138,19584,19585],{"class":1045},"    fileName",[138,19587,782],{"class":1112},[138,19589,7564],{"class":3061},[138,19591,19592,19595],{"class":140,"line":182},[138,19593,19594],{"class":809},"  )",[138,19596,934],{"class":809},[138,19598,19599,19602,19604,19606,19608],{"class":140,"line":188},[138,19600,19601],{"class":805},"    isGenerating",[138,19603,363],{"class":809},[138,19605,3102],{"class":805},[138,19607,1146],{"class":1112},[138,19609,11756],{"class":3862},[138,19611,19612,19614],{"class":140,"line":194},[138,19613,12060],{"class":794},[138,19615,934],{"class":809},[138,19617,19618],{"class":140,"line":199},[138,19619,19620],{"class":911},"      \u002F\u002F captura com html-to-image, monta o PDF com jsPDF, faz o download\n",[138,19622,19623,19625,19627],{"class":140,"line":204},[138,19624,12106],{"class":809},[138,19626,11866],{"class":794},[138,19628,934],{"class":809},[138,19630,19631,19634,19636,19638,19640],{"class":140,"line":209},[138,19632,19633],{"class":805},"      isGenerating",[138,19635,363],{"class":809},[138,19637,3102],{"class":805},[138,19639,1146],{"class":1112},[138,19641,3863],{"class":3862},[138,19643,19644],{"class":140,"line":215},[138,19645,1179],{"class":809},[138,19647,19648],{"class":140,"line":221},[138,19649,1184],{"class":809},[138,19651,19652],{"class":140,"line":227},[138,19653,649],{"emptyLinePlaceholder":86},[138,19655,19656,19658,19660,19662,19664,19666],{"class":140,"line":233},[138,19657,3199],{"class":794},[138,19659,2812],{"class":809},[138,19661,19531],{"class":805},[138,19663,954],{"class":809},[138,19665,19554],{"class":805},[138,19667,1015],{"class":809},[138,19669,19670],{"class":140,"line":239},[138,19671,2082],{"class":809},[12,19673,19674,19675,19678],{},"Não conhece a store, não conhece o formulário. Recebe os elementos do DOM e o nome do arquivo, só isso. O ",[135,19676,19677],{},"isGenerating"," é exposto para a UI reagir durante a geração.",[1643,19680,19682],{"id":19681},"como-a-página-ficou","Como a página ficou",[129,19684,19686],{"className":785,"code":19685,"language":787,"meta":75,"style":75},"const store = useEditorStore()\nconst { isGenerating, exportPDF } = useCardExport()\n\nonMounted(() => store.load())\n\nasync function handleGenerate() {\n  const frontEl = previewRef.value?.cardFrenteRef?.$el as HTMLElement\n  const backEl = previewRef.value?.cardVersoRef?.$el as HTMLElement\n  const fileName = (store.form.companyName || 'cartao')\n    .toLowerCase()\n    .replace(\u002F\\s+\u002Fg, '-')\n  await exportPDF(frontEl, backEl, fileName)\n}\n",[135,19687,19688,19700,19720,19724,19743,19747,19760,19791,19819,19852,19862,19894,19916],{"__ignoreMap":75},[138,19689,19690,19692,19694,19696,19698],{"class":140,"line":141},[138,19691,2518],{"class":1073},[138,19693,8228],{"class":2521},[138,19695,1146],{"class":1112},[138,19697,19117],{"class":801},[138,19699,2796],{"class":805},[138,19701,19702,19704,19706,19708,19710,19712,19714,19716,19718],{"class":140,"line":76},[138,19703,2518],{"class":1073},[138,19705,2812],{"class":809},[138,19707,19531],{"class":2521},[138,19709,954],{"class":809},[138,19711,19554],{"class":2521},[138,19713,2823],{"class":809},[138,19715,1146],{"class":1112},[138,19717,19520],{"class":801},[138,19719,2796],{"class":805},[138,19721,19722],{"class":140,"line":152},[138,19723,649],{"emptyLinePlaceholder":86},[138,19725,19726,19728,19730,19732,19734,19736,19738,19741],{"class":140,"line":158},[138,19727,10225],{"class":801},[138,19729,806],{"class":805},[138,19731,3093],{"class":809},[138,19733,1074],{"class":1073},[138,19735,8228],{"class":805},[138,19737,363],{"class":809},[138,19739,19740],{"class":801},"load",[138,19742,8394],{"class":805},[138,19744,19745],{"class":140,"line":164},[138,19746,649],{"emptyLinePlaceholder":86},[138,19748,19749,19751,19753,19756,19758],{"class":140,"line":170},[138,19750,2591],{"class":1073},[138,19752,3931],{"class":1073},[138,19754,19755],{"class":801}," handleGenerate",[138,19757,3093],{"class":809},[138,19759,934],{"class":809},[138,19761,19762,19764,19767,19769,19772,19774,19776,19778,19781,19783,19786,19788],{"class":140,"line":176},[138,19763,2607],{"class":1073},[138,19765,19766],{"class":2521}," frontEl",[138,19768,1146],{"class":1112},[138,19770,19771],{"class":805}," previewRef",[138,19773,363],{"class":809},[138,19775,3102],{"class":805},[138,19777,1094],{"class":809},[138,19779,19780],{"class":805},"cardFrenteRef",[138,19782,1094],{"class":809},[138,19784,19785],{"class":805},"$el",[138,19787,2944],{"class":794},[138,19789,19790],{"class":1556}," HTMLElement\n",[138,19792,19793,19795,19798,19800,19802,19804,19806,19808,19811,19813,19815,19817],{"class":140,"line":182},[138,19794,2607],{"class":1073},[138,19796,19797],{"class":2521}," backEl",[138,19799,1146],{"class":1112},[138,19801,19771],{"class":805},[138,19803,363],{"class":809},[138,19805,3102],{"class":805},[138,19807,1094],{"class":809},[138,19809,19810],{"class":805},"cardVersoRef",[138,19812,1094],{"class":809},[138,19814,19785],{"class":805},[138,19816,2944],{"class":794},[138,19818,19790],{"class":1556},[138,19820,19821,19823,19826,19828,19830,19832,19834,19837,19839,19841,19843,19845,19848,19850],{"class":140,"line":188},[138,19822,2607],{"class":1073},[138,19824,19825],{"class":2521}," fileName",[138,19827,1146],{"class":1112},[138,19829,1084],{"class":815},[138,19831,5049],{"class":805},[138,19833,363],{"class":809},[138,19835,19836],{"class":805},"form",[138,19838,363],{"class":809},[138,19840,19337],{"class":805},[138,19842,2646],{"class":1112},[138,19844,957],{"class":826},[138,19846,19847],{"class":830},"cartao",[138,19849,834],{"class":826},[138,19851,872],{"class":815},[138,19853,19854,19857,19860],{"class":140,"line":194},[138,19855,19856],{"class":809},"    .",[138,19858,19859],{"class":801},"toLowerCase",[138,19861,2796],{"class":815},[138,19863,19864,19866,19869,19871,19873,19876,19878,19880,19883,19885,19887,19890,19892],{"class":140,"line":199},[138,19865,19856],{"class":809},[138,19867,19868],{"class":801},"replace",[138,19870,806],{"class":815},[138,19872,1393],{"class":826},[138,19874,19875],{"class":1563},"\\s",[138,19877,6530],{"class":1112},[138,19879,1393],{"class":826},[138,19881,19882],{"class":3904},"g",[138,19884,954],{"class":809},[138,19886,957],{"class":826},[138,19888,19889],{"class":830},"-",[138,19891,834],{"class":826},[138,19893,872],{"class":815},[138,19895,19896,19899,19901,19903,19906,19908,19910,19912,19914],{"class":140,"line":204},[138,19897,19898],{"class":794},"  await",[138,19900,19554],{"class":801},[138,19902,806],{"class":815},[138,19904,19905],{"class":805},"frontEl",[138,19907,954],{"class":809},[138,19909,19797],{"class":805},[138,19911,954],{"class":809},[138,19913,19825],{"class":805},[138,19915,872],{"class":815},[138,19917,19918],{"class":140,"line":209},[138,19919,2082],{"class":809},[12,19921,19922],{},"A página tem ~40 linhas de lógica. Sem estado de formulário, sem composição de props, sem persistência. Só orquestração.",[1643,19924,19926],{"id":19925},"como-o-card-é-construído","Como o card é construído",[12,19928,19929,19931,19932,19934],{},[135,19930,19048],{}," recebe props e converte tudo em estilos inline via ",[135,19933,7111],{},". O card tem dimensões fixas de 520x296px, que correspondem ao tamanho real de um cartão de visita padrão para gráficas (88.9x50.8mm em 150dpi):",[129,19936,19938],{"className":785,"code":19937,"language":787,"meta":75,"style":75},"const cardStyle = computed(() => ({\n  width: '520px',\n  height: '296px',\n  padding: '28px 34px 26px',\n  background: backgroundColor.value,\n  fontFamily: \"'DM Sans', sans-serif\",\n  position: 'relative',\n  overflow: 'hidden',\n  display: 'flex',\n  flexDirection: 'column',\n  justifyContent: 'space-between',\n  boxSizing: 'border-box'\n}))\n",[135,19939,19940,19961,19977,19993,20009,20025,20041,20057,20073,20089,20105,20121,20135],{"__ignoreMap":75},[138,19941,19942,19944,19947,19949,19951,19953,19955,19957,19959],{"class":140,"line":141},[138,19943,2518],{"class":1073},[138,19945,19946],{"class":2521}," cardStyle",[138,19948,1146],{"class":1112},[138,19950,3088],{"class":801},[138,19952,806],{"class":805},[138,19954,3093],{"class":809},[138,19956,1074],{"class":1073},[138,19958,1084],{"class":805},[138,19960,810],{"class":809},[138,19962,19963,19966,19968,19970,19973,19975],{"class":140,"line":76},[138,19964,19965],{"class":815},"  width",[138,19967,782],{"class":809},[138,19969,957],{"class":826},[138,19971,19972],{"class":830},"520px",[138,19974,834],{"class":826},[138,19976,837],{"class":809},[138,19978,19979,19982,19984,19986,19989,19991],{"class":140,"line":152},[138,19980,19981],{"class":815},"  height",[138,19983,782],{"class":809},[138,19985,957],{"class":826},[138,19987,19988],{"class":830},"296px",[138,19990,834],{"class":826},[138,19992,837],{"class":809},[138,19994,19995,19998,20000,20002,20005,20007],{"class":140,"line":158},[138,19996,19997],{"class":815},"  padding",[138,19999,782],{"class":809},[138,20001,957],{"class":826},[138,20003,20004],{"class":830},"28px 34px 26px",[138,20006,834],{"class":826},[138,20008,837],{"class":809},[138,20010,20011,20014,20016,20019,20021,20023],{"class":140,"line":164},[138,20012,20013],{"class":815},"  background",[138,20015,782],{"class":809},[138,20017,20018],{"class":805}," backgroundColor",[138,20020,363],{"class":809},[138,20022,3102],{"class":805},[138,20024,837],{"class":809},[138,20026,20027,20030,20032,20034,20037,20039],{"class":140,"line":170},[138,20028,20029],{"class":815},"  fontFamily",[138,20031,782],{"class":809},[138,20033,1567],{"class":826},[138,20035,20036],{"class":830},"'DM Sans', sans-serif",[138,20038,1433],{"class":826},[138,20040,837],{"class":809},[138,20042,20043,20046,20048,20050,20053,20055],{"class":140,"line":176},[138,20044,20045],{"class":815},"  position",[138,20047,782],{"class":809},[138,20049,957],{"class":826},[138,20051,20052],{"class":830},"relative",[138,20054,834],{"class":826},[138,20056,837],{"class":809},[138,20058,20059,20062,20064,20066,20069,20071],{"class":140,"line":182},[138,20060,20061],{"class":815},"  overflow",[138,20063,782],{"class":809},[138,20065,957],{"class":826},[138,20067,20068],{"class":830},"hidden",[138,20070,834],{"class":826},[138,20072,837],{"class":809},[138,20074,20075,20078,20080,20082,20085,20087],{"class":140,"line":188},[138,20076,20077],{"class":815},"  display",[138,20079,782],{"class":809},[138,20081,957],{"class":826},[138,20083,20084],{"class":830},"flex",[138,20086,834],{"class":826},[138,20088,837],{"class":809},[138,20090,20091,20094,20096,20098,20101,20103],{"class":140,"line":194},[138,20092,20093],{"class":815},"  flexDirection",[138,20095,782],{"class":809},[138,20097,957],{"class":826},[138,20099,20100],{"class":830},"column",[138,20102,834],{"class":826},[138,20104,837],{"class":809},[138,20106,20107,20110,20112,20114,20117,20119],{"class":140,"line":199},[138,20108,20109],{"class":815},"  justifyContent",[138,20111,782],{"class":809},[138,20113,957],{"class":826},[138,20115,20116],{"class":830},"space-between",[138,20118,834],{"class":826},[138,20120,837],{"class":809},[138,20122,20123,20126,20128,20130,20133],{"class":140,"line":204},[138,20124,20125],{"class":815},"  boxSizing",[138,20127,782],{"class":809},[138,20129,957],{"class":826},[138,20131,20132],{"class":830},"border-box",[138,20134,2361],{"class":826},[138,20136,20137,20139],{"class":140,"line":209},[138,20138,869],{"class":809},[138,20140,4205],{"class":805},[12,20142,20143],{},"Todos os outros elementos, cores, fontes, espaçamentos, posicionamento, seguem o mesmo padrão: computeds derivados das props.",[12,20145,20146,20149],{},[19,20147,20148],{},"Por que 100% inline e não Tailwind?"," Porque o card serve três contextos diferentes ao mesmo tempo:",[402,20151,20152,20166,20172],{},[405,20153,20154,20157,20158,20161,20162,20165],{},[19,20155,20156],{},"Preview:"," o ",[135,20159,20160],{},"Preview.vue"," escala o card com ",[135,20163,20164],{},"transform: scale()"," para caber na tela sem alterar seus 520x296px reais.",[405,20167,20168,20171],{},[19,20169,20170],{},"Landing page:"," o mesmo componente aparece no exemplo interativo com efeito 3D de perspectiva no mouse, sem adaptação.",[405,20173,20174,20157,20177,20179],{},[19,20175,20176],{},"Exportação:",[135,20178,18976],{}," captura o DOM diretamente. Se qualquer estilo viesse de classe Tailwind ou variável CSS externa, a captura poderia falhar silenciosamente ou produzir resultado diferente do preview.",[12,20181,20182],{},"O inline style garante que o que está no DOM é exatamente o que vai para o PDF.",[1643,20184,20186],{"id":20185},"alinhamento-e-padrões-geométricos","Alinhamento e padrões geométricos",[12,20188,20189,20190,370,20193,370,20196,377,20199,20202,20203,20206,20207,20210],{},"O card suporta 4 opções de alinhamento: ",[135,20191,20192],{},"custom",[135,20194,20195],{},"left",[135,20197,20198],{},"center",[135,20200,20201],{},"right",". Em vez de condicionais espalhadas pelo template, toda a configuração visual de cada opção vive num objeto estático chamado ",[135,20204,20205],{},"ALIGN_MAP",". Cada entrada define o comportamento do cabeçalho, alinhamento do texto, posição dos contatos e se o logo aparece antes ou depois do nome. O template usa apenas o resultado do computed que consulta esse objeto, sem nenhum ",[135,20208,20209],{},"v-if"," de layout. Adicionar um novo alinhamento é só adicionar uma entrada no objeto.",[12,20212,20213],{},"Os padrões geométricos são SVGs gerados por funções que recebem a cor de destaque como parâmetro, garantindo que o padrão sempre combine com as cores escolhidas. A intensidade é controlada por um slider que ajusta a opacidade do SVG no DOM.",[27,20215],{},[30,20217,20219,20220,20222],{"id":20218},"o-problema-com-html2canvas-e-oklch","O problema com ",[135,20221,18968],{}," e oklch()",[12,20224,20225,20226,20228,20229,20231],{},"O desafio técnico mais relevante do projeto: o Tailwind v4 gera cores em ",[135,20227,18972],{},", e o ",[135,20230,18968],{}," simplesmente não suporta esse espaço de cor. O resultado era um card completamente branco no PDF.",[12,20233,20234,20235,1197,20237,782],{},"A solução foi trocar para ",[135,20236,18976],{},[135,20238,20239],{},"skipFonts: true",[129,20241,20243],{"className":785,"code":20242,"language":787,"meta":75,"style":75},"const [pngFront, pngBack] = await Promise.all([\n  toPng(frontEl, { pixelRatio: 3, skipFonts: true }),\n  toPng(backEl, { pixelRatio: 3, skipFonts: true })\n])\n",[135,20244,20245,20276,20310,20339],{"__ignoreMap":75},[138,20246,20247,20249,20251,20254,20256,20259,20261,20263,20265,20268,20270,20273],{"class":140,"line":141},[138,20248,2518],{"class":1073},[138,20250,944],{"class":809},[138,20252,20253],{"class":2521},"pngFront",[138,20255,954],{"class":809},[138,20257,20258],{"class":2521}," pngBack",[138,20260,965],{"class":809},[138,20262,1146],{"class":1112},[138,20264,2828],{"class":794},[138,20266,20267],{"class":3061}," Promise",[138,20269,363],{"class":809},[138,20271,20272],{"class":801},"all",[138,20274,20275],{"class":805},"([\n",[138,20277,20278,20281,20284,20286,20288,20291,20293,20295,20297,20300,20302,20304,20306,20308],{"class":140,"line":76},[138,20279,20280],{"class":801},"  toPng",[138,20282,20283],{"class":805},"(frontEl",[138,20285,954],{"class":809},[138,20287,2812],{"class":809},[138,20289,20290],{"class":815}," pixelRatio",[138,20292,782],{"class":809},[138,20294,13122],{"class":5295},[138,20296,954],{"class":809},[138,20298,20299],{"class":815}," skipFonts",[138,20301,782],{"class":809},[138,20303,5917],{"class":3862},[138,20305,2823],{"class":809},[138,20307,1049],{"class":805},[138,20309,837],{"class":809},[138,20311,20312,20314,20317,20319,20321,20323,20325,20327,20329,20331,20333,20335,20337],{"class":140,"line":152},[138,20313,20280],{"class":801},[138,20315,20316],{"class":805},"(backEl",[138,20318,954],{"class":809},[138,20320,2812],{"class":809},[138,20322,20290],{"class":815},[138,20324,782],{"class":809},[138,20326,13122],{"class":5295},[138,20328,954],{"class":809},[138,20330,20299],{"class":815},[138,20332,782],{"class":809},[138,20334,5917],{"class":3862},[138,20336,2823],{"class":809},[138,20338,872],{"class":805},[138,20340,20341],{"class":140,"line":158},[138,20342,5801],{"class":805},[12,20344,1193,20345,20347,20348,20350],{},[135,20346,20239],{}," é necessário porque o ",[135,20349,18976],{}," tenta serializar as fontes externas (Google Fonts) e falha silenciosamente em alguns navegadores. Com a flag, ele ignora a tentativa e usa o que já está renderizado no browser, o resultado visual é idêntico.",[27,20352],{},[30,20354,20356],{"id":20355},"geração-do-pdf","Geração do PDF",[12,20358,20359],{},"O PDF final tem duas páginas em formato 88.9x50.8mm (tamanho padrão para cartões de visita em gráficas brasileiras):",[129,20361,20363],{"className":785,"code":20362,"language":787,"meta":75,"style":75},"const pdf = new jsPDF({\n  orientation: 'landscape',\n  unit: 'mm',\n  format: [88.9, 50.8]\n})\n\npdf.addImage(pngFront, 'PNG', 0, 0, 88.9, 50.8)\npdf.addPage()\npdf.addImage(pngBack, 'PNG', 0, 0, 88.9, 50.8)\n",[135,20364,20365,20383,20399,20415,20434,20440,20444,20485,20496],{"__ignoreMap":75},[138,20366,20367,20369,20372,20374,20376,20379,20381],{"class":140,"line":141},[138,20368,2518],{"class":1073},[138,20370,20371],{"class":2521}," pdf",[138,20373,1146],{"class":1112},[138,20375,8662],{"class":1112},[138,20377,20378],{"class":801}," jsPDF",[138,20380,806],{"class":805},[138,20382,810],{"class":809},[138,20384,20385,20388,20390,20392,20395,20397],{"class":140,"line":76},[138,20386,20387],{"class":815},"  orientation",[138,20389,782],{"class":809},[138,20391,957],{"class":826},[138,20393,20394],{"class":830},"landscape",[138,20396,834],{"class":826},[138,20398,837],{"class":809},[138,20400,20401,20404,20406,20408,20411,20413],{"class":140,"line":152},[138,20402,20403],{"class":815},"  unit",[138,20405,782],{"class":809},[138,20407,957],{"class":826},[138,20409,20410],{"class":830},"mm",[138,20412,834],{"class":826},[138,20414,837],{"class":809},[138,20416,20417,20420,20422,20424,20427,20429,20432],{"class":140,"line":158},[138,20418,20419],{"class":815},"  format",[138,20421,782],{"class":809},[138,20423,944],{"class":805},[138,20425,20426],{"class":5295},"88.9",[138,20428,954],{"class":809},[138,20430,20431],{"class":5295}," 50.8",[138,20433,8056],{"class":805},[138,20435,20436,20438],{"class":140,"line":164},[138,20437,869],{"class":809},[138,20439,872],{"class":805},[138,20441,20442],{"class":140,"line":170},[138,20443,649],{"emptyLinePlaceholder":86},[138,20445,20446,20449,20451,20454,20457,20459,20461,20464,20466,20468,20470,20472,20474,20476,20479,20481,20483],{"class":140,"line":176},[138,20447,20448],{"class":805},"pdf",[138,20450,363],{"class":809},[138,20452,20453],{"class":801},"addImage",[138,20455,20456],{"class":805},"(pngFront",[138,20458,954],{"class":809},[138,20460,957],{"class":826},[138,20462,20463],{"class":830},"PNG",[138,20465,834],{"class":826},[138,20467,954],{"class":809},[138,20469,5296],{"class":5295},[138,20471,954],{"class":809},[138,20473,5296],{"class":5295},[138,20475,954],{"class":809},[138,20477,20478],{"class":5295}," 88.9",[138,20480,954],{"class":809},[138,20482,20431],{"class":5295},[138,20484,872],{"class":805},[138,20486,20487,20489,20491,20494],{"class":140,"line":182},[138,20488,20448],{"class":805},[138,20490,363],{"class":809},[138,20492,20493],{"class":801},"addPage",[138,20495,2796],{"class":805},[138,20497,20498,20500,20502,20504,20507,20509,20511,20513,20515,20517,20519,20521,20523,20525,20527,20529,20531],{"class":140,"line":188},[138,20499,20448],{"class":805},[138,20501,363],{"class":809},[138,20503,20453],{"class":801},[138,20505,20506],{"class":805},"(pngBack",[138,20508,954],{"class":809},[138,20510,957],{"class":826},[138,20512,20463],{"class":830},[138,20514,834],{"class":826},[138,20516,954],{"class":809},[138,20518,5296],{"class":5295},[138,20520,954],{"class":809},[138,20522,5296],{"class":5295},[138,20524,954],{"class":809},[138,20526,20478],{"class":5295},[138,20528,954],{"class":809},[138,20530,20431],{"class":5295},[138,20532,872],{"class":805},[12,20534,20535,20536,20539],{},"Um detalhe importante: antes da captura, os dois cards precisam estar visíveis no DOM mesmo que apenas um esteja sendo exibido ao usuário. A solução foi forçar ",[135,20537,20538],{},"display: flex\u002Fblock"," temporariamente, capturar, e restaurar o estado original:",[129,20541,20543],{"className":785,"code":20542,"language":787,"meta":75,"style":75},"const prevFront = frontEl.style.display\nfrontEl.style.display = 'flex'\n\u002F\u002F captura...\nfrontEl.style.display = prevFront\n",[135,20544,20545,20565,20586,20591],{"__ignoreMap":75},[138,20546,20547,20549,20552,20554,20556,20558,20560,20562],{"class":140,"line":141},[138,20548,2518],{"class":1073},[138,20550,20551],{"class":2521}," prevFront",[138,20553,1146],{"class":1112},[138,20555,19766],{"class":805},[138,20557,363],{"class":809},[138,20559,1744],{"class":805},[138,20561,363],{"class":809},[138,20563,20564],{"class":805},"display\n",[138,20566,20567,20569,20571,20573,20575,20578,20580,20582,20584],{"class":140,"line":76},[138,20568,19905],{"class":805},[138,20570,363],{"class":809},[138,20572,1744],{"class":805},[138,20574,363],{"class":809},[138,20576,20577],{"class":805},"display ",[138,20579,1430],{"class":1112},[138,20581,957],{"class":826},[138,20583,20084],{"class":830},[138,20585,2361],{"class":826},[138,20587,20588],{"class":140,"line":152},[138,20589,20590],{"class":911},"\u002F\u002F captura...\n",[138,20592,20593,20595,20597,20599,20601,20603,20605],{"class":140,"line":158},[138,20594,19905],{"class":805},[138,20596,363],{"class":809},[138,20598,1744],{"class":805},[138,20600,363],{"class":809},[138,20602,20577],{"class":805},[138,20604,1430],{"class":1112},[138,20606,20607],{"class":805}," prevFront\n",[27,20609],{},[30,20611,20613],{"id":20612},"ícones-de-contato-como-svg-inline","Ícones de contato como SVG inline",[12,20615,20616,20617,782],{},"Os ícones de telefone, e-mail, site e endereço no card são SVGs inline gerados por ",[135,20618,7111],{},[129,20620,20622],{"className":785,"code":20621,"language":787,"meta":75,"style":75},"const icon = (path: string) =>\n  computed(\n    () =>\n      `\u003Csvg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\"\n      stroke=\"${accentColor.value}\" stroke-width=\"2\">${path}\u003C\u002Fsvg>`\n  )\n\nconst phoneIcon = icon(`\u003Cpath d=\"M22 16.92v3...\"\u002F>`)\n",[135,20623,20624,20645,20651,20658,20666,20696,20700,20704],{"__ignoreMap":75},[138,20625,20626,20628,20631,20633,20635,20637,20639,20641,20643],{"class":140,"line":141},[138,20627,2518],{"class":1073},[138,20629,20630],{"class":12722}," icon",[138,20632,1146],{"class":1112},[138,20634,1084],{"class":809},[138,20636,1121],{"class":1045},[138,20638,782],{"class":1112},[138,20640,3808],{"class":3061},[138,20642,1049],{"class":809},[138,20644,7278],{"class":1073},[138,20646,20647,20649],{"class":140,"line":76},[138,20648,5720],{"class":801},[138,20650,19557],{"class":805},[138,20652,20653,20656],{"class":140,"line":152},[138,20654,20655],{"class":809},"    ()",[138,20657,7278],{"class":1073},[138,20659,20660,20663],{"class":140,"line":158},[138,20661,20662],{"class":826},"      `",[138,20664,20665],{"class":830},"\u003Csvg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\"\n",[138,20667,20668,20671,20673,20676,20678,20680,20682,20685,20687,20689,20691,20694],{"class":140,"line":164},[138,20669,20670],{"class":830},"      stroke=\"",[138,20672,3797],{"class":826},[138,20674,20675],{"class":805},"accentColor",[138,20677,363],{"class":826},[138,20679,3102],{"class":805},[138,20681,869],{"class":826},[138,20683,20684],{"class":830},"\" stroke-width=\"2\">",[138,20686,3797],{"class":826},[138,20688,1121],{"class":805},[138,20690,869],{"class":826},[138,20692,20693],{"class":830},"\u003C\u002Fsvg>",[138,20695,9631],{"class":826},[138,20697,20698],{"class":140,"line":170},[138,20699,2456],{"class":805},[138,20701,20702],{"class":140,"line":176},[138,20703,649],{"emptyLinePlaceholder":86},[138,20705,20706,20708,20711,20713,20715,20717,20719,20722,20724],{"class":140,"line":182},[138,20707,2518],{"class":1073},[138,20709,20710],{"class":2521}," phoneIcon",[138,20712,1146],{"class":1112},[138,20714,20630],{"class":801},[138,20716,806],{"class":805},[138,20718,3791],{"class":826},[138,20720,20721],{"class":830},"\u003Cpath d=\"M22 16.92v3...\"\u002F>",[138,20723,3791],{"class":826},[138,20725,872],{"class":805},[12,20727,20728,20729,20732,20733,20735,20736,20739,20740,20742,20743,363],{},"A cor do ícone (",[135,20730,20731],{},"stroke",") acompanha automaticamente a ",[135,20734,20675],{},". Por ser SVG inline via ",[135,20737,20738],{},"v-html",", é capturado perfeitamente pelo ",[135,20741,18976],{},", diferente de fontes de ícones externas como MDI, que seriam ignoradas com ",[135,20744,20239],{},[27,20746],{},[30,20748,20750],{"id":20749},"seo-e-og-image","SEO e OG Image",[12,20752,20753,20754,782],{},"Para o OG Image customizado usei o sistema de componentes do ",[135,20755,20756],{},"@nuxtjs\u002Fseo",[129,20758,20760],{"className":785,"code":20759,"language":787,"meta":75,"style":75},"defineOgImageComponent('OgImageVisitCardOg', {\n  title: 'Editor de Cartão',\n  description: 'Personalize cores, logo e padrões...'\n})\n",[135,20761,20762,20780,20795,20808],{"__ignoreMap":75},[138,20763,20764,20767,20769,20771,20774,20776,20778],{"class":140,"line":141},[138,20765,20766],{"class":801},"defineOgImageComponent",[138,20768,806],{"class":805},[138,20770,834],{"class":826},[138,20772,20773],{"class":830},"OgImageVisitCardOg",[138,20775,834],{"class":826},[138,20777,954],{"class":809},[138,20779,934],{"class":809},[138,20781,20782,20784,20786,20788,20791,20793],{"class":140,"line":76},[138,20783,3367],{"class":815},[138,20785,782],{"class":809},[138,20787,957],{"class":826},[138,20789,20790],{"class":830},"Editor de Cartão",[138,20792,834],{"class":826},[138,20794,837],{"class":809},[138,20796,20797,20799,20801,20803,20806],{"class":140,"line":152},[138,20798,3402],{"class":815},[138,20800,782],{"class":809},[138,20802,957],{"class":826},[138,20804,20805],{"class":830},"Personalize cores, logo e padrões...",[138,20807,2361],{"class":826},[138,20809,20810,20812],{"class":140,"line":158},[138,20811,869],{"class":809},[138,20813,872],{"class":805},[12,20815,20816,20817,20820],{},"Um aprendizado: o LinkedIn cacheia OG Images de forma agressiva. Para forçar a atualização após deploy, é preciso usar o ",[105,20818,16880],{"href":16888,"rel":20819},[109]," para invalidar o cache manualmente.",[27,20822],{},[30,20824,20826],{"id":20825},"layouts-no-nuxt","Layouts no Nuxt",[12,20828,20829,20830,782],{},"O projeto tem duas páginas com layouts completamente diferentes: landing page e editor. No Nuxt, isso é resolvido com ",[135,20831,1403],{},[129,20833,20835],{"className":785,"code":20834,"language":787,"meta":75,"style":75},"definePageMeta({ layout: 'editor-layout' })\ndefinePageMeta({ layout: 'land-page-layout' })\n",[135,20836,20837,20860],{"__ignoreMap":75},[138,20838,20839,20841,20843,20845,20847,20849,20851,20854,20856,20858],{"class":140,"line":141},[138,20840,1403],{"class":801},[138,20842,806],{"class":805},[138,20844,3284],{"class":809},[138,20846,3287],{"class":815},[138,20848,782],{"class":809},[138,20850,957],{"class":826},[138,20852,20853],{"class":830},"editor-layout",[138,20855,834],{"class":826},[138,20857,2823],{"class":809},[138,20859,872],{"class":805},[138,20861,20862,20864,20866,20868,20870,20872,20874,20877,20879,20881],{"class":140,"line":76},[138,20863,1403],{"class":801},[138,20865,806],{"class":805},[138,20867,3284],{"class":809},[138,20869,3287],{"class":815},[138,20871,782],{"class":809},[138,20873,957],{"class":826},[138,20875,20876],{"class":830},"land-page-layout",[138,20878,834],{"class":826},[138,20880,2823],{"class":809},[138,20882,872],{"class":805},[12,20884,20885,20886,20889],{},"É o equivalente ao ",[135,20887,20888],{},"meta: {}"," do Vue Router convencional, sem necessidade de middleware ou lógica adicional.",[27,20891],{},[30,20893,4866],{"id":4865},[12,20895,20896,20897,20900],{},"O VisitCardGenerator cresceu além do escopo original à medida que eu resolvia problemas reais: captura de DOM com cores ",[135,20898,20899],{},"oklch",", geração de PDF em tamanho exato para gráfica, alinhamento flexível do layout, responsividade do editor, gerenciamento de estado com Pinia.",[12,20902,20903],{},"O resultado é uma ferramenta que eu mesma usaria, e que qualquer pessoa pode usar, sem cadastro e sem pagar nada.",[12,20905,20906,20907,363],{},"O projeto está no ar em ",[105,20908,20911],{"href":20909,"rel":20910},"https:\u002F\u002Fvisitcard-larisantos.vercel.app\u002F",[109],"visitcard-larisantos.vercel.app",[12,20913,20914],{},"Se você tiver dúvidas sobre alguma das decisões técnicas ou quiser trocar ideia sobre o projeto, me chama!",[1744,20916,20917],{},"html pre.shiki code .s3Er8, html code.shiki .s3Er8{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sFsEu, html code.shiki .sFsEu{--shiki-light:#9C3EDA;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sVPC0, html code.shiki .sVPC0{--shiki-light:#90A4AE;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sFfmW, html code.shiki .sFfmW{--shiki-light:#39ADB5;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sK_r7, html code.shiki .sK_r7{--shiki-light:#6182B8;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sMo7A, html code.shiki .sMo7A{--shiki-light:#90A4AE;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sF_wb, html code.shiki .sF_wb{--shiki-light:#39ADB5;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .s0vBq, html code.shiki .s0vBq{--shiki-light:#91B859;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sG-J9, html code.shiki .sG-J9{--shiki-light:#39ADB5;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .soiBB, html code.shiki .soiBB{--shiki-light:#E2931D;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sdv8B, html code.shiki .sdv8B{--shiki-light:#E53935;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s3afY, html code.shiki .s3afY{--shiki-light:#E2931D;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .swu5b, html code.shiki .swu5b{--shiki-light:#39ADB5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s_k96, html code.shiki .s_k96{--shiki-light:#F76D47;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMrrN, html code.shiki .sMrrN{--shiki-light:#FF5370;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sk1zL, html code.shiki .sk1zL{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#FFAB70;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sSJ72, html code.shiki .sSJ72{--shiki-light:#91B859;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s1Wpa, html code.shiki .s1Wpa{--shiki-light:#F76D47;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sqHAQ, html code.shiki .sqHAQ{--shiki-light:#90A4AE;--shiki-default:#B392F0;--shiki-dark:#B392F0}",{"title":75,"searchDepth":76,"depth":76,"links":20919},[20920,20927,20937,20939,20940,20941,20942,20943],{"id":4798,"depth":76,"text":4799,"children":20921},[20922,20923,20924,20925,20926],{"id":18951,"depth":152,"text":1792},{"id":4975,"depth":152,"text":4819},{"id":18962,"depth":152,"text":18963},{"id":18980,"depth":152,"text":18981},{"id":1773,"depth":152,"text":4859},{"id":18994,"depth":76,"text":18995,"children":20928},[20929,20930,20932,20934,20935,20936],{"id":19062,"depth":152,"text":19063},{"id":19097,"depth":152,"text":20931},"A store useEditorStore",{"id":19503,"depth":152,"text":20933},"O composable useCardExport",{"id":19681,"depth":152,"text":19682},{"id":19925,"depth":152,"text":19926},{"id":20185,"depth":152,"text":20186},{"id":20218,"depth":76,"text":20938},"O problema com html2canvas e oklch()",{"id":20355,"depth":76,"text":20356},{"id":20612,"depth":76,"text":20613},{"id":20749,"depth":76,"text":20750},{"id":20825,"depth":76,"text":20826},{"id":4865,"depth":76,"text":4866},"2026-03-15T08:15:00-03:00","Como construí um gerador de cartões de visita profissionais em PDF com Nuxt 3, Vue 3, jsPDF e html-to-image — sem cadastro, sem custo, sem complicação. Detalhes das decisões técnicas, desafios e aprendizados.","\u002Fimages\u002Fblog\u002Fvisitcard-editor.png",{},"\u002Fblog\u002Fvisitcard-generator-nuxt-pdf-do-zero",{"title":18933,"description":20945},"blog\u002Fvisitcard-generator-nuxt-pdf-do-zero",[1769,20952,13337,1773,13338],"vue-js","FHdCd0bkj2gTU7iVV-tdcDDg9wjmA8Qlt0ussVocMnA",{"id":20955,"title":1729,"author":7,"body":20956,"date":22847,"description":22848,"extension":83,"image":22849,"meta":22850,"navigation":86,"path":22851,"seo":22852,"stem":22853,"tags":22854,"__hash__":22855},"blog\u002Fblog\u002Fprincipio-solid.md",{"type":9,"value":20957,"toc":22836},[20958,20961,20964,20967,20970,20972,20976,20983,21015,21018,21020,21024,21029,21032,21035,21127,21130,21133,21252,21263,21265,21269,21274,21277,21280,21436,21439,21696,21703,21705,21709,21714,21717,21735,21979,21993,21996,21998,22002,22007,22010,22013,22163,22166,22366,22369,22371,22375,22380,22383,22397,22500,22506,22760,22763,22765,22769,22772,22775,22792,22795,22797,22799,22802,22805,22808,22810,22833],[12,20959,20960],{},"Se você já abriu um arquivo de código antigo e sentiu aquela mistura de vergonha e confusão, você não está sozinha. Todo desenvolvedor tem esse momento. E muitas vezes, o que está por trás desse código difícil de entender não é falta de conhecimento técnico: é falta de estrutura.",[12,20962,20963],{},"É aí que entram os princípios SOLID.",[12,20965,20966],{},"SOLID é um conjunto de cinco diretrizes para escrever código orientado a objetos que seja mais fácil de manter, estender e entender. Não são regras gravadas em pedra, são princípios que, quando internalizados, mudam a forma como você pensa na hora de organizar seu código.",[12,20968,20969],{},"Neste artigo vamos passar por cada um dos cinco princípios com exemplos práticos em JavaScript. A ideia não é decorar as siglas, é entender o problema que cada uma resolve.",[27,20971],{},[30,20973,20975],{"id":20974},"de-onde-veio-o-solid","De onde veio o SOLID?",[12,20977,20978,20979,20982],{},"O acrônimo foi popularizado por ",[19,20980,20981],{},"Robert C. Martin",", o Uncle Bob, e reúne cinco princípios que já existiam de forma dispersa na comunidade de engenharia de software. Cada letra representa um princípio:",[402,20984,20985,20991,20997,21003,21009],{},[405,20986,20987,20990],{},[19,20988,20989],{},"S"," — Single Responsibility Principle",[405,20992,20993,20996],{},[19,20994,20995],{},"O"," — Open\u002FClosed Principle",[405,20998,20999,21002],{},[19,21000,21001],{},"L"," — Liskov Substitution Principle",[405,21004,21005,21008],{},[19,21006,21007],{},"I"," — Interface Segregation Principle",[405,21010,21011,21014],{},[19,21012,21013],{},"D"," — Dependency Inversion Principle",[12,21016,21017],{},"Pode parecer intimidador à primeira vista, mas você vai perceber que a maioria desses princípios faz sentido intuitivo quando você vê o problema que eles estão tentando resolver.",[27,21019],{},[30,21021,21023],{"id":21022},"s-single-responsibility-principle","S — Single Responsibility Principle",[12,21025,21026],{},[19,21027,21028],{},"Uma classe deve ter uma única razão para mudar.",[12,21030,21031],{},"Traduzindo para o dia a dia: cada pedaço de código deve ser responsável por uma coisa só. Quando você mistura responsabilidades em um lugar só, qualquer mudança em uma delas pode quebrar as outras. E pior: você nunca sabe exatamente onde mexer quando algo dá errado.",[12,21033,21034],{},"Imagine uma classe que cria um usuário no banco de dados e também envia o e-mail de boas-vindas:",[129,21036,21038],{"className":5084,"code":21037,"language":5086,"meta":75,"style":75},"\u002F\u002F ❌ Fazendo coisas demais no mesmo lugar\nclass Usuario {\n  criar(usuario) {\n    \u002F\u002F salva no banco\n    console.log('Usuário salvo no banco')\n\n    \u002F\u002F envia e-mail logo em seguida\n    console.log('E-mail de boas-vindas enviado')\n  }\n}\n",[135,21039,21040,21045,21054,21067,21072,21091,21095,21100,21119,21123],{"__ignoreMap":75},[138,21041,21042],{"class":140,"line":141},[138,21043,21044],{"class":911},"\u002F\u002F ❌ Fazendo coisas demais no mesmo lugar\n",[138,21046,21047,21049,21052],{"class":140,"line":76},[138,21048,13794],{"class":1073},[138,21050,21051],{"class":1556}," Usuario",[138,21053,934],{"class":809},[138,21055,21056,21059,21061,21063,21065],{"class":140,"line":152},[138,21057,21058],{"class":5196},"  criar",[138,21060,806],{"class":809},[138,21062,5362],{"class":1045},[138,21064,1049],{"class":809},[138,21066,934],{"class":809},[138,21068,21069],{"class":140,"line":158},[138,21070,21071],{"class":911},"    \u002F\u002F salva no banco\n",[138,21073,21074,21076,21078,21080,21082,21084,21087,21089],{"class":140,"line":164},[138,21075,9722],{"class":805},[138,21077,363],{"class":809},[138,21079,9727],{"class":801},[138,21081,806],{"class":815},[138,21083,834],{"class":826},[138,21085,21086],{"class":830},"Usuário salvo no banco",[138,21088,834],{"class":826},[138,21090,872],{"class":815},[138,21092,21093],{"class":140,"line":170},[138,21094,649],{"emptyLinePlaceholder":86},[138,21096,21097],{"class":140,"line":176},[138,21098,21099],{"class":911},"    \u002F\u002F envia e-mail logo em seguida\n",[138,21101,21102,21104,21106,21108,21110,21112,21115,21117],{"class":140,"line":182},[138,21103,9722],{"class":805},[138,21105,363],{"class":809},[138,21107,9727],{"class":801},[138,21109,806],{"class":815},[138,21111,834],{"class":826},[138,21113,21114],{"class":830},"E-mail de boas-vindas enviado",[138,21116,834],{"class":826},[138,21118,872],{"class":815},[138,21120,21121],{"class":140,"line":188},[138,21122,1184],{"class":809},[138,21124,21125],{"class":140,"line":194},[138,21126,2082],{"class":809},[12,21128,21129],{},"O problema aqui é que se a lógica de e-mail mudar (troca de provedor, novo template, adição de anexo), você vai precisar mexer na mesma classe que cuida de salvar usuários. São duas responsabilidades diferentes convivendo no mesmo lugar.",[12,21131,21132],{},"A solução é separar:",[129,21134,21136],{"className":5084,"code":21135,"language":5086,"meta":75,"style":75},"\u002F\u002F ✅ Cada classe com uma responsabilidade clara\nclass UsuarioService {\n  criar(usuario) {\n    \u002F\u002F essa classe só sabe sobre criação de usuário\n    console.log('Usuário salvo no banco')\n  }\n}\n\nclass EmailService {\n  enviarBoasVindas(usuario) {\n    \u002F\u002F essa classe só sabe sobre envio de e-mail\n    console.log('E-mail de boas-vindas enviado')\n  }\n}\n",[135,21137,21138,21143,21152,21164,21169,21187,21191,21195,21199,21208,21221,21226,21244,21248],{"__ignoreMap":75},[138,21139,21140],{"class":140,"line":141},[138,21141,21142],{"class":911},"\u002F\u002F ✅ Cada classe com uma responsabilidade clara\n",[138,21144,21145,21147,21150],{"class":140,"line":76},[138,21146,13794],{"class":1073},[138,21148,21149],{"class":1556}," UsuarioService",[138,21151,934],{"class":809},[138,21153,21154,21156,21158,21160,21162],{"class":140,"line":152},[138,21155,21058],{"class":5196},[138,21157,806],{"class":809},[138,21159,5362],{"class":1045},[138,21161,1049],{"class":809},[138,21163,934],{"class":809},[138,21165,21166],{"class":140,"line":158},[138,21167,21168],{"class":911},"    \u002F\u002F essa classe só sabe sobre criação de usuário\n",[138,21170,21171,21173,21175,21177,21179,21181,21183,21185],{"class":140,"line":164},[138,21172,9722],{"class":805},[138,21174,363],{"class":809},[138,21176,9727],{"class":801},[138,21178,806],{"class":815},[138,21180,834],{"class":826},[138,21182,21086],{"class":830},[138,21184,834],{"class":826},[138,21186,872],{"class":815},[138,21188,21189],{"class":140,"line":170},[138,21190,1184],{"class":809},[138,21192,21193],{"class":140,"line":176},[138,21194,2082],{"class":809},[138,21196,21197],{"class":140,"line":182},[138,21198,649],{"emptyLinePlaceholder":86},[138,21200,21201,21203,21206],{"class":140,"line":188},[138,21202,13794],{"class":1073},[138,21204,21205],{"class":1556}," EmailService",[138,21207,934],{"class":809},[138,21209,21210,21213,21215,21217,21219],{"class":140,"line":194},[138,21211,21212],{"class":5196},"  enviarBoasVindas",[138,21214,806],{"class":809},[138,21216,5362],{"class":1045},[138,21218,1049],{"class":809},[138,21220,934],{"class":809},[138,21222,21223],{"class":140,"line":199},[138,21224,21225],{"class":911},"    \u002F\u002F essa classe só sabe sobre envio de e-mail\n",[138,21227,21228,21230,21232,21234,21236,21238,21240,21242],{"class":140,"line":204},[138,21229,9722],{"class":805},[138,21231,363],{"class":809},[138,21233,9727],{"class":801},[138,21235,806],{"class":815},[138,21237,834],{"class":826},[138,21239,21114],{"class":830},[138,21241,834],{"class":826},[138,21243,872],{"class":815},[138,21245,21246],{"class":140,"line":209},[138,21247,1184],{"class":809},[138,21249,21250],{"class":140,"line":215},[138,21251,2082],{"class":809},[12,21253,21254,21255,21258,21259,21262],{},"Agora cada classe tem uma razão clara para existir e uma razão clara para mudar. Se o fluxo de e-mail mudar, você mexe só no ",[135,21256,21257],{},"EmailService",". Se a lógica de persistência mudar, você mexe só no ",[135,21260,21261],{},"UsuarioService",". Simples assim.",[27,21264],{},[30,21266,21268],{"id":21267},"o-openclosed-principle","O — Open\u002FClosed Principle",[12,21270,21271],{},[19,21272,21273],{},"O código deve ser aberto para extensão, mas fechado para modificação.",[12,21275,21276],{},"Esse é o princípio que mais causa confusão no começo, então vamos direto ao exemplo.",[12,21278,21279],{},"Imagine que você tem um sistema de pagamentos. Hoje aceita PIX e cartão. Amanhã vão pedir boleto. Depois, PayPal. Se a lógica de processar o pagamento vive em um único lugar com condicionais para cada método, toda nova forma de pagamento exige que você abra aquele arquivo e modifique código que já está funcionando. E mexer em código que já funciona é sempre um risco.",[129,21281,21283],{"className":5084,"code":21282,"language":5086,"meta":75,"style":75},"\u002F\u002F ❌ Toda vez que surgir um novo método de pagamento,\n\u002F\u002F você vai precisar modificar esta classe\nclass Checkout {\n  finalizar(valor, metodo) {\n    if (metodo === 'pix') {\n      console.log(`Pagamento via PIX: R$ ${valor}`)\n    } else if (metodo === 'cartao') {\n      console.log(`Pagamento via Cartão: R$ ${valor}`)\n    }\n    \u002F\u002F e quando vier o boleto? Mais um if aqui...\n  }\n}\n",[135,21284,21285,21290,21295,21304,21323,21346,21370,21396,21419,21423,21428,21432],{"__ignoreMap":75},[138,21286,21287],{"class":140,"line":141},[138,21288,21289],{"class":911},"\u002F\u002F ❌ Toda vez que surgir um novo método de pagamento,\n",[138,21291,21292],{"class":140,"line":76},[138,21293,21294],{"class":911},"\u002F\u002F você vai precisar modificar esta classe\n",[138,21296,21297,21299,21302],{"class":140,"line":152},[138,21298,13794],{"class":1073},[138,21300,21301],{"class":1556}," Checkout",[138,21303,934],{"class":809},[138,21305,21306,21309,21311,21314,21316,21319,21321],{"class":140,"line":158},[138,21307,21308],{"class":5196},"  finalizar",[138,21310,806],{"class":809},[138,21312,21313],{"class":1045},"valor",[138,21315,954],{"class":809},[138,21317,21318],{"class":1045}," metodo",[138,21320,1049],{"class":809},[138,21322,934],{"class":809},[138,21324,21325,21328,21330,21333,21335,21337,21340,21342,21344],{"class":140,"line":164},[138,21326,21327],{"class":794},"    if",[138,21329,1084],{"class":815},[138,21331,21332],{"class":805},"metodo",[138,21334,2765],{"class":1112},[138,21336,957],{"class":826},[138,21338,21339],{"class":830},"pix",[138,21341,834],{"class":826},[138,21343,1109],{"class":815},[138,21345,810],{"class":809},[138,21347,21348,21351,21353,21355,21357,21359,21362,21364,21366,21368],{"class":140,"line":170},[138,21349,21350],{"class":805},"      console",[138,21352,363],{"class":809},[138,21354,9727],{"class":801},[138,21356,806],{"class":815},[138,21358,3791],{"class":826},[138,21360,21361],{"class":830},"Pagamento via PIX: R$ ",[138,21363,3797],{"class":826},[138,21365,21313],{"class":805},[138,21367,3803],{"class":826},[138,21369,872],{"class":815},[138,21371,21372,21374,21377,21380,21382,21384,21386,21388,21390,21392,21394],{"class":140,"line":176},[138,21373,12106],{"class":809},[138,21375,21376],{"class":794}," else",[138,21378,21379],{"class":794}," if",[138,21381,1084],{"class":815},[138,21383,21332],{"class":805},[138,21385,2765],{"class":1112},[138,21387,957],{"class":826},[138,21389,19847],{"class":830},[138,21391,834],{"class":826},[138,21393,1109],{"class":815},[138,21395,810],{"class":809},[138,21397,21398,21400,21402,21404,21406,21408,21411,21413,21415,21417],{"class":140,"line":182},[138,21399,21350],{"class":805},[138,21401,363],{"class":809},[138,21403,9727],{"class":801},[138,21405,806],{"class":815},[138,21407,3791],{"class":826},[138,21409,21410],{"class":830},"Pagamento via Cartão: R$ ",[138,21412,3797],{"class":826},[138,21414,21313],{"class":805},[138,21416,3803],{"class":826},[138,21418,872],{"class":815},[138,21420,21421],{"class":140,"line":188},[138,21422,1179],{"class":809},[138,21424,21425],{"class":140,"line":194},[138,21426,21427],{"class":911},"    \u002F\u002F e quando vier o boleto? Mais um if aqui...\n",[138,21429,21430],{"class":140,"line":199},[138,21431,1184],{"class":809},[138,21433,21434],{"class":140,"line":204},[138,21435,2082],{"class":809},[12,21437,21438],{},"A solução é estruturar o código de forma que você possa adicionar novos comportamentos sem precisar tocar no que já existe:",[129,21440,21442],{"className":5084,"code":21441,"language":5086,"meta":75,"style":75},"\u002F\u002F ✅ Cada método de pagamento é uma classe independente\nclass PagamentoPix {\n  pagar(valor) {\n    return `Pagamento via PIX: R$ ${valor}`\n  }\n}\n\nclass PagamentoCartao {\n  pagar(valor) {\n    return `Pagamento via Cartão: R$ ${valor}`\n  }\n}\n\n\u002F\u002F Checkout não sabe qual método de pagamento está usando,\n\u002F\u002F só sabe que ele tem um método pagar()\nclass Checkout {\n  constructor(metodoPagamento) {\n    this.metodoPagamento = metodoPagamento\n  }\n\n  finalizar(valor) {\n    return this.metodoPagamento.pagar(valor)\n  }\n}\n\n\u002F\u002F Para adicionar boleto, basta criar uma nova classe\n\u002F\u002F sem mexer em nada que já existe\nclass PagamentoBoleto {\n  pagar(valor) {\n    return `Boleto gerado: R$ ${valor}`\n  }\n}\n",[135,21443,21444,21449,21458,21471,21486,21490,21494,21498,21507,21519,21533,21537,21541,21545,21550,21555,21563,21576,21589,21593,21597,21609,21630,21634,21638,21642,21647,21652,21661,21673,21688,21692],{"__ignoreMap":75},[138,21445,21446],{"class":140,"line":141},[138,21447,21448],{"class":911},"\u002F\u002F ✅ Cada método de pagamento é uma classe independente\n",[138,21450,21451,21453,21456],{"class":140,"line":76},[138,21452,13794],{"class":1073},[138,21454,21455],{"class":1556}," PagamentoPix",[138,21457,934],{"class":809},[138,21459,21460,21463,21465,21467,21469],{"class":140,"line":152},[138,21461,21462],{"class":5196},"  pagar",[138,21464,806],{"class":809},[138,21466,21313],{"class":1045},[138,21468,1049],{"class":809},[138,21470,934],{"class":809},[138,21472,21473,21475,21477,21479,21481,21483],{"class":140,"line":158},[138,21474,2900],{"class":794},[138,21476,9612],{"class":826},[138,21478,21361],{"class":830},[138,21480,3797],{"class":826},[138,21482,21313],{"class":805},[138,21484,21485],{"class":826},"}`\n",[138,21487,21488],{"class":140,"line":164},[138,21489,1184],{"class":809},[138,21491,21492],{"class":140,"line":170},[138,21493,2082],{"class":809},[138,21495,21496],{"class":140,"line":176},[138,21497,649],{"emptyLinePlaceholder":86},[138,21499,21500,21502,21505],{"class":140,"line":182},[138,21501,13794],{"class":1073},[138,21503,21504],{"class":1556}," PagamentoCartao",[138,21506,934],{"class":809},[138,21508,21509,21511,21513,21515,21517],{"class":140,"line":188},[138,21510,21462],{"class":5196},[138,21512,806],{"class":809},[138,21514,21313],{"class":1045},[138,21516,1049],{"class":809},[138,21518,934],{"class":809},[138,21520,21521,21523,21525,21527,21529,21531],{"class":140,"line":194},[138,21522,2900],{"class":794},[138,21524,9612],{"class":826},[138,21526,21410],{"class":830},[138,21528,3797],{"class":826},[138,21530,21313],{"class":805},[138,21532,21485],{"class":826},[138,21534,21535],{"class":140,"line":199},[138,21536,1184],{"class":809},[138,21538,21539],{"class":140,"line":204},[138,21540,2082],{"class":809},[138,21542,21543],{"class":140,"line":209},[138,21544,649],{"emptyLinePlaceholder":86},[138,21546,21547],{"class":140,"line":215},[138,21548,21549],{"class":911},"\u002F\u002F Checkout não sabe qual método de pagamento está usando,\n",[138,21551,21552],{"class":140,"line":221},[138,21553,21554],{"class":911},"\u002F\u002F só sabe que ele tem um método pagar()\n",[138,21556,21557,21559,21561],{"class":140,"line":227},[138,21558,13794],{"class":1073},[138,21560,21301],{"class":1556},[138,21562,934],{"class":809},[138,21564,21565,21567,21569,21572,21574],{"class":140,"line":233},[138,21566,13803],{"class":1073},[138,21568,806],{"class":809},[138,21570,21571],{"class":1045},"metodoPagamento",[138,21573,1049],{"class":809},[138,21575,934],{"class":809},[138,21577,21578,21580,21582,21584,21586],{"class":140,"line":239},[138,21579,13816],{"class":2041},[138,21581,363],{"class":809},[138,21583,21571],{"class":805},[138,21585,1146],{"class":1112},[138,21587,21588],{"class":805}," metodoPagamento\n",[138,21590,21591],{"class":140,"line":245},[138,21592,1184],{"class":809},[138,21594,21595],{"class":140,"line":251},[138,21596,649],{"emptyLinePlaceholder":86},[138,21598,21599,21601,21603,21605,21607],{"class":140,"line":257},[138,21600,21308],{"class":5196},[138,21602,806],{"class":809},[138,21604,21313],{"class":1045},[138,21606,1049],{"class":809},[138,21608,934],{"class":809},[138,21610,21611,21613,21615,21617,21619,21621,21624,21626,21628],{"class":140,"line":263},[138,21612,2900],{"class":794},[138,21614,6638],{"class":2041},[138,21616,363],{"class":809},[138,21618,21571],{"class":805},[138,21620,363],{"class":809},[138,21622,21623],{"class":801},"pagar",[138,21625,806],{"class":815},[138,21627,21313],{"class":805},[138,21629,872],{"class":815},[138,21631,21632],{"class":140,"line":269},[138,21633,1184],{"class":809},[138,21635,21636],{"class":140,"line":275},[138,21637,2082],{"class":809},[138,21639,21640],{"class":140,"line":281},[138,21641,649],{"emptyLinePlaceholder":86},[138,21643,21644],{"class":140,"line":287},[138,21645,21646],{"class":911},"\u002F\u002F Para adicionar boleto, basta criar uma nova classe\n",[138,21648,21649],{"class":140,"line":293},[138,21650,21651],{"class":911},"\u002F\u002F sem mexer em nada que já existe\n",[138,21653,21654,21656,21659],{"class":140,"line":299},[138,21655,13794],{"class":1073},[138,21657,21658],{"class":1556}," PagamentoBoleto",[138,21660,934],{"class":809},[138,21662,21663,21665,21667,21669,21671],{"class":140,"line":305},[138,21664,21462],{"class":5196},[138,21666,806],{"class":809},[138,21668,21313],{"class":1045},[138,21670,1049],{"class":809},[138,21672,934],{"class":809},[138,21674,21675,21677,21679,21682,21684,21686],{"class":140,"line":311},[138,21676,2900],{"class":794},[138,21678,9612],{"class":826},[138,21680,21681],{"class":830},"Boleto gerado: R$ ",[138,21683,3797],{"class":826},[138,21685,21313],{"class":805},[138,21687,21485],{"class":826},[138,21689,21690],{"class":140,"line":317},[138,21691,1184],{"class":809},[138,21693,21694],{"class":140,"line":323},[138,21695,2082],{"class":809},[12,21697,21698,21699,21702],{},"Perceba: para adicionar um novo método de pagamento, você cria uma nova classe e pronto. O ",[135,21700,21701],{},"Checkout"," não precisa nem saber que o boleto existe.",[27,21704],{},[30,21706,21708],{"id":21707},"l-liskov-substitution-principle","L — Liskov Substitution Principle",[12,21710,21711],{},[19,21712,21713],{},"Se B herda de A, você deve conseguir usar B em qualquer lugar que esperaria A, sem quebrar nada.",[12,21715,21716],{},"Esse princípio foi definido por Barbara Liskov nos anos 80, e o nome técnico pode assustar, mas a ideia é direta: se você tem uma herança, a classe filha precisa honrar o contrato da classe pai. Ela pode adicionar comportamentos, mas não pode quebrar os que já existiam.",[12,21718,21719,21720,21723,21724,21727,21728,21731,21732,21734],{},"Um exemplo clássico que viola esse princípio: você tem uma classe ",[135,21721,21722],{},"Passaro"," com um método ",[135,21725,21726],{},"mover()",", e resolve criar ",[135,21729,21730],{},"Pinguim extends Passaro",". O pinguim é um pássaro, tecnicamente. Mas se ",[135,21733,21726],{}," na classe pai implica \"voar\", o pinguim quebra essa expectativa.",[129,21736,21738],{"className":5084,"code":21737,"language":5086,"meta":75,"style":75},"\u002F\u002F ✅ Cada subclasse honra o contrato da classe pai\n\u002F\u002F sem surpresas para quem está usando\nclass Passaro {\n  mover() {\n    return 'O pássaro está se movendo'\n  }\n}\n\nclass Pardal extends Passaro {\n  mover() {\n    \u002F\u002F pardal voa: comportamento esperado de um pássaro\n    return 'O pardal está voando'\n  }\n}\n\nclass Pinguim extends Passaro {\n  mover() {\n    \u002F\u002F pinguim nada: diferente, mas ainda é \"mover\"\n    \u002F\u002F o contrato de mover() é honrado sem gerar surpresas\n    return 'O pinguim está nadando'\n  }\n}\n\n\u002F\u002F Esta função aceita qualquer Passaro\n\u002F\u002F e vai funcionar corretamente com qualquer subclasse\nfunction mostrarMovimento(passaro) {\n  console.log(passaro.mover())\n}\n\nmostrarMovimento(new Pardal()) \u002F\u002F \"O pardal está voando\"\nmostrarMovimento(new Pinguim()) \u002F\u002F \"O pinguim está nadando\"\n",[135,21739,21740,21745,21750,21759,21768,21779,21783,21787,21791,21804,21812,21817,21828,21832,21836,21840,21853,21861,21866,21871,21882,21886,21890,21894,21899,21904,21920,21939,21943,21947,21964],{"__ignoreMap":75},[138,21741,21742],{"class":140,"line":141},[138,21743,21744],{"class":911},"\u002F\u002F ✅ Cada subclasse honra o contrato da classe pai\n",[138,21746,21747],{"class":140,"line":76},[138,21748,21749],{"class":911},"\u002F\u002F sem surpresas para quem está usando\n",[138,21751,21752,21754,21757],{"class":140,"line":152},[138,21753,13794],{"class":1073},[138,21755,21756],{"class":1556}," Passaro",[138,21758,934],{"class":809},[138,21760,21761,21764,21766],{"class":140,"line":158},[138,21762,21763],{"class":5196},"  mover",[138,21765,3093],{"class":809},[138,21767,934],{"class":809},[138,21769,21770,21772,21774,21777],{"class":140,"line":164},[138,21771,2900],{"class":794},[138,21773,957],{"class":826},[138,21775,21776],{"class":830},"O pássaro está se movendo",[138,21778,2361],{"class":826},[138,21780,21781],{"class":140,"line":170},[138,21782,1184],{"class":809},[138,21784,21785],{"class":140,"line":176},[138,21786,2082],{"class":809},[138,21788,21789],{"class":140,"line":182},[138,21790,649],{"emptyLinePlaceholder":86},[138,21792,21793,21795,21798,21800,21802],{"class":140,"line":188},[138,21794,13794],{"class":1073},[138,21796,21797],{"class":1556}," Pardal",[138,21799,14008],{"class":1073},[138,21801,21756],{"class":1556},[138,21803,934],{"class":809},[138,21805,21806,21808,21810],{"class":140,"line":194},[138,21807,21763],{"class":5196},[138,21809,3093],{"class":809},[138,21811,934],{"class":809},[138,21813,21814],{"class":140,"line":199},[138,21815,21816],{"class":911},"    \u002F\u002F pardal voa: comportamento esperado de um pássaro\n",[138,21818,21819,21821,21823,21826],{"class":140,"line":204},[138,21820,2900],{"class":794},[138,21822,957],{"class":826},[138,21824,21825],{"class":830},"O pardal está voando",[138,21827,2361],{"class":826},[138,21829,21830],{"class":140,"line":209},[138,21831,1184],{"class":809},[138,21833,21834],{"class":140,"line":215},[138,21835,2082],{"class":809},[138,21837,21838],{"class":140,"line":221},[138,21839,649],{"emptyLinePlaceholder":86},[138,21841,21842,21844,21847,21849,21851],{"class":140,"line":227},[138,21843,13794],{"class":1073},[138,21845,21846],{"class":1556}," Pinguim",[138,21848,14008],{"class":1073},[138,21850,21756],{"class":1556},[138,21852,934],{"class":809},[138,21854,21855,21857,21859],{"class":140,"line":233},[138,21856,21763],{"class":5196},[138,21858,3093],{"class":809},[138,21860,934],{"class":809},[138,21862,21863],{"class":140,"line":239},[138,21864,21865],{"class":911},"    \u002F\u002F pinguim nada: diferente, mas ainda é \"mover\"\n",[138,21867,21868],{"class":140,"line":245},[138,21869,21870],{"class":911},"    \u002F\u002F o contrato de mover() é honrado sem gerar surpresas\n",[138,21872,21873,21875,21877,21880],{"class":140,"line":251},[138,21874,2900],{"class":794},[138,21876,957],{"class":826},[138,21878,21879],{"class":830},"O pinguim está nadando",[138,21881,2361],{"class":826},[138,21883,21884],{"class":140,"line":257},[138,21885,1184],{"class":809},[138,21887,21888],{"class":140,"line":263},[138,21889,2082],{"class":809},[138,21891,21892],{"class":140,"line":269},[138,21893,649],{"emptyLinePlaceholder":86},[138,21895,21896],{"class":140,"line":275},[138,21897,21898],{"class":911},"\u002F\u002F Esta função aceita qualquer Passaro\n",[138,21900,21901],{"class":140,"line":281},[138,21902,21903],{"class":911},"\u002F\u002F e vai funcionar corretamente com qualquer subclasse\n",[138,21905,21906,21908,21911,21913,21916,21918],{"class":140,"line":287},[138,21907,3824],{"class":1073},[138,21909,21910],{"class":801}," mostrarMovimento",[138,21912,806],{"class":809},[138,21914,21915],{"class":1045},"passaro",[138,21917,1049],{"class":809},[138,21919,934],{"class":809},[138,21921,21922,21924,21926,21928,21930,21932,21934,21937],{"class":140,"line":293},[138,21923,10238],{"class":805},[138,21925,363],{"class":809},[138,21927,9727],{"class":801},[138,21929,806],{"class":815},[138,21931,21915],{"class":805},[138,21933,363],{"class":809},[138,21935,21936],{"class":801},"mover",[138,21938,8394],{"class":815},[138,21940,21941],{"class":140,"line":299},[138,21942,2082],{"class":809},[138,21944,21945],{"class":140,"line":305},[138,21946,649],{"emptyLinePlaceholder":86},[138,21948,21949,21952,21954,21956,21958,21961],{"class":140,"line":311},[138,21950,21951],{"class":801},"mostrarMovimento",[138,21953,806],{"class":805},[138,21955,13767],{"class":1112},[138,21957,21797],{"class":801},[138,21959,21960],{"class":805},"()) ",[138,21962,21963],{"class":911},"\u002F\u002F \"O pardal está voando\"\n",[138,21965,21966,21968,21970,21972,21974,21976],{"class":140,"line":317},[138,21967,21951],{"class":801},[138,21969,806],{"class":805},[138,21971,13767],{"class":1112},[138,21973,21846],{"class":801},[138,21975,21960],{"class":805},[138,21977,21978],{"class":911},"\u002F\u002F \"O pinguim está nadando\"\n",[12,21980,21981,21982,21984,21985,21988,21989,21992],{},"O ponto central aqui é: qualquer código que funciona com um ",[135,21983,21722],{}," deve continuar funcionando quando você passar um ",[135,21986,21987],{},"Pardal"," ou um ",[135,21990,21991],{},"Pinguim"," no lugar. Se alguma subclasse quebra esse comportamento, o princípio está sendo violado.",[12,21994,21995],{},"Na prática, esse princípio é especialmente importante quando você está criando hierarquias de componentes ou serviços que vão ser substituídos ou injetados em outras partes do sistema.",[27,21997],{},[30,21999,22001],{"id":22000},"i-interface-segregation-principle","I — Interface Segregation Principle",[12,22003,22004],{},[19,22005,22006],{},"Ninguém deve ser obrigado a depender de métodos que não usa.",[12,22008,22009],{},"Em JavaScript puro não temos interfaces como em TypeScript ou Java, mas o conceito se aplica igualmente. A ideia é: não force uma classe a implementar comportamentos que não fazem sentido para ela.",[12,22011,22012],{},"Pense em uma impressora. Uma impressora simples imprime. Uma multifuncional imprime e digitaliza. Se você criar um \"contrato\" que exige os dois comportamentos, a impressora simples vai ser obrigada a ter um método de digitalização que não faz nada, só existe para satisfazer uma regra que não é dela.",[129,22014,22016],{"className":5084,"code":22015,"language":5086,"meta":75,"style":75},"\u002F\u002F ❌ Um objeto \"gordo\" que força quem não precisa\n\u002F\u002F a carregar comportamentos que não usa\nconst impressoraMultifuncional = {\n  imprimir(doc) {\n    console.log('Imprimindo:', doc)\n  },\n  digitalizar() {\n    console.log('Digitalizando')\n  },\n  enviarFax() {\n    console.log('Enviando fax')\n  }\n}\n\n\u002F\u002F A impressora simples não digitaliza e não manda fax,\n\u002F\u002F mas está sendo forçada a ter esses métodos\n",[135,22017,22018,22023,22028,22039,22053,22077,22081,22090,22109,22113,22122,22141,22145,22149,22153,22158],{"__ignoreMap":75},[138,22019,22020],{"class":140,"line":141},[138,22021,22022],{"class":911},"\u002F\u002F ❌ Um objeto \"gordo\" que força quem não precisa\n",[138,22024,22025],{"class":140,"line":76},[138,22026,22027],{"class":911},"\u002F\u002F a carregar comportamentos que não usa\n",[138,22029,22030,22032,22035,22037],{"class":140,"line":152},[138,22031,2518],{"class":1073},[138,22033,22034],{"class":2521}," impressoraMultifuncional",[138,22036,1146],{"class":1112},[138,22038,934],{"class":809},[138,22040,22041,22044,22046,22049,22051],{"class":140,"line":158},[138,22042,22043],{"class":5196},"  imprimir",[138,22045,806],{"class":809},[138,22047,22048],{"class":1045},"doc",[138,22050,1049],{"class":809},[138,22052,934],{"class":809},[138,22054,22055,22057,22059,22061,22063,22065,22068,22070,22072,22075],{"class":140,"line":164},[138,22056,9722],{"class":805},[138,22058,363],{"class":809},[138,22060,9727],{"class":801},[138,22062,806],{"class":815},[138,22064,834],{"class":826},[138,22066,22067],{"class":830},"Imprimindo:",[138,22069,834],{"class":826},[138,22071,954],{"class":809},[138,22073,22074],{"class":805}," doc",[138,22076,872],{"class":815},[138,22078,22079],{"class":140,"line":170},[138,22080,972],{"class":809},[138,22082,22083,22086,22088],{"class":140,"line":176},[138,22084,22085],{"class":5196},"  digitalizar",[138,22087,3093],{"class":809},[138,22089,934],{"class":809},[138,22091,22092,22094,22096,22098,22100,22102,22105,22107],{"class":140,"line":182},[138,22093,9722],{"class":805},[138,22095,363],{"class":809},[138,22097,9727],{"class":801},[138,22099,806],{"class":815},[138,22101,834],{"class":826},[138,22103,22104],{"class":830},"Digitalizando",[138,22106,834],{"class":826},[138,22108,872],{"class":815},[138,22110,22111],{"class":140,"line":188},[138,22112,972],{"class":809},[138,22114,22115,22118,22120],{"class":140,"line":194},[138,22116,22117],{"class":5196},"  enviarFax",[138,22119,3093],{"class":809},[138,22121,934],{"class":809},[138,22123,22124,22126,22128,22130,22132,22134,22137,22139],{"class":140,"line":199},[138,22125,9722],{"class":805},[138,22127,363],{"class":809},[138,22129,9727],{"class":801},[138,22131,806],{"class":815},[138,22133,834],{"class":826},[138,22135,22136],{"class":830},"Enviando fax",[138,22138,834],{"class":826},[138,22140,872],{"class":815},[138,22142,22143],{"class":140,"line":204},[138,22144,1184],{"class":809},[138,22146,22147],{"class":140,"line":209},[138,22148,2082],{"class":809},[138,22150,22151],{"class":140,"line":215},[138,22152,649],{"emptyLinePlaceholder":86},[138,22154,22155],{"class":140,"line":221},[138,22156,22157],{"class":911},"\u002F\u002F A impressora simples não digitaliza e não manda fax,\n",[138,22159,22160],{"class":140,"line":227},[138,22161,22162],{"class":911},"\u002F\u002F mas está sendo forçada a ter esses métodos\n",[12,22164,22165],{},"A solução é separar as capacidades em partes menores e combinar só o que faz sentido para cada caso:",[129,22167,22169],{"className":5084,"code":22168,"language":5086,"meta":75,"style":75},"\u002F\u002F ✅ Capacidades separadas, combinadas conforme necessário\nconst podeImprimir = {\n  imprimir(doc) {\n    console.log('Imprimindo:', doc)\n  }\n}\n\nconst podeDigitalizar = {\n  digitalizar() {\n    console.log('Digitalizando documento')\n  }\n}\n\n\u002F\u002F Impressora simples: só imprime\nclass ImpressoraSimples {}\nObject.assign(ImpressoraSimples.prototype, podeImprimir)\n\n\u002F\u002F Multifuncional: imprime e digitaliza\nclass ImpressoraMultifuncional {}\nObject.assign(ImpressoraMultifuncional.prototype, podeImprimir, podeDigitalizar)\n",[135,22170,22171,22176,22187,22199,22221,22225,22229,22233,22244,22252,22271,22275,22279,22283,22288,22298,22322,22326,22331,22340],{"__ignoreMap":75},[138,22172,22173],{"class":140,"line":141},[138,22174,22175],{"class":911},"\u002F\u002F ✅ Capacidades separadas, combinadas conforme necessário\n",[138,22177,22178,22180,22183,22185],{"class":140,"line":76},[138,22179,2518],{"class":1073},[138,22181,22182],{"class":2521}," podeImprimir",[138,22184,1146],{"class":1112},[138,22186,934],{"class":809},[138,22188,22189,22191,22193,22195,22197],{"class":140,"line":152},[138,22190,22043],{"class":5196},[138,22192,806],{"class":809},[138,22194,22048],{"class":1045},[138,22196,1049],{"class":809},[138,22198,934],{"class":809},[138,22200,22201,22203,22205,22207,22209,22211,22213,22215,22217,22219],{"class":140,"line":158},[138,22202,9722],{"class":805},[138,22204,363],{"class":809},[138,22206,9727],{"class":801},[138,22208,806],{"class":815},[138,22210,834],{"class":826},[138,22212,22067],{"class":830},[138,22214,834],{"class":826},[138,22216,954],{"class":809},[138,22218,22074],{"class":805},[138,22220,872],{"class":815},[138,22222,22223],{"class":140,"line":164},[138,22224,1184],{"class":809},[138,22226,22227],{"class":140,"line":170},[138,22228,2082],{"class":809},[138,22230,22231],{"class":140,"line":176},[138,22232,649],{"emptyLinePlaceholder":86},[138,22234,22235,22237,22240,22242],{"class":140,"line":182},[138,22236,2518],{"class":1073},[138,22238,22239],{"class":2521}," podeDigitalizar",[138,22241,1146],{"class":1112},[138,22243,934],{"class":809},[138,22245,22246,22248,22250],{"class":140,"line":188},[138,22247,22085],{"class":5196},[138,22249,3093],{"class":809},[138,22251,934],{"class":809},[138,22253,22254,22256,22258,22260,22262,22264,22267,22269],{"class":140,"line":194},[138,22255,9722],{"class":805},[138,22257,363],{"class":809},[138,22259,9727],{"class":801},[138,22261,806],{"class":815},[138,22263,834],{"class":826},[138,22265,22266],{"class":830},"Digitalizando documento",[138,22268,834],{"class":826},[138,22270,872],{"class":815},[138,22272,22273],{"class":140,"line":199},[138,22274,1184],{"class":809},[138,22276,22277],{"class":140,"line":204},[138,22278,2082],{"class":809},[138,22280,22281],{"class":140,"line":209},[138,22282,649],{"emptyLinePlaceholder":86},[138,22284,22285],{"class":140,"line":215},[138,22286,22287],{"class":911},"\u002F\u002F Impressora simples: só imprime\n",[138,22289,22290,22292,22295],{"class":140,"line":221},[138,22291,13794],{"class":1073},[138,22293,22294],{"class":1556}," ImpressoraSimples",[138,22296,22297],{"class":809}," {}\n",[138,22299,22300,22303,22305,22308,22310,22313,22315,22317,22319],{"class":140,"line":227},[138,22301,22302],{"class":805},"Object",[138,22304,363],{"class":809},[138,22306,22307],{"class":801},"assign",[138,22309,806],{"class":805},[138,22311,22312],{"class":3061},"ImpressoraSimples",[138,22314,363],{"class":809},[138,22316,13564],{"class":2521},[138,22318,954],{"class":809},[138,22320,22321],{"class":805}," podeImprimir)\n",[138,22323,22324],{"class":140,"line":233},[138,22325,649],{"emptyLinePlaceholder":86},[138,22327,22328],{"class":140,"line":239},[138,22329,22330],{"class":911},"\u002F\u002F Multifuncional: imprime e digitaliza\n",[138,22332,22333,22335,22338],{"class":140,"line":245},[138,22334,13794],{"class":1073},[138,22336,22337],{"class":1556}," ImpressoraMultifuncional",[138,22339,22297],{"class":809},[138,22341,22342,22344,22346,22348,22350,22353,22355,22357,22359,22361,22363],{"class":140,"line":251},[138,22343,22302],{"class":805},[138,22345,363],{"class":809},[138,22347,22307],{"class":801},[138,22349,806],{"class":805},[138,22351,22352],{"class":3061},"ImpressoraMultifuncional",[138,22354,363],{"class":809},[138,22356,13564],{"class":2521},[138,22358,954],{"class":809},[138,22360,22182],{"class":805},[138,22362,954],{"class":809},[138,22364,22365],{"class":805}," podeDigitalizar)\n",[12,22367,22368],{},"Cada classe recebe exatamente o que precisa, sem carregar peso extra. Em TypeScript, isso fica ainda mais explícito com interfaces separadas, mas o raciocínio é o mesmo.",[27,22370],{},[30,22372,22374],{"id":22373},"d-dependency-inversion-principle","D — Dependency Inversion Principle",[12,22376,22377],{},[19,22378,22379],{},"Dependa de abstrações, não de implementações concretas.",[12,22381,22382],{},"Esse é provavelmente o princípio com maior impacto prático no dia a dia de desenvolvimento frontend, especialmente quando você começa a lidar com injeção de dependência, testes e troca de implementações.",[12,22384,22385,22386,22389,22390,22393,22394,22396],{},"O problema acontece quando uma parte do seu código está fortemente acoplada a outra. Se o ",[135,22387,22388],{},"ServicoDeEmail"," chama diretamente o ",[135,22391,22392],{},"SendGrid",", qualquer dia que você quiser trocar de provedor, vai precisar abrir o ",[135,22395,22388],{}," e modificar código que deveria estar funcionando bem.",[129,22398,22400],{"className":5084,"code":22399,"language":5086,"meta":75,"style":75},"\u002F\u002F ❌ Acoplamento direto com uma implementação específica\nclass ServicoDeEmail {\n  constructor() {\n    \u002F\u002F ServicoDeEmail agora depende diretamente do Sendgrid\n    \u002F\u002F Quer trocar de provedor? Vai precisar mexer aqui\n    this.provedor = new Sendgrid()\n  }\n\n  enviar(mensagem) {\n    this.provedor.send(mensagem)\n  }\n}\n",[135,22401,22402,22407,22416,22424,22429,22434,22452,22456,22460,22473,22492,22496],{"__ignoreMap":75},[138,22403,22404],{"class":140,"line":141},[138,22405,22406],{"class":911},"\u002F\u002F ❌ Acoplamento direto com uma implementação específica\n",[138,22408,22409,22411,22414],{"class":140,"line":76},[138,22410,13794],{"class":1073},[138,22412,22413],{"class":1556}," ServicoDeEmail",[138,22415,934],{"class":809},[138,22417,22418,22420,22422],{"class":140,"line":152},[138,22419,13803],{"class":1073},[138,22421,3093],{"class":809},[138,22423,934],{"class":809},[138,22425,22426],{"class":140,"line":158},[138,22427,22428],{"class":911},"    \u002F\u002F ServicoDeEmail agora depende diretamente do Sendgrid\n",[138,22430,22431],{"class":140,"line":164},[138,22432,22433],{"class":911},"    \u002F\u002F Quer trocar de provedor? Vai precisar mexer aqui\n",[138,22435,22436,22438,22440,22443,22445,22447,22450],{"class":140,"line":170},[138,22437,13816],{"class":2041},[138,22439,363],{"class":809},[138,22441,22442],{"class":805},"provedor",[138,22444,1146],{"class":1112},[138,22446,8662],{"class":1112},[138,22448,22449],{"class":801}," Sendgrid",[138,22451,2796],{"class":815},[138,22453,22454],{"class":140,"line":176},[138,22455,1184],{"class":809},[138,22457,22458],{"class":140,"line":182},[138,22459,649],{"emptyLinePlaceholder":86},[138,22461,22462,22465,22467,22469,22471],{"class":140,"line":188},[138,22463,22464],{"class":5196},"  enviar",[138,22466,806],{"class":809},[138,22468,12914],{"class":1045},[138,22470,1049],{"class":809},[138,22472,934],{"class":809},[138,22474,22475,22477,22479,22481,22483,22486,22488,22490],{"class":140,"line":194},[138,22476,13816],{"class":2041},[138,22478,363],{"class":809},[138,22480,22442],{"class":805},[138,22482,363],{"class":809},[138,22484,22485],{"class":801},"send",[138,22487,806],{"class":815},[138,22489,12914],{"class":805},[138,22491,872],{"class":815},[138,22493,22494],{"class":140,"line":199},[138,22495,1184],{"class":809},[138,22497,22498],{"class":140,"line":204},[138,22499,2082],{"class":809},[12,22501,22502,22503,22505],{},"A solução é inverter essa dependência: em vez de o ",[135,22504,22388],{}," criar o provedor, o provedor é passado de fora. O serviço apenas espera receber algo que sabe enviar e-mail, sem se importar com qual implementação é essa:",[129,22507,22509],{"className":5084,"code":22508,"language":5086,"meta":75,"style":75},"\u002F\u002F ✅ O serviço depende de uma abstração,\n\u002F\u002F não sabe (nem precisa saber) qual provedor está usando\nclass SendgridGateway {\n  enviar(mensagem) {\n    return `Enviado via SendGrid: ${mensagem}`\n  }\n}\n\nclass MailgunGateway {\n  enviar(mensagem) {\n    return `Enviado via Mailgun: ${mensagem}`\n  }\n}\n\nclass ServicoDeEmail {\n  constructor(provedor) {\n    \u002F\u002F recebe o provedor de fora, não cria ele mesmo\n    this.provedor = provedor\n  }\n\n  enviar(mensagem) {\n    return this.provedor.enviar(mensagem)\n  }\n}\n\n\u002F\u002F Para trocar de provedor, só muda o que você passa aqui\nconst servico = new ServicoDeEmail(new SendgridGateway())\n\u002F\u002F ou:\nconst servicoAlternativo = new ServicoDeEmail(new MailgunGateway())\n",[135,22510,22511,22516,22521,22530,22542,22557,22561,22565,22569,22578,22590,22605,22609,22613,22617,22625,22637,22642,22655,22659,22663,22675,22696,22700,22704,22708,22713,22734,22739],{"__ignoreMap":75},[138,22512,22513],{"class":140,"line":141},[138,22514,22515],{"class":911},"\u002F\u002F ✅ O serviço depende de uma abstração,\n",[138,22517,22518],{"class":140,"line":76},[138,22519,22520],{"class":911},"\u002F\u002F não sabe (nem precisa saber) qual provedor está usando\n",[138,22522,22523,22525,22528],{"class":140,"line":152},[138,22524,13794],{"class":1073},[138,22526,22527],{"class":1556}," SendgridGateway",[138,22529,934],{"class":809},[138,22531,22532,22534,22536,22538,22540],{"class":140,"line":158},[138,22533,22464],{"class":5196},[138,22535,806],{"class":809},[138,22537,12914],{"class":1045},[138,22539,1049],{"class":809},[138,22541,934],{"class":809},[138,22543,22544,22546,22548,22551,22553,22555],{"class":140,"line":164},[138,22545,2900],{"class":794},[138,22547,9612],{"class":826},[138,22549,22550],{"class":830},"Enviado via SendGrid: ",[138,22552,3797],{"class":826},[138,22554,12914],{"class":805},[138,22556,21485],{"class":826},[138,22558,22559],{"class":140,"line":170},[138,22560,1184],{"class":809},[138,22562,22563],{"class":140,"line":176},[138,22564,2082],{"class":809},[138,22566,22567],{"class":140,"line":182},[138,22568,649],{"emptyLinePlaceholder":86},[138,22570,22571,22573,22576],{"class":140,"line":188},[138,22572,13794],{"class":1073},[138,22574,22575],{"class":1556}," MailgunGateway",[138,22577,934],{"class":809},[138,22579,22580,22582,22584,22586,22588],{"class":140,"line":194},[138,22581,22464],{"class":5196},[138,22583,806],{"class":809},[138,22585,12914],{"class":1045},[138,22587,1049],{"class":809},[138,22589,934],{"class":809},[138,22591,22592,22594,22596,22599,22601,22603],{"class":140,"line":199},[138,22593,2900],{"class":794},[138,22595,9612],{"class":826},[138,22597,22598],{"class":830},"Enviado via Mailgun: ",[138,22600,3797],{"class":826},[138,22602,12914],{"class":805},[138,22604,21485],{"class":826},[138,22606,22607],{"class":140,"line":204},[138,22608,1184],{"class":809},[138,22610,22611],{"class":140,"line":209},[138,22612,2082],{"class":809},[138,22614,22615],{"class":140,"line":215},[138,22616,649],{"emptyLinePlaceholder":86},[138,22618,22619,22621,22623],{"class":140,"line":221},[138,22620,13794],{"class":1073},[138,22622,22413],{"class":1556},[138,22624,934],{"class":809},[138,22626,22627,22629,22631,22633,22635],{"class":140,"line":227},[138,22628,13803],{"class":1073},[138,22630,806],{"class":809},[138,22632,22442],{"class":1045},[138,22634,1049],{"class":809},[138,22636,934],{"class":809},[138,22638,22639],{"class":140,"line":233},[138,22640,22641],{"class":911},"    \u002F\u002F recebe o provedor de fora, não cria ele mesmo\n",[138,22643,22644,22646,22648,22650,22652],{"class":140,"line":239},[138,22645,13816],{"class":2041},[138,22647,363],{"class":809},[138,22649,22442],{"class":805},[138,22651,1146],{"class":1112},[138,22653,22654],{"class":805}," provedor\n",[138,22656,22657],{"class":140,"line":245},[138,22658,1184],{"class":809},[138,22660,22661],{"class":140,"line":251},[138,22662,649],{"emptyLinePlaceholder":86},[138,22664,22665,22667,22669,22671,22673],{"class":140,"line":257},[138,22666,22464],{"class":5196},[138,22668,806],{"class":809},[138,22670,12914],{"class":1045},[138,22672,1049],{"class":809},[138,22674,934],{"class":809},[138,22676,22677,22679,22681,22683,22685,22687,22690,22692,22694],{"class":140,"line":263},[138,22678,2900],{"class":794},[138,22680,6638],{"class":2041},[138,22682,363],{"class":809},[138,22684,22442],{"class":805},[138,22686,363],{"class":809},[138,22688,22689],{"class":801},"enviar",[138,22691,806],{"class":815},[138,22693,12914],{"class":805},[138,22695,872],{"class":815},[138,22697,22698],{"class":140,"line":269},[138,22699,1184],{"class":809},[138,22701,22702],{"class":140,"line":275},[138,22703,2082],{"class":809},[138,22705,22706],{"class":140,"line":281},[138,22707,649],{"emptyLinePlaceholder":86},[138,22709,22710],{"class":140,"line":287},[138,22711,22712],{"class":911},"\u002F\u002F Para trocar de provedor, só muda o que você passa aqui\n",[138,22714,22715,22717,22720,22722,22724,22726,22728,22730,22732],{"class":140,"line":293},[138,22716,2518],{"class":1073},[138,22718,22719],{"class":2521}," servico",[138,22721,1146],{"class":1112},[138,22723,8662],{"class":1112},[138,22725,22413],{"class":801},[138,22727,806],{"class":805},[138,22729,13767],{"class":1112},[138,22731,22527],{"class":801},[138,22733,8394],{"class":805},[138,22735,22736],{"class":140,"line":299},[138,22737,22738],{"class":911},"\u002F\u002F ou:\n",[138,22740,22741,22743,22746,22748,22750,22752,22754,22756,22758],{"class":140,"line":305},[138,22742,2518],{"class":1073},[138,22744,22745],{"class":2521}," servicoAlternativo",[138,22747,1146],{"class":1112},[138,22749,8662],{"class":1112},[138,22751,22413],{"class":801},[138,22753,806],{"class":805},[138,22755,13767],{"class":1112},[138,22757,22575],{"class":801},[138,22759,8394],{"class":805},[12,22761,22762],{},"Além de facilitar a troca de implementação, esse padrão torna os testes muito mais fáceis: você pode passar um provedor falso nos testes sem precisar configurar nada externo.",[27,22764],{},[30,22766,22768],{"id":22767},"solid-na-prática-o-que-muda-no-seu-código","SOLID na prática: o que muda no seu código",[12,22770,22771],{},"Ler sobre os princípios é uma coisa. A mudança real acontece quando você começa a reconhecer as violações no código que já existe, e nos que você está escrevendo agora.",[12,22773,22774],{},"Algumas perguntas que ajudam a identificar onde os princípios estão sendo ignorados:",[402,22776,22777,22780,22783,22786,22789],{},[405,22778,22779],{},"Quando uma classe tem métodos que parecem não ter nada a ver entre si, provavelmente está violando o S.",[405,22781,22782],{},"Quando você precisa abrir vários arquivos para adicionar um novo comportamento, o O pode estar sendo ignorado.",[405,22784,22785],{},"Quando uma subclasse precisa \"anular\" métodos do pai para funcionar, o L está em risco.",[405,22787,22788],{},"Quando você vê uma classe com métodos vazios que existem \"só para satisfazer a estrutura\", o I está sendo violado.",[405,22790,22791],{},"E quando um módulo cria diretamente os objetos de que depende em vez de recebê-los, o D está sendo ignorado.",[12,22793,22794],{},"SOLID não é uma checklist que você marca antes de dar merge. É uma forma de pensar na organização do código que, com o tempo, passa a ser natural.",[27,22796],{},[30,22798,4866],{"id":4865},[12,22800,22801],{},"Os princípios SOLID não existem para complicar o código ou aumentar o número de arquivos. Eles existem para tornar o código mais honesto: cada parte faz o que diz que faz, depende só do que realmente precisa e pode ser estendida sem medo de quebrar o que já funciona.",[12,22803,22804],{},"Você não precisa aplicar tudo de uma vez. Comece pelo S: identifique uma classe no seu projeto que está fazendo coisas demais e separe as responsabilidades. Quando isso virar natural, os outros princípios começam a fazer mais sentido na sequência.",[12,22806,22807],{},"O melhor código não é o que usa mais padrões, é o que você consegue abrir seis meses depois e entender sem precisar de coragem.",[30,22809,9153],{"id":9152},[402,22811,22812,22819,22826],{},[405,22813,22814],{},[105,22815,22818],{"href":22816,"rel":22817},"https:\u002F\u002Fwww.alura.com.br\u002Fartigos\u002Fsolid",[109],"Alura: O que é SOLID",[405,22820,22821],{},[105,22822,22825],{"href":22823,"rel":22824},"https:\u002F\u002Fwww.digitalocean.com\u002Fcommunity\u002Fconceptual-articles\u002Fs-o-l-i-d-the-first-five-principles-of-object-oriented-design",[109],"SOLID Design Principles Explained",[405,22827,22828],{},[105,22829,22832],{"href":22830,"rel":22831},"https:\u002F\u002Fmedium.com\u002Fdesenvolvendo-com-paixao\u002Fo-que-%C3%A9-solid-o-guia-completo-para-voc%C3%AA-entender-os-5-princ%C3%ADpios-da-poo-2b937b3fc530",[109],"O que é SOLID: O guia completo",[1744,22834,22835],{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sFsEu, html code.shiki .sFsEu{--shiki-light:#9C3EDA;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .soiBB, html code.shiki .soiBB{--shiki-light:#E2931D;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sG-J9, html code.shiki .sG-J9{--shiki-light:#39ADB5;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s0u7J, html code.shiki .s0u7J{--shiki-light:#E53935;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sk1zL, html code.shiki .sk1zL{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#FFAB70;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sMo7A, html code.shiki .sMo7A{--shiki-light:#90A4AE;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sK_r7, html code.shiki .sK_r7{--shiki-light:#6182B8;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sdv8B, html code.shiki .sdv8B{--shiki-light:#E53935;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sF_wb, html code.shiki .sF_wb{--shiki-light:#39ADB5;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .s0vBq, html code.shiki .s0vBq{--shiki-light:#91B859;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s3Er8, html code.shiki .s3Er8{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sFfmW, html code.shiki .sFfmW{--shiki-light:#39ADB5;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .swu5b, html code.shiki .swu5b{--shiki-light:#39ADB5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sVPC0, html code.shiki .sVPC0{--shiki-light:#90A4AE;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s3afY, html code.shiki .s3afY{--shiki-light:#E2931D;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}",{"title":75,"searchDepth":76,"depth":76,"links":22837},[22838,22839,22840,22841,22842,22843,22844,22845,22846],{"id":20974,"depth":76,"text":20975},{"id":21022,"depth":76,"text":21023},{"id":21267,"depth":76,"text":21268},{"id":21707,"depth":76,"text":21708},{"id":22000,"depth":76,"text":22001},{"id":22373,"depth":76,"text":22374},{"id":22767,"depth":76,"text":22768},{"id":4865,"depth":76,"text":4866},{"id":9152,"depth":76,"text":9153},"2026-03-12T19:23:00-03:00","Entenda o que é SOLID e como aplicar os cinco princípios para escrever códigos mais organizados, flexíveis e fáceis de manter.","\u002Fimages\u002Fblog\u002Fsolid.png",{},"\u002Fblog\u002Fprincipio-solid",{"title":1729,"description":22848},"blog\u002Fprincipio-solid",[4792,9241,16441,1770,15490],"DIrvJop551rbX3THY9_LChDF-XuZyUZwGHBaR3QNiR4",{"id":22857,"title":22858,"author":7,"body":22859,"date":26469,"description":26470,"extension":83,"image":26471,"meta":26472,"navigation":86,"path":26473,"seo":26474,"stem":26475,"tags":26476,"__hash__":26478},"blog\u002Fblog\u002Fvirtual-dom-mini-framework-do-zero.md","Virtual DOM por baixo dos panos: construindo um mini-framework do zero",{"type":9,"value":22860,"toc":26450},[22861,22867,22870,22874,22877,22880,22883,22889,22903,22906,22910,22913,22985,22992,23003,23054,23057,23112,23115,23119,23126,23195,23202,23395,23403,23406,23623,23629,23637,23640,24117,24131,24135,24142,24210,24213,24322,24331,24335,24338,24349,24352,24584,24595,24601,24611,24621,25167,25182,25186,25192,25199,25344,25347,25351,25362,25850,25861,26337,26343,26347,26350,26356,26362,26380,26401,26410,26413,26415,26418,26421,26433,26436,26438,26447],[12,22862,22863,22864,22866],{},"Se você trabalha com Vue ou React, já usa Virtual DOM todo dia. Mas provavelmente nunca parou pra pensar no que acontece de verdade quando você muda um ",[135,22865,5010],{}," e a tela atualiza.",[12,22868,22869],{},"Neste artigo vamos desmistificar o Virtual DOM construindo um mini-framework do zero, com reatividade, ciclo de vida e componentes. Sem dependências, só JavaScript puro.",[30,22871,22873],{"id":22872},"por-que-o-dom-real-é-lento","Por que o DOM real é lento?",[12,22875,22876],{},"Antes de entender o Virtual DOM, precisamos entender o problema que ele resolve.",[12,22878,22879],{},"O DOM não é JavaScript. Ele é implementado em C++ no browser, e toda vez que você o toca, há uma travessia de fronteira entre o engine JS (V8, SpiderMonkey) e o engine de renderização (Blink, WebKit). Essa travessia tem custo.",[12,22881,22882],{},"Além disso, certas operações disparam processos caros em cascata:",[129,22884,22887],{"className":22885,"code":22886,"language":13552},[13550],"Mudança no DOM\n  → Style recalculation  (quais regras CSS se aplicam agora?)\n  → Layout \u002F Reflow      (onde cada elemento fica na tela?)\n  → Paint                (como cada pixel fica?)\n  → Composite            (juntar as layers)\n",[135,22888,22886],{"__ignoreMap":75},[12,22890,22891,22892,1207,22895,22898,22899,22902],{},"Ler propriedades como ",[135,22893,22894],{},"offsetHeight",[135,22896,22897],{},"getBoundingClientRect"," força o browser a executar todo esse processo de forma síncrona. Isso se chama ",[19,22900,22901],{},"forced reflow",", e é um dos maiores vilões de performance em aplicações web.",[12,22904,22905],{},"O problema não é criar elementos. É atualizar desnecessariamente: pintar a parede inteira quando só um cantinho mudou.",[30,22907,22909],{"id":22908},"o-que-é-o-virtual-dom","O que é o Virtual DOM?",[12,22911,22912],{},"Como o nome já diz, se trata de uma representação virtual (em memória) do DOM real, um objeto JavaScript que representa um elemento da UI.",[129,22914,22916],{"className":5084,"code":22915,"language":5086,"meta":75,"style":75},"const vnode = {\n  tag: 'p',\n  props: { style: 'color: red' },\n  children: 'Contador: 1'\n}\n",[135,22917,22918,22929,22944,22967,22981],{"__ignoreMap":75},[138,22919,22920,22922,22925,22927],{"class":140,"line":141},[138,22921,2518],{"class":1073},[138,22923,22924],{"class":2521}," vnode",[138,22926,1146],{"class":1112},[138,22928,934],{"class":809},[138,22930,22931,22934,22936,22938,22940,22942],{"class":140,"line":76},[138,22932,22933],{"class":815},"  tag",[138,22935,782],{"class":809},[138,22937,957],{"class":826},[138,22939,12],{"class":830},[138,22941,834],{"class":826},[138,22943,837],{"class":809},[138,22945,22946,22949,22951,22953,22955,22957,22959,22962,22964],{"class":140,"line":152},[138,22947,22948],{"class":815},"  props",[138,22950,782],{"class":809},[138,22952,2812],{"class":809},[138,22954,4513],{"class":815},[138,22956,782],{"class":809},[138,22958,957],{"class":826},[138,22960,22961],{"class":830},"color: red",[138,22963,834],{"class":826},[138,22965,22966],{"class":809}," },\n",[138,22968,22969,22972,22974,22976,22979],{"class":140,"line":158},[138,22970,22971],{"class":815},"  children",[138,22973,782],{"class":809},[138,22975,957],{"class":826},[138,22977,22978],{"class":830},"Contador: 1",[138,22980,2361],{"class":826},[138,22982,22983],{"class":140,"line":164},[138,22984,2082],{"class":809},[12,22986,22987,22988,22991],{},"Isso é um VNode. Um objeto plain que descreve como um ",[135,22989,22990],{},"\u003Cp>"," deveria parecer.",[12,22993,22994,22995,22998,22999,23002],{},"O nome \"VNode\" foi popularizado pelos frameworks, mas você poderia chamar de qualquer coisa. O Vue usa ",[135,22996,22997],{},"h()"," para criá-los (a letra vem de ",[4986,23000,23001],{},"hyperscript",", uma convenção antiga da comunidade). Quando você escreve um template Vue como este:",[129,23004,23008],{"className":23005,"code":23006,"language":23007,"meta":75,"style":75},"language-html shiki shiki-themes material-theme-lighter github-dark github-dark","\u003Cdiv class=\"box\">\n  \u003Cp>{{ msg }}\u003C\u002Fp>\n\u003C\u002Fdiv>\n","html",[135,23009,23010,23029,23046],{"__ignoreMap":75},[138,23011,23012,23014,23016,23018,23020,23022,23025,23027],{"class":140,"line":141},[138,23013,1416],{"class":809},[138,23015,4369],{"class":1419},[138,23017,3582],{"class":1423},[138,23019,1430],{"class":809},[138,23021,1433],{"class":826},[138,23023,23024],{"class":830},"box",[138,23026,1433],{"class":826},[138,23028,1440],{"class":809},[138,23030,23031,23033,23035,23037,23040,23042,23044],{"class":140,"line":76},[138,23032,3567],{"class":809},[138,23034,12],{"class":1419},[138,23036,3065],{"class":809},[138,23038,23039],{"class":805},"{{ msg }}",[138,23041,1505],{"class":809},[138,23043,12],{"class":1419},[138,23045,1440],{"class":809},[138,23047,23048,23050,23052],{"class":140,"line":152},[138,23049,1505],{"class":809},[138,23051,4369],{"class":1419},[138,23053,1440],{"class":809},[12,23055,23056],{},"O compilador transforma isso em:",[129,23058,23060],{"className":5084,"code":23059,"language":5086,"meta":75,"style":75},"h('div', { class: 'box' }, [h('p', null, msg)])\n",[135,23061,23062],{"__ignoreMap":75},[138,23063,23064,23067,23069,23071,23073,23075,23077,23079,23081,23083,23085,23087,23089,23091,23093,23095,23097,23099,23101,23103,23105,23107,23109],{"class":140,"line":141},[138,23065,23066],{"class":801},"h",[138,23068,806],{"class":805},[138,23070,834],{"class":826},[138,23072,4369],{"class":830},[138,23074,834],{"class":826},[138,23076,954],{"class":809},[138,23078,2812],{"class":809},[138,23080,3582],{"class":815},[138,23082,782],{"class":809},[138,23084,957],{"class":826},[138,23086,23024],{"class":830},[138,23088,834],{"class":826},[138,23090,5524],{"class":809},[138,23092,944],{"class":805},[138,23094,23066],{"class":801},[138,23096,806],{"class":805},[138,23098,834],{"class":826},[138,23100,12],{"class":830},[138,23102,834],{"class":826},[138,23104,954],{"class":809},[138,23106,3062],{"class":2041},[138,23108,954],{"class":809},[138,23110,23111],{"class":805}," msg)])\n",[12,23113,23114],{},"Que por sua vez retorna exatamente aquele objeto simples. O framework inteiro gira em torno de criar, comparar e aplicar esses objetos no DOM real.",[30,23116,23118],{"id":23117},"as-três-peças-fundamentais","As três peças fundamentais",[1643,23120,23122,23123,23125],{"id":23121},"_1-h-criar-o-vnode","1. ",[135,23124,22997],{},": criar o VNode",[129,23127,23129],{"className":5084,"code":23128,"language":5086,"meta":75,"style":75},"function h(tag, props, children) {\n  return { tag, props: props || {}, children: children || [] }\n}\n",[135,23130,23131,23157,23191],{"__ignoreMap":75},[138,23132,23133,23135,23138,23140,23143,23145,23148,23150,23153,23155],{"class":140,"line":141},[138,23134,3824],{"class":1073},[138,23136,23137],{"class":801}," h",[138,23139,806],{"class":809},[138,23141,23142],{"class":1045},"tag",[138,23144,954],{"class":809},[138,23146,23147],{"class":1045}," props",[138,23149,954],{"class":809},[138,23151,23152],{"class":1045}," children",[138,23154,1049],{"class":809},[138,23156,934],{"class":809},[138,23158,23159,23161,23163,23166,23168,23170,23172,23174,23176,23179,23181,23183,23185,23187,23189],{"class":140,"line":76},[138,23160,3199],{"class":794},[138,23162,2812],{"class":809},[138,23164,23165],{"class":805}," tag",[138,23167,954],{"class":809},[138,23169,23147],{"class":815},[138,23171,782],{"class":809},[138,23173,23147],{"class":805},[138,23175,2646],{"class":1112},[138,23177,23178],{"class":809}," {},",[138,23180,23152],{"class":815},[138,23182,782],{"class":809},[138,23184,23152],{"class":805},[138,23186,2646],{"class":1112},[138,23188,5941],{"class":815},[138,23190,2082],{"class":809},[138,23192,23193],{"class":140,"line":152},[138,23194,2082],{"class":809},[12,23196,23197,23198,23201],{},"Parece inútil, só retorna o que recebe. Mas é uma conveniência: sem ela você repetiria ",[135,23199,23200],{},"{ tag, props, children }"," em cada lugar. Em árvores aninhadas a diferença é enorme:",[129,23203,23205],{"className":5084,"code":23204,"language":5086,"meta":75,"style":75},"\u002F\u002F sem h()\n{ tag: 'div', props: {}, children: [\n  { tag: 'p', props: {}, children: [\n    { tag: 'span', props: {}, children: 'texto' }\n  ]}\n]}\n\n\u002F\u002F com h()\nh('div', {}, [\n  h('p', {}, [\n    h('span', {}, 'texto')\n  ])\n])\n",[135,23206,23207,23212,23240,23268,23303,23309,23315,23319,23324,23342,23361,23386,23391],{"__ignoreMap":75},[138,23208,23209],{"class":140,"line":141},[138,23210,23211],{"class":911},"\u002F\u002F sem h()\n",[138,23213,23214,23216,23218,23220,23222,23224,23226,23228,23230,23232,23234,23236,23238],{"class":140,"line":76},[138,23215,3284],{"class":809},[138,23217,23165],{"class":1556},[138,23219,782],{"class":809},[138,23221,957],{"class":826},[138,23223,4369],{"class":830},[138,23225,834],{"class":826},[138,23227,954],{"class":809},[138,23229,23147],{"class":1556},[138,23231,782],{"class":809},[138,23233,23178],{"class":809},[138,23235,23152],{"class":1556},[138,23237,782],{"class":809},[138,23239,821],{"class":815},[138,23241,23242,23244,23246,23248,23250,23252,23254,23256,23258,23260,23262,23264,23266],{"class":140,"line":152},[138,23243,19371],{"class":809},[138,23245,23165],{"class":815},[138,23247,782],{"class":809},[138,23249,957],{"class":826},[138,23251,12],{"class":830},[138,23253,834],{"class":826},[138,23255,954],{"class":809},[138,23257,23147],{"class":815},[138,23259,782],{"class":809},[138,23261,23178],{"class":809},[138,23263,23152],{"class":815},[138,23265,782],{"class":809},[138,23267,821],{"class":815},[138,23269,23270,23272,23274,23276,23278,23280,23282,23284,23286,23288,23290,23292,23294,23296,23299,23301],{"class":140,"line":158},[138,23271,986],{"class":809},[138,23273,23165],{"class":815},[138,23275,782],{"class":809},[138,23277,957],{"class":826},[138,23279,138],{"class":830},[138,23281,834],{"class":826},[138,23283,954],{"class":809},[138,23285,23147],{"class":815},[138,23287,782],{"class":809},[138,23289,23178],{"class":809},[138,23291,23152],{"class":815},[138,23293,782],{"class":809},[138,23295,957],{"class":826},[138,23297,23298],{"class":830},"texto",[138,23300,834],{"class":826},[138,23302,1015],{"class":809},[138,23304,23305,23307],{"class":140,"line":164},[138,23306,1020],{"class":815},[138,23308,2082],{"class":809},[138,23310,23311,23313],{"class":140,"line":170},[138,23312,965],{"class":815},[138,23314,2082],{"class":809},[138,23316,23317],{"class":140,"line":176},[138,23318,649],{"emptyLinePlaceholder":86},[138,23320,23321],{"class":140,"line":182},[138,23322,23323],{"class":911},"\u002F\u002F com h()\n",[138,23325,23326,23328,23330,23332,23334,23336,23338,23340],{"class":140,"line":188},[138,23327,23066],{"class":801},[138,23329,806],{"class":805},[138,23331,834],{"class":826},[138,23333,4369],{"class":830},[138,23335,834],{"class":826},[138,23337,954],{"class":809},[138,23339,23178],{"class":809},[138,23341,821],{"class":805},[138,23343,23344,23347,23349,23351,23353,23355,23357,23359],{"class":140,"line":194},[138,23345,23346],{"class":801},"  h",[138,23348,806],{"class":805},[138,23350,834],{"class":826},[138,23352,12],{"class":830},[138,23354,834],{"class":826},[138,23356,954],{"class":809},[138,23358,23178],{"class":809},[138,23360,821],{"class":805},[138,23362,23363,23366,23368,23370,23372,23374,23376,23378,23380,23382,23384],{"class":140,"line":199},[138,23364,23365],{"class":801},"    h",[138,23367,806],{"class":805},[138,23369,834],{"class":826},[138,23371,138],{"class":830},[138,23373,834],{"class":826},[138,23375,954],{"class":809},[138,23377,23178],{"class":809},[138,23379,957],{"class":826},[138,23381,23298],{"class":830},[138,23383,834],{"class":826},[138,23385,872],{"class":805},[138,23387,23388],{"class":140,"line":204},[138,23389,23390],{"class":805},"  ])\n",[138,23392,23393],{"class":140,"line":209},[138,23394,5801],{"class":805},[1643,23396,23398,23399,23402],{"id":23397},"_2-mount-primeira-montagem","2. ",[135,23400,23401],{},"mount()",": primeira montagem",[12,23404,23405],{},"Pega um VNode e cria os elementos reais no DOM. Só é chamado uma vez, quando o componente aparece pela primeira vez na página.",[129,23407,23409],{"className":5084,"code":23408,"language":5086,"meta":75,"style":75},"function mount(vnode, container) {\n  const el = document.createElement(vnode.tag)\n  vnode._el = el \u002F\u002F guarda a referencia que liga o objeto ao elemento real\n\n  for (const [k, v] of Object.entries(vnode.props || {})) {\n    el.setAttribute(k, v)\n  }\n\n  for (const child of vnode.children || []) {\n    mount(child, el)\n  }\n  container.appendChild(el)\n}\n",[135,23410,23411,23432,23459,23476,23480,23527,23547,23551,23555,23582,23598,23602,23619],{"__ignoreMap":75},[138,23412,23413,23415,23418,23420,23423,23425,23428,23430],{"class":140,"line":141},[138,23414,3824],{"class":1073},[138,23416,23417],{"class":801}," mount",[138,23419,806],{"class":809},[138,23421,23422],{"class":1045},"vnode",[138,23424,954],{"class":809},[138,23426,23427],{"class":1045}," container",[138,23429,1049],{"class":809},[138,23431,934],{"class":809},[138,23433,23434,23436,23439,23441,23444,23446,23449,23451,23453,23455,23457],{"class":140,"line":76},[138,23435,2607],{"class":1073},[138,23437,23438],{"class":2521}," el",[138,23440,1146],{"class":1112},[138,23442,23443],{"class":805}," document",[138,23445,363],{"class":809},[138,23447,23448],{"class":801},"createElement",[138,23450,806],{"class":815},[138,23452,23422],{"class":805},[138,23454,363],{"class":809},[138,23456,23142],{"class":805},[138,23458,872],{"class":815},[138,23460,23461,23464,23466,23469,23471,23473],{"class":140,"line":152},[138,23462,23463],{"class":805},"  vnode",[138,23465,363],{"class":809},[138,23467,23468],{"class":805},"_el",[138,23470,1146],{"class":1112},[138,23472,23438],{"class":805},[138,23474,23475],{"class":911}," \u002F\u002F guarda a referencia que liga o objeto ao elemento real\n",[138,23477,23478],{"class":140,"line":158},[138,23479,649],{"emptyLinePlaceholder":86},[138,23481,23482,23485,23487,23489,23491,23494,23496,23499,23501,23504,23506,23508,23511,23513,23515,23517,23519,23521,23523,23525],{"class":140,"line":164},[138,23483,23484],{"class":794},"  for",[138,23486,1084],{"class":815},[138,23488,2518],{"class":1073},[138,23490,944],{"class":809},[138,23492,23493],{"class":2521},"k",[138,23495,954],{"class":809},[138,23497,23498],{"class":2521}," v",[138,23500,965],{"class":809},[138,23502,23503],{"class":1112}," of",[138,23505,13431],{"class":805},[138,23507,363],{"class":809},[138,23509,23510],{"class":801},"entries",[138,23512,806],{"class":815},[138,23514,23422],{"class":805},[138,23516,363],{"class":809},[138,23518,9319],{"class":805},[138,23520,2646],{"class":1112},[138,23522,14545],{"class":809},[138,23524,2661],{"class":815},[138,23526,810],{"class":809},[138,23528,23529,23532,23534,23537,23539,23541,23543,23545],{"class":140,"line":170},[138,23530,23531],{"class":805},"    el",[138,23533,363],{"class":809},[138,23535,23536],{"class":801},"setAttribute",[138,23538,806],{"class":815},[138,23540,23493],{"class":805},[138,23542,954],{"class":809},[138,23544,23498],{"class":805},[138,23546,872],{"class":815},[138,23548,23549],{"class":140,"line":176},[138,23550,1184],{"class":809},[138,23552,23553],{"class":140,"line":182},[138,23554,649],{"emptyLinePlaceholder":86},[138,23556,23557,23559,23561,23563,23566,23568,23570,23572,23575,23577,23580],{"class":140,"line":188},[138,23558,23484],{"class":794},[138,23560,1084],{"class":815},[138,23562,2518],{"class":1073},[138,23564,23565],{"class":2521}," child",[138,23567,23503],{"class":1112},[138,23569,22924],{"class":805},[138,23571,363],{"class":809},[138,23573,23574],{"class":805},"children",[138,23576,2646],{"class":1112},[138,23578,23579],{"class":815}," []) ",[138,23581,810],{"class":809},[138,23583,23584,23587,23589,23592,23594,23596],{"class":140,"line":194},[138,23585,23586],{"class":801},"    mount",[138,23588,806],{"class":815},[138,23590,23591],{"class":805},"child",[138,23593,954],{"class":809},[138,23595,23438],{"class":805},[138,23597,872],{"class":815},[138,23599,23600],{"class":140,"line":199},[138,23601,1184],{"class":809},[138,23603,23604,23607,23609,23612,23614,23617],{"class":140,"line":204},[138,23605,23606],{"class":805},"  container",[138,23608,363],{"class":809},[138,23610,23611],{"class":801},"appendChild",[138,23613,806],{"class":815},[138,23615,23616],{"class":805},"el",[138,23618,872],{"class":815},[138,23620,23621],{"class":140,"line":209},[138,23622,2082],{"class":809},[12,23624,1193,23625,23628],{},[135,23626,23627],{},"vnode._el"," é a peça central: ele conecta o objeto JS ao elemento real da página. Sem ele, nas próximas atualizações você não saberia qual elemento do DOM alterar.",[1643,23630,23632,23633,23636],{"id":23631},"_3-patch-atualizar-só-o-que-mudou","3. ",[135,23634,23635],{},"patch()",": atualizar só o que mudou",[12,23638,23639],{},"Compara dois VNodes e aplica no DOM só as diferenças. Nunca recria o elemento, apenas edita o que existe.",[129,23641,23643],{"className":5084,"code":23642,"language":5086,"meta":75,"style":75},"function patch(oldV, newV) {\n  newV._el = oldV._el\n  const el = newV._el\n\n  const oldProps = oldV.props || {}\n  const newProps = newV.props || {}\n\n  for (const [k, v] of Object.entries(newProps)) {\n    if (oldProps[k] !== v) el.setAttribute(k, v)\n  }\n\n  for (const k of Object.keys(oldProps)) {\n    if (!(k in newProps)) el.removeAttribute(k)\n  }\n\n  const oc = oldV.children || []\n  const nc = newV.children || []\n  const len = Math.max(oc.length, nc.length)\n\n  for (let i = 0; i \u003C len; i++) {\n    if (!oc[i]) mount(nc[i], el)\n    else if (!nc[i]) el.removeChild(oc[i]._el)\n    else patch(oc[i], nc[i])\n  }\n}\n",[135,23644,23645,23666,23685,23699,23703,23722,23741,23745,23780,23818,23822,23826,23854,23886,23890,23894,23913,23932,23968,23972,24005,24041,24083,24109,24113],{"__ignoreMap":75},[138,23646,23647,23649,23652,23654,23657,23659,23662,23664],{"class":140,"line":141},[138,23648,3824],{"class":1073},[138,23650,23651],{"class":801}," patch",[138,23653,806],{"class":809},[138,23655,23656],{"class":1045},"oldV",[138,23658,954],{"class":809},[138,23660,23661],{"class":1045}," newV",[138,23663,1049],{"class":809},[138,23665,934],{"class":809},[138,23667,23668,23671,23673,23675,23677,23680,23682],{"class":140,"line":76},[138,23669,23670],{"class":805},"  newV",[138,23672,363],{"class":809},[138,23674,23468],{"class":805},[138,23676,1146],{"class":1112},[138,23678,23679],{"class":805}," oldV",[138,23681,363],{"class":809},[138,23683,23684],{"class":805},"_el\n",[138,23686,23687,23689,23691,23693,23695,23697],{"class":140,"line":152},[138,23688,2607],{"class":1073},[138,23690,23438],{"class":2521},[138,23692,1146],{"class":1112},[138,23694,23661],{"class":805},[138,23696,363],{"class":809},[138,23698,23684],{"class":805},[138,23700,23701],{"class":140,"line":158},[138,23702,649],{"emptyLinePlaceholder":86},[138,23704,23705,23707,23710,23712,23714,23716,23718,23720],{"class":140,"line":164},[138,23706,2607],{"class":1073},[138,23708,23709],{"class":2521}," oldProps",[138,23711,1146],{"class":1112},[138,23713,23679],{"class":805},[138,23715,363],{"class":809},[138,23717,9319],{"class":805},[138,23719,2646],{"class":1112},[138,23721,22297],{"class":809},[138,23723,23724,23726,23729,23731,23733,23735,23737,23739],{"class":140,"line":170},[138,23725,2607],{"class":1073},[138,23727,23728],{"class":2521}," newProps",[138,23730,1146],{"class":1112},[138,23732,23661],{"class":805},[138,23734,363],{"class":809},[138,23736,9319],{"class":805},[138,23738,2646],{"class":1112},[138,23740,22297],{"class":809},[138,23742,23743],{"class":140,"line":176},[138,23744,649],{"emptyLinePlaceholder":86},[138,23746,23747,23749,23751,23753,23755,23757,23759,23761,23763,23765,23767,23769,23771,23773,23776,23778],{"class":140,"line":182},[138,23748,23484],{"class":794},[138,23750,1084],{"class":815},[138,23752,2518],{"class":1073},[138,23754,944],{"class":809},[138,23756,23493],{"class":2521},[138,23758,954],{"class":809},[138,23760,23498],{"class":2521},[138,23762,965],{"class":809},[138,23764,23503],{"class":1112},[138,23766,13431],{"class":805},[138,23768,363],{"class":809},[138,23770,23510],{"class":801},[138,23772,806],{"class":815},[138,23774,23775],{"class":805},"newProps",[138,23777,2661],{"class":815},[138,23779,810],{"class":809},[138,23781,23782,23784,23786,23789,23791,23793,23795,23798,23800,23802,23804,23806,23808,23810,23812,23814,23816],{"class":140,"line":188},[138,23783,21327],{"class":794},[138,23785,1084],{"class":815},[138,23787,23788],{"class":805},"oldProps",[138,23790,3883],{"class":815},[138,23792,23493],{"class":805},[138,23794,2570],{"class":815},[138,23796,23797],{"class":1112},"!==",[138,23799,23498],{"class":805},[138,23801,1109],{"class":815},[138,23803,23616],{"class":805},[138,23805,363],{"class":809},[138,23807,23536],{"class":801},[138,23809,806],{"class":815},[138,23811,23493],{"class":805},[138,23813,954],{"class":809},[138,23815,23498],{"class":805},[138,23817,872],{"class":815},[138,23819,23820],{"class":140,"line":194},[138,23821,1184],{"class":809},[138,23823,23824],{"class":140,"line":199},[138,23825,649],{"emptyLinePlaceholder":86},[138,23827,23828,23830,23832,23834,23837,23839,23841,23843,23846,23848,23850,23852],{"class":140,"line":204},[138,23829,23484],{"class":794},[138,23831,1084],{"class":815},[138,23833,2518],{"class":1073},[138,23835,23836],{"class":2521}," k",[138,23838,23503],{"class":1112},[138,23840,13431],{"class":805},[138,23842,363],{"class":809},[138,23844,23845],{"class":801},"keys",[138,23847,806],{"class":815},[138,23849,23788],{"class":805},[138,23851,2661],{"class":815},[138,23853,810],{"class":809},[138,23855,23856,23858,23860,23862,23864,23866,23869,23871,23873,23875,23877,23880,23882,23884],{"class":140,"line":209},[138,23857,21327],{"class":794},[138,23859,1084],{"class":815},[138,23861,2640],{"class":1112},[138,23863,806],{"class":815},[138,23865,23493],{"class":805},[138,23867,23868],{"class":1112}," in",[138,23870,23728],{"class":805},[138,23872,2661],{"class":815},[138,23874,23616],{"class":805},[138,23876,363],{"class":809},[138,23878,23879],{"class":801},"removeAttribute",[138,23881,806],{"class":815},[138,23883,23493],{"class":805},[138,23885,872],{"class":815},[138,23887,23888],{"class":140,"line":215},[138,23889,1184],{"class":809},[138,23891,23892],{"class":140,"line":221},[138,23893,649],{"emptyLinePlaceholder":86},[138,23895,23896,23898,23901,23903,23905,23907,23909,23911],{"class":140,"line":227},[138,23897,2607],{"class":1073},[138,23899,23900],{"class":2521}," oc",[138,23902,1146],{"class":1112},[138,23904,23679],{"class":805},[138,23906,363],{"class":809},[138,23908,23574],{"class":805},[138,23910,2646],{"class":1112},[138,23912,5164],{"class":815},[138,23914,23915,23917,23920,23922,23924,23926,23928,23930],{"class":140,"line":233},[138,23916,2607],{"class":1073},[138,23918,23919],{"class":2521}," nc",[138,23921,1146],{"class":1112},[138,23923,23661],{"class":805},[138,23925,363],{"class":809},[138,23927,23574],{"class":805},[138,23929,2646],{"class":1112},[138,23931,5164],{"class":815},[138,23933,23934,23936,23939,23941,23944,23946,23949,23951,23954,23956,23958,23960,23962,23964,23966],{"class":140,"line":239},[138,23935,2607],{"class":1073},[138,23937,23938],{"class":2521}," len",[138,23940,1146],{"class":1112},[138,23942,23943],{"class":805}," Math",[138,23945,363],{"class":809},[138,23947,23948],{"class":801},"max",[138,23950,806],{"class":815},[138,23952,23953],{"class":805},"oc",[138,23955,363],{"class":809},[138,23957,6484],{"class":2521},[138,23959,954],{"class":809},[138,23961,23919],{"class":805},[138,23963,363],{"class":809},[138,23965,6484],{"class":2521},[138,23967,872],{"class":815},[138,23969,23970],{"class":140,"line":245},[138,23971,649],{"emptyLinePlaceholder":86},[138,23973,23974,23976,23978,23980,23982,23984,23986,23988,23990,23993,23995,23997,23999,24001,24003],{"class":140,"line":251},[138,23975,23484],{"class":794},[138,23977,1084],{"class":815},[138,23979,12450],{"class":1073},[138,23981,13127],{"class":805},[138,23983,1146],{"class":1112},[138,23985,5296],{"class":5295},[138,23987,1829],{"class":809},[138,23989,13127],{"class":805},[138,23991,23992],{"class":1112}," \u003C",[138,23994,23938],{"class":805},[138,23996,1829],{"class":809},[138,23998,13127],{"class":805},[138,24000,10352],{"class":1112},[138,24002,1109],{"class":815},[138,24004,810],{"class":809},[138,24006,24007,24009,24011,24013,24015,24017,24019,24022,24024,24026,24029,24031,24033,24035,24037,24039],{"class":140,"line":257},[138,24008,21327],{"class":794},[138,24010,1084],{"class":815},[138,24012,2640],{"class":1112},[138,24014,23953],{"class":805},[138,24016,3883],{"class":815},[138,24018,3905],{"class":805},[138,24020,24021],{"class":815},"]) ",[138,24023,8282],{"class":801},[138,24025,806],{"class":815},[138,24027,24028],{"class":805},"nc",[138,24030,3883],{"class":815},[138,24032,3905],{"class":805},[138,24034,965],{"class":815},[138,24036,954],{"class":809},[138,24038,23438],{"class":805},[138,24040,872],{"class":815},[138,24042,24043,24046,24048,24050,24052,24054,24056,24058,24060,24062,24064,24067,24069,24071,24073,24075,24077,24079,24081],{"class":140,"line":263},[138,24044,24045],{"class":794},"    else",[138,24047,21379],{"class":794},[138,24049,1084],{"class":815},[138,24051,2640],{"class":1112},[138,24053,24028],{"class":805},[138,24055,3883],{"class":815},[138,24057,3905],{"class":805},[138,24059,24021],{"class":815},[138,24061,23616],{"class":805},[138,24063,363],{"class":809},[138,24065,24066],{"class":801},"removeChild",[138,24068,806],{"class":815},[138,24070,23953],{"class":805},[138,24072,3883],{"class":815},[138,24074,3905],{"class":805},[138,24076,965],{"class":815},[138,24078,363],{"class":809},[138,24080,23468],{"class":805},[138,24082,872],{"class":815},[138,24084,24085,24087,24089,24091,24093,24095,24097,24099,24101,24103,24105,24107],{"class":140,"line":269},[138,24086,24045],{"class":794},[138,24088,23651],{"class":801},[138,24090,806],{"class":815},[138,24092,23953],{"class":805},[138,24094,3883],{"class":815},[138,24096,3905],{"class":805},[138,24098,965],{"class":815},[138,24100,954],{"class":809},[138,24102,23919],{"class":805},[138,24104,3883],{"class":815},[138,24106,3905],{"class":805},[138,24108,5801],{"class":815},[138,24110,24111],{"class":140,"line":275},[138,24112,1184],{"class":809},[138,24114,24115],{"class":140,"line":281},[138,24116,2082],{"class":809},[12,24118,12575,24119,24122,24123,24126,24127,24130],{},[135,24120,24121],{},"newV._el = oldV._el"," merece atenção: o ",[135,24124,24125],{},"newVnode"," é criado do zero sem nenhuma referência ao DOM. O ",[135,24128,24129],{},"patch"," transfere a referência do nó antigo pro novo antes de jogar o antigo fora.",[1643,24132,24134],{"id":24133},"um-detalhe-importante-nós-de-texto","Um detalhe importante: nós de texto",[12,24136,24137,24138,24141],{},"Strings primitivas em JavaScript não aceitam propriedades. Se você tentar salvar ",[135,24139,24140],{},"._el"," numa string, a propriedade some:",[129,24143,24145],{"className":5084,"code":24144,"language":5086,"meta":75,"style":75},"const texto = 'Contador: 0'\ntexto._el = document.createTextNode('...')\nconsole.log(texto._el) \u002F\u002F undefined\n",[135,24146,24147,24163,24192],{"__ignoreMap":75},[138,24148,24149,24151,24154,24156,24158,24161],{"class":140,"line":141},[138,24150,2518],{"class":1073},[138,24152,24153],{"class":2521}," texto",[138,24155,1146],{"class":1112},[138,24157,957],{"class":826},[138,24159,24160],{"class":830},"Contador: 0",[138,24162,2361],{"class":826},[138,24164,24165,24167,24169,24172,24174,24176,24178,24181,24183,24185,24188,24190],{"class":140,"line":76},[138,24166,23298],{"class":805},[138,24168,363],{"class":809},[138,24170,24171],{"class":805},"_el ",[138,24173,1430],{"class":1112},[138,24175,23443],{"class":805},[138,24177,363],{"class":809},[138,24179,24180],{"class":801},"createTextNode",[138,24182,806],{"class":805},[138,24184,834],{"class":826},[138,24186,24187],{"class":830},"...",[138,24189,834],{"class":826},[138,24191,872],{"class":805},[138,24193,24194,24196,24198,24200,24203,24205,24208],{"class":140,"line":152},[138,24195,12480],{"class":805},[138,24197,363],{"class":809},[138,24199,9727],{"class":801},[138,24201,24202],{"class":805},"(texto",[138,24204,363],{"class":809},[138,24206,24207],{"class":805},"_el) ",[138,24209,12490],{"class":911},[12,24211,24212],{},"A solução é envolver o texto num objeto, igual a qualquer outro VNode:",[129,24214,24216],{"className":5084,"code":24215,"language":5086,"meta":75,"style":75},"function t(text) {\n  return { tag: '#text', text: String(text), _el: null }\n}\n\nh('p', {}, [t(`Contador: ${contador}`)])\n",[135,24217,24218,24233,24277,24281,24285],{"__ignoreMap":75},[138,24219,24220,24222,24225,24227,24229,24231],{"class":140,"line":141},[138,24221,3824],{"class":1073},[138,24223,24224],{"class":801}," t",[138,24226,806],{"class":809},[138,24228,13552],{"class":1045},[138,24230,1049],{"class":809},[138,24232,934],{"class":809},[138,24234,24235,24237,24239,24241,24243,24245,24248,24250,24252,24255,24257,24260,24262,24264,24266,24268,24271,24273,24275],{"class":140,"line":76},[138,24236,3199],{"class":794},[138,24238,2812],{"class":809},[138,24240,23165],{"class":815},[138,24242,782],{"class":809},[138,24244,957],{"class":826},[138,24246,24247],{"class":830},"#text",[138,24249,834],{"class":826},[138,24251,954],{"class":809},[138,24253,24254],{"class":815}," text",[138,24256,782],{"class":809},[138,24258,24259],{"class":801}," String",[138,24261,806],{"class":815},[138,24263,13552],{"class":805},[138,24265,1049],{"class":815},[138,24267,954],{"class":809},[138,24269,24270],{"class":815}," _el",[138,24272,782],{"class":809},[138,24274,3062],{"class":2041},[138,24276,1015],{"class":809},[138,24278,24279],{"class":140,"line":152},[138,24280,2082],{"class":809},[138,24282,24283],{"class":140,"line":158},[138,24284,649],{"emptyLinePlaceholder":86},[138,24286,24287,24289,24291,24293,24295,24297,24299,24301,24303,24306,24308,24310,24313,24315,24317,24319],{"class":140,"line":164},[138,24288,23066],{"class":801},[138,24290,806],{"class":805},[138,24292,834],{"class":826},[138,24294,12],{"class":830},[138,24296,834],{"class":826},[138,24298,954],{"class":809},[138,24300,23178],{"class":809},[138,24302,944],{"class":805},[138,24304,24305],{"class":801},"t",[138,24307,806],{"class":805},[138,24309,3791],{"class":826},[138,24311,24312],{"class":830},"Contador: ",[138,24314,3797],{"class":826},[138,24316,15612],{"class":805},[138,24318,3803],{"class":826},[138,24320,24321],{"class":805},")])\n",[12,24323,24324,24325,24327,24328,24330],{},"Agora o ",[135,24326,23468],{}," é salvo normalmente e o ",[135,24329,24129],{}," consegue encontrar e atualizar o nó de texto exato no DOM.",[30,24332,24334],{"id":24333},"adicionando-reatividade-com-proxy","Adicionando reatividade com Proxy",[12,24336,24337],{},"O Virtual DOM resolve o \"como atualizar\". Mas quem decide \"quando atualizar\" e \"quais componentes precisam re-renderizar\"?",[12,24339,24340,24341,24344,24345,24348],{},"No Vue 3, isso é feito com ",[135,24342,24343],{},"Proxy",", uma feature nativa do JavaScript que intercepta leitura e escrita de propriedades de um objeto. Vale mencionar que o Vue 2 resolvia isso com ",[135,24346,24347],{},"Object.defineProperty"," (getter\u002Fsetter), mas com limitações: não conseguia detectar adição de novas propriedades nem mudanças em arrays por índice. O Proxy do Vue 3 resolve esses problemas.",[12,24350,24351],{},"A ideia é simples:",[129,24353,24355],{"className":5084,"code":24354,"language":5086,"meta":75,"style":75},"const estado = { contador: 0 }\n\nconst reativo = new Proxy(estado, {\n  get(target, key) {\n    console.log(`alguém leu \"${key}\"`)\n    return target[key]\n  },\n  set(target, key, value) {\n    console.log(`alguém escreveu \"${key}\" = ${value}`)\n    target[key] = value\n    return true\n  }\n})\n\nreativo.contador \u002F\u002F log: alguém leu \"contador\"\nreativo.contador = 5 \u002F\u002F log: alguém escreveu \"contador\" = 5\n",[135,24356,24357,24376,24380,24401,24420,24448,24461,24465,24487,24519,24535,24541,24545,24551,24555,24568],{"__ignoreMap":75},[138,24358,24359,24361,24364,24366,24368,24370,24372,24374],{"class":140,"line":141},[138,24360,2518],{"class":1073},[138,24362,24363],{"class":2521}," estado",[138,24365,1146],{"class":1112},[138,24367,2812],{"class":809},[138,24369,15597],{"class":815},[138,24371,782],{"class":809},[138,24373,5296],{"class":5295},[138,24375,1015],{"class":809},[138,24377,24378],{"class":140,"line":76},[138,24379,649],{"emptyLinePlaceholder":86},[138,24381,24382,24384,24387,24389,24391,24394,24397,24399],{"class":140,"line":152},[138,24383,2518],{"class":1073},[138,24385,24386],{"class":2521}," reativo",[138,24388,1146],{"class":1112},[138,24390,8662],{"class":1112},[138,24392,24393],{"class":801}," Proxy",[138,24395,24396],{"class":805},"(estado",[138,24398,954],{"class":809},[138,24400,934],{"class":809},[138,24402,24403,24406,24408,24411,24413,24416,24418],{"class":140,"line":158},[138,24404,24405],{"class":5196},"  get",[138,24407,806],{"class":809},[138,24409,24410],{"class":1045},"target",[138,24412,954],{"class":809},[138,24414,24415],{"class":1045}," key",[138,24417,1049],{"class":809},[138,24419,934],{"class":809},[138,24421,24422,24424,24426,24428,24430,24432,24435,24437,24440,24442,24444,24446],{"class":140,"line":164},[138,24423,9722],{"class":805},[138,24425,363],{"class":809},[138,24427,9727],{"class":801},[138,24429,806],{"class":815},[138,24431,3791],{"class":826},[138,24433,24434],{"class":830},"alguém leu \"",[138,24436,3797],{"class":826},[138,24438,24439],{"class":805},"key",[138,24441,869],{"class":826},[138,24443,1433],{"class":830},[138,24445,3791],{"class":826},[138,24447,872],{"class":815},[138,24449,24450,24452,24455,24457,24459],{"class":140,"line":170},[138,24451,2900],{"class":794},[138,24453,24454],{"class":805}," target",[138,24456,3883],{"class":815},[138,24458,24439],{"class":805},[138,24460,8056],{"class":815},[138,24462,24463],{"class":140,"line":176},[138,24464,972],{"class":809},[138,24466,24467,24470,24472,24474,24476,24478,24480,24483,24485],{"class":140,"line":182},[138,24468,24469],{"class":5196},"  set",[138,24471,806],{"class":809},[138,24473,24410],{"class":1045},[138,24475,954],{"class":809},[138,24477,24415],{"class":1045},[138,24479,954],{"class":809},[138,24481,24482],{"class":1045}," value",[138,24484,1049],{"class":809},[138,24486,934],{"class":809},[138,24488,24489,24491,24493,24495,24497,24499,24502,24504,24506,24508,24511,24513,24515,24517],{"class":140,"line":188},[138,24490,9722],{"class":805},[138,24492,363],{"class":809},[138,24494,9727],{"class":801},[138,24496,806],{"class":815},[138,24498,3791],{"class":826},[138,24500,24501],{"class":830},"alguém escreveu \"",[138,24503,3797],{"class":826},[138,24505,24439],{"class":805},[138,24507,869],{"class":826},[138,24509,24510],{"class":830},"\" = ",[138,24512,3797],{"class":826},[138,24514,3102],{"class":805},[138,24516,3803],{"class":826},[138,24518,872],{"class":815},[138,24520,24521,24524,24526,24528,24530,24532],{"class":140,"line":194},[138,24522,24523],{"class":805},"    target",[138,24525,3883],{"class":815},[138,24527,24439],{"class":805},[138,24529,2570],{"class":815},[138,24531,1430],{"class":1112},[138,24533,24534],{"class":805}," value\n",[138,24536,24537,24539],{"class":140,"line":199},[138,24538,2900],{"class":794},[138,24540,11756],{"class":3862},[138,24542,24543],{"class":140,"line":204},[138,24544,1184],{"class":809},[138,24546,24547,24549],{"class":140,"line":209},[138,24548,869],{"class":809},[138,24550,872],{"class":805},[138,24552,24553],{"class":140,"line":215},[138,24554,649],{"emptyLinePlaceholder":86},[138,24556,24557,24560,24562,24565],{"class":140,"line":221},[138,24558,24559],{"class":805},"reativo",[138,24561,363],{"class":809},[138,24563,24564],{"class":805},"contador ",[138,24566,24567],{"class":911},"\u002F\u002F log: alguém leu \"contador\"\n",[138,24569,24570,24572,24574,24576,24578,24581],{"class":140,"line":227},[138,24571,24559],{"class":805},[138,24573,363],{"class":809},[138,24575,24564],{"class":805},[138,24577,1430],{"class":1112},[138,24579,24580],{"class":5295}," 5",[138,24582,24583],{"class":911}," \u002F\u002F log: alguém escreveu \"contador\" = 5\n",[12,24585,24586,24587,24590,24591,24594],{},"O Vue usa esse mecanismo para montar um mapa de dependências: quando um componente renderiza, o ",[135,24588,24589],{},"get"," do Proxy registra quais propriedades foram lidas. Quando uma delas muda, o ",[135,24592,24593],{},"set"," notifica só os componentes que a leram.",[129,24596,24599],{"className":24597,"code":24598,"language":13552},[13550],"Header leu → estado.titulo\nMain   leu → estado.conteudo\nFooter leu → estado.ano\n\nestado.titulo muda → só o Header re-renderiza\n",[135,24600,24598],{"__ignoreMap":75},[12,24602,24603,24604,24607,24608,24610],{},"A estrutura que usamos para guardar as dependências importa. Um objeto simples como ",[135,24605,24606],{},"const deps = {}"," funciona para um estado global único, mas causa dois problemas em escala: dois objetos reativos com uma propriedade ",[135,24609,3107],{}," conflitariam na mesma chave, e a referência ficaria presa na memória mesmo após o componente ser destruído.",[12,24612,24613,24614,24617,24618,782],{},"A solução é usar ",[135,24615,24616],{},"WeakMap",", que aceita objetos como chave e não impede o garbage collector de limpar o objeto quando ele não é mais usado. A estrutura fica ",[135,24619,24620],{},"WeakMap\u003Cobjeto, Map\u003Cpropriedade, Set\u003Ccomponentes>>>",[129,24622,24624],{"className":5084,"code":24623,"language":5086,"meta":75,"style":75},"const deps = new WeakMap()\nlet componenteAtivo = null\n\nfunction registrarDependencia(obj, propriedade) {\n  if (!componenteAtivo) return\n\n  let propriedades = deps.get(obj)\n  if (!propriedades) {\n    propriedades = new Map()\n    deps.set(obj, propriedades)\n  }\n\n  let componentes = propriedades.get(propriedade)\n  if (!componentes) {\n    componentes = new Set()\n    propriedades.set(propriedade, componentes)\n  }\n\n  componentes.add(componenteAtivo)\n}\n\nfunction notificarDependentes(obj, propriedade) {\n  const propriedades = deps.get(obj)\n  if (!propriedades) return\n\n  const componentes = propriedades.get(propriedade)\n  if (componentes) componentes.forEach(c => c._enfileirar())\n}\n\nfunction reativo(obj) {\n  return new Proxy(obj, {\n    get(target, key) {\n      registrarDependencia(target, key)\n      return target[key]\n    },\n    set(target, key, value) {\n      if (target[key] === value) return true\n      target[key] = value\n      notificarDependentes(target, key)\n      return true\n    }\n  })\n}\n",[135,24625,24626,24642,24653,24657,24678,24693,24697,24718,24733,24747,24766,24770,24774,24796,24811,24825,24843,24847,24851,24867,24871,24875,24894,24914,24928,24932,24952,24985,24989,24993,25007,25023,25040,25055,25067,25071,25092,25117,25132,25147,25153,25157,25163],{"__ignoreMap":75},[138,24627,24628,24630,24633,24635,24637,24640],{"class":140,"line":141},[138,24629,2518],{"class":1073},[138,24631,24632],{"class":2521}," deps",[138,24634,1146],{"class":1112},[138,24636,8662],{"class":1112},[138,24638,24639],{"class":801}," WeakMap",[138,24641,2796],{"class":805},[138,24643,24644,24646,24649,24651],{"class":140,"line":76},[138,24645,12450],{"class":1073},[138,24647,24648],{"class":805}," componenteAtivo ",[138,24650,1430],{"class":1112},[138,24652,3186],{"class":2041},[138,24654,24655],{"class":140,"line":152},[138,24656,649],{"emptyLinePlaceholder":86},[138,24658,24659,24661,24664,24666,24669,24671,24674,24676],{"class":140,"line":158},[138,24660,3824],{"class":1073},[138,24662,24663],{"class":801}," registrarDependencia",[138,24665,806],{"class":809},[138,24667,24668],{"class":1045},"obj",[138,24670,954],{"class":809},[138,24672,24673],{"class":1045}," propriedade",[138,24675,1049],{"class":809},[138,24677,934],{"class":809},[138,24679,24680,24682,24684,24686,24689,24691],{"class":140,"line":164},[138,24681,2635],{"class":794},[138,24683,1084],{"class":815},[138,24685,2640],{"class":1112},[138,24687,24688],{"class":805},"componenteAtivo",[138,24690,1109],{"class":815},[138,24692,2772],{"class":794},[138,24694,24695],{"class":140,"line":170},[138,24696,649],{"emptyLinePlaceholder":86},[138,24698,24699,24701,24704,24706,24708,24710,24712,24714,24716],{"class":140,"line":176},[138,24700,12889],{"class":1073},[138,24702,24703],{"class":805}," propriedades",[138,24705,1146],{"class":1112},[138,24707,24632],{"class":805},[138,24709,363],{"class":809},[138,24711,24589],{"class":801},[138,24713,806],{"class":815},[138,24715,24668],{"class":805},[138,24717,872],{"class":815},[138,24719,24720,24722,24724,24726,24729,24731],{"class":140,"line":182},[138,24721,2635],{"class":794},[138,24723,1084],{"class":815},[138,24725,2640],{"class":1112},[138,24727,24728],{"class":805},"propriedades",[138,24730,1109],{"class":815},[138,24732,810],{"class":809},[138,24734,24735,24738,24740,24742,24745],{"class":140,"line":188},[138,24736,24737],{"class":805},"    propriedades",[138,24739,1146],{"class":1112},[138,24741,8662],{"class":1112},[138,24743,24744],{"class":801}," Map",[138,24746,2796],{"class":815},[138,24748,24749,24752,24754,24756,24758,24760,24762,24764],{"class":140,"line":194},[138,24750,24751],{"class":805},"    deps",[138,24753,363],{"class":809},[138,24755,24593],{"class":801},[138,24757,806],{"class":815},[138,24759,24668],{"class":805},[138,24761,954],{"class":809},[138,24763,24703],{"class":805},[138,24765,872],{"class":815},[138,24767,24768],{"class":140,"line":199},[138,24769,1184],{"class":809},[138,24771,24772],{"class":140,"line":204},[138,24773,649],{"emptyLinePlaceholder":86},[138,24775,24776,24778,24781,24783,24785,24787,24789,24791,24794],{"class":140,"line":209},[138,24777,12889],{"class":1073},[138,24779,24780],{"class":805}," componentes",[138,24782,1146],{"class":1112},[138,24784,24703],{"class":805},[138,24786,363],{"class":809},[138,24788,24589],{"class":801},[138,24790,806],{"class":815},[138,24792,24793],{"class":805},"propriedade",[138,24795,872],{"class":815},[138,24797,24798,24800,24802,24804,24807,24809],{"class":140,"line":215},[138,24799,2635],{"class":794},[138,24801,1084],{"class":815},[138,24803,2640],{"class":1112},[138,24805,24806],{"class":805},"componentes",[138,24808,1109],{"class":815},[138,24810,810],{"class":809},[138,24812,24813,24816,24818,24820,24823],{"class":140,"line":221},[138,24814,24815],{"class":805},"    componentes",[138,24817,1146],{"class":1112},[138,24819,8662],{"class":1112},[138,24821,24822],{"class":801}," Set",[138,24824,2796],{"class":815},[138,24826,24827,24829,24831,24833,24835,24837,24839,24841],{"class":140,"line":227},[138,24828,24737],{"class":805},[138,24830,363],{"class":809},[138,24832,24593],{"class":801},[138,24834,806],{"class":815},[138,24836,24793],{"class":805},[138,24838,954],{"class":809},[138,24840,24780],{"class":805},[138,24842,872],{"class":815},[138,24844,24845],{"class":140,"line":233},[138,24846,1184],{"class":809},[138,24848,24849],{"class":140,"line":239},[138,24850,649],{"emptyLinePlaceholder":86},[138,24852,24853,24856,24858,24861,24863,24865],{"class":140,"line":245},[138,24854,24855],{"class":805},"  componentes",[138,24857,363],{"class":809},[138,24859,24860],{"class":801},"add",[138,24862,806],{"class":815},[138,24864,24688],{"class":805},[138,24866,872],{"class":815},[138,24868,24869],{"class":140,"line":251},[138,24870,2082],{"class":809},[138,24872,24873],{"class":140,"line":257},[138,24874,649],{"emptyLinePlaceholder":86},[138,24876,24877,24879,24882,24884,24886,24888,24890,24892],{"class":140,"line":263},[138,24878,3824],{"class":1073},[138,24880,24881],{"class":801}," notificarDependentes",[138,24883,806],{"class":809},[138,24885,24668],{"class":1045},[138,24887,954],{"class":809},[138,24889,24673],{"class":1045},[138,24891,1049],{"class":809},[138,24893,934],{"class":809},[138,24895,24896,24898,24900,24902,24904,24906,24908,24910,24912],{"class":140,"line":269},[138,24897,2607],{"class":1073},[138,24899,24703],{"class":2521},[138,24901,1146],{"class":1112},[138,24903,24632],{"class":805},[138,24905,363],{"class":809},[138,24907,24589],{"class":801},[138,24909,806],{"class":815},[138,24911,24668],{"class":805},[138,24913,872],{"class":815},[138,24915,24916,24918,24920,24922,24924,24926],{"class":140,"line":275},[138,24917,2635],{"class":794},[138,24919,1084],{"class":815},[138,24921,2640],{"class":1112},[138,24923,24728],{"class":805},[138,24925,1109],{"class":815},[138,24927,2772],{"class":794},[138,24929,24930],{"class":140,"line":281},[138,24931,649],{"emptyLinePlaceholder":86},[138,24933,24934,24936,24938,24940,24942,24944,24946,24948,24950],{"class":140,"line":287},[138,24935,2607],{"class":1073},[138,24937,24780],{"class":2521},[138,24939,1146],{"class":1112},[138,24941,24703],{"class":805},[138,24943,363],{"class":809},[138,24945,24589],{"class":801},[138,24947,806],{"class":815},[138,24949,24793],{"class":805},[138,24951,872],{"class":815},[138,24953,24954,24956,24958,24960,24962,24964,24966,24968,24970,24973,24975,24978,24980,24983],{"class":140,"line":293},[138,24955,2635],{"class":794},[138,24957,1084],{"class":815},[138,24959,24806],{"class":805},[138,24961,1109],{"class":815},[138,24963,24806],{"class":805},[138,24965,363],{"class":809},[138,24967,1061],{"class":801},[138,24969,806],{"class":815},[138,24971,24972],{"class":1045},"c",[138,24974,1074],{"class":1073},[138,24976,24977],{"class":805}," c",[138,24979,363],{"class":809},[138,24981,24982],{"class":801},"_enfileirar",[138,24984,8394],{"class":815},[138,24986,24987],{"class":140,"line":299},[138,24988,2082],{"class":809},[138,24990,24991],{"class":140,"line":305},[138,24992,649],{"emptyLinePlaceholder":86},[138,24994,24995,24997,24999,25001,25003,25005],{"class":140,"line":311},[138,24996,3824],{"class":1073},[138,24998,24386],{"class":801},[138,25000,806],{"class":809},[138,25002,24668],{"class":1045},[138,25004,1049],{"class":809},[138,25006,934],{"class":809},[138,25008,25009,25011,25013,25015,25017,25019,25021],{"class":140,"line":317},[138,25010,3199],{"class":794},[138,25012,8662],{"class":1112},[138,25014,24393],{"class":801},[138,25016,806],{"class":815},[138,25018,24668],{"class":805},[138,25020,954],{"class":809},[138,25022,934],{"class":809},[138,25024,25025,25028,25030,25032,25034,25036,25038],{"class":140,"line":323},[138,25026,25027],{"class":5196},"    get",[138,25029,806],{"class":809},[138,25031,24410],{"class":1045},[138,25033,954],{"class":809},[138,25035,24415],{"class":1045},[138,25037,1049],{"class":809},[138,25039,934],{"class":809},[138,25041,25042,25045,25047,25049,25051,25053],{"class":140,"line":329},[138,25043,25044],{"class":801},"      registrarDependencia",[138,25046,806],{"class":815},[138,25048,24410],{"class":805},[138,25050,954],{"class":809},[138,25052,24415],{"class":805},[138,25054,872],{"class":815},[138,25056,25057,25059,25061,25063,25065],{"class":140,"line":335},[138,25058,5211],{"class":794},[138,25060,24454],{"class":805},[138,25062,3883],{"class":815},[138,25064,24439],{"class":805},[138,25066,8056],{"class":815},[138,25068,25069],{"class":140,"line":341},[138,25070,5229],{"class":809},[138,25072,25073,25076,25078,25080,25082,25084,25086,25088,25090],{"class":140,"line":347},[138,25074,25075],{"class":5196},"    set",[138,25077,806],{"class":809},[138,25079,24410],{"class":1045},[138,25081,954],{"class":809},[138,25083,24415],{"class":1045},[138,25085,954],{"class":809},[138,25087,24482],{"class":1045},[138,25089,1049],{"class":809},[138,25091,934],{"class":809},[138,25093,25094,25096,25098,25100,25102,25104,25106,25109,25111,25113,25115],{"class":140,"line":353},[138,25095,8489],{"class":794},[138,25097,1084],{"class":815},[138,25099,24410],{"class":805},[138,25101,3883],{"class":815},[138,25103,24439],{"class":805},[138,25105,2570],{"class":815},[138,25107,25108],{"class":1112},"===",[138,25110,24482],{"class":805},[138,25112,1109],{"class":815},[138,25114,2664],{"class":794},[138,25116,11756],{"class":3862},[138,25118,25119,25122,25124,25126,25128,25130],{"class":140,"line":622},[138,25120,25121],{"class":805},"      target",[138,25123,3883],{"class":815},[138,25125,24439],{"class":805},[138,25127,2570],{"class":815},[138,25129,1430],{"class":1112},[138,25131,24534],{"class":805},[138,25133,25134,25137,25139,25141,25143,25145],{"class":140,"line":628},[138,25135,25136],{"class":801},"      notificarDependentes",[138,25138,806],{"class":815},[138,25140,24410],{"class":805},[138,25142,954],{"class":809},[138,25144,24415],{"class":805},[138,25146,872],{"class":815},[138,25148,25149,25151],{"class":140,"line":634},[138,25150,5211],{"class":794},[138,25152,11756],{"class":3862},[138,25154,25155],{"class":140,"line":640},[138,25156,1179],{"class":809},[138,25158,25159,25161],{"class":140,"line":646},[138,25160,2863],{"class":809},[138,25162,872],{"class":815},[138,25164,25165],{"class":140,"line":652},[138,25166,2082],{"class":809},[12,25168,1193,25169,25171,25172,25175,25176,25171,25178,25181],{},[135,25170,24589],{}," chama ",[135,25173,25174],{},"registrarDependencia",", que anota qual componente está lendo aquela propriedade. O ",[135,25177,24593],{},[135,25179,25180],{},"notificarDependentes",", que avisa só quem precisa re-renderizar.",[30,25183,25185],{"id":25184},"batch-update-o-nexttick-por-baixo","Batch update: o nextTick por baixo",[12,25187,25188,25189,363],{},"Se você mudar três propriedades seguidas, não faz sentido re-renderizar três vezes. O Vue acumula todas as mudanças do ciclo síncrono atual e processa tudo de uma vez. Isso é o ",[135,25190,25191],{},"nextTick",[12,25193,25194,25195,25198],{},"Por baixo, ele usa ",[135,25196,25197],{},"Promise.resolve().then()",", que agenda a execução depois que todo o código síncrono terminar:",[129,25200,25202],{"className":5084,"code":25201,"language":5086,"meta":75,"style":75},"let fila = new Set()\nlet flushAgendado = false\n\nfunction agendarFlush() {\n  if (flushAgendado) return\n  flushAgendado = true\n  Promise.resolve().then(() => {\n    fila.forEach(c => c._rerender())\n    fila.clear()\n    flushAgendado = false\n  })\n}\n",[135,25203,25204,25219,25230,25234,25245,25258,25267,25290,25314,25325,25334,25340],{"__ignoreMap":75},[138,25205,25206,25208,25211,25213,25215,25217],{"class":140,"line":141},[138,25207,12450],{"class":1073},[138,25209,25210],{"class":805}," fila ",[138,25212,1430],{"class":1112},[138,25214,8662],{"class":1112},[138,25216,24822],{"class":801},[138,25218,2796],{"class":805},[138,25220,25221,25223,25226,25228],{"class":140,"line":76},[138,25222,12450],{"class":1073},[138,25224,25225],{"class":805}," flushAgendado ",[138,25227,1430],{"class":1112},[138,25229,3863],{"class":3862},[138,25231,25232],{"class":140,"line":152},[138,25233,649],{"emptyLinePlaceholder":86},[138,25235,25236,25238,25241,25243],{"class":140,"line":158},[138,25237,3824],{"class":1073},[138,25239,25240],{"class":801}," agendarFlush",[138,25242,3093],{"class":809},[138,25244,934],{"class":809},[138,25246,25247,25249,25251,25254,25256],{"class":140,"line":164},[138,25248,2635],{"class":794},[138,25250,1084],{"class":815},[138,25252,25253],{"class":805},"flushAgendado",[138,25255,1109],{"class":815},[138,25257,2772],{"class":794},[138,25259,25260,25263,25265],{"class":140,"line":170},[138,25261,25262],{"class":805},"  flushAgendado",[138,25264,1146],{"class":1112},[138,25266,11756],{"class":3862},[138,25268,25269,25272,25274,25276,25278,25280,25282,25284,25286,25288],{"class":140,"line":176},[138,25270,25271],{"class":3061},"  Promise",[138,25273,363],{"class":809},[138,25275,15283],{"class":801},[138,25277,3093],{"class":815},[138,25279,363],{"class":809},[138,25281,15290],{"class":801},[138,25283,806],{"class":815},[138,25285,3093],{"class":809},[138,25287,1074],{"class":1073},[138,25289,934],{"class":809},[138,25291,25292,25295,25297,25299,25301,25303,25305,25307,25309,25312],{"class":140,"line":182},[138,25293,25294],{"class":805},"    fila",[138,25296,363],{"class":809},[138,25298,1061],{"class":801},[138,25300,806],{"class":815},[138,25302,24972],{"class":1045},[138,25304,1074],{"class":1073},[138,25306,24977],{"class":805},[138,25308,363],{"class":809},[138,25310,25311],{"class":801},"_rerender",[138,25313,8394],{"class":815},[138,25315,25316,25318,25320,25323],{"class":140,"line":188},[138,25317,25294],{"class":805},[138,25319,363],{"class":809},[138,25321,25322],{"class":801},"clear",[138,25324,2796],{"class":815},[138,25326,25327,25330,25332],{"class":140,"line":194},[138,25328,25329],{"class":805},"    flushAgendado",[138,25331,1146],{"class":1112},[138,25333,3863],{"class":3862},[138,25335,25336,25338],{"class":140,"line":199},[138,25337,2863],{"class":809},[138,25339,872],{"class":815},[138,25341,25342],{"class":140,"line":204},[138,25343,2082],{"class":809},[12,25345,25346],{},"Muda 10 propriedades no mesmo tick, re-renderiza uma vez só.",[30,25348,25350],{"id":25349},"juntando-tudo-o-mini-framework","Juntando tudo: o mini-framework",[12,25352,25353,25354,25357,25358,25361],{},"Com reatividade e Virtual DOM prontos, a função ",[135,25355,25356],{},"criarComponente"," é a cola entre os dois. O ponto central é marcar o componente como ",[135,25359,25360],{},"efeitoAtivo"," antes de renderizar, para que o track saiba a quem atribuir as dependências:",[129,25363,25365],{"className":5084,"code":25364,"language":5086,"meta":75,"style":75},"function criarComponente(opcoes, container) {\n  const componente = {\n    props: opcoes.props || {},\n    estado: reativo(opcoes.estado || {}),\n    _vnode: null,\n\n    _enfileirar() {\n      fila.add(componente)\n      agendarFlush()\n    },\n\n    _rerender() {\n      efeitoAtivo = componente._enfileirar\n      const novoVnode = opcoes.render.call(componente)\n      efeitoAtivo = null\n\n      if (!componente._vnode) {\n        mount(novoVnode, container)\n        componente._vnode = novoVnode\n        if (opcoes.onMount) opcoes.onMount.call(componente)\n      } else {\n        patch(componente._vnode, novoVnode)\n        componente._vnode = novoVnode\n        if (opcoes.onUpdate) opcoes.onUpdate.call(componente)\n      }\n    },\n\n    destruir() {\n      if (opcoes.onDestroy) opcoes.onDestroy.call(componente)\n      if (componente._vnode) {\n        container.removeChild(componente._vnode._el)\n        componente._vnode = null\n      }\n    }\n  }\n\n  componente._rerender()\n  return componente\n}\n",[135,25366,25367,25387,25398,25417,25442,25453,25457,25466,25482,25489,25493,25497,25506,25520,25547,25555,25559,25578,25594,25608,25639,25647,25666,25678,25709,25713,25717,25721,25730,25761,25777,25800,25812,25816,25820,25824,25828,25839,25846],{"__ignoreMap":75},[138,25368,25369,25371,25374,25376,25379,25381,25383,25385],{"class":140,"line":141},[138,25370,3824],{"class":1073},[138,25372,25373],{"class":801}," criarComponente",[138,25375,806],{"class":809},[138,25377,25378],{"class":1045},"opcoes",[138,25380,954],{"class":809},[138,25382,23427],{"class":1045},[138,25384,1049],{"class":809},[138,25386,934],{"class":809},[138,25388,25389,25391,25394,25396],{"class":140,"line":76},[138,25390,2607],{"class":1073},[138,25392,25393],{"class":2521}," componente",[138,25395,1146],{"class":1112},[138,25397,934],{"class":809},[138,25399,25400,25403,25405,25408,25410,25412,25414],{"class":140,"line":152},[138,25401,25402],{"class":815},"    props",[138,25404,782],{"class":809},[138,25406,25407],{"class":805}," opcoes",[138,25409,363],{"class":809},[138,25411,9319],{"class":805},[138,25413,2646],{"class":1112},[138,25415,25416],{"class":809}," {},\n",[138,25418,25419,25422,25424,25426,25428,25430,25432,25434,25436,25438,25440],{"class":140,"line":158},[138,25420,25421],{"class":815},"    estado",[138,25423,782],{"class":809},[138,25425,24386],{"class":801},[138,25427,806],{"class":815},[138,25429,25378],{"class":805},[138,25431,363],{"class":809},[138,25433,16394],{"class":805},[138,25435,2646],{"class":1112},[138,25437,14545],{"class":809},[138,25439,1049],{"class":815},[138,25441,837],{"class":809},[138,25443,25444,25447,25449,25451],{"class":140,"line":164},[138,25445,25446],{"class":815},"    _vnode",[138,25448,782],{"class":809},[138,25450,3062],{"class":2041},[138,25452,837],{"class":809},[138,25454,25455],{"class":140,"line":170},[138,25456,649],{"emptyLinePlaceholder":86},[138,25458,25459,25462,25464],{"class":140,"line":176},[138,25460,25461],{"class":5196},"    _enfileirar",[138,25463,3093],{"class":809},[138,25465,934],{"class":809},[138,25467,25468,25471,25473,25475,25477,25480],{"class":140,"line":182},[138,25469,25470],{"class":805},"      fila",[138,25472,363],{"class":809},[138,25474,24860],{"class":801},[138,25476,806],{"class":815},[138,25478,25479],{"class":805},"componente",[138,25481,872],{"class":815},[138,25483,25484,25487],{"class":140,"line":188},[138,25485,25486],{"class":801},"      agendarFlush",[138,25488,2796],{"class":815},[138,25490,25491],{"class":140,"line":194},[138,25492,5229],{"class":809},[138,25494,25495],{"class":140,"line":199},[138,25496,649],{"emptyLinePlaceholder":86},[138,25498,25499,25502,25504],{"class":140,"line":204},[138,25500,25501],{"class":5196},"    _rerender",[138,25503,3093],{"class":809},[138,25505,934],{"class":809},[138,25507,25508,25511,25513,25515,25517],{"class":140,"line":209},[138,25509,25510],{"class":805},"      efeitoAtivo",[138,25512,1146],{"class":1112},[138,25514,25393],{"class":805},[138,25516,363],{"class":809},[138,25518,25519],{"class":805},"_enfileirar\n",[138,25521,25522,25524,25527,25529,25531,25533,25536,25538,25541,25543,25545],{"class":140,"line":215},[138,25523,5536],{"class":1073},[138,25525,25526],{"class":2521}," novoVnode",[138,25528,1146],{"class":1112},[138,25530,25407],{"class":805},[138,25532,363],{"class":809},[138,25534,25535],{"class":805},"render",[138,25537,363],{"class":809},[138,25539,25540],{"class":801},"call",[138,25542,806],{"class":815},[138,25544,25479],{"class":805},[138,25546,872],{"class":815},[138,25548,25549,25551,25553],{"class":140,"line":221},[138,25550,25510],{"class":805},[138,25552,1146],{"class":1112},[138,25554,3186],{"class":2041},[138,25556,25557],{"class":140,"line":227},[138,25558,649],{"emptyLinePlaceholder":86},[138,25560,25561,25563,25565,25567,25569,25571,25574,25576],{"class":140,"line":233},[138,25562,8489],{"class":794},[138,25564,1084],{"class":815},[138,25566,2640],{"class":1112},[138,25568,25479],{"class":805},[138,25570,363],{"class":809},[138,25572,25573],{"class":805},"_vnode",[138,25575,1109],{"class":815},[138,25577,810],{"class":809},[138,25579,25580,25583,25585,25588,25590,25592],{"class":140,"line":239},[138,25581,25582],{"class":801},"        mount",[138,25584,806],{"class":815},[138,25586,25587],{"class":805},"novoVnode",[138,25589,954],{"class":809},[138,25591,23427],{"class":805},[138,25593,872],{"class":815},[138,25595,25596,25599,25601,25603,25605],{"class":140,"line":245},[138,25597,25598],{"class":805},"        componente",[138,25600,363],{"class":809},[138,25602,25573],{"class":805},[138,25604,1146],{"class":1112},[138,25606,25607],{"class":805}," novoVnode\n",[138,25609,25610,25612,25614,25616,25618,25621,25623,25625,25627,25629,25631,25633,25635,25637],{"class":140,"line":251},[138,25611,1081],{"class":794},[138,25613,1084],{"class":815},[138,25615,25378],{"class":805},[138,25617,363],{"class":809},[138,25619,25620],{"class":805},"onMount",[138,25622,1109],{"class":815},[138,25624,25378],{"class":805},[138,25626,363],{"class":809},[138,25628,25620],{"class":805},[138,25630,363],{"class":809},[138,25632,25540],{"class":801},[138,25634,806],{"class":815},[138,25636,25479],{"class":805},[138,25638,872],{"class":815},[138,25640,25641,25643,25645],{"class":140,"line":257},[138,25642,1172],{"class":809},[138,25644,21376],{"class":794},[138,25646,934],{"class":809},[138,25648,25649,25652,25654,25656,25658,25660,25662,25664],{"class":140,"line":263},[138,25650,25651],{"class":801},"        patch",[138,25653,806],{"class":815},[138,25655,25479],{"class":805},[138,25657,363],{"class":809},[138,25659,25573],{"class":805},[138,25661,954],{"class":809},[138,25663,25526],{"class":805},[138,25665,872],{"class":815},[138,25667,25668,25670,25672,25674,25676],{"class":140,"line":269},[138,25669,25598],{"class":805},[138,25671,363],{"class":809},[138,25673,25573],{"class":805},[138,25675,1146],{"class":1112},[138,25677,25607],{"class":805},[138,25679,25680,25682,25684,25686,25688,25691,25693,25695,25697,25699,25701,25703,25705,25707],{"class":140,"line":275},[138,25681,1081],{"class":794},[138,25683,1084],{"class":815},[138,25685,25378],{"class":805},[138,25687,363],{"class":809},[138,25689,25690],{"class":805},"onUpdate",[138,25692,1109],{"class":815},[138,25694,25378],{"class":805},[138,25696,363],{"class":809},[138,25698,25690],{"class":805},[138,25700,363],{"class":809},[138,25702,25540],{"class":801},[138,25704,806],{"class":815},[138,25706,25479],{"class":805},[138,25708,872],{"class":815},[138,25710,25711],{"class":140,"line":281},[138,25712,8527],{"class":809},[138,25714,25715],{"class":140,"line":287},[138,25716,5229],{"class":809},[138,25718,25719],{"class":140,"line":293},[138,25720,649],{"emptyLinePlaceholder":86},[138,25722,25723,25726,25728],{"class":140,"line":299},[138,25724,25725],{"class":5196},"    destruir",[138,25727,3093],{"class":809},[138,25729,934],{"class":809},[138,25731,25732,25734,25736,25738,25740,25743,25745,25747,25749,25751,25753,25755,25757,25759],{"class":140,"line":305},[138,25733,8489],{"class":794},[138,25735,1084],{"class":815},[138,25737,25378],{"class":805},[138,25739,363],{"class":809},[138,25741,25742],{"class":805},"onDestroy",[138,25744,1109],{"class":815},[138,25746,25378],{"class":805},[138,25748,363],{"class":809},[138,25750,25742],{"class":805},[138,25752,363],{"class":809},[138,25754,25540],{"class":801},[138,25756,806],{"class":815},[138,25758,25479],{"class":805},[138,25760,872],{"class":815},[138,25762,25763,25765,25767,25769,25771,25773,25775],{"class":140,"line":311},[138,25764,8489],{"class":794},[138,25766,1084],{"class":815},[138,25768,25479],{"class":805},[138,25770,363],{"class":809},[138,25772,25573],{"class":805},[138,25774,1109],{"class":815},[138,25776,810],{"class":809},[138,25778,25779,25782,25784,25786,25788,25790,25792,25794,25796,25798],{"class":140,"line":317},[138,25780,25781],{"class":805},"        container",[138,25783,363],{"class":809},[138,25785,24066],{"class":801},[138,25787,806],{"class":815},[138,25789,25479],{"class":805},[138,25791,363],{"class":809},[138,25793,25573],{"class":805},[138,25795,363],{"class":809},[138,25797,23468],{"class":805},[138,25799,872],{"class":815},[138,25801,25802,25804,25806,25808,25810],{"class":140,"line":323},[138,25803,25598],{"class":805},[138,25805,363],{"class":809},[138,25807,25573],{"class":805},[138,25809,1146],{"class":1112},[138,25811,3186],{"class":2041},[138,25813,25814],{"class":140,"line":329},[138,25815,8527],{"class":809},[138,25817,25818],{"class":140,"line":335},[138,25819,1179],{"class":809},[138,25821,25822],{"class":140,"line":341},[138,25823,1184],{"class":809},[138,25825,25826],{"class":140,"line":347},[138,25827,649],{"emptyLinePlaceholder":86},[138,25829,25830,25833,25835,25837],{"class":140,"line":353},[138,25831,25832],{"class":805},"  componente",[138,25834,363],{"class":809},[138,25836,25311],{"class":801},[138,25838,2796],{"class":815},[138,25840,25841,25843],{"class":140,"line":622},[138,25842,3199],{"class":794},[138,25844,25845],{"class":805}," componente\n",[138,25847,25848],{"class":140,"line":628},[138,25849,2082],{"class":809},[12,25851,25852,25853,25856,25857,25860],{},"O uso fica separado em dois arquivos. O ",[135,25854,25855],{},"framework.js"," contém toda a lógica acima. O ",[135,25858,25859],{},"index.html"," contém apenas os componentes:",[129,25862,25864],{"className":5084,"code":25863,"language":5086,"meta":75,"style":75},"const app = document.getElementById('app')\nconst estadoGlobal = reativo({ tema: 'claro' })\n\ncriarComponente(\n  {\n    props: { titulo: 'Meu App' },\n    render() {\n      return h('div', {}, [\n        h('h2', {}, [t(this.props.titulo)]),\n        h('small', {}, [t(`Tema: ${estadoGlobal.tema}`)])\n      ])\n    },\n    onMount() {\n      console.log('Header montado')\n    }\n  },\n  app\n)\n\nconst contador = criarComponente(\n  {\n    estado: { valor: 0 },\n    render() {\n      return h('div', {}, [\n        h('p', {}, [t(`Contador: ${this.estado.valor}`)]),\n        h('button', { onclick: () => this.estado.valor++ }, [t('+1')])\n      ])\n    },\n    onDestroy() {\n      console.log('Contador removido')\n    }\n  },\n  app\n)\n",[135,25865,25866,25891,25922,25926,25932,25937,25959,25968,25988,26027,26069,26074,26078,26087,26106,26110,26114,26119,26123,26127,26139,26143,26159,26167,26187,26231,26285,26289,26293,26302,26321,26325,26329,26333],{"__ignoreMap":75},[138,25867,25868,25870,25872,25874,25876,25878,25881,25883,25885,25887,25889],{"class":140,"line":141},[138,25869,2518],{"class":1073},[138,25871,8250],{"class":2521},[138,25873,1146],{"class":1112},[138,25875,23443],{"class":805},[138,25877,363],{"class":809},[138,25879,25880],{"class":801},"getElementById",[138,25882,806],{"class":805},[138,25884,834],{"class":826},[138,25886,8262],{"class":830},[138,25888,834],{"class":826},[138,25890,872],{"class":805},[138,25892,25893,25895,25898,25900,25902,25904,25906,25909,25911,25913,25916,25918,25920],{"class":140,"line":76},[138,25894,2518],{"class":1073},[138,25896,25897],{"class":2521}," estadoGlobal",[138,25899,1146],{"class":1112},[138,25901,24386],{"class":801},[138,25903,806],{"class":805},[138,25905,3284],{"class":809},[138,25907,25908],{"class":815}," tema",[138,25910,782],{"class":809},[138,25912,957],{"class":826},[138,25914,25915],{"class":830},"claro",[138,25917,834],{"class":826},[138,25919,2823],{"class":809},[138,25921,872],{"class":805},[138,25923,25924],{"class":140,"line":152},[138,25925,649],{"emptyLinePlaceholder":86},[138,25927,25928,25930],{"class":140,"line":158},[138,25929,25356],{"class":801},[138,25931,19557],{"class":805},[138,25933,25934],{"class":140,"line":164},[138,25935,25936],{"class":809},"  {\n",[138,25938,25939,25941,25943,25945,25948,25950,25952,25955,25957],{"class":140,"line":170},[138,25940,25402],{"class":815},[138,25942,782],{"class":809},[138,25944,2812],{"class":809},[138,25946,25947],{"class":815}," titulo",[138,25949,782],{"class":809},[138,25951,957],{"class":826},[138,25953,25954],{"class":830},"Meu App",[138,25956,834],{"class":826},[138,25958,22966],{"class":809},[138,25960,25961,25964,25966],{"class":140,"line":176},[138,25962,25963],{"class":5196},"    render",[138,25965,3093],{"class":809},[138,25967,934],{"class":809},[138,25969,25970,25972,25974,25976,25978,25980,25982,25984,25986],{"class":140,"line":182},[138,25971,5211],{"class":794},[138,25973,23137],{"class":801},[138,25975,806],{"class":815},[138,25977,834],{"class":826},[138,25979,4369],{"class":830},[138,25981,834],{"class":826},[138,25983,954],{"class":809},[138,25985,23178],{"class":809},[138,25987,821],{"class":815},[138,25989,25990,25993,25995,25997,25999,26001,26003,26005,26007,26009,26011,26013,26015,26017,26019,26022,26025],{"class":140,"line":188},[138,25991,25992],{"class":801},"        h",[138,25994,806],{"class":815},[138,25996,834],{"class":826},[138,25998,30],{"class":830},[138,26000,834],{"class":826},[138,26002,954],{"class":809},[138,26004,23178],{"class":809},[138,26006,944],{"class":815},[138,26008,24305],{"class":801},[138,26010,806],{"class":815},[138,26012,8821],{"class":2041},[138,26014,363],{"class":809},[138,26016,9319],{"class":805},[138,26018,363],{"class":809},[138,26020,26021],{"class":805},"titulo",[138,26023,26024],{"class":815},")])",[138,26026,837],{"class":809},[138,26028,26029,26031,26033,26035,26038,26040,26042,26044,26046,26048,26050,26052,26055,26057,26060,26062,26065,26067],{"class":140,"line":194},[138,26030,25992],{"class":801},[138,26032,806],{"class":815},[138,26034,834],{"class":826},[138,26036,26037],{"class":830},"small",[138,26039,834],{"class":826},[138,26041,954],{"class":809},[138,26043,23178],{"class":809},[138,26045,944],{"class":815},[138,26047,24305],{"class":801},[138,26049,806],{"class":815},[138,26051,3791],{"class":826},[138,26053,26054],{"class":830},"Tema: ",[138,26056,3797],{"class":826},[138,26058,26059],{"class":805},"estadoGlobal",[138,26061,363],{"class":826},[138,26063,26064],{"class":805},"tema",[138,26066,3803],{"class":826},[138,26068,24321],{"class":815},[138,26070,26071],{"class":140,"line":199},[138,26072,26073],{"class":815},"      ])\n",[138,26075,26076],{"class":140,"line":204},[138,26077,5229],{"class":809},[138,26079,26080,26083,26085],{"class":140,"line":209},[138,26081,26082],{"class":5196},"    onMount",[138,26084,3093],{"class":809},[138,26086,934],{"class":809},[138,26088,26089,26091,26093,26095,26097,26099,26102,26104],{"class":140,"line":215},[138,26090,21350],{"class":805},[138,26092,363],{"class":809},[138,26094,9727],{"class":801},[138,26096,806],{"class":815},[138,26098,834],{"class":826},[138,26100,26101],{"class":830},"Header montado",[138,26103,834],{"class":826},[138,26105,872],{"class":815},[138,26107,26108],{"class":140,"line":221},[138,26109,1179],{"class":809},[138,26111,26112],{"class":140,"line":227},[138,26113,972],{"class":809},[138,26115,26116],{"class":140,"line":233},[138,26117,26118],{"class":805},"  app\n",[138,26120,26121],{"class":140,"line":239},[138,26122,872],{"class":805},[138,26124,26125],{"class":140,"line":245},[138,26126,649],{"emptyLinePlaceholder":86},[138,26128,26129,26131,26133,26135,26137],{"class":140,"line":251},[138,26130,2518],{"class":1073},[138,26132,15597],{"class":2521},[138,26134,1146],{"class":1112},[138,26136,25373],{"class":801},[138,26138,19557],{"class":805},[138,26140,26141],{"class":140,"line":257},[138,26142,25936],{"class":809},[138,26144,26145,26147,26149,26151,26153,26155,26157],{"class":140,"line":263},[138,26146,25421],{"class":815},[138,26148,782],{"class":809},[138,26150,2812],{"class":809},[138,26152,12969],{"class":815},[138,26154,782],{"class":809},[138,26156,5296],{"class":5295},[138,26158,22966],{"class":809},[138,26160,26161,26163,26165],{"class":140,"line":269},[138,26162,25963],{"class":5196},[138,26164,3093],{"class":809},[138,26166,934],{"class":809},[138,26168,26169,26171,26173,26175,26177,26179,26181,26183,26185],{"class":140,"line":275},[138,26170,5211],{"class":794},[138,26172,23137],{"class":801},[138,26174,806],{"class":815},[138,26176,834],{"class":826},[138,26178,4369],{"class":830},[138,26180,834],{"class":826},[138,26182,954],{"class":809},[138,26184,23178],{"class":809},[138,26186,821],{"class":815},[138,26188,26189,26191,26193,26195,26197,26199,26201,26203,26205,26207,26209,26211,26213,26215,26217,26219,26221,26223,26225,26227,26229],{"class":140,"line":281},[138,26190,25992],{"class":801},[138,26192,806],{"class":815},[138,26194,834],{"class":826},[138,26196,12],{"class":830},[138,26198,834],{"class":826},[138,26200,954],{"class":809},[138,26202,23178],{"class":809},[138,26204,944],{"class":815},[138,26206,24305],{"class":801},[138,26208,806],{"class":815},[138,26210,3791],{"class":826},[138,26212,24312],{"class":830},[138,26214,3797],{"class":826},[138,26216,8821],{"class":2041},[138,26218,363],{"class":826},[138,26220,16394],{"class":805},[138,26222,363],{"class":826},[138,26224,21313],{"class":805},[138,26226,3803],{"class":826},[138,26228,26024],{"class":815},[138,26230,837],{"class":809},[138,26232,26233,26235,26237,26239,26241,26243,26245,26247,26250,26252,26254,26256,26258,26260,26262,26264,26266,26268,26270,26272,26274,26276,26278,26281,26283],{"class":140,"line":287},[138,26234,25992],{"class":801},[138,26236,806],{"class":815},[138,26238,834],{"class":826},[138,26240,4510],{"class":830},[138,26242,834],{"class":826},[138,26244,954],{"class":809},[138,26246,2812],{"class":809},[138,26248,26249],{"class":801}," onclick",[138,26251,782],{"class":809},[138,26253,3034],{"class":809},[138,26255,1074],{"class":1073},[138,26257,6638],{"class":2041},[138,26259,363],{"class":809},[138,26261,16394],{"class":805},[138,26263,363],{"class":809},[138,26265,21313],{"class":805},[138,26267,10352],{"class":1112},[138,26269,5524],{"class":809},[138,26271,944],{"class":815},[138,26273,24305],{"class":801},[138,26275,806],{"class":815},[138,26277,834],{"class":826},[138,26279,26280],{"class":830},"+1",[138,26282,834],{"class":826},[138,26284,24321],{"class":815},[138,26286,26287],{"class":140,"line":293},[138,26288,26073],{"class":815},[138,26290,26291],{"class":140,"line":299},[138,26292,5229],{"class":809},[138,26294,26295,26298,26300],{"class":140,"line":305},[138,26296,26297],{"class":5196},"    onDestroy",[138,26299,3093],{"class":809},[138,26301,934],{"class":809},[138,26303,26304,26306,26308,26310,26312,26314,26317,26319],{"class":140,"line":311},[138,26305,21350],{"class":805},[138,26307,363],{"class":809},[138,26309,9727],{"class":801},[138,26311,806],{"class":815},[138,26313,834],{"class":826},[138,26315,26316],{"class":830},"Contador removido",[138,26318,834],{"class":826},[138,26320,872],{"class":815},[138,26322,26323],{"class":140,"line":317},[138,26324,1179],{"class":809},[138,26326,26327],{"class":140,"line":323},[138,26328,972],{"class":809},[138,26330,26331],{"class":140,"line":329},[138,26332,26118],{"class":805},[138,26334,26335],{"class":140,"line":335},[138,26336,872],{"class":805},[12,26338,26339,26340,26342],{},"Abre o DevTools na aba Elements e clica no botão. Você vai ver só o texto do ",[135,26341,22990],{}," sendo alterado, nada mais sendo recriado.",[30,26344,26346],{"id":26345},"o-que-o-vue-faz-além-disso","O que o Vue faz além disso",[12,26348,26349],{},"O que construímos aqui é o núcleo real. O Vue adiciona em cima:",[12,26351,26352,26355],{},[19,26353,26354],{},"Static hoisting:"," o compilador identifica VNodes que nunca mudam e os cria uma única vez fora da função de render, reutilizando na memória em vez de recriar a cada ciclo.",[12,26357,26358,26361],{},[19,26359,26360],{},"Patch flags:"," o compilador anota em tempo de build o que pode mudar em cada VNode. Em runtime, o patch pula a comparação do que está marcado como estático, tornando o diff cirúrgico por design.",[12,26363,26364,26367,26368,26370,26371,26373,26374,26367,26377,363],{},[19,26365,26366],{},"Keys em listas:"," sem ",[135,26369,24439],{},", o diff compara filhos por posição. Com ",[135,26372,24439],{},", o Vue identifica qual item é qual e consegue reordenar sem recriar. É por isso que o Vue reclama quando você usa ",[135,26375,26376],{},"v-for",[135,26378,26379],{},":key",[12,26381,26382,20157,26388,26390,26391,26393,26394,26397,26398,26400],{},[19,26383,26384,14438,26386,782],{},[135,26385,5010],{},[135,26387,9848],{},[135,26389,9848],{}," do Vue é exatamente o Proxy que implementamos. O ",[135,26392,5010],{}," é diferente: é implementado com getter\u002Fsetter em volta de um objeto ",[135,26395,26396],{},"{ value }",", por isso você acessa ",[135,26399,10288],{}," em vez de acessar direto. São duas formas de criar reatividade, com trade-offs diferentes.",[12,26402,26403,26406,26407,26409],{},[19,26404,26405],{},"Slots e Teleport:"," abstrações em cima do sistema de componentes que o ",[135,26408,25356],{}," simples não suporta.",[12,26411,26412],{},"A diferença entre o nosso mini-framework e o Vue não é conceitual, é o quanto de borda foi resolvida.",[30,26414,4866],{"id":4865},[12,26416,26417],{},"Virtual DOM é um objeto JavaScript que representa um elemento da UI. O framework mantém uma cópia desse objeto, compara com o estado anterior a cada mudança e aplica no DOM real só o que diferiu.",[12,26419,26420],{},"Reatividade com Proxy é o mecanismo que decide quem re-renderizar. Os dois sistemas juntos, Virtual DOM e Proxy, são o coração do Vue 3.",[12,26422,26423,26424,26426,26427,26429,26430,26432],{},"Entender isso muda como você usa o framework no dia a dia. Quando você vê ",[135,26425,25191],{},", sabe que é o ",[135,26428,25197],{}," aguardando o flush. Quando o Vue reclama de ",[135,26431,26379],{},", você sabe por que o diff precisa dela. Quando um componente re-renderiza mais do que deveria, você sabe onde procurar.",[12,26434,26435],{},"O framework não é mágica. É só JavaScript bem organizado.",[30,26437,9153],{"id":9152},[402,26439,26440],{},[405,26441,26442],{},[105,26443,26446],{"href":26444,"rel":26445},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fextras\u002Freactivity-in-depth",[109],"Reactivity in Depth - Documentação oficial do Vue",[1744,26448,26449],{},"html pre.shiki code .sFsEu, html code.shiki .sFsEu{--shiki-light:#9C3EDA;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sVPC0, html code.shiki .sVPC0{--shiki-light:#90A4AE;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sFfmW, html code.shiki .sFfmW{--shiki-light:#39ADB5;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sG-J9, html code.shiki .sG-J9{--shiki-light:#39ADB5;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sdv8B, html code.shiki .sdv8B{--shiki-light:#E53935;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sF_wb, html code.shiki .sF_wb{--shiki-light:#39ADB5;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .s0vBq, html code.shiki .s0vBq{--shiki-light:#91B859;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sqIbZ, html code.shiki .sqIbZ{--shiki-light:#E53935;--shiki-default:#85E89D;--shiki-dark:#85E89D}html pre.shiki code .s7047, html code.shiki .s7047{--shiki-light:#9C3EDA;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sMo7A, html code.shiki .sMo7A{--shiki-light:#90A4AE;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sK_r7, html code.shiki .sK_r7{--shiki-light:#6182B8;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .swu5b, html code.shiki .swu5b{--shiki-light:#39ADB5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sk1zL, html code.shiki .sk1zL{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#FFAB70;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .s3Er8, html code.shiki .s3Er8{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .soiBB, html code.shiki .soiBB{--shiki-light:#E2931D;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .s_k96, html code.shiki .s_k96{--shiki-light:#F76D47;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s0u7J, html code.shiki .s0u7J{--shiki-light:#E53935;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sMrrN, html code.shiki .sMrrN{--shiki-light:#FF5370;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s3afY, html code.shiki .s3afY{--shiki-light:#E2931D;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}",{"title":75,"searchDepth":76,"depth":76,"links":26451},[26452,26453,26454,26463,26464,26465,26466,26467,26468],{"id":22872,"depth":76,"text":22873},{"id":22908,"depth":76,"text":22909},{"id":23117,"depth":76,"text":23118,"children":26455},[26456,26458,26460,26462],{"id":23121,"depth":152,"text":26457},"1. h(): criar o VNode",{"id":23397,"depth":152,"text":26459},"2. mount(): primeira montagem",{"id":23631,"depth":152,"text":26461},"3. patch(): atualizar só o que mudou",{"id":24133,"depth":152,"text":24134},{"id":24333,"depth":76,"text":24334},{"id":25184,"depth":76,"text":25185},{"id":25349,"depth":76,"text":25350},{"id":26345,"depth":76,"text":26346},{"id":4865,"depth":76,"text":4866},{"id":9152,"depth":76,"text":9153},"2026-03-08T22:05:00-03:00","Se você usa Vue ou React, já usa Virtual DOM todo dia. Mas o que acontece de verdade quando você muda um ref() e a tela atualiza? Neste artigo construímos um mini-framework do zero com reatividade, ciclo de vida e componentes — só JavaScript puro.","\u002Fimages\u002Fblog\u002Fvirtual-dom-capa.png",{},"\u002Fblog\u002Fvirtual-dom-mini-framework-do-zero",{"title":22858,"description":26470},"blog\u002Fvirtual-dom-mini-framework-do-zero",[20952,13337,13338,26477],"performance","s8onckTubAmY8q4-j44fsuo4Yr26i6dGY4-PWFgAphM",{"id":26480,"title":26481,"author":7,"body":26482,"date":26606,"description":26607,"extension":83,"image":26608,"meta":26609,"navigation":86,"path":26610,"seo":26611,"stem":26612,"tags":26613,"__hash__":26614},"blog\u002Fblog\u002Fia-no-frontend-ameaca-ou-multiplicador.md","O Papel da IA no Frontend: ameaça ou multiplicador de habilidade?",{"type":9,"value":26483,"toc":26599},[26484,26487,26491,26494,26500,26503,26520,26523,26527,26530,26533,26540,26543,26547,26553,26556,26559,26563,26566,26583,26586,26590,26593,26596],[12,26485,26486],{},"A chegada das IAs ao fluxo de desenvolvimento levantou muitas discussões sobre substituição de profissionais, inclusive no frontend. Quero compartilhar minha visão sobre isso, não como especulação, mas com base no que tenho vivido no dia a dia.",[30,26488,26490],{"id":26489},"se-a-ia-cria-uma-aplicação-em-minutos-pra-que-um-desenvolvedor","\"Se a IA cria uma aplicação em minutos, pra que um desenvolvedor?\"",[12,26492,26493],{},"Com a facilidade de gerar interfaces, protótipos e até trechos inteiros de código em segundos, surgiram discursos como esse. E entendo de onde vêm. Visualmente, parece razoável: você descreve uma tela, a IA entrega, você ajusta. Rápido demais.",[12,26495,26496,26497],{},"Mas essa visão ignora algo fundamental: ",[19,26498,26499],{},"a IA só funciona bem quando guiada por alguém que realmente entende o que está fazendo.",[12,26501,26502],{},"Gerar telas bonitas e cheias de elementos interativos é fácil. Difícil é garantir:",[402,26504,26505,26508,26511,26514,26517],{},[405,26506,26507],{},"arquitetura sólida e escalável",[405,26509,26510],{},"segurança nas camadas certas",[405,26512,26513],{},"organização do projeto pensando em manutenção",[405,26515,26516],{},"regras de negócio aplicadas corretamente",[405,26518,26519],{},"consistência no padrão da aplicação ao longo do tempo",[12,26521,26522],{},"Uma interface gerada em segundos não aponta problemas ocultos, não antecipa gargalos, não entende o contexto do negócio e, principalmente, não tem discernimento técnico. Sem orientação, a IA produz código frágil, acoplado, que desmorona conforme o projeto cresce.",[30,26524,26526],{"id":26525},"o-que-realmente-acontece-no-dia-a-dia","O que realmente acontece no dia a dia",[12,26528,26529],{},"No meu caso concreto: tarefas que levariam dias foram resolvidas em horas.",[12,26531,26532],{},"Tenho um portfólio que criei originalmente em Quasar. Com IA, converti os componentes para Nuxt rapidamente, sem precisar reescrever tudo do zero com base na memória da documentação. Gerei as configurações de SEO, resolvi dúvidas pontuais de sintaxe e depurei problemas específicos tudo em uma fração do tempo que levaria antes.",[12,26534,26535,26536,26539],{},"Mas o que mais me surpreendeu foi um uso menos óbvio: durante o desenvolvimento dos projetos pessoais, usei a IA como um ",[19,26537,26538],{},"diário técnico",". Ia relatando o que estava fazendo, as decisões que tomava, os problemas que encontrava. No final, pedi pra ela gerar um relatório completo com base nos meus relatos. O resultado foi um registro detalhado de tudo que construí útil pra documentação, pra portfólio e pra reflexão sobre as próprias decisões técnicas.",[12,26541,26542],{},"Isso não é a IA trabalhando por mim. É eu usando uma ferramenta de forma inteligente.",[30,26544,26546],{"id":26545},"a-combinação-que-funciona","A combinação que funciona",[12,26548,26549,26550],{},"O que realmente funciona é: ",[19,26551,26552],{},"expertise humana + ferramenta bem direcionada.",[12,26554,26555],{},"Quando você sabe avaliar, ajustar, orientar e revisar o código gerado, a IA se torna um multiplicador de produtividade. Ela acelera o trabalho, mas não pensa por você. A força não está em \"mandar a IA fazer\" está em saber o que pedir, por que pedir e como validar o que veio.",[12,26557,26558],{},"Sem esse discernimento, o código gerado parece funcionar, passa nos testes básicos, mas carrega decisões questionáveis que só aparecem quando o projeto cresce ou quando algo quebra em produção.",[30,26560,26562],{"id":26561},"o-que-muda-na-carreira","O que muda na carreira",[12,26564,26565],{},"Apenas \"digitadores de código\" são substituíveis. O mercado agora valoriza quem:",[402,26567,26568,26571,26574,26577,26580],{},[405,26569,26570],{},"entende tecnologia de verdade, não só a sintaxe",[405,26572,26573],{},"sabe estruturar, revisar e pensar em solução",[405,26575,26576],{},"domina prompting e contexto sabe como conversar com a ferramenta",[405,26578,26579],{},"consegue transformar um objetivo de negócio em arquitetura",[405,26581,26582],{},"usa a IA como ferramenta, não como muleta",[12,26584,26585],{},"Essa nova fase exige evolução profissional. Não é uma ameaça pra quem já pensa dessa forma é uma aceleração.",[30,26587,26589],{"id":26588},"não-é-sobre-competir-com-ia","Não é sobre competir com IA",[12,26591,26592],{},"No fim, não é sobre competir com IA. É sobre ser capaz de trabalhar com ela, aumentando sua capacidade e entregando resultados melhores e mais rápidos.",[12,26594,26595],{},"Quem entende o que está fazendo vai usar IA e produzir mais. Quem não entende vai usar IA e produzir código que ninguém consegue manter.",[12,26597,26598],{},"A diferença está no conhecimento de base e esse, por enquanto, ainda é nosso.",{"title":75,"searchDepth":76,"depth":76,"links":26600},[26601,26602,26603,26604,26605],{"id":26489,"depth":76,"text":26490},{"id":26525,"depth":76,"text":26526},{"id":26545,"depth":76,"text":26546},{"id":26561,"depth":76,"text":26562},{"id":26588,"depth":76,"text":26589},"2025-12-18T17:30:00-03:00","A IA está mudando o fluxo de desenvolvimento frontend. Mas substituição ou aceleração? Depois de usar IA no dia a dia por meses, essa é minha visão honesta sobre o que ela resolve, o que ela não resolve, e o que muda na carreira de quem desenvolve.","\u002Fimages\u002Fblog\u002Fia-no-front.png",{},"\u002Fblog\u002Fia-no-frontend-ameaca-ou-multiplicador",{"title":26481,"description":26607},"blog\u002Fia-no-frontend-ameaca-ou-multiplicador",[13338,92,91,94],"h1ljQY7zewTjB0IAzzeTGTGSApD9LhyAa41wdLCnCT4",{"id":26616,"title":26617,"author":7,"body":26618,"date":27566,"description":27567,"extension":83,"image":27568,"meta":27569,"navigation":86,"path":27570,"seo":27571,"stem":27572,"tags":27573,"__hash__":27575},"blog\u002Fblog\u002Fwebinar-claude-code.md","Claude Code em Ação: do Prompt ao Código",{"type":9,"value":26619,"toc":27546},[26620,26637,26640,26643,26647,26650,26653,26657,26676,26681,26689,26692,26724,26727,26762,26770,26774,26777,26783,26789,26795,26805,26809,26812,26886,26894,26898,26901,27003,27011,27015,27018,27026,27032,27052,27060,27064,27067,27071,27077,27104,27109,27132,27138,27146,27150,27153,27158,27192,27200,27253,27256,27269,27274,27279,27283,27286,27292,27298,27304,27310,27315,27329,27333,27336,27356,27361,27367,27372,27378,27382,27385,27388,27432,27436,27439,27445,27451,27457,27461,27464,27467,27473,27482,27484,27488,27543],[12,26621,26622,26623,26626,26627,377,26632,363],{},"Algumas oportunidades chegam de um jeito que você não esperava. Foi assim com o convite para ministrar o webinar ",[19,26624,26625],{},"\"Claude Code em Ação: do Prompt ao Código\"",", promovido pela ",[105,26628,26631],{"href":26629,"rel":26630},"https:\u002F\u002Fnuvini.com",[109],"Nuvini",[105,26633,26636],{"href":26634,"rel":26635},"https:\u002F\u002Fdatahub.com.br",[109],"Datahub Big Data & Analytics",[12,26638,26639],{},"Fui convidada após me destacar nos testes e aprendizados internos com a ferramenta, como parte de um investimento das empresas para explorar o potencial do Claude Code no dia a dia de desenvolvimento. Foi uma responsabilidade enorme e, ao mesmo tempo, uma das experiências mais gratificantes que já tive na carreira.",[12,26641,26642],{},"Neste post, trago o conteúdo completo que apresentei no evento.",[30,26644,26646],{"id":26645},"o-que-é-claude-code","O que é Claude Code?",[12,26648,26649],{},"Claude Code é a ferramenta de IA da Anthropic integrada a editores de código como o VSCode, projetada especificamente para assistência em programação. Ela funciona diretamente via terminal, oferecendo comandos slash para interações rápidas e poderosas com a inteligência artificial.",[12,26651,26652],{},"No cenário atual de IA generativa, o Claude Code se destaca por focar em segurança, produtividade e personalização avançada.",[1643,26654,26656],{"id":26655},"instalação","Instalação",[129,26658,26660],{"className":1547,"code":26659,"language":1549,"meta":75,"style":75},"npm install -g @anthropic\u002Fclaude-code\n",[135,26661,26662],{"__ignoreMap":75},[138,26663,26664,26667,26670,26673],{"class":140,"line":141},[138,26665,26666],{"class":1556},"npm",[138,26668,26669],{"class":830}," install",[138,26671,26672],{"class":1563}," -g",[138,26674,26675],{"class":830}," @anthropic\u002Fclaude-code\n",[12,26677,26678],{},[19,26679,26680],{},"Pré-requisitos:",[402,26682,26683,26686],{},[405,26684,26685],{},"Node.js 18 ou superior",[405,26687,26688],{},"Uma conta na Anthropic (solicitada na primeira execução)",[12,26690,26691],{},"Após a instalação, você pode iniciar o Claude Code de duas formas:",[129,26693,26695],{"className":1547,"code":26694,"language":1549,"meta":75,"style":75},"# Iniciar no diretório atual\nclaude\n\n# Ou especificar um diretório\nclaude \u002Fcaminho\u002Fpara\u002Fprojeto\n",[135,26696,26697,26702,26707,26711,26716],{"__ignoreMap":75},[138,26698,26699],{"class":140,"line":141},[138,26700,26701],{"class":911},"# Iniciar no diretório atual\n",[138,26703,26704],{"class":140,"line":76},[138,26705,26706],{"class":1556},"claude\n",[138,26708,26709],{"class":140,"line":152},[138,26710,649],{"emptyLinePlaceholder":86},[138,26712,26713],{"class":140,"line":158},[138,26714,26715],{"class":911},"# Ou especificar um diretório\n",[138,26717,26718,26721],{"class":140,"line":164},[138,26719,26720],{"class":1556},"claude",[138,26722,26723],{"class":830}," \u002Fcaminho\u002Fpara\u002Fprojeto\n",[12,26725,26726],{},"Também é possível usar o Claude sem o terminal interativo, passando comandos diretamente:",[129,26728,26730],{"className":1547,"code":26729,"language":1549,"meta":75,"style":75},"claude -p \"Stage my changes and write a set of commits for them\" --allowedTools \"Bash,Read\" --permission-mode acceptEdits\n",[135,26731,26732],{"__ignoreMap":75},[138,26733,26734,26736,26739,26741,26744,26746,26749,26751,26754,26756,26759],{"class":140,"line":141},[138,26735,26720],{"class":1556},[138,26737,26738],{"class":1563}," -p",[138,26740,1567],{"class":826},[138,26742,26743],{"class":830},"Stage my changes and write a set of commits for them",[138,26745,1433],{"class":826},[138,26747,26748],{"class":1563}," --allowedTools",[138,26750,1567],{"class":826},[138,26752,26753],{"class":830},"Bash,Read",[138,26755,1433],{"class":826},[138,26757,26758],{"class":1563}," --permission-mode",[138,26760,26761],{"class":830}," acceptEdits\n",[12,26763,26764,26765],{},"Documentação sobre o modo headless: ",[105,26766,26769],{"href":26767,"rel":26768},"https:\u002F\u002Fdocs.claude.com\u002Fen\u002Fdocs\u002Fclaude-code\u002Fheadless",[109],"docs.claude.com",[30,26771,26773],{"id":26772},"conceitos-fundamentais","Conceitos Fundamentais",[12,26775,26776],{},"Antes de sair usando a ferramenta, vale entender os quatro pilares que estruturam o funcionamento do Claude Code.",[12,26778,26779,26782],{},[19,26780,26781],{},"Prompt:"," instrução que guia a IA para gerar respostas úteis e contextualizadas para o seu projeto.",[12,26784,26785,26788],{},[19,26786,26787],{},"Agentes:"," subassistentes especializados, configuráveis para tarefas específicas como revisão de código ou debugging.",[12,26790,26791,26794],{},[19,26792,26793],{},"MCP Protocol:"," Model Context Protocol para gerenciar contexto e referências de arquivos de forma inteligente.",[12,26796,26797,26800,26801,26804],{},[19,26798,26799],{},"Memória:"," arquivos ",[135,26802,26803],{},"CLAUDE.md"," que armazenam informações persistentes e contexto do projeto para a IA.",[1643,26806,26808],{"id":26807},"ferramentas-internas-tools","Ferramentas internas (Tools)",[12,26810,26811],{},"O Claude Code usa ferramentas internas para executar tarefas de desenvolvimento. Cada uma tem um papel específico, permitindo que o modelo não fique só no texto, mas interaja com o ambiente de programação:",[1915,26813,26814,26824],{},[1918,26815,26816],{},[1921,26817,26818,26821],{},[1924,26819,26820],{},"Tool",[1924,26822,26823],{},"O que faz",[1931,26825,26826,26836,26846,26856,26866,26876],{},[1921,26827,26828,26833],{},[1936,26829,26830],{},[135,26831,26832],{},"Read(arquivo)",[1936,26834,26835],{},"Leitura do arquivo para analisar o conteúdo",[1921,26837,26838,26843],{},[1936,26839,26840],{},[135,26841,26842],{},"Write(arquivo)",[1936,26844,26845],{},"Criação ou sobrescrita de arquivo",[1921,26847,26848,26853],{},[1936,26849,26850],{},[135,26851,26852],{},"Update(arquivo)",[1936,26854,26855],{},"Alteração parcial dentro de um arquivo existente",[1921,26857,26858,26863],{},[1936,26859,26860],{},[135,26861,26862],{},"Delete(arquivo)",[1936,26864,26865],{},"Exclusão de arquivo do projeto",[1921,26867,26868,26873],{},[1936,26869,26870],{},[135,26871,26872],{},"Search(...)",[1936,26874,26875],{},"Busca de informação e documentação",[1921,26877,26878,26883],{},[1936,26879,26880],{},[135,26881,26882],{},"Run(comando)",[1936,26884,26885],{},"Execução de comando no terminal",[12,26887,26888,26889,363],{},"Todas as ferramentas disponíveis estão na ",[105,26890,26893],{"href":26891,"rel":26892},"https:\u002F\u002Fdocs.claude.com\u002Fen\u002Fdocs\u002Fclaude-code\u002Fsettings#tools-available-to-claude",[109],"documentação oficial",[30,26895,26897],{"id":26896},"comandos-essenciais-built-in-slash-commands","Comandos Essenciais (Built-in Slash Commands)",[12,26899,26900],{},"Os slash commands são uma das partes mais práticas do Claude Code. Aqui estão os principais:",[1915,26902,26903,26912],{},[1918,26904,26905],{},[1921,26906,26907,26910],{},[1924,26908,26909],{},"Comando",[1924,26911,26823],{},[1931,26913,26914,26924,26934,26944,26957,26970,26983,26993],{},[1921,26915,26916,26921],{},[1936,26917,26918],{},[135,26919,26920],{},"\u002Finit",[1936,26922,26923],{},"Inicializa um novo projeto Claude Code no diretório atual",[1921,26925,26926,26931],{},[1936,26927,26928],{},[135,26929,26930],{},"\u002Fmodel",[1936,26932,26933],{},"Alterna entre diferentes modelos disponíveis (Sonnet, Opus, etc.)",[1921,26935,26936,26941],{},[1936,26937,26938],{},[135,26939,26940],{},"\u002Fagents",[1936,26942,26943],{},"Gerencia agentes personalizados para tarefas específicas",[1921,26945,26946,26954],{},[1936,26947,26948,377,26951],{},[135,26949,26950],{},"\u002Fclear",[135,26952,26953],{},"\u002Fcompact",[1936,26955,26956],{},"Limpa o histórico ou compacta conversas para manter o foco",[1921,26958,26959,26967],{},[1936,26960,26961,377,26964],{},[135,26962,26963],{},"\u002Fconfig",[135,26965,26966],{},"\u002Fpermissions",[1936,26968,26969],{},"Configura o ambiente e gerencia permissões de acesso",[1921,26971,26972,26980],{},[1936,26973,26974,377,26977],{},[135,26975,26976],{},"\u002Freview",[135,26978,26979],{},"\u002Fmemory",[1936,26981,26982],{},"Solicita revisão de código e edita arquivos de memória",[1921,26984,26985,26990],{},[1936,26986,26987],{},[135,26988,26989],{},"\u002Frewind",[1936,26991,26992],{},"Volta a conversa para um ponto anterior",[1921,26994,26995,27000],{},[1936,26996,26997],{},[135,26998,26999],{},"\u002Fmcp",[1936,27001,27002],{},"Gerencia e executa plugins (Model Context Protocols)",[12,27004,27005,27006],{},"Mais comandos disponíveis em: ",[105,27007,27010],{"href":27008,"rel":27009},"https:\u002F\u002Fdocs.claude.com\u002Fen\u002Fdocs\u002Fclaude-code\u002Fslash-commands#personal-commands",[109],"docs.claude.com\u002Fslash-commands",[30,27012,27014],{"id":27013},"criando-e-utilizando-agentes","Criando e Utilizando Agentes",[12,27016,27017],{},"Agentes são subassistentes configuráveis para tarefas específicas, como revisão de segurança ou otimização de performance. Eles aumentam a produtividade por meio da especialização.",[15084,27019,27020],{},[12,27021,27022,27025],{},[19,27023,27024],{},"Atenção:"," cada agente tem seu próprio contexto (system prompt), carregado no momento em que é ativado. Por esse motivo, pode consumir mais tokens.",[12,27027,27028,27029,27031],{},"Para criar um agente, basta rodar ",[135,27030,26940],{},". Uma interface passo a passo vai guiar a criação com as seguintes configurações disponíveis:",[402,27033,27034,27037,27040,27043,27046,27049],{},[405,27035,27036],{},"Nome do agente",[405,27038,27039],{},"System Prompt",[405,27041,27042],{},"Modelo do agente",[405,27044,27045],{},"Permissões de acesso e tools",[405,27047,27048],{},"Tipo do agente (Pessoal ou Projeto)",[405,27050,27051],{},"Cor do agente (para destacar no terminal)",[12,27053,27054,27055],{},"Mais detalhes em: ",[105,27056,27059],{"href":27057,"rel":27058},"https:\u002F\u002Fdocs.claude.com\u002Fen\u002Fdocs\u002Fclaude-code\u002Fsub-agents",[109],"docs.claude.com\u002Fsub-agents",[30,27061,27063],{"id":27062},"model-context-protocol-mcp","Model Context Protocol (MCP)",[12,27065,27066],{},"O MCP é o protocolo que conecta o Claude a recursos externos como APIs, bancos de dados, arquivos e sistemas internos. Ele fornece contexto atualizado de documentação e torna as respostas muito mais precisas e relevantes.",[1643,27068,27070],{"id":27069},"exemplos-de-mcps-úteis","Exemplos de MCPs úteis",[12,27072,27073,27076],{},[19,27074,27075],{},"Context7"," para documentação atualizada de bibliotecas:",[129,27078,27080],{"className":1547,"code":27079,"language":1549,"meta":75,"style":75},"claude mcp add --transport http context7 https:\u002F\u002Fmcp.context7.com\u002Fmcp\n",[135,27081,27082],{"__ignoreMap":75},[138,27083,27084,27086,27089,27092,27095,27098,27101],{"class":140,"line":141},[138,27085,26720],{"class":1556},[138,27087,27088],{"class":830}," mcp",[138,27090,27091],{"class":830}," add",[138,27093,27094],{"class":1563}," --transport",[138,27096,27097],{"class":830}," http",[138,27099,27100],{"class":830}," context7",[138,27102,27103],{"class":830}," https:\u002F\u002Fmcp.context7.com\u002Fmcp\n",[12,27105,27106,27108],{},[19,27107,16782],{}," para acessar designs diretamente:",[129,27110,27112],{"className":1547,"code":27111,"language":1549,"meta":75,"style":75},"claude mcp add --transport http figma-dev-mode-mcp-server https:\u002F\u002Fmcp.figma.com\u002Fmcp\n",[135,27113,27114],{"__ignoreMap":75},[138,27115,27116,27118,27120,27122,27124,27126,27129],{"class":140,"line":141},[138,27117,26720],{"class":1556},[138,27119,27088],{"class":830},[138,27121,27091],{"class":830},[138,27123,27094],{"class":1563},[138,27125,27097],{"class":830},[138,27127,27128],{"class":830}," figma-dev-mode-mcp-server",[138,27130,27131],{"class":830}," https:\u002F\u002Fmcp.figma.com\u002Fmcp\n",[12,27133,27134,27137],{},[19,27135,27136],{},"Exemplo prático:"," se você perguntar \"Como uso useQuery do Apollo Client?\" e incluir \"use context7\", o MCP vai buscar a documentação atual do Apollo Client, e não algo que a IA lembra de versões antigas. Isso faz uma diferença enorme no dia a dia.",[12,27139,27140,27141],{},"Documentação: ",[105,27142,27145],{"href":27143,"rel":27144},"https:\u002F\u002Fdocs.claude.com\u002Fen\u002Fdocs\u002Fclaude-code\u002Fmcp",[109],"docs.claude.com\u002Fmcp",[30,27147,27149],{"id":27148},"criação-de-comandos-personalizados","Criação de Comandos Personalizados",[12,27151,27152],{},"Slash commands personalizados são úteis para automatizar tarefas repetitivas, padronizar processos da equipe e acelerar o fluxo de trabalho com prompts consistentes.",[12,27154,27155],{},[19,27156,27157],{},"Para criar um comando:",[15017,27159,27160,27171,27178],{},[405,27161,27162,27163,27166,27167,27170],{},"Crie a pasta ",[135,27164,27165],{},".claude\u002Fcommands\u002F"," (comandos do projeto) ou ",[135,27168,27169],{},"~\u002F.claude\u002Fcommands\u002F"," (válidos em todos os projetos)",[405,27172,27173,27174,27177],{},"Adicione um arquivo ",[135,27175,27176],{},".md"," descrevendo a função do comando",[405,27179,27180,27181,370,27184,27187,27188,27191],{},"Use ",[135,27182,27183],{},"$1",[135,27185,27186],{},"$2",", etc. para parâmetros individuais, ou ",[135,27189,27190],{},"$ARGUMENTS"," para capturar todos de uma vez",[12,27193,27194],{},[19,27195,27196,27197],{},"Exemplo: ",[135,27198,27199],{},"revisar-codigo.md",[129,27201,27204],{"className":27202,"code":27203,"language":83,"meta":75,"style":75},"language-md shiki shiki-themes material-theme-lighter github-dark github-dark","---\ndescription: Revisar código em busca de melhorias\nargument-hint: [caminho-do-arquivo]\n---\n\nAnalise o código em `$1`, identifique possíveis bugs, problemas de performance e sugira melhorias.\n",[135,27205,27206,27212,27217,27230,27235,27239],{"__ignoreMap":75},[138,27207,27208],{"class":140,"line":141},[138,27209,27211],{"class":27210},"sLXsH","---\n",[138,27213,27214],{"class":140,"line":76},[138,27215,27216],{"class":805},"description: Revisar código em busca de melhorias\n",[138,27218,27219,27222,27224,27228],{"class":140,"line":152},[138,27220,27221],{"class":805},"argument-hint: ",[138,27223,3883],{"class":809},[138,27225,27227],{"class":27226},"sM8Rc","caminho-do-arquivo",[138,27229,8056],{"class":809},[138,27231,27232],{"class":140,"line":158},[138,27233,27211],{"class":27234},"sSElE",[138,27236,27237],{"class":140,"line":164},[138,27238,649],{"emptyLinePlaceholder":86},[138,27240,27241,27244,27246,27248,27250],{"class":140,"line":170},[138,27242,27243],{"class":805},"Analise o código em ",[138,27245,3791],{"class":2041},[138,27247,27183],{"class":1563},[138,27249,3791],{"class":2041},[138,27251,27252],{"class":805},", identifique possíveis bugs, problemas de performance e sugira melhorias.\n",[12,27254,27255],{},"Depois, no Claude Code:",[129,27257,27259],{"className":1547,"code":27258,"language":1549,"meta":75,"style":75},"\u002Frevisar-codigo src\u002Futils\u002Fhelpers.js\n",[135,27260,27261],{"__ignoreMap":75},[138,27262,27263,27266],{"class":140,"line":141},[138,27264,27265],{"class":1556},"\u002Frevisar-codigo",[138,27267,27268],{"class":830}," src\u002Futils\u002Fhelpers.js\n",[12,27270,1193,27271,27273],{},[135,27272,27183],{}," será substituído pelo caminho informado e a revisão acontece automaticamente.",[12,27275,27140,27276],{},[105,27277,27010],{"href":27008,"rel":27278},[109],[30,27280,27282],{"id":27281},"modos-de-operação","Modos de Operação",[12,27284,27285],{},"O Claude Code tem quatro modos de operação, cada um adequado a um tipo diferente de situação:",[12,27287,27288,27291],{},[19,27289,27290],{},"Safe Mode (padrão):"," Claude solicita confirmação antes de implementar cada modificação. Ideal para revisar mudanças sensíveis.",[12,27293,27294,27297],{},[19,27295,27296],{},"Plan Mode:"," Claude cria um plano de ação detalhado antes de realizar qualquer edição. Perfeito para tarefas complexas.",[12,27299,27300,27303],{},[19,27301,27302],{},"All Edits On:"," Claude edita múltiplos arquivos simultaneamente sem pedir confirmação. Agiliza o desenvolvimento, mas exige confiança total no modelo.",[12,27305,27306,27309],{},[19,27307,27308],{},"Thinking On:"," Claude mostra seu processo de raciocínio antes de responder. Útil para entender a lógica por trás das soluções.",[12,27311,27312],{},[19,27313,27314],{},"Atalhos:",[402,27316,27317,27323],{},[405,27318,27319,27320],{},"Alternar entre modos: ",[135,27321,27322],{},"Alt+M",[405,27324,27325,27326],{},"Ativar\u002FDesativar o modo Thinking: ",[135,27327,27328],{},"Tab",[30,27330,27332],{"id":27331},"como-criar-prompts-eficazes","Como Criar Prompts Eficazes",[12,27334,27335],{},"A qualidade do prompt define a qualidade do resultado. Três princípios básicos:",[15017,27337,27338,27344,27350],{},[405,27339,27340,27343],{},[19,27341,27342],{},"Seja claro:"," descreva exatamente o que você precisa, incluindo contexto e objetivos específicos.",[405,27345,27346,27349],{},[19,27347,27348],{},"Use exemplos:"," forneça exemplos concretos do input e output desejado para guiar a IA.",[405,27351,27352,27355],{},[19,27353,27354],{},"Delimite o escopo:"," defina limites claros e especifique o formato de resposta esperado.",[12,27357,27358],{},[19,27359,27360],{},"Exemplo para otimização de código:",[129,27362,27365],{"className":27363,"code":27364,"language":13552},[13550],"Otimize esta função JavaScript para melhor performance, mantendo a legibilidade. Foque em reduzir complexidade O(n²) e explique as mudanças.\n",[135,27366,27364],{"__ignoreMap":75},[12,27368,27369],{},[19,27370,27371],{},"Exemplo com menção de arquivos:",[129,27373,27376],{"className":27374,"code":27375,"language":13552},[13550],"Crie um novo service em @src\u002Fservices\u002F chamado UserService.js. Nele iremos lidar com CRUD de usuários nos seguintes endpoints: get-users (GET), get-user-by-id (GET), get-user-details (GET), add-users (POST), edit-users (POST) e delete-users (POST). Siga o padrão de @services\u002FApiService.js e, assim como nele, utilize axios.\n",[135,27377,27375],{"__ignoreMap":75},[30,27379,27381],{"id":27380},"desafios-do-uso-de-ia-na-geração-de-código","Desafios do Uso de IA na Geração de Código",[12,27383,27384],{},"Por mais incrível que seja a ferramenta, é importante entender seus limites. Por mais que o código seja gerado por IA, a responsabilidade ainda é sua.",[12,27386,27387],{},"Os principais desafios que você vai encontrar:",[402,27389,27390,27396,27402,27408,27414,27420,27426],{},[405,27391,27392,27395],{},[19,27393,27394],{},"Contradições:"," o modelo pode gerar respostas inconsistentes em momentos diferentes da mesma sessão.",[405,27397,27398,27401],{},[19,27399,27400],{},"Repetição:"," respostas redundantes que atrapalham a clareza do resultado.",[405,27403,27404,27407],{},[19,27405,27406],{},"Ambiguidade:"," respostas com múltiplas interpretações, dificultando a implementação correta.",[405,27409,27410,27413],{},[19,27411,27412],{},"Estereótipos:"," decisões baseadas em ideias simplificadas sobre determinadas situações ou personas.",[405,27415,27416,27419],{},[19,27417,27418],{},"Alucinações:"," informações inventadas pelo modelo que parecem corretas, mas não têm base real.",[405,27421,27422,27425],{},[19,27423,27424],{},"Preconceito:"," sugestões enviesadas, como indicar ferramentas ou frameworks que não fazem sentido para aquele contexto.",[405,27427,27428,27431],{},[19,27429,27430],{},"Generalização excessiva:"," tirar conclusões amplas a partir de poucos exemplos, aplicando uma regra de forma errada para todos os casos.",[30,27433,27435],{"id":27434},"claude-code-vs-claude-no-navegador","Claude Code vs. Claude no Navegador",[12,27437,27438],{},"Para quem está acostumado a usar o Claude pelo navegador, vale entender as vantagens específicas do Claude Code para desenvolvimento:",[12,27440,27441,27444],{},[19,27442,27443],{},"Integração nativa:"," você trabalha diretamente no VSCode, sem alternar entre ferramentas e mantendo o foco total no código.",[12,27446,27447,27450],{},[19,27448,27449],{},"Automação avançada:"," os comandos slash permitem automação e personalização do fluxo de trabalho de um jeito que o navegador não oferece.",[12,27452,27453,27456],{},[19,27454,27455],{},"Segurança local:"," os dados permanecem no ambiente local, reduzindo exposição e aumentando a segurança do projeto.",[30,27458,27460],{"id":27459},"o-que-ficou-de-aprendizado","O que ficou de aprendizado",[12,27462,27463],{},"Preparar e ministrar esse webinar foi uma experiência que me fez enxergar o Claude Code de uma forma ainda mais completa. Explicar algo para outras pessoas exige que você realmente entenda o que está falando, e isso me fez aprofundar em partes da ferramenta que ainda não tinha explorado tanto.",[12,27465,27466],{},"O que mais me surpreende no Claude Code é a possibilidade de personalização: agentes especializados, comandos próprios, integração com MCPs externos. Não é só um chat com IA dentro do editor. É uma camada de automação que você molda para o seu fluxo de trabalho.",[12,27468,27469,27470,27472],{},"Se você ainda não testou, minha sugestão é começar pelo básico: instale, rode ",[135,27471,26920],{}," no seu projeto e experimente descrever uma tarefa real. A partir daí, as possibilidades se abrem naturalmente.",[12,27474,27475],{},[138,27476,27477],{},[27478,27479],"img",{"alt":27480,"src":27481},"Certificado","\u002Fimages\u002Fblog\u002Fwebinar-certificado.png",[27,27483],{},[30,27485,27487],{"id":27486},"links-e-referências","Links e Referências",[402,27489,27490,27497,27504,27510,27516,27522,27529,27536],{},[405,27491,27492],{},[105,27493,27496],{"href":27494,"rel":27495},"https:\u002F\u002Fdocs.claude.com\u002Fen\u002Fdocs\u002Fclaude-code\u002Foverview",[109],"Documentação oficial do Claude Code",[405,27498,27499],{},[105,27500,27503],{"href":27501,"rel":27502},"https:\u002F\u002Fdocs.claude.com\u002Fen\u002Fdocs\u002Fclaude-code\u002Fslash-commands",[109],"Slash commands",[405,27505,27506],{},[105,27507,27509],{"href":27057,"rel":27508},[109],"Sub-agents",[405,27511,27512],{},[105,27513,27515],{"href":27143,"rel":27514},[109],"Model Context Protocol",[405,27517,27518],{},[105,27519,27521],{"href":26767,"rel":27520},[109],"Modo headless",[405,27523,27524],{},[105,27525,27528],{"href":27526,"rel":27527},"https:\u002F\u002Fdocs.claude.com\u002Fen\u002Fdocs\u002Fclaude-code\u002Fsettings",[109],"Configurações e permissões",[405,27530,27531],{},[105,27532,27535],{"href":27533,"rel":27534},"https:\u002F\u002Fgithub.com\u002Fupstash\u002Fcontext7",[109],"Context7 no GitHub",[405,27537,27538],{},[105,27539,27542],{"href":27540,"rel":27541},"https:\u002F\u002Fdevelopers.figma.com\u002Fdocs\u002Ffigma-mcp-server\u002Fremote-server-installation\u002F#claude-code",[109],"Figma MCP Server",[1744,27544,27545],{},"html pre.shiki code .soiBB, html code.shiki .soiBB{--shiki-light:#E2931D;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .s0vBq, html code.shiki .s0vBq{--shiki-light:#91B859;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sSJ72, html code.shiki .sSJ72{--shiki-light:#91B859;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sF_wb, html code.shiki .sF_wb{--shiki-light:#39ADB5;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sLXsH, html code.shiki .sLXsH{--shiki-light:#90A4AE;--shiki-light-font-weight:inherit;--shiki-default:#79B8FF;--shiki-default-font-weight:bold;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold}html pre.shiki code .sMo7A, html code.shiki .sMo7A{--shiki-light:#90A4AE;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sG-J9, html code.shiki .sG-J9{--shiki-light:#39ADB5;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sM8Rc, html code.shiki .sM8Rc{--shiki-light:#91B859;--shiki-light-text-decoration:inherit;--shiki-default:#DBEDFF;--shiki-default-text-decoration:underline;--shiki-dark:#DBEDFF;--shiki-dark-text-decoration:underline}html pre.shiki code .sSElE, html code.shiki .sSElE{--shiki-light:#39ADB5;--shiki-light-font-weight:inherit;--shiki-default:#79B8FF;--shiki-default-font-weight:bold;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold}html pre.shiki code .swu5b, html code.shiki .swu5b{--shiki-light:#39ADB5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}",{"title":75,"searchDepth":76,"depth":76,"links":27547},[27548,27551,27554,27555,27556,27559,27560,27561,27562,27563,27564,27565],{"id":26645,"depth":76,"text":26646,"children":27549},[27550],{"id":26655,"depth":152,"text":26656},{"id":26772,"depth":76,"text":26773,"children":27552},[27553],{"id":26807,"depth":152,"text":26808},{"id":26896,"depth":76,"text":26897},{"id":27013,"depth":76,"text":27014},{"id":27062,"depth":76,"text":27063,"children":27557},[27558],{"id":27069,"depth":152,"text":27070},{"id":27148,"depth":76,"text":27149},{"id":27281,"depth":76,"text":27282},{"id":27331,"depth":76,"text":27332},{"id":27380,"depth":76,"text":27381},{"id":27434,"depth":76,"text":27435},{"id":27459,"depth":76,"text":27460},{"id":27486,"depth":76,"text":27487},"2025-10-09T18:15:00-03:00","Tive a oportunidade de ministrar um webinar sobre Claude Code para a Nuvini e Datahub. Aqui está o relato dessa experiência e tudo que foi abordado: comandos slash, agentes, MCP, modos de operação e como criar prompts eficazes.","\u002Fimages\u002Fblog\u002Fwebinar-claude.png",{},"\u002Fblog\u002Fwebinar-claude-code",{"title":26617,"description":27567},"blog\u002Fwebinar-claude-code",[91,18927,94,93,27574],"dev","FqS9sSUFQPHW1rEEIYmBBvyTDg3aOkzjm0TrJOndYPc",{"id":27577,"title":27578,"author":7,"body":27579,"date":27778,"description":27779,"extension":83,"image":27780,"meta":27781,"navigation":86,"path":27782,"seo":27783,"stem":27784,"tags":27785,"__hash__":27789},"blog\u002Fblog\u002Fuxui-frontend.md","A Conexão entre UX\u002FUI e Desenvolvimento Frontend",{"type":9,"value":27580,"toc":27770},[27581,27584,27587,27590,27593,27595,27599,27602,27608,27614,27617,27619,27623,27626,27633,27640,27647,27653,27660,27663,27665,27669,27672,27675,27692,27698,27704,27710,27712,27716,27719,27722,27725,27728,27730,27734,27737,27740,27743,27745,27747],[12,27582,27583],{},"Ser desenvolvedora frontend é, muitas vezes, estar no meio do caminho entre o que foi pensado e o que o usuário vai sentir. E foi exatamente sobre esse \"meio do caminho\" que me pediram para falar.",[12,27585,27586],{},"Recebi o convite para fazer uma apresentação interna na Datahub sobre a conexão entre UX\u002FUI e desenvolvimento frontend, uma oportunidade que me deixou muito feliz, porque é um tema que me interessa bastante. Não é só sobre pixels ou componentes: é sobre entender que o nosso papel como frontend vai além de transformar código em tela.",[12,27588,27589],{},"Vale contextualizar: na empresa onde trabalho não temos um designer dedicado. Quando surge uma tela nova, um formulário ou um componente com mais complexidade, sou eu que vou ao Figma, monto uma proposta e levo ao time de produto para validar antes de implementar. Então quando fui preparar essa apresentação, percebi que boa parte do que ia explicar sobre o processo de UX\u002FUI já era, na prática, algo que eu estava fazendo à minha maneira.",[12,27591,27592],{},"Neste post trago o que apresentei, mas desenvolvido com mais calma do que uma apresentação permite.",[27,27594],{},[30,27596,27598],{"id":27597},"ux-e-ui-não-são-a-mesma-coisa","UX e UI: não são a mesma coisa",[12,27600,27601],{},"Essa é a primeira confusão que a maioria das pessoas faz, inclusive desenvolvedores com anos de experiência. Durante a apresentação, foi o primeiro ponto que trouxe, porque é exatamente onde muitos se perdem.",[12,27603,27604,27607],{},[19,27605,27606],{},"UX (User Experience)"," é sobre a experiência completa do usuário com um produto: o que ele sente, o que ele espera, onde ele trava, o que o frustra, o que o encanta. UX é estratégia. Envolve pesquisa, testes com usuários reais, análise de comportamento e uma pergunta central: esse produto está resolvendo o problema certo, da forma certa, para a pessoa certa?",[12,27609,27610,27613],{},[19,27611,27612],{},"UI (User Interface)"," é sobre a camada visual e interativa: o layout, a tipografia, as cores, os ícones, os estados dos botões, o espaçamento entre elementos. UI é execução visual. É o que o usuário vê e clica.",[12,27615,27616],{},"As duas disciplinas são completamente interdependentes, e o frontend é quem as conecta na prática. Um produto com UX excelente e UI ruim vai frustrar o usuário, mesmo que o fluxo seja lógico. Um produto com UI bonita e UX ruim vai parecer agradável por trinta segundos, até o usuário não encontrar o que precisa e abandonar a tela. E o que acontece quando o frontend implementa mal algum dos dois? A experiência quebra, porque o problema passou a existir no código.",[27,27618],{},[30,27620,27622],{"id":27621},"o-que-acontece-antes-de-começar-a-implementar","O que acontece antes de começar a implementar",[12,27624,27625],{},"É muito comum, como desenvolvedores, entrar na história na etapa de implementação, como se o código fosse o começo de tudo. Mas existe um processo que acontece antes disso, e entender isso muda a qualidade das decisões que você toma, especialmente quando, como no meu caso, você está nos dois lados: é você quem desenha e quem implementa.",[12,27627,27628,27629,27632],{},"Esse processo começa com ",[19,27630,27631],{},"pesquisa e compreensão do usuário",": entender quem vai usar o produto, o que essa pessoa realmente precisa e onde ela trava hoje. Mesmo sem uma pesquisa formal, essa pergunta deveria guiar qualquer decisão de interface. Quando ela é ignorada, o resultado costuma ser uma tela tecnicamente funcional que resolve o problema errado.",[12,27634,27635,27636,27639],{},"Depois vem a ",[19,27637,27638],{},"definição de objetivos",": o que a feature precisa efetivamente resolver, com critérios claros. Não \"criar uma tela de cadastro\", mas \"fazer o usuário conseguir se cadastrar sem precisar de suporte\". Quando esse objetivo está claro antes de abrir o Figma, as decisões de layout e fluxo passam a ter uma direção. Sem ele, é fácil cair no ciclo de redesenhar a mesma tela várias vezes sem saber por que nenhuma versão parece certa.",[12,27641,27642,27643,27646],{},"Em seguida, a ",[19,27644,27645],{},"criação de fluxos de interação",": quais telas existem, em que ordem aparecem, quais ações levam a quais resultados. Um fluxo bem pensado responde perguntas que você ainda nem formulou: o que acontece se o usuário clicar em voltar no meio do processo? E se a lista estiver vazia? E se a requisição falhar? Desenhar o fluxo antes do visual ajuda a encontrar esses buracos antes de o código existir, que é exatamente o momento certo para corrigir.",[12,27648,27635,27649,27652],{},[19,27650,27651],{},"prototipagem",", que no meu processo é justamente quando levo o Figma para o time de produto avaliar. Não precisa estar perfeito visualmente: o objetivo é validar se o fluxo e a estrutura fazem sentido para quem vai aprovar e para quem vai usar. Uma alteração no Figma antes da implementação custa minutos. A mesma alteração depois que o componente está pronto pode custar mais.",[12,27654,27655,27656,27659],{},"Por fim, o ",[19,27657,27658],{},"design visual"," consolida as decisões: hierarquia de informação, estados de cada elemento, consistência com o restante do produto. É aqui que UI e UX se encontram antes de virar código, e é onde a clareza das etapas anteriores faz toda a diferença na velocidade de execução.",[12,27661,27662],{},"Entender esse processo como um todo, mesmo quando você está sozinha executando cada etapa, evita o retrabalho de implementar algo que o produto não queria.",[27,27664],{},[30,27666,27668],{"id":27667},"onde-o-código-decide-a-experiência","Onde o código decide a experiência",[12,27670,27671],{},"O que me ficou mais claro ao preparar essa apresentação é que uma parte enorme da experiência do usuário não é definida pelo design. É definida pelo código.",[12,27673,27674],{},"O design entrega uma intenção. Quem decide se essa intenção chega intacta ao usuário é o frontend.",[12,27676,27677,27680,27681,10369,27684,27687,27688,27691],{},[19,27678,27679],{},"Acessibilidade"," é o exemplo mais direto disso. Um Figma não tem tag HTML. A decisão de usar ",[135,27682,27683],{},"\u003Cbutton>",[135,27685,27686],{},"\u003Cdiv onclick>",", de incluir ",[135,27689,27690],{},"aria-label"," nos ícones sem texto, de garantir contraste suficiente entre fundo e texto, de fazer a navegação por teclado funcionar: tudo isso acontece no código. Acessibilidade não é uma feature que o designer coloca no Figma e o dev implementa. É uma responsabilidade que vive quase inteiramente no lado do desenvolvimento, e que a maioria dos projetos trata como opcional até o dia que alguém reclama.",[12,27693,27694,27697],{},[19,27695,27696],{},"Feedback claro"," também vive no código. Aquele estado de loading que você às vezes deixa para depois porque \"a requisição é rápida\"? Para um usuário em conexão ruim, sem esse estado, a interface parece travada. A mensagem de erro genérica que ficou porque ia \"ajustar depois\"? Ela está em produção, frustrando alguém agora. O toast de confirmação que parece detalhe? Para o usuário, é a diferença entre confiar que a ação aconteceu e ficar clicando no botão três vezes para ter certeza. Cada um desses itens é uma linha de código que decide como o usuário se sente usando o produto.",[12,27699,27700,27703],{},[19,27701,27702],{},"Consistência"," depende de como o código é organizado. Quando cada desenvolvedor reimplementa um componente do zero porque o existente \"não serve exatamente pra esse caso\", o resultado é um produto com quatro variações do mesmo botão primário que deveriam ser visualmente iguais. Seguir o sistema de design com disciplina não é burocracia: é o que garante que o produto pareça coeso e não uma colagem de telas independentes construídas por pessoas diferentes.",[12,27705,27706,27709],{},[19,27707,27708],{},"Performance"," entra aqui também, e é frequentemente ignorada na conversa sobre UX. Uma interface bonita que trava por dois segundos ao carregar tem uma experiência ruim por definição. Tempo de resposta, tamanho dos bundles, estratégias de carregamento, uso de cache: são decisões técnicas com impacto direto em como o usuário percebe o produto. Um design perfeito não salva uma aplicação lenta.",[27,27711],{},[30,27713,27715],{"id":27714},"a-colaboração-que-faz-tudo-funcionar-ou-não","A colaboração que faz tudo funcionar (ou não)",[12,27717,27718],{},"Um ponto que enfatizei bastante na apresentação: UX\u002FUI e frontend não são etapas sequenciais que se passam o bastão, são partes de um ciclo contínuo. Mesmo num contexto como o meu, onde não há um designer separado, esse ciclo existe: entre mim, o time de produto e o usuário final.",[12,27720,27721],{},"Na implementação, sempre surgem coisas que o protótipo não previu, e isso é completamente esperado. Um comportamento que parecia simples no Figma tem vários estados de erro quando você começa a escrever o código. Um componente que funciona bem no desktop quebra no mobile de um jeito que o layout não mostrou. Não são falhas do processo: são a realidade de transformar um modelo estático em código que roda em condições variáveis, em conexões variáveis, em dispositivos variáveis.",[12,27723,27724],{},"O que muda quando a validação acontece cedo, antes da implementação, é que esses ajustes custam muito menos. Levar o Figma ao time de produto antes de codificar é isso: uma chance de encontrar o que não faz sentido enquanto ainda é fácil mudar. As perguntas que surgem nessa revisão, \"e quando a lista está vazia?\", \"e se o usuário não tiver permissão para essa ação?\", \"e no mobile?\", são muito mais fáceis de resolver no Figma do que depois que o componente está pronto.",[12,27726,27727],{},"Esse hábito de questionar antes de implementar, de mapear os estados que o protótipo não mostra, é onde o conhecimento de UX\u002FUI mais se traduz em valor prático para quem desenvolve.",[27,27729],{},[30,27731,27733],{"id":27732},"o-que-ficou-pra-mim","O que ficou pra mim",[12,27735,27736],{},"Preparar e apresentar esse conteúdo me fez dar nome a coisas que já faziam parte do meu processo sem eu ter parado para sistematizar. Montar no Figma antes de implementar, validar com o produto antes de codar, pensar nos estados antes de abrir o editor: tudo isso já era minha rotina, mas entender os conceitos por trás tornou esse processo mais intencional.",[12,27738,27739],{},"O frontend, especialmente num contexto sem designer dedicado, acaba ocupando um espaço maior do que o código em si. Você pensa no fluxo, no estado vazio, no erro, na acessibilidade, na consistência visual, na performance. Tudo isso é experiência do usuário, mesmo quando ninguém chama pelo nome.",[12,27741,27742],{},"Foi uma experiência incrível poder compartilhar isso com o time, e escrever este post me fez perceber que ainda tenho muito mais para explorar nesse tema.",[27,27744],{},[30,27746,9153],{"id":9152},[402,27748,27749,27756,27763],{},[405,27750,27751],{},[105,27752,27755],{"href":27753,"rel":27754},"https:\u002F\u002Fmerehead.com\u002Fpt\u002Fblog\u002Fmanual-tendencias-design-ux-ui-2023\u002F",[109],"Manual de Tendências de Design UX\u002FUI 2023 Merehead",[405,27757,27758],{},[105,27759,27762],{"href":27760,"rel":27761},"https:\u002F\u002Fuserguiding.com\u002Fpt-br\u002Fblog\u002Ftendencias-de-ux-ui\u002F",[109],"Tendencias de UX\u002FUI",[405,27764,27765],{},[105,27766,27769],{"href":27767,"rel":27768},"https:\u002F\u002Fbrasil.uxdesign.cc\u002Falgumas-tend%C3%AAncias-de-ui-design-para-serem-exploradas-em-2023-a663d9230fa8",[109],"Tendencias de UX\u002FUI 2023",{"title":75,"searchDepth":76,"depth":76,"links":27771},[27772,27773,27774,27775,27776,27777],{"id":27597,"depth":76,"text":27598},{"id":27621,"depth":76,"text":27622},{"id":27667,"depth":76,"text":27668},{"id":27714,"depth":76,"text":27715},{"id":27732,"depth":76,"text":27733},{"id":9152,"depth":76,"text":9153},"2023-06-13T13:30:00-03:00","Fui convidada para apresentar internamente na Datahub sobre a relação entre UX\u002FUI e desenvolvimento frontend. Neste post, trago os principais conceitos que abordei e o que aprendi ao olhar para o design com os olhos de quem escreve o código.","\u002Fimages\u002Fblog\u002Fuxui-frontend.png",{},"\u002Fblog\u002Fuxui-frontend",{"title":27578,"description":27779},"blog\u002Fuxui-frontend",[13338,27786,27787,27788,92],"ux","ui","design","d_hC83tfcVDf4lQuCAeua2ILButCo16UaPOEYWZBqt8",1782501670941]