[{"data":1,"prerenderedAt":3845},["ShallowReactive",2],{"blog-virtual-dom-mini-framework-do-zero":3},{"id":4,"title":5,"author":6,"body":7,"date":3830,"description":3831,"extension":3832,"faq":3833,"image":3834,"meta":3835,"navigation":544,"path":3836,"seo":3837,"stem":3838,"tags":3839,"updated":3833,"__hash__":3844},"blog\u002Fblog\u002Fvirtual-dom-mini-framework-do-zero.md","Virtual DOM por baixo dos panos: construindo um mini-framework do zero","Larissa Santos",{"type":8,"value":9,"toc":3811},"minimark",[10,19,22,27,30,33,36,46,62,65,69,72,171,178,190,253,256,318,321,325,333,409,416,630,638,641,867,874,882,885,1391,1406,1410,1417,1488,1491,1604,1613,1617,1620,1631,1634,1877,1888,1894,1905,1915,2493,2508,2512,2518,2525,2675,2678,2682,2693,3187,3198,3685,3691,3695,3698,3704,3710,3728,3753,3762,3765,3769,3772,3775,3787,3790,3794,3807],[11,12,13,14,18],"p",{},"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 ",[15,16,17],"code",{},"ref()"," e a tela atualiza.",[11,20,21],{},"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.",[23,24,26],"h2",{"id":25},"por-que-o-dom-real-é-lento","Por que o DOM real é lento?",[11,28,29],{},"Antes de entender o Virtual DOM, precisamos entender o problema que ele resolve.",[11,31,32],{},"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.",[11,34,35],{},"Além disso, certas operações disparam processos caros em cascata:",[37,38,43],"pre",{"className":39,"code":41,"language":42},[40],"language-text","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","text",[15,44,41],{"__ignoreMap":45},"",[11,47,48,49,52,53,56,57,61],{},"Ler propriedades como ",[15,50,51],{},"offsetHeight"," ou ",[15,54,55],{},"getBoundingClientRect"," força o browser a executar todo esse processo de forma síncrona. Isso se chama ",[58,59,60],"strong",{},"forced reflow",", e é um dos maiores vilões de performance em aplicações web.",[11,63,64],{},"O problema não é criar elementos. É atualizar desnecessariamente: pintar a parede inteira quando só um cantinho mudou.",[23,66,68],{"id":67},"o-que-é-o-virtual-dom","O que é o Virtual DOM?",[11,70,71],{},"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.",[37,73,77],{"className":74,"code":75,"language":76,"meta":45,"style":45},"language-js shiki shiki-themes material-theme-lighter github-dark github-dark","const vnode = {\n  tag: 'p',\n  props: { style: 'color: red' },\n  children: 'Contador: 1'\n}\n","js",[15,78,79,100,123,149,165],{"__ignoreMap":45},[80,81,84,88,92,96],"span",{"class":82,"line":83},"line",1,[80,85,87],{"class":86},"sFsEu","const",[80,89,91],{"class":90},"sVPC0"," vnode",[80,93,95],{"class":94},"sFfmW"," =",[80,97,99],{"class":98},"sG-J9"," {\n",[80,101,103,107,110,114,117,120],{"class":82,"line":102},2,[80,104,106],{"class":105},"sdv8B","  tag",[80,108,109],{"class":98},":",[80,111,113],{"class":112},"sF_wb"," '",[80,115,11],{"class":116},"s0vBq",[80,118,119],{"class":112},"'",[80,121,122],{"class":98},",\n",[80,124,126,129,131,134,137,139,141,144,146],{"class":82,"line":125},3,[80,127,128],{"class":105},"  props",[80,130,109],{"class":98},[80,132,133],{"class":98}," {",[80,135,136],{"class":105}," style",[80,138,109],{"class":98},[80,140,113],{"class":112},[80,142,143],{"class":116},"color: red",[80,145,119],{"class":112},[80,147,148],{"class":98}," },\n",[80,150,152,155,157,159,162],{"class":82,"line":151},4,[80,153,154],{"class":105},"  children",[80,156,109],{"class":98},[80,158,113],{"class":112},[80,160,161],{"class":116},"Contador: 1",[80,163,164],{"class":112},"'\n",[80,166,168],{"class":82,"line":167},5,[80,169,170],{"class":98},"}\n",[11,172,173,174,177],{},"Isso é um VNode. Um objeto plain que descreve como um ",[15,175,176],{},"\u003Cp>"," deveria parecer.",[11,179,180,181,184,185,189],{},"O nome \"VNode\" foi popularizado pelos frameworks, mas você poderia chamar de qualquer coisa. O Vue usa ",[15,182,183],{},"h()"," para criá-los (a letra vem de ",[186,187,188],"em",{},"hyperscript",", uma convenção antiga da comunidade). Quando você escreve um template Vue como este:",[37,191,195],{"className":192,"code":193,"language":194,"meta":45,"style":45},"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",[15,196,197,224,245],{"__ignoreMap":45},[80,198,199,202,206,210,213,216,219,221],{"class":82,"line":83},[80,200,201],{"class":98},"\u003C",[80,203,205],{"class":204},"sqIbZ","div",[80,207,209],{"class":208},"s7047"," class",[80,211,212],{"class":98},"=",[80,214,215],{"class":112},"\"",[80,217,218],{"class":116},"box",[80,220,215],{"class":112},[80,222,223],{"class":98},">\n",[80,225,226,229,231,234,238,241,243],{"class":82,"line":102},[80,227,228],{"class":98},"  \u003C",[80,230,11],{"class":204},[80,232,233],{"class":98},">",[80,235,237],{"class":236},"sMo7A","{{ msg }}",[80,239,240],{"class":98},"\u003C\u002F",[80,242,11],{"class":204},[80,244,223],{"class":98},[80,246,247,249,251],{"class":82,"line":125},[80,248,240],{"class":98},[80,250,205],{"class":204},[80,252,223],{"class":98},[11,254,255],{},"O compilador transforma isso em:",[37,257,259],{"className":74,"code":258,"language":76,"meta":45,"style":45},"h('div', { class: 'box' }, [h('p', null, msg)])\n",[15,260,261],{"__ignoreMap":45},[80,262,263,267,270,272,274,276,279,281,283,285,287,289,291,294,297,299,301,303,305,307,309,313,315],{"class":82,"line":83},[80,264,266],{"class":265},"sK_r7","h",[80,268,269],{"class":236},"(",[80,271,119],{"class":112},[80,273,205],{"class":116},[80,275,119],{"class":112},[80,277,278],{"class":98},",",[80,280,133],{"class":98},[80,282,209],{"class":105},[80,284,109],{"class":98},[80,286,113],{"class":112},[80,288,218],{"class":116},[80,290,119],{"class":112},[80,292,293],{"class":98}," },",[80,295,296],{"class":236}," [",[80,298,266],{"class":265},[80,300,269],{"class":236},[80,302,119],{"class":112},[80,304,11],{"class":116},[80,306,119],{"class":112},[80,308,278],{"class":98},[80,310,312],{"class":311},"swu5b"," null",[80,314,278],{"class":98},[80,316,317],{"class":236}," msg)])\n",[11,319,320],{},"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.",[23,322,324],{"id":323},"as-três-peças-fundamentais","As três peças fundamentais",[326,327,329,330,332],"h3",{"id":328},"_1-h-criar-o-vnode","1. ",[15,331,183],{},": criar o VNode",[37,334,336],{"className":74,"code":335,"language":76,"meta":45,"style":45},"function h(tag, props, children) {\n  return { tag, props: props || {}, children: children || [] }\n}\n",[15,337,338,367,405],{"__ignoreMap":45},[80,339,340,343,346,348,352,354,357,359,362,365],{"class":82,"line":83},[80,341,342],{"class":86},"function",[80,344,345],{"class":265}," h",[80,347,269],{"class":98},[80,349,351],{"class":350},"sk1zL","tag",[80,353,278],{"class":98},[80,355,356],{"class":350}," props",[80,358,278],{"class":98},[80,360,361],{"class":350}," children",[80,363,364],{"class":98},")",[80,366,99],{"class":98},[80,368,369,373,375,378,380,382,384,386,389,392,394,396,398,400,403],{"class":82,"line":102},[80,370,372],{"class":371},"s3Er8","  return",[80,374,133],{"class":98},[80,376,377],{"class":236}," tag",[80,379,278],{"class":98},[80,381,356],{"class":105},[80,383,109],{"class":98},[80,385,356],{"class":236},[80,387,388],{"class":94}," ||",[80,390,391],{"class":98}," {},",[80,393,361],{"class":105},[80,395,109],{"class":98},[80,397,361],{"class":236},[80,399,388],{"class":94},[80,401,402],{"class":105}," [] ",[80,404,170],{"class":98},[80,406,407],{"class":82,"line":125},[80,408,170],{"class":98},[11,410,411,412,415],{},"Parece inútil, só retorna o que recebe. Mas é uma conveniência: sem ela você repetiria ",[15,413,414],{},"{ tag, props, children }"," em cada lugar. Em árvores aninhadas a diferença é enorme:",[37,417,419],{"className":74,"code":418,"language":76,"meta":45,"style":45},"\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",[15,420,421,427,458,487,524,531,539,546,552,571,591,618,624],{"__ignoreMap":45},[80,422,423],{"class":82,"line":83},[80,424,426],{"class":425},"sutJx","\u002F\u002F sem h()\n",[80,428,429,432,435,437,439,441,443,445,447,449,451,453,455],{"class":82,"line":102},[80,430,431],{"class":98},"{",[80,433,377],{"class":434},"soiBB",[80,436,109],{"class":98},[80,438,113],{"class":112},[80,440,205],{"class":116},[80,442,119],{"class":112},[80,444,278],{"class":98},[80,446,356],{"class":434},[80,448,109],{"class":98},[80,450,391],{"class":98},[80,452,361],{"class":434},[80,454,109],{"class":98},[80,456,457],{"class":105}," [\n",[80,459,460,463,465,467,469,471,473,475,477,479,481,483,485],{"class":82,"line":125},[80,461,462],{"class":98},"  {",[80,464,377],{"class":105},[80,466,109],{"class":98},[80,468,113],{"class":112},[80,470,11],{"class":116},[80,472,119],{"class":112},[80,474,278],{"class":98},[80,476,356],{"class":105},[80,478,109],{"class":98},[80,480,391],{"class":98},[80,482,361],{"class":105},[80,484,109],{"class":98},[80,486,457],{"class":105},[80,488,489,492,494,496,498,500,502,504,506,508,510,512,514,516,519,521],{"class":82,"line":151},[80,490,491],{"class":98},"    {",[80,493,377],{"class":105},[80,495,109],{"class":98},[80,497,113],{"class":112},[80,499,80],{"class":116},[80,501,119],{"class":112},[80,503,278],{"class":98},[80,505,356],{"class":105},[80,507,109],{"class":98},[80,509,391],{"class":98},[80,511,361],{"class":105},[80,513,109],{"class":98},[80,515,113],{"class":112},[80,517,518],{"class":116},"texto",[80,520,119],{"class":112},[80,522,523],{"class":98}," }\n",[80,525,526,529],{"class":82,"line":167},[80,527,528],{"class":105},"  ]",[80,530,170],{"class":98},[80,532,534,537],{"class":82,"line":533},6,[80,535,536],{"class":105},"]",[80,538,170],{"class":98},[80,540,542],{"class":82,"line":541},7,[80,543,545],{"emptyLinePlaceholder":544},true,"\n",[80,547,549],{"class":82,"line":548},8,[80,550,551],{"class":425},"\u002F\u002F com h()\n",[80,553,555,557,559,561,563,565,567,569],{"class":82,"line":554},9,[80,556,266],{"class":265},[80,558,269],{"class":236},[80,560,119],{"class":112},[80,562,205],{"class":116},[80,564,119],{"class":112},[80,566,278],{"class":98},[80,568,391],{"class":98},[80,570,457],{"class":236},[80,572,574,577,579,581,583,585,587,589],{"class":82,"line":573},10,[80,575,576],{"class":265},"  h",[80,578,269],{"class":236},[80,580,119],{"class":112},[80,582,11],{"class":116},[80,584,119],{"class":112},[80,586,278],{"class":98},[80,588,391],{"class":98},[80,590,457],{"class":236},[80,592,594,597,599,601,603,605,607,609,611,613,615],{"class":82,"line":593},11,[80,595,596],{"class":265},"    h",[80,598,269],{"class":236},[80,600,119],{"class":112},[80,602,80],{"class":116},[80,604,119],{"class":112},[80,606,278],{"class":98},[80,608,391],{"class":98},[80,610,113],{"class":112},[80,612,518],{"class":116},[80,614,119],{"class":112},[80,616,617],{"class":236},")\n",[80,619,621],{"class":82,"line":620},12,[80,622,623],{"class":236},"  ])\n",[80,625,627],{"class":82,"line":626},13,[80,628,629],{"class":236},"])\n",[326,631,633,634,637],{"id":632},"_2-mount-primeira-montagem","2. ",[15,635,636],{},"mount()",": primeira montagem",[11,639,640],{},"Pega um VNode e cria os elementos reais no DOM. Só é chamado uma vez, quando o componente aparece pela primeira vez na página.",[37,642,644],{"className":74,"code":643,"language":76,"meta":45,"style":45},"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",[15,645,646,667,696,713,717,770,790,795,799,826,842,846,863],{"__ignoreMap":45},[80,647,648,650,653,655,658,660,663,665],{"class":82,"line":83},[80,649,342],{"class":86},[80,651,652],{"class":265}," mount",[80,654,269],{"class":98},[80,656,657],{"class":350},"vnode",[80,659,278],{"class":98},[80,661,662],{"class":350}," container",[80,664,364],{"class":98},[80,666,99],{"class":98},[80,668,669,672,675,677,680,683,686,688,690,692,694],{"class":82,"line":102},[80,670,671],{"class":86},"  const",[80,673,674],{"class":90}," el",[80,676,95],{"class":94},[80,678,679],{"class":236}," document",[80,681,682],{"class":98},".",[80,684,685],{"class":265},"createElement",[80,687,269],{"class":105},[80,689,657],{"class":236},[80,691,682],{"class":98},[80,693,351],{"class":236},[80,695,617],{"class":105},[80,697,698,701,703,706,708,710],{"class":82,"line":125},[80,699,700],{"class":236},"  vnode",[80,702,682],{"class":98},[80,704,705],{"class":236},"_el",[80,707,95],{"class":94},[80,709,674],{"class":236},[80,711,712],{"class":425}," \u002F\u002F guarda a referencia que liga o objeto ao elemento real\n",[80,714,715],{"class":82,"line":151},[80,716,545],{"emptyLinePlaceholder":544},[80,718,719,722,725,727,729,732,734,737,739,742,745,747,750,752,754,756,759,761,764,767],{"class":82,"line":167},[80,720,721],{"class":371},"  for",[80,723,724],{"class":105}," (",[80,726,87],{"class":86},[80,728,296],{"class":98},[80,730,731],{"class":90},"k",[80,733,278],{"class":98},[80,735,736],{"class":90}," v",[80,738,536],{"class":98},[80,740,741],{"class":94}," of",[80,743,744],{"class":236}," Object",[80,746,682],{"class":98},[80,748,749],{"class":265},"entries",[80,751,269],{"class":105},[80,753,657],{"class":236},[80,755,682],{"class":98},[80,757,758],{"class":236},"props",[80,760,388],{"class":94},[80,762,763],{"class":98}," {}",[80,765,766],{"class":105},")) ",[80,768,769],{"class":98},"{\n",[80,771,772,775,777,780,782,784,786,788],{"class":82,"line":533},[80,773,774],{"class":236},"    el",[80,776,682],{"class":98},[80,778,779],{"class":265},"setAttribute",[80,781,269],{"class":105},[80,783,731],{"class":236},[80,785,278],{"class":98},[80,787,736],{"class":236},[80,789,617],{"class":105},[80,791,792],{"class":82,"line":541},[80,793,794],{"class":98},"  }\n",[80,796,797],{"class":82,"line":548},[80,798,545],{"emptyLinePlaceholder":544},[80,800,801,803,805,807,810,812,814,816,819,821,824],{"class":82,"line":554},[80,802,721],{"class":371},[80,804,724],{"class":105},[80,806,87],{"class":86},[80,808,809],{"class":90}," child",[80,811,741],{"class":94},[80,813,91],{"class":236},[80,815,682],{"class":98},[80,817,818],{"class":236},"children",[80,820,388],{"class":94},[80,822,823],{"class":105}," []) ",[80,825,769],{"class":98},[80,827,828,831,833,836,838,840],{"class":82,"line":573},[80,829,830],{"class":265},"    mount",[80,832,269],{"class":105},[80,834,835],{"class":236},"child",[80,837,278],{"class":98},[80,839,674],{"class":236},[80,841,617],{"class":105},[80,843,844],{"class":82,"line":593},[80,845,794],{"class":98},[80,847,848,851,853,856,858,861],{"class":82,"line":620},[80,849,850],{"class":236},"  container",[80,852,682],{"class":98},[80,854,855],{"class":265},"appendChild",[80,857,269],{"class":105},[80,859,860],{"class":236},"el",[80,862,617],{"class":105},[80,864,865],{"class":82,"line":626},[80,866,170],{"class":98},[11,868,869,870,873],{},"O ",[15,871,872],{},"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.",[326,875,877,878,881],{"id":876},"_3-patch-atualizar-só-o-que-mudou","3. ",[15,879,880],{},"patch()",": atualizar só o que mudou",[11,883,884],{},"Compara dois VNodes e aplica no DOM só as diferenças. Nunca recria o elemento, apenas edita o que existe.",[37,886,888],{"className":74,"code":887,"language":76,"meta":45,"style":45},"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",[15,889,890,911,930,944,948,968,987,991,1026,1068,1072,1076,1104,1137,1142,1147,1168,1188,1226,1231,1271,1310,1354,1381,1386],{"__ignoreMap":45},[80,891,892,894,897,899,902,904,907,909],{"class":82,"line":83},[80,893,342],{"class":86},[80,895,896],{"class":265}," patch",[80,898,269],{"class":98},[80,900,901],{"class":350},"oldV",[80,903,278],{"class":98},[80,905,906],{"class":350}," newV",[80,908,364],{"class":98},[80,910,99],{"class":98},[80,912,913,916,918,920,922,925,927],{"class":82,"line":102},[80,914,915],{"class":236},"  newV",[80,917,682],{"class":98},[80,919,705],{"class":236},[80,921,95],{"class":94},[80,923,924],{"class":236}," oldV",[80,926,682],{"class":98},[80,928,929],{"class":236},"_el\n",[80,931,932,934,936,938,940,942],{"class":82,"line":125},[80,933,671],{"class":86},[80,935,674],{"class":90},[80,937,95],{"class":94},[80,939,906],{"class":236},[80,941,682],{"class":98},[80,943,929],{"class":236},[80,945,946],{"class":82,"line":151},[80,947,545],{"emptyLinePlaceholder":544},[80,949,950,952,955,957,959,961,963,965],{"class":82,"line":167},[80,951,671],{"class":86},[80,953,954],{"class":90}," oldProps",[80,956,95],{"class":94},[80,958,924],{"class":236},[80,960,682],{"class":98},[80,962,758],{"class":236},[80,964,388],{"class":94},[80,966,967],{"class":98}," {}\n",[80,969,970,972,975,977,979,981,983,985],{"class":82,"line":533},[80,971,671],{"class":86},[80,973,974],{"class":90}," newProps",[80,976,95],{"class":94},[80,978,906],{"class":236},[80,980,682],{"class":98},[80,982,758],{"class":236},[80,984,388],{"class":94},[80,986,967],{"class":98},[80,988,989],{"class":82,"line":541},[80,990,545],{"emptyLinePlaceholder":544},[80,992,993,995,997,999,1001,1003,1005,1007,1009,1011,1013,1015,1017,1019,1022,1024],{"class":82,"line":548},[80,994,721],{"class":371},[80,996,724],{"class":105},[80,998,87],{"class":86},[80,1000,296],{"class":98},[80,1002,731],{"class":90},[80,1004,278],{"class":98},[80,1006,736],{"class":90},[80,1008,536],{"class":98},[80,1010,741],{"class":94},[80,1012,744],{"class":236},[80,1014,682],{"class":98},[80,1016,749],{"class":265},[80,1018,269],{"class":105},[80,1020,1021],{"class":236},"newProps",[80,1023,766],{"class":105},[80,1025,769],{"class":98},[80,1027,1028,1031,1033,1036,1039,1041,1044,1047,1049,1052,1054,1056,1058,1060,1062,1064,1066],{"class":82,"line":554},[80,1029,1030],{"class":371},"    if",[80,1032,724],{"class":105},[80,1034,1035],{"class":236},"oldProps",[80,1037,1038],{"class":105},"[",[80,1040,731],{"class":236},[80,1042,1043],{"class":105},"] ",[80,1045,1046],{"class":94},"!==",[80,1048,736],{"class":236},[80,1050,1051],{"class":105},") ",[80,1053,860],{"class":236},[80,1055,682],{"class":98},[80,1057,779],{"class":265},[80,1059,269],{"class":105},[80,1061,731],{"class":236},[80,1063,278],{"class":98},[80,1065,736],{"class":236},[80,1067,617],{"class":105},[80,1069,1070],{"class":82,"line":573},[80,1071,794],{"class":98},[80,1073,1074],{"class":82,"line":593},[80,1075,545],{"emptyLinePlaceholder":544},[80,1077,1078,1080,1082,1084,1087,1089,1091,1093,1096,1098,1100,1102],{"class":82,"line":620},[80,1079,721],{"class":371},[80,1081,724],{"class":105},[80,1083,87],{"class":86},[80,1085,1086],{"class":90}," k",[80,1088,741],{"class":94},[80,1090,744],{"class":236},[80,1092,682],{"class":98},[80,1094,1095],{"class":265},"keys",[80,1097,269],{"class":105},[80,1099,1035],{"class":236},[80,1101,766],{"class":105},[80,1103,769],{"class":98},[80,1105,1106,1108,1110,1113,1115,1117,1120,1122,1124,1126,1128,1131,1133,1135],{"class":82,"line":626},[80,1107,1030],{"class":371},[80,1109,724],{"class":105},[80,1111,1112],{"class":94},"!",[80,1114,269],{"class":105},[80,1116,731],{"class":236},[80,1118,1119],{"class":94}," in",[80,1121,974],{"class":236},[80,1123,766],{"class":105},[80,1125,860],{"class":236},[80,1127,682],{"class":98},[80,1129,1130],{"class":265},"removeAttribute",[80,1132,269],{"class":105},[80,1134,731],{"class":236},[80,1136,617],{"class":105},[80,1138,1140],{"class":82,"line":1139},14,[80,1141,794],{"class":98},[80,1143,1145],{"class":82,"line":1144},15,[80,1146,545],{"emptyLinePlaceholder":544},[80,1148,1150,1152,1155,1157,1159,1161,1163,1165],{"class":82,"line":1149},16,[80,1151,671],{"class":86},[80,1153,1154],{"class":90}," oc",[80,1156,95],{"class":94},[80,1158,924],{"class":236},[80,1160,682],{"class":98},[80,1162,818],{"class":236},[80,1164,388],{"class":94},[80,1166,1167],{"class":105}," []\n",[80,1169,1171,1173,1176,1178,1180,1182,1184,1186],{"class":82,"line":1170},17,[80,1172,671],{"class":86},[80,1174,1175],{"class":90}," nc",[80,1177,95],{"class":94},[80,1179,906],{"class":236},[80,1181,682],{"class":98},[80,1183,818],{"class":236},[80,1185,388],{"class":94},[80,1187,1167],{"class":105},[80,1189,1191,1193,1196,1198,1201,1203,1206,1208,1211,1213,1216,1218,1220,1222,1224],{"class":82,"line":1190},18,[80,1192,671],{"class":86},[80,1194,1195],{"class":90}," len",[80,1197,95],{"class":94},[80,1199,1200],{"class":236}," Math",[80,1202,682],{"class":98},[80,1204,1205],{"class":265},"max",[80,1207,269],{"class":105},[80,1209,1210],{"class":236},"oc",[80,1212,682],{"class":98},[80,1214,1215],{"class":90},"length",[80,1217,278],{"class":98},[80,1219,1175],{"class":236},[80,1221,682],{"class":98},[80,1223,1215],{"class":90},[80,1225,617],{"class":105},[80,1227,1229],{"class":82,"line":1228},19,[80,1230,545],{"emptyLinePlaceholder":544},[80,1232,1234,1236,1238,1241,1244,1246,1250,1253,1255,1258,1260,1262,1264,1267,1269],{"class":82,"line":1233},20,[80,1235,721],{"class":371},[80,1237,724],{"class":105},[80,1239,1240],{"class":86},"let",[80,1242,1243],{"class":236}," i",[80,1245,95],{"class":94},[80,1247,1249],{"class":1248},"s_k96"," 0",[80,1251,1252],{"class":98},";",[80,1254,1243],{"class":236},[80,1256,1257],{"class":94}," \u003C",[80,1259,1195],{"class":236},[80,1261,1252],{"class":98},[80,1263,1243],{"class":236},[80,1265,1266],{"class":94},"++",[80,1268,1051],{"class":105},[80,1270,769],{"class":98},[80,1272,1274,1276,1278,1280,1282,1284,1287,1290,1293,1295,1298,1300,1302,1304,1306,1308],{"class":82,"line":1273},21,[80,1275,1030],{"class":371},[80,1277,724],{"class":105},[80,1279,1112],{"class":94},[80,1281,1210],{"class":236},[80,1283,1038],{"class":105},[80,1285,1286],{"class":236},"i",[80,1288,1289],{"class":105},"]) ",[80,1291,1292],{"class":265},"mount",[80,1294,269],{"class":105},[80,1296,1297],{"class":236},"nc",[80,1299,1038],{"class":105},[80,1301,1286],{"class":236},[80,1303,536],{"class":105},[80,1305,278],{"class":98},[80,1307,674],{"class":236},[80,1309,617],{"class":105},[80,1311,1313,1316,1319,1321,1323,1325,1327,1329,1331,1333,1335,1338,1340,1342,1344,1346,1348,1350,1352],{"class":82,"line":1312},22,[80,1314,1315],{"class":371},"    else",[80,1317,1318],{"class":371}," if",[80,1320,724],{"class":105},[80,1322,1112],{"class":94},[80,1324,1297],{"class":236},[80,1326,1038],{"class":105},[80,1328,1286],{"class":236},[80,1330,1289],{"class":105},[80,1332,860],{"class":236},[80,1334,682],{"class":98},[80,1336,1337],{"class":265},"removeChild",[80,1339,269],{"class":105},[80,1341,1210],{"class":236},[80,1343,1038],{"class":105},[80,1345,1286],{"class":236},[80,1347,536],{"class":105},[80,1349,682],{"class":98},[80,1351,705],{"class":236},[80,1353,617],{"class":105},[80,1355,1357,1359,1361,1363,1365,1367,1369,1371,1373,1375,1377,1379],{"class":82,"line":1356},23,[80,1358,1315],{"class":371},[80,1360,896],{"class":265},[80,1362,269],{"class":105},[80,1364,1210],{"class":236},[80,1366,1038],{"class":105},[80,1368,1286],{"class":236},[80,1370,536],{"class":105},[80,1372,278],{"class":98},[80,1374,1175],{"class":236},[80,1376,1038],{"class":105},[80,1378,1286],{"class":236},[80,1380,629],{"class":105},[80,1382,1384],{"class":82,"line":1383},24,[80,1385,794],{"class":98},[80,1387,1389],{"class":82,"line":1388},25,[80,1390,170],{"class":98},[11,1392,1393,1394,1397,1398,1401,1402,1405],{},"A linha ",[15,1395,1396],{},"newV._el = oldV._el"," merece atenção: o ",[15,1399,1400],{},"newVnode"," é criado do zero sem nenhuma referência ao DOM. O ",[15,1403,1404],{},"patch"," transfere a referência do nó antigo pro novo antes de jogar o antigo fora.",[326,1407,1409],{"id":1408},"um-detalhe-importante-nós-de-texto","Um detalhe importante: nós de texto",[11,1411,1412,1413,1416],{},"Strings primitivas em JavaScript não aceitam propriedades. Se você tentar salvar ",[15,1414,1415],{},"._el"," numa string, a propriedade some:",[37,1418,1420],{"className":74,"code":1419,"language":76,"meta":45,"style":45},"const texto = 'Contador: 0'\ntexto._el = document.createTextNode('...')\nconsole.log(texto._el) \u002F\u002F undefined\n",[15,1421,1422,1438,1467],{"__ignoreMap":45},[80,1423,1424,1426,1429,1431,1433,1436],{"class":82,"line":83},[80,1425,87],{"class":86},[80,1427,1428],{"class":90}," texto",[80,1430,95],{"class":94},[80,1432,113],{"class":112},[80,1434,1435],{"class":116},"Contador: 0",[80,1437,164],{"class":112},[80,1439,1440,1442,1444,1447,1449,1451,1453,1456,1458,1460,1463,1465],{"class":82,"line":102},[80,1441,518],{"class":236},[80,1443,682],{"class":98},[80,1445,1446],{"class":236},"_el ",[80,1448,212],{"class":94},[80,1450,679],{"class":236},[80,1452,682],{"class":98},[80,1454,1455],{"class":265},"createTextNode",[80,1457,269],{"class":236},[80,1459,119],{"class":112},[80,1461,1462],{"class":116},"...",[80,1464,119],{"class":112},[80,1466,617],{"class":236},[80,1468,1469,1472,1474,1477,1480,1482,1485],{"class":82,"line":125},[80,1470,1471],{"class":236},"console",[80,1473,682],{"class":98},[80,1475,1476],{"class":265},"log",[80,1478,1479],{"class":236},"(texto",[80,1481,682],{"class":98},[80,1483,1484],{"class":236},"_el) ",[80,1486,1487],{"class":425},"\u002F\u002F undefined\n",[11,1489,1490],{},"A solução é envolver o texto num objeto, igual a qualquer outro VNode:",[37,1492,1494],{"className":74,"code":1493,"language":76,"meta":45,"style":45},"function t(text) {\n  return { tag: '#text', text: String(text), _el: null }\n}\n\nh('p', {}, [t(`Contador: ${contador}`)])\n",[15,1495,1496,1511,1555,1559,1563],{"__ignoreMap":45},[80,1497,1498,1500,1503,1505,1507,1509],{"class":82,"line":83},[80,1499,342],{"class":86},[80,1501,1502],{"class":265}," t",[80,1504,269],{"class":98},[80,1506,42],{"class":350},[80,1508,364],{"class":98},[80,1510,99],{"class":98},[80,1512,1513,1515,1517,1519,1521,1523,1526,1528,1530,1533,1535,1538,1540,1542,1544,1546,1549,1551,1553],{"class":82,"line":102},[80,1514,372],{"class":371},[80,1516,133],{"class":98},[80,1518,377],{"class":105},[80,1520,109],{"class":98},[80,1522,113],{"class":112},[80,1524,1525],{"class":116},"#text",[80,1527,119],{"class":112},[80,1529,278],{"class":98},[80,1531,1532],{"class":105}," text",[80,1534,109],{"class":98},[80,1536,1537],{"class":265}," String",[80,1539,269],{"class":105},[80,1541,42],{"class":236},[80,1543,364],{"class":105},[80,1545,278],{"class":98},[80,1547,1548],{"class":105}," _el",[80,1550,109],{"class":98},[80,1552,312],{"class":311},[80,1554,523],{"class":98},[80,1556,1557],{"class":82,"line":125},[80,1558,170],{"class":98},[80,1560,1561],{"class":82,"line":151},[80,1562,545],{"emptyLinePlaceholder":544},[80,1564,1565,1567,1569,1571,1573,1575,1577,1579,1581,1584,1586,1589,1592,1595,1598,1601],{"class":82,"line":167},[80,1566,266],{"class":265},[80,1568,269],{"class":236},[80,1570,119],{"class":112},[80,1572,11],{"class":116},[80,1574,119],{"class":112},[80,1576,278],{"class":98},[80,1578,391],{"class":98},[80,1580,296],{"class":236},[80,1582,1583],{"class":265},"t",[80,1585,269],{"class":236},[80,1587,1588],{"class":112},"`",[80,1590,1591],{"class":116},"Contador: ",[80,1593,1594],{"class":112},"${",[80,1596,1597],{"class":236},"contador",[80,1599,1600],{"class":112},"}`",[80,1602,1603],{"class":236},")])\n",[11,1605,1606,1607,1609,1610,1612],{},"Agora o ",[15,1608,705],{}," é salvo normalmente e o ",[15,1611,1404],{}," consegue encontrar e atualizar o nó de texto exato no DOM.",[23,1614,1616],{"id":1615},"adicionando-reatividade-com-proxy","Adicionando reatividade com Proxy",[11,1618,1619],{},"O Virtual DOM resolve o \"como atualizar\". Mas quem decide \"quando atualizar\" e \"quais componentes precisam re-renderizar\"?",[11,1621,1622,1623,1626,1627,1630],{},"No Vue 3, isso é feito com ",[15,1624,1625],{},"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 ",[15,1628,1629],{},"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.",[11,1632,1633],{},"A ideia é simples:",[37,1635,1637],{"className":74,"code":1636,"language":76,"meta":45,"style":45},"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",[15,1638,1639,1659,1663,1685,1705,1735,1750,1755,1777,1810,1826,1834,1838,1844,1848,1861],{"__ignoreMap":45},[80,1640,1641,1643,1646,1648,1650,1653,1655,1657],{"class":82,"line":83},[80,1642,87],{"class":86},[80,1644,1645],{"class":90}," estado",[80,1647,95],{"class":94},[80,1649,133],{"class":98},[80,1651,1652],{"class":105}," contador",[80,1654,109],{"class":98},[80,1656,1249],{"class":1248},[80,1658,523],{"class":98},[80,1660,1661],{"class":82,"line":102},[80,1662,545],{"emptyLinePlaceholder":544},[80,1664,1665,1667,1670,1672,1675,1678,1681,1683],{"class":82,"line":125},[80,1666,87],{"class":86},[80,1668,1669],{"class":90}," reativo",[80,1671,95],{"class":94},[80,1673,1674],{"class":94}," new",[80,1676,1677],{"class":265}," Proxy",[80,1679,1680],{"class":236},"(estado",[80,1682,278],{"class":98},[80,1684,99],{"class":98},[80,1686,1687,1691,1693,1696,1698,1701,1703],{"class":82,"line":151},[80,1688,1690],{"class":1689},"s0u7J","  get",[80,1692,269],{"class":98},[80,1694,1695],{"class":350},"target",[80,1697,278],{"class":98},[80,1699,1700],{"class":350}," key",[80,1702,364],{"class":98},[80,1704,99],{"class":98},[80,1706,1707,1710,1712,1714,1716,1718,1721,1723,1726,1729,1731,1733],{"class":82,"line":167},[80,1708,1709],{"class":236},"    console",[80,1711,682],{"class":98},[80,1713,1476],{"class":265},[80,1715,269],{"class":105},[80,1717,1588],{"class":112},[80,1719,1720],{"class":116},"alguém leu \"",[80,1722,1594],{"class":112},[80,1724,1725],{"class":236},"key",[80,1727,1728],{"class":112},"}",[80,1730,215],{"class":116},[80,1732,1588],{"class":112},[80,1734,617],{"class":105},[80,1736,1737,1740,1743,1745,1747],{"class":82,"line":533},[80,1738,1739],{"class":371},"    return",[80,1741,1742],{"class":236}," target",[80,1744,1038],{"class":105},[80,1746,1725],{"class":236},[80,1748,1749],{"class":105},"]\n",[80,1751,1752],{"class":82,"line":541},[80,1753,1754],{"class":98},"  },\n",[80,1756,1757,1760,1762,1764,1766,1768,1770,1773,1775],{"class":82,"line":548},[80,1758,1759],{"class":1689},"  set",[80,1761,269],{"class":98},[80,1763,1695],{"class":350},[80,1765,278],{"class":98},[80,1767,1700],{"class":350},[80,1769,278],{"class":98},[80,1771,1772],{"class":350}," value",[80,1774,364],{"class":98},[80,1776,99],{"class":98},[80,1778,1779,1781,1783,1785,1787,1789,1792,1794,1796,1798,1801,1803,1806,1808],{"class":82,"line":554},[80,1780,1709],{"class":236},[80,1782,682],{"class":98},[80,1784,1476],{"class":265},[80,1786,269],{"class":105},[80,1788,1588],{"class":112},[80,1790,1791],{"class":116},"alguém escreveu \"",[80,1793,1594],{"class":112},[80,1795,1725],{"class":236},[80,1797,1728],{"class":112},[80,1799,1800],{"class":116},"\" = ",[80,1802,1594],{"class":112},[80,1804,1805],{"class":236},"value",[80,1807,1600],{"class":112},[80,1809,617],{"class":105},[80,1811,1812,1815,1817,1819,1821,1823],{"class":82,"line":573},[80,1813,1814],{"class":236},"    target",[80,1816,1038],{"class":105},[80,1818,1725],{"class":236},[80,1820,1043],{"class":105},[80,1822,212],{"class":94},[80,1824,1825],{"class":236}," value\n",[80,1827,1828,1830],{"class":82,"line":593},[80,1829,1739],{"class":371},[80,1831,1833],{"class":1832},"sMrrN"," true\n",[80,1835,1836],{"class":82,"line":620},[80,1837,794],{"class":98},[80,1839,1840,1842],{"class":82,"line":626},[80,1841,1728],{"class":98},[80,1843,617],{"class":236},[80,1845,1846],{"class":82,"line":1139},[80,1847,545],{"emptyLinePlaceholder":544},[80,1849,1850,1853,1855,1858],{"class":82,"line":1144},[80,1851,1852],{"class":236},"reativo",[80,1854,682],{"class":98},[80,1856,1857],{"class":236},"contador ",[80,1859,1860],{"class":425},"\u002F\u002F log: alguém leu \"contador\"\n",[80,1862,1863,1865,1867,1869,1871,1874],{"class":82,"line":1149},[80,1864,1852],{"class":236},[80,1866,682],{"class":98},[80,1868,1857],{"class":236},[80,1870,212],{"class":94},[80,1872,1873],{"class":1248}," 5",[80,1875,1876],{"class":425}," \u002F\u002F log: alguém escreveu \"contador\" = 5\n",[11,1878,1879,1880,1883,1884,1887],{},"O Vue usa esse mecanismo para montar um mapa de dependências: quando um componente renderiza, o ",[15,1881,1882],{},"get"," do Proxy registra quais propriedades foram lidas. Quando uma delas muda, o ",[15,1885,1886],{},"set"," notifica só os componentes que a leram.",[37,1889,1892],{"className":1890,"code":1891,"language":42},[40],"Header leu → estado.titulo\nMain   leu → estado.conteudo\nFooter leu → estado.ano\n\nestado.titulo muda → só o Header re-renderiza\n",[15,1893,1891],{"__ignoreMap":45},[11,1895,1896,1897,1900,1901,1904],{},"A estrutura que usamos para guardar as dependências importa. Um objeto simples como ",[15,1898,1899],{},"const deps = {}"," funciona para um estado global único, mas causa dois problemas em escala: dois objetos reativos com uma propriedade ",[15,1902,1903],{},"nome"," conflitariam na mesma chave, e a referência ficaria presa na memória mesmo após o componente ser destruído.",[11,1906,1907,1908,1911,1912,109],{},"A solução é usar ",[15,1909,1910],{},"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 ",[15,1913,1914],{},"WeakMap\u003Cobjeto, Map\u003Cpropriedade, Set\u003Ccomponentes>>>",[37,1916,1918],{"className":74,"code":1917,"language":76,"meta":45,"style":45},"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",[15,1919,1920,1937,1949,1953,1974,1991,1995,2017,2032,2046,2065,2069,2073,2095,2110,2124,2142,2146,2150,2166,2170,2174,2193,2213,2227,2231,2252,2289,2294,2299,2314,2331,2349,2365,2379,2385,2407,2435,2451,2467,2474,2480,2488],{"__ignoreMap":45},[80,1921,1922,1924,1927,1929,1931,1934],{"class":82,"line":83},[80,1923,87],{"class":86},[80,1925,1926],{"class":90}," deps",[80,1928,95],{"class":94},[80,1930,1674],{"class":94},[80,1932,1933],{"class":265}," WeakMap",[80,1935,1936],{"class":236},"()\n",[80,1938,1939,1941,1944,1946],{"class":82,"line":102},[80,1940,1240],{"class":86},[80,1942,1943],{"class":236}," componenteAtivo ",[80,1945,212],{"class":94},[80,1947,1948],{"class":311}," null\n",[80,1950,1951],{"class":82,"line":125},[80,1952,545],{"emptyLinePlaceholder":544},[80,1954,1955,1957,1960,1962,1965,1967,1970,1972],{"class":82,"line":151},[80,1956,342],{"class":86},[80,1958,1959],{"class":265}," registrarDependencia",[80,1961,269],{"class":98},[80,1963,1964],{"class":350},"obj",[80,1966,278],{"class":98},[80,1968,1969],{"class":350}," propriedade",[80,1971,364],{"class":98},[80,1973,99],{"class":98},[80,1975,1976,1979,1981,1983,1986,1988],{"class":82,"line":167},[80,1977,1978],{"class":371},"  if",[80,1980,724],{"class":105},[80,1982,1112],{"class":94},[80,1984,1985],{"class":236},"componenteAtivo",[80,1987,1051],{"class":105},[80,1989,1990],{"class":371},"return\n",[80,1992,1993],{"class":82,"line":533},[80,1994,545],{"emptyLinePlaceholder":544},[80,1996,1997,2000,2003,2005,2007,2009,2011,2013,2015],{"class":82,"line":541},[80,1998,1999],{"class":86},"  let",[80,2001,2002],{"class":236}," propriedades",[80,2004,95],{"class":94},[80,2006,1926],{"class":236},[80,2008,682],{"class":98},[80,2010,1882],{"class":265},[80,2012,269],{"class":105},[80,2014,1964],{"class":236},[80,2016,617],{"class":105},[80,2018,2019,2021,2023,2025,2028,2030],{"class":82,"line":548},[80,2020,1978],{"class":371},[80,2022,724],{"class":105},[80,2024,1112],{"class":94},[80,2026,2027],{"class":236},"propriedades",[80,2029,1051],{"class":105},[80,2031,769],{"class":98},[80,2033,2034,2037,2039,2041,2044],{"class":82,"line":554},[80,2035,2036],{"class":236},"    propriedades",[80,2038,95],{"class":94},[80,2040,1674],{"class":94},[80,2042,2043],{"class":265}," Map",[80,2045,1936],{"class":105},[80,2047,2048,2051,2053,2055,2057,2059,2061,2063],{"class":82,"line":573},[80,2049,2050],{"class":236},"    deps",[80,2052,682],{"class":98},[80,2054,1886],{"class":265},[80,2056,269],{"class":105},[80,2058,1964],{"class":236},[80,2060,278],{"class":98},[80,2062,2002],{"class":236},[80,2064,617],{"class":105},[80,2066,2067],{"class":82,"line":593},[80,2068,794],{"class":98},[80,2070,2071],{"class":82,"line":620},[80,2072,545],{"emptyLinePlaceholder":544},[80,2074,2075,2077,2080,2082,2084,2086,2088,2090,2093],{"class":82,"line":626},[80,2076,1999],{"class":86},[80,2078,2079],{"class":236}," componentes",[80,2081,95],{"class":94},[80,2083,2002],{"class":236},[80,2085,682],{"class":98},[80,2087,1882],{"class":265},[80,2089,269],{"class":105},[80,2091,2092],{"class":236},"propriedade",[80,2094,617],{"class":105},[80,2096,2097,2099,2101,2103,2106,2108],{"class":82,"line":1139},[80,2098,1978],{"class":371},[80,2100,724],{"class":105},[80,2102,1112],{"class":94},[80,2104,2105],{"class":236},"componentes",[80,2107,1051],{"class":105},[80,2109,769],{"class":98},[80,2111,2112,2115,2117,2119,2122],{"class":82,"line":1144},[80,2113,2114],{"class":236},"    componentes",[80,2116,95],{"class":94},[80,2118,1674],{"class":94},[80,2120,2121],{"class":265}," Set",[80,2123,1936],{"class":105},[80,2125,2126,2128,2130,2132,2134,2136,2138,2140],{"class":82,"line":1149},[80,2127,2036],{"class":236},[80,2129,682],{"class":98},[80,2131,1886],{"class":265},[80,2133,269],{"class":105},[80,2135,2092],{"class":236},[80,2137,278],{"class":98},[80,2139,2079],{"class":236},[80,2141,617],{"class":105},[80,2143,2144],{"class":82,"line":1170},[80,2145,794],{"class":98},[80,2147,2148],{"class":82,"line":1190},[80,2149,545],{"emptyLinePlaceholder":544},[80,2151,2152,2155,2157,2160,2162,2164],{"class":82,"line":1228},[80,2153,2154],{"class":236},"  componentes",[80,2156,682],{"class":98},[80,2158,2159],{"class":265},"add",[80,2161,269],{"class":105},[80,2163,1985],{"class":236},[80,2165,617],{"class":105},[80,2167,2168],{"class":82,"line":1233},[80,2169,170],{"class":98},[80,2171,2172],{"class":82,"line":1273},[80,2173,545],{"emptyLinePlaceholder":544},[80,2175,2176,2178,2181,2183,2185,2187,2189,2191],{"class":82,"line":1312},[80,2177,342],{"class":86},[80,2179,2180],{"class":265}," notificarDependentes",[80,2182,269],{"class":98},[80,2184,1964],{"class":350},[80,2186,278],{"class":98},[80,2188,1969],{"class":350},[80,2190,364],{"class":98},[80,2192,99],{"class":98},[80,2194,2195,2197,2199,2201,2203,2205,2207,2209,2211],{"class":82,"line":1356},[80,2196,671],{"class":86},[80,2198,2002],{"class":90},[80,2200,95],{"class":94},[80,2202,1926],{"class":236},[80,2204,682],{"class":98},[80,2206,1882],{"class":265},[80,2208,269],{"class":105},[80,2210,1964],{"class":236},[80,2212,617],{"class":105},[80,2214,2215,2217,2219,2221,2223,2225],{"class":82,"line":1383},[80,2216,1978],{"class":371},[80,2218,724],{"class":105},[80,2220,1112],{"class":94},[80,2222,2027],{"class":236},[80,2224,1051],{"class":105},[80,2226,1990],{"class":371},[80,2228,2229],{"class":82,"line":1388},[80,2230,545],{"emptyLinePlaceholder":544},[80,2232,2234,2236,2238,2240,2242,2244,2246,2248,2250],{"class":82,"line":2233},26,[80,2235,671],{"class":86},[80,2237,2079],{"class":90},[80,2239,95],{"class":94},[80,2241,2002],{"class":236},[80,2243,682],{"class":98},[80,2245,1882],{"class":265},[80,2247,269],{"class":105},[80,2249,2092],{"class":236},[80,2251,617],{"class":105},[80,2253,2255,2257,2259,2261,2263,2265,2267,2270,2272,2275,2278,2281,2283,2286],{"class":82,"line":2254},27,[80,2256,1978],{"class":371},[80,2258,724],{"class":105},[80,2260,2105],{"class":236},[80,2262,1051],{"class":105},[80,2264,2105],{"class":236},[80,2266,682],{"class":98},[80,2268,2269],{"class":265},"forEach",[80,2271,269],{"class":105},[80,2273,2274],{"class":350},"c",[80,2276,2277],{"class":86}," =>",[80,2279,2280],{"class":236}," c",[80,2282,682],{"class":98},[80,2284,2285],{"class":265},"_enfileirar",[80,2287,2288],{"class":105},"())\n",[80,2290,2292],{"class":82,"line":2291},28,[80,2293,170],{"class":98},[80,2295,2297],{"class":82,"line":2296},29,[80,2298,545],{"emptyLinePlaceholder":544},[80,2300,2302,2304,2306,2308,2310,2312],{"class":82,"line":2301},30,[80,2303,342],{"class":86},[80,2305,1669],{"class":265},[80,2307,269],{"class":98},[80,2309,1964],{"class":350},[80,2311,364],{"class":98},[80,2313,99],{"class":98},[80,2315,2317,2319,2321,2323,2325,2327,2329],{"class":82,"line":2316},31,[80,2318,372],{"class":371},[80,2320,1674],{"class":94},[80,2322,1677],{"class":265},[80,2324,269],{"class":105},[80,2326,1964],{"class":236},[80,2328,278],{"class":98},[80,2330,99],{"class":98},[80,2332,2334,2337,2339,2341,2343,2345,2347],{"class":82,"line":2333},32,[80,2335,2336],{"class":1689},"    get",[80,2338,269],{"class":98},[80,2340,1695],{"class":350},[80,2342,278],{"class":98},[80,2344,1700],{"class":350},[80,2346,364],{"class":98},[80,2348,99],{"class":98},[80,2350,2352,2355,2357,2359,2361,2363],{"class":82,"line":2351},33,[80,2353,2354],{"class":265},"      registrarDependencia",[80,2356,269],{"class":105},[80,2358,1695],{"class":236},[80,2360,278],{"class":98},[80,2362,1700],{"class":236},[80,2364,617],{"class":105},[80,2366,2368,2371,2373,2375,2377],{"class":82,"line":2367},34,[80,2369,2370],{"class":371},"      return",[80,2372,1742],{"class":236},[80,2374,1038],{"class":105},[80,2376,1725],{"class":236},[80,2378,1749],{"class":105},[80,2380,2382],{"class":82,"line":2381},35,[80,2383,2384],{"class":98},"    },\n",[80,2386,2388,2391,2393,2395,2397,2399,2401,2403,2405],{"class":82,"line":2387},36,[80,2389,2390],{"class":1689},"    set",[80,2392,269],{"class":98},[80,2394,1695],{"class":350},[80,2396,278],{"class":98},[80,2398,1700],{"class":350},[80,2400,278],{"class":98},[80,2402,1772],{"class":350},[80,2404,364],{"class":98},[80,2406,99],{"class":98},[80,2408,2410,2413,2415,2417,2419,2421,2423,2426,2428,2430,2433],{"class":82,"line":2409},37,[80,2411,2412],{"class":371},"      if",[80,2414,724],{"class":105},[80,2416,1695],{"class":236},[80,2418,1038],{"class":105},[80,2420,1725],{"class":236},[80,2422,1043],{"class":105},[80,2424,2425],{"class":94},"===",[80,2427,1772],{"class":236},[80,2429,1051],{"class":105},[80,2431,2432],{"class":371},"return",[80,2434,1833],{"class":1832},[80,2436,2438,2441,2443,2445,2447,2449],{"class":82,"line":2437},38,[80,2439,2440],{"class":236},"      target",[80,2442,1038],{"class":105},[80,2444,1725],{"class":236},[80,2446,1043],{"class":105},[80,2448,212],{"class":94},[80,2450,1825],{"class":236},[80,2452,2454,2457,2459,2461,2463,2465],{"class":82,"line":2453},39,[80,2455,2456],{"class":265},"      notificarDependentes",[80,2458,269],{"class":105},[80,2460,1695],{"class":236},[80,2462,278],{"class":98},[80,2464,1700],{"class":236},[80,2466,617],{"class":105},[80,2468,2470,2472],{"class":82,"line":2469},40,[80,2471,2370],{"class":371},[80,2473,1833],{"class":1832},[80,2475,2477],{"class":82,"line":2476},41,[80,2478,2479],{"class":98},"    }\n",[80,2481,2483,2486],{"class":82,"line":2482},42,[80,2484,2485],{"class":98},"  }",[80,2487,617],{"class":105},[80,2489,2491],{"class":82,"line":2490},43,[80,2492,170],{"class":98},[11,2494,869,2495,2497,2498,2501,2502,2497,2504,2507],{},[15,2496,1882],{}," chama ",[15,2499,2500],{},"registrarDependencia",", que anota qual componente está lendo aquela propriedade. O ",[15,2503,1886],{},[15,2505,2506],{},"notificarDependentes",", que avisa só quem precisa re-renderizar.",[23,2509,2511],{"id":2510},"batch-update-o-nexttick-por-baixo","Batch update: o nextTick por baixo",[11,2513,2514,2515,682],{},"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 ",[15,2516,2517],{},"nextTick",[11,2519,2520,2521,2524],{},"Por baixo, ele usa ",[15,2522,2523],{},"Promise.resolve().then()",", que agenda a execução depois que todo o código síncrono terminar:",[37,2526,2528],{"className":74,"code":2527,"language":76,"meta":45,"style":45},"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",[15,2529,2530,2545,2557,2561,2573,2586,2595,2621,2645,2656,2665,2671],{"__ignoreMap":45},[80,2531,2532,2534,2537,2539,2541,2543],{"class":82,"line":83},[80,2533,1240],{"class":86},[80,2535,2536],{"class":236}," fila ",[80,2538,212],{"class":94},[80,2540,1674],{"class":94},[80,2542,2121],{"class":265},[80,2544,1936],{"class":236},[80,2546,2547,2549,2552,2554],{"class":82,"line":102},[80,2548,1240],{"class":86},[80,2550,2551],{"class":236}," flushAgendado ",[80,2553,212],{"class":94},[80,2555,2556],{"class":1832}," false\n",[80,2558,2559],{"class":82,"line":125},[80,2560,545],{"emptyLinePlaceholder":544},[80,2562,2563,2565,2568,2571],{"class":82,"line":151},[80,2564,342],{"class":86},[80,2566,2567],{"class":265}," agendarFlush",[80,2569,2570],{"class":98},"()",[80,2572,99],{"class":98},[80,2574,2575,2577,2579,2582,2584],{"class":82,"line":167},[80,2576,1978],{"class":371},[80,2578,724],{"class":105},[80,2580,2581],{"class":236},"flushAgendado",[80,2583,1051],{"class":105},[80,2585,1990],{"class":371},[80,2587,2588,2591,2593],{"class":82,"line":533},[80,2589,2590],{"class":236},"  flushAgendado",[80,2592,95],{"class":94},[80,2594,1833],{"class":1832},[80,2596,2597,2601,2603,2606,2608,2610,2613,2615,2617,2619],{"class":82,"line":541},[80,2598,2600],{"class":2599},"s3afY","  Promise",[80,2602,682],{"class":98},[80,2604,2605],{"class":265},"resolve",[80,2607,2570],{"class":105},[80,2609,682],{"class":98},[80,2611,2612],{"class":265},"then",[80,2614,269],{"class":105},[80,2616,2570],{"class":98},[80,2618,2277],{"class":86},[80,2620,99],{"class":98},[80,2622,2623,2626,2628,2630,2632,2634,2636,2638,2640,2643],{"class":82,"line":548},[80,2624,2625],{"class":236},"    fila",[80,2627,682],{"class":98},[80,2629,2269],{"class":265},[80,2631,269],{"class":105},[80,2633,2274],{"class":350},[80,2635,2277],{"class":86},[80,2637,2280],{"class":236},[80,2639,682],{"class":98},[80,2641,2642],{"class":265},"_rerender",[80,2644,2288],{"class":105},[80,2646,2647,2649,2651,2654],{"class":82,"line":554},[80,2648,2625],{"class":236},[80,2650,682],{"class":98},[80,2652,2653],{"class":265},"clear",[80,2655,1936],{"class":105},[80,2657,2658,2661,2663],{"class":82,"line":573},[80,2659,2660],{"class":236},"    flushAgendado",[80,2662,95],{"class":94},[80,2664,2556],{"class":1832},[80,2666,2667,2669],{"class":82,"line":593},[80,2668,2485],{"class":98},[80,2670,617],{"class":105},[80,2672,2673],{"class":82,"line":620},[80,2674,170],{"class":98},[11,2676,2677],{},"Muda 10 propriedades no mesmo tick, re-renderiza uma vez só.",[23,2679,2681],{"id":2680},"juntando-tudo-o-mini-framework","Juntando tudo: o mini-framework",[11,2683,2684,2685,2688,2689,2692],{},"Com reatividade e Virtual DOM prontos, a função ",[15,2686,2687],{},"criarComponente"," é a cola entre os dois. O ponto central é marcar o componente como ",[15,2690,2691],{},"efeitoAtivo"," antes de renderizar, para que o track saiba a quem atribuir as dependências:",[37,2694,2696],{"className":74,"code":2695,"language":76,"meta":45,"style":45},"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",[15,2697,2698,2718,2729,2748,2774,2785,2789,2798,2814,2821,2825,2829,2838,2852,2880,2888,2892,2911,2927,2941,2973,2983,3002,3014,3045,3050,3054,3058,3067,3098,3114,3137,3149,3153,3157,3161,3165,3176,3183],{"__ignoreMap":45},[80,2699,2700,2702,2705,2707,2710,2712,2714,2716],{"class":82,"line":83},[80,2701,342],{"class":86},[80,2703,2704],{"class":265}," criarComponente",[80,2706,269],{"class":98},[80,2708,2709],{"class":350},"opcoes",[80,2711,278],{"class":98},[80,2713,662],{"class":350},[80,2715,364],{"class":98},[80,2717,99],{"class":98},[80,2719,2720,2722,2725,2727],{"class":82,"line":102},[80,2721,671],{"class":86},[80,2723,2724],{"class":90}," componente",[80,2726,95],{"class":94},[80,2728,99],{"class":98},[80,2730,2731,2734,2736,2739,2741,2743,2745],{"class":82,"line":125},[80,2732,2733],{"class":105},"    props",[80,2735,109],{"class":98},[80,2737,2738],{"class":236}," opcoes",[80,2740,682],{"class":98},[80,2742,758],{"class":236},[80,2744,388],{"class":94},[80,2746,2747],{"class":98}," {},\n",[80,2749,2750,2753,2755,2757,2759,2761,2763,2766,2768,2770,2772],{"class":82,"line":151},[80,2751,2752],{"class":105},"    estado",[80,2754,109],{"class":98},[80,2756,1669],{"class":265},[80,2758,269],{"class":105},[80,2760,2709],{"class":236},[80,2762,682],{"class":98},[80,2764,2765],{"class":236},"estado",[80,2767,388],{"class":94},[80,2769,763],{"class":98},[80,2771,364],{"class":105},[80,2773,122],{"class":98},[80,2775,2776,2779,2781,2783],{"class":82,"line":167},[80,2777,2778],{"class":105},"    _vnode",[80,2780,109],{"class":98},[80,2782,312],{"class":311},[80,2784,122],{"class":98},[80,2786,2787],{"class":82,"line":533},[80,2788,545],{"emptyLinePlaceholder":544},[80,2790,2791,2794,2796],{"class":82,"line":541},[80,2792,2793],{"class":1689},"    _enfileirar",[80,2795,2570],{"class":98},[80,2797,99],{"class":98},[80,2799,2800,2803,2805,2807,2809,2812],{"class":82,"line":548},[80,2801,2802],{"class":236},"      fila",[80,2804,682],{"class":98},[80,2806,2159],{"class":265},[80,2808,269],{"class":105},[80,2810,2811],{"class":236},"componente",[80,2813,617],{"class":105},[80,2815,2816,2819],{"class":82,"line":554},[80,2817,2818],{"class":265},"      agendarFlush",[80,2820,1936],{"class":105},[80,2822,2823],{"class":82,"line":573},[80,2824,2384],{"class":98},[80,2826,2827],{"class":82,"line":593},[80,2828,545],{"emptyLinePlaceholder":544},[80,2830,2831,2834,2836],{"class":82,"line":620},[80,2832,2833],{"class":1689},"    _rerender",[80,2835,2570],{"class":98},[80,2837,99],{"class":98},[80,2839,2840,2843,2845,2847,2849],{"class":82,"line":626},[80,2841,2842],{"class":236},"      efeitoAtivo",[80,2844,95],{"class":94},[80,2846,2724],{"class":236},[80,2848,682],{"class":98},[80,2850,2851],{"class":236},"_enfileirar\n",[80,2853,2854,2857,2860,2862,2864,2866,2869,2871,2874,2876,2878],{"class":82,"line":1139},[80,2855,2856],{"class":86},"      const",[80,2858,2859],{"class":90}," novoVnode",[80,2861,95],{"class":94},[80,2863,2738],{"class":236},[80,2865,682],{"class":98},[80,2867,2868],{"class":236},"render",[80,2870,682],{"class":98},[80,2872,2873],{"class":265},"call",[80,2875,269],{"class":105},[80,2877,2811],{"class":236},[80,2879,617],{"class":105},[80,2881,2882,2884,2886],{"class":82,"line":1144},[80,2883,2842],{"class":236},[80,2885,95],{"class":94},[80,2887,1948],{"class":311},[80,2889,2890],{"class":82,"line":1149},[80,2891,545],{"emptyLinePlaceholder":544},[80,2893,2894,2896,2898,2900,2902,2904,2907,2909],{"class":82,"line":1170},[80,2895,2412],{"class":371},[80,2897,724],{"class":105},[80,2899,1112],{"class":94},[80,2901,2811],{"class":236},[80,2903,682],{"class":98},[80,2905,2906],{"class":236},"_vnode",[80,2908,1051],{"class":105},[80,2910,769],{"class":98},[80,2912,2913,2916,2918,2921,2923,2925],{"class":82,"line":1190},[80,2914,2915],{"class":265},"        mount",[80,2917,269],{"class":105},[80,2919,2920],{"class":236},"novoVnode",[80,2922,278],{"class":98},[80,2924,662],{"class":236},[80,2926,617],{"class":105},[80,2928,2929,2932,2934,2936,2938],{"class":82,"line":1228},[80,2930,2931],{"class":236},"        componente",[80,2933,682],{"class":98},[80,2935,2906],{"class":236},[80,2937,95],{"class":94},[80,2939,2940],{"class":236}," novoVnode\n",[80,2942,2943,2946,2948,2950,2952,2955,2957,2959,2961,2963,2965,2967,2969,2971],{"class":82,"line":1233},[80,2944,2945],{"class":371},"        if",[80,2947,724],{"class":105},[80,2949,2709],{"class":236},[80,2951,682],{"class":98},[80,2953,2954],{"class":236},"onMount",[80,2956,1051],{"class":105},[80,2958,2709],{"class":236},[80,2960,682],{"class":98},[80,2962,2954],{"class":236},[80,2964,682],{"class":98},[80,2966,2873],{"class":265},[80,2968,269],{"class":105},[80,2970,2811],{"class":236},[80,2972,617],{"class":105},[80,2974,2975,2978,2981],{"class":82,"line":1273},[80,2976,2977],{"class":98},"      }",[80,2979,2980],{"class":371}," else",[80,2982,99],{"class":98},[80,2984,2985,2988,2990,2992,2994,2996,2998,3000],{"class":82,"line":1312},[80,2986,2987],{"class":265},"        patch",[80,2989,269],{"class":105},[80,2991,2811],{"class":236},[80,2993,682],{"class":98},[80,2995,2906],{"class":236},[80,2997,278],{"class":98},[80,2999,2859],{"class":236},[80,3001,617],{"class":105},[80,3003,3004,3006,3008,3010,3012],{"class":82,"line":1356},[80,3005,2931],{"class":236},[80,3007,682],{"class":98},[80,3009,2906],{"class":236},[80,3011,95],{"class":94},[80,3013,2940],{"class":236},[80,3015,3016,3018,3020,3022,3024,3027,3029,3031,3033,3035,3037,3039,3041,3043],{"class":82,"line":1383},[80,3017,2945],{"class":371},[80,3019,724],{"class":105},[80,3021,2709],{"class":236},[80,3023,682],{"class":98},[80,3025,3026],{"class":236},"onUpdate",[80,3028,1051],{"class":105},[80,3030,2709],{"class":236},[80,3032,682],{"class":98},[80,3034,3026],{"class":236},[80,3036,682],{"class":98},[80,3038,2873],{"class":265},[80,3040,269],{"class":105},[80,3042,2811],{"class":236},[80,3044,617],{"class":105},[80,3046,3047],{"class":82,"line":1388},[80,3048,3049],{"class":98},"      }\n",[80,3051,3052],{"class":82,"line":2233},[80,3053,2384],{"class":98},[80,3055,3056],{"class":82,"line":2254},[80,3057,545],{"emptyLinePlaceholder":544},[80,3059,3060,3063,3065],{"class":82,"line":2291},[80,3061,3062],{"class":1689},"    destruir",[80,3064,2570],{"class":98},[80,3066,99],{"class":98},[80,3068,3069,3071,3073,3075,3077,3080,3082,3084,3086,3088,3090,3092,3094,3096],{"class":82,"line":2296},[80,3070,2412],{"class":371},[80,3072,724],{"class":105},[80,3074,2709],{"class":236},[80,3076,682],{"class":98},[80,3078,3079],{"class":236},"onDestroy",[80,3081,1051],{"class":105},[80,3083,2709],{"class":236},[80,3085,682],{"class":98},[80,3087,3079],{"class":236},[80,3089,682],{"class":98},[80,3091,2873],{"class":265},[80,3093,269],{"class":105},[80,3095,2811],{"class":236},[80,3097,617],{"class":105},[80,3099,3100,3102,3104,3106,3108,3110,3112],{"class":82,"line":2301},[80,3101,2412],{"class":371},[80,3103,724],{"class":105},[80,3105,2811],{"class":236},[80,3107,682],{"class":98},[80,3109,2906],{"class":236},[80,3111,1051],{"class":105},[80,3113,769],{"class":98},[80,3115,3116,3119,3121,3123,3125,3127,3129,3131,3133,3135],{"class":82,"line":2316},[80,3117,3118],{"class":236},"        container",[80,3120,682],{"class":98},[80,3122,1337],{"class":265},[80,3124,269],{"class":105},[80,3126,2811],{"class":236},[80,3128,682],{"class":98},[80,3130,2906],{"class":236},[80,3132,682],{"class":98},[80,3134,705],{"class":236},[80,3136,617],{"class":105},[80,3138,3139,3141,3143,3145,3147],{"class":82,"line":2333},[80,3140,2931],{"class":236},[80,3142,682],{"class":98},[80,3144,2906],{"class":236},[80,3146,95],{"class":94},[80,3148,1948],{"class":311},[80,3150,3151],{"class":82,"line":2351},[80,3152,3049],{"class":98},[80,3154,3155],{"class":82,"line":2367},[80,3156,2479],{"class":98},[80,3158,3159],{"class":82,"line":2381},[80,3160,794],{"class":98},[80,3162,3163],{"class":82,"line":2387},[80,3164,545],{"emptyLinePlaceholder":544},[80,3166,3167,3170,3172,3174],{"class":82,"line":2409},[80,3168,3169],{"class":236},"  componente",[80,3171,682],{"class":98},[80,3173,2642],{"class":265},[80,3175,1936],{"class":105},[80,3177,3178,3180],{"class":82,"line":2437},[80,3179,372],{"class":371},[80,3181,3182],{"class":236}," componente\n",[80,3184,3185],{"class":82,"line":2453},[80,3186,170],{"class":98},[11,3188,3189,3190,3193,3194,3197],{},"O uso fica separado em dois arquivos. O ",[15,3191,3192],{},"framework.js"," contém toda a lógica acima. O ",[15,3195,3196],{},"index.html"," contém apenas os componentes:",[37,3199,3201],{"className":74,"code":3200,"language":76,"meta":45,"style":45},"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",[15,3202,3203,3230,3262,3266,3273,3278,3300,3309,3329,3369,3411,3416,3420,3429,3449,3453,3457,3462,3466,3470,3482,3486,3503,3511,3531,3576,3633,3637,3641,3650,3669,3673,3677,3681],{"__ignoreMap":45},[80,3204,3205,3207,3210,3212,3214,3216,3219,3221,3223,3226,3228],{"class":82,"line":83},[80,3206,87],{"class":86},[80,3208,3209],{"class":90}," app",[80,3211,95],{"class":94},[80,3213,679],{"class":236},[80,3215,682],{"class":98},[80,3217,3218],{"class":265},"getElementById",[80,3220,269],{"class":236},[80,3222,119],{"class":112},[80,3224,3225],{"class":116},"app",[80,3227,119],{"class":112},[80,3229,617],{"class":236},[80,3231,3232,3234,3237,3239,3241,3243,3245,3248,3250,3252,3255,3257,3260],{"class":82,"line":102},[80,3233,87],{"class":86},[80,3235,3236],{"class":90}," estadoGlobal",[80,3238,95],{"class":94},[80,3240,1669],{"class":265},[80,3242,269],{"class":236},[80,3244,431],{"class":98},[80,3246,3247],{"class":105}," tema",[80,3249,109],{"class":98},[80,3251,113],{"class":112},[80,3253,3254],{"class":116},"claro",[80,3256,119],{"class":112},[80,3258,3259],{"class":98}," }",[80,3261,617],{"class":236},[80,3263,3264],{"class":82,"line":125},[80,3265,545],{"emptyLinePlaceholder":544},[80,3267,3268,3270],{"class":82,"line":151},[80,3269,2687],{"class":265},[80,3271,3272],{"class":236},"(\n",[80,3274,3275],{"class":82,"line":167},[80,3276,3277],{"class":98},"  {\n",[80,3279,3280,3282,3284,3286,3289,3291,3293,3296,3298],{"class":82,"line":533},[80,3281,2733],{"class":105},[80,3283,109],{"class":98},[80,3285,133],{"class":98},[80,3287,3288],{"class":105}," titulo",[80,3290,109],{"class":98},[80,3292,113],{"class":112},[80,3294,3295],{"class":116},"Meu App",[80,3297,119],{"class":112},[80,3299,148],{"class":98},[80,3301,3302,3305,3307],{"class":82,"line":541},[80,3303,3304],{"class":1689},"    render",[80,3306,2570],{"class":98},[80,3308,99],{"class":98},[80,3310,3311,3313,3315,3317,3319,3321,3323,3325,3327],{"class":82,"line":548},[80,3312,2370],{"class":371},[80,3314,345],{"class":265},[80,3316,269],{"class":105},[80,3318,119],{"class":112},[80,3320,205],{"class":116},[80,3322,119],{"class":112},[80,3324,278],{"class":98},[80,3326,391],{"class":98},[80,3328,457],{"class":105},[80,3330,3331,3334,3336,3338,3340,3342,3344,3346,3348,3350,3352,3355,3357,3359,3361,3364,3367],{"class":82,"line":554},[80,3332,3333],{"class":265},"        h",[80,3335,269],{"class":105},[80,3337,119],{"class":112},[80,3339,23],{"class":116},[80,3341,119],{"class":112},[80,3343,278],{"class":98},[80,3345,391],{"class":98},[80,3347,296],{"class":105},[80,3349,1583],{"class":265},[80,3351,269],{"class":105},[80,3353,3354],{"class":311},"this",[80,3356,682],{"class":98},[80,3358,758],{"class":236},[80,3360,682],{"class":98},[80,3362,3363],{"class":236},"titulo",[80,3365,3366],{"class":105},")])",[80,3368,122],{"class":98},[80,3370,3371,3373,3375,3377,3380,3382,3384,3386,3388,3390,3392,3394,3397,3399,3402,3404,3407,3409],{"class":82,"line":573},[80,3372,3333],{"class":265},[80,3374,269],{"class":105},[80,3376,119],{"class":112},[80,3378,3379],{"class":116},"small",[80,3381,119],{"class":112},[80,3383,278],{"class":98},[80,3385,391],{"class":98},[80,3387,296],{"class":105},[80,3389,1583],{"class":265},[80,3391,269],{"class":105},[80,3393,1588],{"class":112},[80,3395,3396],{"class":116},"Tema: ",[80,3398,1594],{"class":112},[80,3400,3401],{"class":236},"estadoGlobal",[80,3403,682],{"class":112},[80,3405,3406],{"class":236},"tema",[80,3408,1600],{"class":112},[80,3410,1603],{"class":105},[80,3412,3413],{"class":82,"line":593},[80,3414,3415],{"class":105},"      ])\n",[80,3417,3418],{"class":82,"line":620},[80,3419,2384],{"class":98},[80,3421,3422,3425,3427],{"class":82,"line":626},[80,3423,3424],{"class":1689},"    onMount",[80,3426,2570],{"class":98},[80,3428,99],{"class":98},[80,3430,3431,3434,3436,3438,3440,3442,3445,3447],{"class":82,"line":1139},[80,3432,3433],{"class":236},"      console",[80,3435,682],{"class":98},[80,3437,1476],{"class":265},[80,3439,269],{"class":105},[80,3441,119],{"class":112},[80,3443,3444],{"class":116},"Header montado",[80,3446,119],{"class":112},[80,3448,617],{"class":105},[80,3450,3451],{"class":82,"line":1144},[80,3452,2479],{"class":98},[80,3454,3455],{"class":82,"line":1149},[80,3456,1754],{"class":98},[80,3458,3459],{"class":82,"line":1170},[80,3460,3461],{"class":236},"  app\n",[80,3463,3464],{"class":82,"line":1190},[80,3465,617],{"class":236},[80,3467,3468],{"class":82,"line":1228},[80,3469,545],{"emptyLinePlaceholder":544},[80,3471,3472,3474,3476,3478,3480],{"class":82,"line":1233},[80,3473,87],{"class":86},[80,3475,1652],{"class":90},[80,3477,95],{"class":94},[80,3479,2704],{"class":265},[80,3481,3272],{"class":236},[80,3483,3484],{"class":82,"line":1273},[80,3485,3277],{"class":98},[80,3487,3488,3490,3492,3494,3497,3499,3501],{"class":82,"line":1312},[80,3489,2752],{"class":105},[80,3491,109],{"class":98},[80,3493,133],{"class":98},[80,3495,3496],{"class":105}," valor",[80,3498,109],{"class":98},[80,3500,1249],{"class":1248},[80,3502,148],{"class":98},[80,3504,3505,3507,3509],{"class":82,"line":1356},[80,3506,3304],{"class":1689},[80,3508,2570],{"class":98},[80,3510,99],{"class":98},[80,3512,3513,3515,3517,3519,3521,3523,3525,3527,3529],{"class":82,"line":1383},[80,3514,2370],{"class":371},[80,3516,345],{"class":265},[80,3518,269],{"class":105},[80,3520,119],{"class":112},[80,3522,205],{"class":116},[80,3524,119],{"class":112},[80,3526,278],{"class":98},[80,3528,391],{"class":98},[80,3530,457],{"class":105},[80,3532,3533,3535,3537,3539,3541,3543,3545,3547,3549,3551,3553,3555,3557,3559,3561,3563,3565,3567,3570,3572,3574],{"class":82,"line":1388},[80,3534,3333],{"class":265},[80,3536,269],{"class":105},[80,3538,119],{"class":112},[80,3540,11],{"class":116},[80,3542,119],{"class":112},[80,3544,278],{"class":98},[80,3546,391],{"class":98},[80,3548,296],{"class":105},[80,3550,1583],{"class":265},[80,3552,269],{"class":105},[80,3554,1588],{"class":112},[80,3556,1591],{"class":116},[80,3558,1594],{"class":112},[80,3560,3354],{"class":311},[80,3562,682],{"class":112},[80,3564,2765],{"class":236},[80,3566,682],{"class":112},[80,3568,3569],{"class":236},"valor",[80,3571,1600],{"class":112},[80,3573,3366],{"class":105},[80,3575,122],{"class":98},[80,3577,3578,3580,3582,3584,3587,3589,3591,3593,3596,3598,3601,3603,3606,3608,3610,3612,3614,3616,3618,3620,3622,3624,3626,3629,3631],{"class":82,"line":2233},[80,3579,3333],{"class":265},[80,3581,269],{"class":105},[80,3583,119],{"class":112},[80,3585,3586],{"class":116},"button",[80,3588,119],{"class":112},[80,3590,278],{"class":98},[80,3592,133],{"class":98},[80,3594,3595],{"class":265}," onclick",[80,3597,109],{"class":98},[80,3599,3600],{"class":98}," ()",[80,3602,2277],{"class":86},[80,3604,3605],{"class":311}," this",[80,3607,682],{"class":98},[80,3609,2765],{"class":236},[80,3611,682],{"class":98},[80,3613,3569],{"class":236},[80,3615,1266],{"class":94},[80,3617,293],{"class":98},[80,3619,296],{"class":105},[80,3621,1583],{"class":265},[80,3623,269],{"class":105},[80,3625,119],{"class":112},[80,3627,3628],{"class":116},"+1",[80,3630,119],{"class":112},[80,3632,1603],{"class":105},[80,3634,3635],{"class":82,"line":2254},[80,3636,3415],{"class":105},[80,3638,3639],{"class":82,"line":2291},[80,3640,2384],{"class":98},[80,3642,3643,3646,3648],{"class":82,"line":2296},[80,3644,3645],{"class":1689},"    onDestroy",[80,3647,2570],{"class":98},[80,3649,99],{"class":98},[80,3651,3652,3654,3656,3658,3660,3662,3665,3667],{"class":82,"line":2301},[80,3653,3433],{"class":236},[80,3655,682],{"class":98},[80,3657,1476],{"class":265},[80,3659,269],{"class":105},[80,3661,119],{"class":112},[80,3663,3664],{"class":116},"Contador removido",[80,3666,119],{"class":112},[80,3668,617],{"class":105},[80,3670,3671],{"class":82,"line":2316},[80,3672,2479],{"class":98},[80,3674,3675],{"class":82,"line":2333},[80,3676,1754],{"class":98},[80,3678,3679],{"class":82,"line":2351},[80,3680,3461],{"class":236},[80,3682,3683],{"class":82,"line":2367},[80,3684,617],{"class":236},[11,3686,3687,3688,3690],{},"Abre o DevTools na aba Elements e clica no botão. Você vai ver só o texto do ",[15,3689,176],{}," sendo alterado, nada mais sendo recriado.",[23,3692,3694],{"id":3693},"o-que-o-vue-faz-além-disso","O que o Vue faz além disso",[11,3696,3697],{},"O que construímos aqui é o núcleo real. O Vue adiciona em cima:",[11,3699,3700,3703],{},[58,3701,3702],{},"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.",[11,3705,3706,3709],{},[58,3707,3708],{},"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.",[11,3711,3712,3715,3716,3718,3719,3721,3722,3715,3725,682],{},[58,3713,3714],{},"Keys em listas:"," sem ",[15,3717,1725],{},", o diff compara filhos por posição. Com ",[15,3720,1725],{},", o Vue identifica qual item é qual e consegue reordenar sem recriar. É por isso que o Vue reclama quando você usa ",[15,3723,3724],{},"v-for",[15,3726,3727],{},":key",[11,3729,3730,3738,3739,3741,3742,3744,3745,3748,3749,3752],{},[58,3731,3732,3734,3735,109],{},[15,3733,17],{}," vs ",[15,3736,3737],{},"reactive()"," o ",[15,3740,3737],{}," do Vue é exatamente o Proxy que implementamos. O ",[15,3743,17],{}," é diferente: é implementado com getter\u002Fsetter em volta de um objeto ",[15,3746,3747],{},"{ value }",", por isso você acessa ",[15,3750,3751],{},".value"," em vez de acessar direto. São duas formas de criar reatividade, com trade-offs diferentes.",[11,3754,3755,3758,3759,3761],{},[58,3756,3757],{},"Slots e Teleport:"," abstrações em cima do sistema de componentes que o ",[15,3760,2687],{}," simples não suporta.",[11,3763,3764],{},"A diferença entre o nosso mini-framework e o Vue não é conceitual, é o quanto de borda foi resolvida.",[23,3766,3768],{"id":3767},"conclusão","Conclusão",[11,3770,3771],{},"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.",[11,3773,3774],{},"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.",[11,3776,3777,3778,3780,3781,3783,3784,3786],{},"Entender isso muda como você usa o framework no dia a dia. Quando você vê ",[15,3779,2517],{},", sabe que é o ",[15,3782,2523],{}," aguardando o flush. Quando o Vue reclama de ",[15,3785,3727],{},", você sabe por que o diff precisa dela. Quando um componente re-renderiza mais do que deveria, você sabe onde procurar.",[11,3788,3789],{},"O framework não é mágica. É só JavaScript bem organizado.",[23,3791,3793],{"id":3792},"referências","Referências",[3795,3796,3797],"ul",{},[3798,3799,3800],"li",{},[3801,3802,3806],"a",{"href":3803,"rel":3804},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fextras\u002Freactivity-in-depth",[3805],"nofollow","Reactivity in Depth - Documentação oficial do Vue",[3808,3809,3810],"style",{},"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":45,"searchDepth":102,"depth":102,"links":3812},[3813,3814,3815,3824,3825,3826,3827,3828,3829],{"id":25,"depth":102,"text":26},{"id":67,"depth":102,"text":68},{"id":323,"depth":102,"text":324,"children":3816},[3817,3819,3821,3823],{"id":328,"depth":125,"text":3818},"1. h(): criar o VNode",{"id":632,"depth":125,"text":3820},"2. mount(): primeira montagem",{"id":876,"depth":125,"text":3822},"3. patch(): atualizar só o que mudou",{"id":1408,"depth":125,"text":1409},{"id":1615,"depth":102,"text":1616},{"id":2510,"depth":102,"text":2511},{"id":2680,"depth":102,"text":2681},{"id":3693,"depth":102,"text":3694},{"id":3767,"depth":102,"text":3768},{"id":3792,"depth":102,"text":3793},"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.","md",null,"\u002Fimages\u002Fblog\u002Fvirtual-dom-capa.png",{},"\u002Fblog\u002Fvirtual-dom-mini-framework-do-zero",{"title":5,"description":3831},"blog\u002Fvirtual-dom-mini-framework-do-zero",[3840,3841,3842,3843],"vue-js","javascript","frontend","performance","VvxC7Mdy8D9TqcAyPdiP6phdRc-o0vVEg2Zp9-IbnJM",1783037053256]