[{"data":1,"prerenderedAt":5071},["ShallowReactive",2],{"post-\u002Ffr\u002Fposts\u002Fa-journey-to-craft-interactive-ui-experiences-with-dialogs":3,"post-link-fr-75df6bff-de40-4056-af23-fae2401cd21a":3001},{"id":4,"title":5,"body":6,"contentId":2989,"date":2990,"description":2991,"extension":2992,"meta":2993,"navigation":360,"path":2994,"rawbody":2995,"robots":2996,"schemaOrg":2996,"seo":2997,"sitemap":2998,"stem":2999,"__hash__":3000},"posts\u002Ffr\u002Fposts\u002Fa-journey-to-craft-interactive-ui-experiences-with-dialogs.md","Les boîtes de dialogue dans l'expérience utilisateur",{"type":7,"value":8,"toc":2981},"minimark",[9,18,2977],[10,11,12,13,17],"p",{},"Autant que je me souvienne, ce défi a été le plus persistant depuis que j'ai commencé le ",[14,15,16],"strong",{},"développement web",". Récemment, j'ai découvert une méthode pour construire une bibliothèque de composants, ce qui a été un long processus. Cependant, résoudre les subtilités de la gestion des boîtes de dialogue s'est avéré encore plus vaste, complexe, et crucial en raison de leur présence omniprésente dans chaque application.",[19,20,22,30,35,50,53,72,75,85,88,91,210,293,296,299,302,348,351,354,370,374,381,465,468,476,741,744,748,751,758,871,874,1047,1050,1177,1369,1372,1630,1657,1661,1668,1675,1838,1841,1860,1958,1961,1980,2137,2144,2147,2151,2154,2156,2406,2763,2897,2899,2912,2926,2930,2933,2950,2974],"callout-read-more",{"id":21},"75df6bff-de40-4056-af23-fae2401cd21a",[10,23,24,25,29],{},"Aujourd'hui, je suis ravi de partager la solution que j'ai trouvée, ",[26,27,28],"em",{},"je ne l'ai pas développée",", pour gérer efficacement les boîtes de dialogue au sein de vos applications. Cette solution découle de plusieurs années d'expérience, et j'espère qu'elle vous sera aussi bénéfique qu'elle l'a été pour moi.",[31,32,34],"h2",{"id":33},"comprendre-le-problème","Comprendre le problème",[10,36,37,38,41,42,45,46,49],{},"Avant de plonger dans le problème, alignons-nous sur ce dont nous parlons. Cet article se concentre sur les ",[14,39,40],{},"boîtes de dialogue",", ",[14,43,44],{},"modaux",", et ",[14,47,48],{},"slideover",". Chacun sert à afficher des informations ou à solliciter une confirmation, différant principalement en termes de présentation et d'utilisation.",[10,51,52],{},"Essayez d'ouvrir la boîte de dialogue en cliquant sur le bouton ci-dessous.",[54,55,59,60],"div",{"className":56},[57,58],"flex","justify-center","\n  ",[61,62,66,67],"modal",{"title":63,"description":64,":ui":65},"Boîte de Dialogue","Ceci est une boîte de dialogue","{ content: 'min-w-[280px]' }","\n    ",[68,69,71],"button",{"label":70},"Ouvrir la Boîte de Dialogue","\n  \n",[10,73,74],{},"De même, vous pouvez ouvrir un slideover en cliquant sur le bouton.",[54,76,59,78],{"className":77},[57,58],[48,79,66,82],{"title":80,"description":81,":ui":65},"Slideover","Ceci est un slideover",[68,83,71],{"label":84},"Ouvrir le Slideover",[10,86,87],{},"Le design et les types de dialogues peuvent sembler secondaires ; l'aspect important réside dans le code sous-jacent. Examinons-le :",[10,89,90],{},"::: code-group",[92,93,99],"pre",{"className":94,"code":95,"filename":96,"language":97,"meta":98,"style":98},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Ctemplate>\n  \u003CModal\n    title=\"Boîte de Dialogue\"\n    description=\"Ceci est une boîte de dialogue\"\n  >\n    \u003CButton label=\"Ouvrir la Boîte de Dialogue\" \u002F>\n  \u003C\u002FModal>\n\u003C\u002Ftemplate>\n","src\u002Fcomponents\u002FModal.vue","vue","",[100,101,102,118,127,146,160,166,189,200],"code",{"__ignoreMap":98},[103,104,107,111,115],"span",{"class":105,"line":106},"line",1,[103,108,110],{"class":109},"sMK4o","\u003C",[103,112,114],{"class":113},"swJcz","template",[103,116,117],{"class":109},">\n",[103,119,121,124],{"class":105,"line":120},2,[103,122,123],{"class":109},"  \u003C",[103,125,126],{"class":113},"Modal\n",[103,128,130,134,137,140,143],{"class":105,"line":129},3,[103,131,133],{"class":132},"spNyl","    title",[103,135,136],{"class":109},"=",[103,138,139],{"class":109},"\"",[103,141,63],{"class":142},"sfazB",[103,144,145],{"class":109},"\"\n",[103,147,149,152,154,156,158],{"class":105,"line":148},4,[103,150,151],{"class":132},"    description",[103,153,136],{"class":109},[103,155,139],{"class":109},[103,157,64],{"class":142},[103,159,145],{"class":109},[103,161,163],{"class":105,"line":162},5,[103,164,165],{"class":109},"  >\n",[103,167,169,172,175,178,180,182,184,186],{"class":105,"line":168},6,[103,170,171],{"class":109},"    \u003C",[103,173,174],{"class":113},"Button",[103,176,177],{"class":132}," label",[103,179,136],{"class":109},[103,181,139],{"class":109},[103,183,70],{"class":142},[103,185,139],{"class":109},[103,187,188],{"class":109}," \u002F>\n",[103,190,192,195,198],{"class":105,"line":191},7,[103,193,194],{"class":109},"  \u003C\u002F",[103,196,197],{"class":113},"Modal",[103,199,117],{"class":109},[103,201,203,206,208],{"class":105,"line":202},8,[103,204,205],{"class":109},"\u003C\u002F",[103,207,114],{"class":113},[103,209,117],{"class":109},[92,211,214],{"className":94,"code":212,"filename":213,"language":97,"meta":98,"style":98},"\u003Ctemplate>\n  \u003CSlideover\n    title=\"Slideover\"\n    description=\"Ceci est un slideover\"\n  >\n    \u003CButton label=\"Ouvrir le Slideover\" \u002F>\n  \u003C\u002FSlideover>\n\u003C\u002Ftemplate>\n","src\u002Fcomponents\u002FSlideover.vue",[100,215,216,224,231,243,255,259,277,285],{"__ignoreMap":98},[103,217,218,220,222],{"class":105,"line":106},[103,219,110],{"class":109},[103,221,114],{"class":113},[103,223,117],{"class":109},[103,225,226,228],{"class":105,"line":120},[103,227,123],{"class":109},[103,229,230],{"class":113},"Slideover\n",[103,232,233,235,237,239,241],{"class":105,"line":129},[103,234,133],{"class":132},[103,236,136],{"class":109},[103,238,139],{"class":109},[103,240,80],{"class":142},[103,242,145],{"class":109},[103,244,245,247,249,251,253],{"class":105,"line":148},[103,246,151],{"class":132},[103,248,136],{"class":109},[103,250,139],{"class":109},[103,252,81],{"class":142},[103,254,145],{"class":109},[103,256,257],{"class":105,"line":162},[103,258,165],{"class":109},[103,260,261,263,265,267,269,271,273,275],{"class":105,"line":168},[103,262,171],{"class":109},[103,264,174],{"class":113},[103,266,177],{"class":132},[103,268,136],{"class":109},[103,270,139],{"class":109},[103,272,84],{"class":142},[103,274,139],{"class":109},[103,276,188],{"class":109},[103,278,279,281,283],{"class":105,"line":191},[103,280,194],{"class":109},[103,282,80],{"class":113},[103,284,117],{"class":109},[103,286,287,289,291],{"class":105,"line":202},[103,288,205],{"class":109},[103,290,114],{"class":113},[103,292,117],{"class":109},[10,294,295],{},":::",[10,297,298],{},"Ils sont très similaires, reposant sur le même composant en coulisses. La véritable intrigue réside dans la manière dont ce code est utilisé. Ces exemples illustrent que les boîtes de dialogue s'intègrent parfaitement comme tout élément HTML, ce qui est le cœur du problème.",[10,300,301],{},"Les boîtes de dialogue sont un type particulier de composant dans une application :",[303,304,305,309,324],"ol",{},[306,307,308],"li",{},"Ils peuvent être invoqués de n'importe où dans l'application.",[306,310,311,312],{},"Ils s'ouvrent par divers moyens, tels que :",[313,314,315,318,321],"ul",{},[306,316,317],{},"Clics de bouton",[306,319,320],{},"Pressions de touches",[306,322,323],{},"Navigations dans les routes",[306,325,326,327,344,347],{},"La nature omniprésente rend des applications sans boîtes de dialogue presque impraticables sans sacrifier l'expérience utilisateur :",[313,328,329,332,335,338,341],{},[306,330,331],{},"Création d'éléments",[306,333,334],{},"Modification d'éléments",[306,336,337],{},"Confirmations d'action",[306,339,340],{},"Visualisations d'éléments",[306,342,343],{},"Etc.",[345,346],"br",{},"Chaque action peut mener à des conséquences immédiates ou attendre une réponse du serveur, ce qui complique les possibilités d'utilisation des boîtes de dialogue.",[10,349,350],{},"En raison de ces facteurs, les boîtes de dialogue nécessitent une attention minutieuse lors du développement et de l'utilisation ; sinon, ils et leur logique peuvent rapidement devenir chaotiques.",[10,352,353],{},"De plus, j'ai toujours eu une préférence personnelle pour permettre l'empilement de boîtes de dialogue avec des animations fluides, comme ceci :",[355,356,59,357,59,366],"figure",{},[358,359,66,361,59],"video",{"autoPlay":360,"loop":360,"muted":360,"playsInline":360},true,[362,363],"source",{"src":364,"type":365},"https:\u002F\u002Fimages.soubiran.dev\u002Fposts\u002Fa-journey-to-craft-interactive-ui-experiences-with-dialogs\u002Fmodal-stacking.mp4","video\u002Fmp4",[367,368,369],"figcaption",{},"Empiler des boîtes de dialogue avec une animation élégante.",[31,371,373],{"id":372},"boîte-de-dialogue-en-ligne","Boîte de dialogue en ligne",[10,375,376,377,380],{},"Une première tentative pour relever ce défi est via des ",[14,378,379],{},"boîtes de dialogue en ligne",". C'est une approche répandue et qui convient aux cas simples. La stratégie consiste à incorporer un composant modal directement dans le template nécessitant celui-ci, comme vu précédemment.",[92,382,384],{"className":94,"code":383,"filename":96,"language":97,"meta":98,"style":98},"\u003Ctemplate>\n  \u003CModal\n    title=\"Dialogue\"\n    description=\"Ceci est un dialogue\"\n  >\n    \u003CButton label=\"Ouvrir le Dialogue\" \u002F>\n  \u003C\u002FModal>\n\u003C\u002Ftemplate>\n",[100,385,386,394,400,413,426,430,449,457],{"__ignoreMap":98},[103,387,388,390,392],{"class":105,"line":106},[103,389,110],{"class":109},[103,391,114],{"class":113},[103,393,117],{"class":109},[103,395,396,398],{"class":105,"line":120},[103,397,123],{"class":109},[103,399,126],{"class":113},[103,401,402,404,406,408,411],{"class":105,"line":129},[103,403,133],{"class":132},[103,405,136],{"class":109},[103,407,139],{"class":109},[103,409,410],{"class":142},"Dialogue",[103,412,145],{"class":109},[103,414,415,417,419,421,424],{"class":105,"line":148},[103,416,151],{"class":132},[103,418,136],{"class":109},[103,420,139],{"class":109},[103,422,423],{"class":142},"Ceci est un dialogue",[103,425,145],{"class":109},[103,427,428],{"class":105,"line":162},[103,429,165],{"class":109},[103,431,432,434,436,438,440,442,445,447],{"class":105,"line":168},[103,433,171],{"class":109},[103,435,174],{"class":113},[103,437,177],{"class":132},[103,439,136],{"class":109},[103,441,139],{"class":109},[103,443,444],{"class":142},"Ouvrir le Dialogue",[103,446,139],{"class":109},[103,448,188],{"class":109},[103,450,451,453,455],{"class":105,"line":191},[103,452,194],{"class":109},[103,454,197],{"class":113},[103,456,117],{"class":109},[103,458,459,461,463],{"class":105,"line":202},[103,460,205],{"class":109},[103,462,114],{"class":113},[103,464,117],{"class":109},[10,466,467],{},"Bien que l'implémentation soit simple, cette approche présente au moins deux inconvénients significatifs :",[303,469,470,473],{},[306,471,472],{},"Les composants de boîte de dialogue deviennent étroitement couplés avec le composant qui les utilise, compliquant la compréhension et le refactoring.",[306,474,475],{},"Il devient coûteux, en terme de code, d'ajouter une nouvelle boîte de dialogue, surtout lorsqu'on ouvre les ouvre sans boutons ou en découplant les templates d'une boîte de dialogue. Un exemple d'ouverture d'une boîte dialogue par pression de touche suit :",[92,477,480],{"className":94,"code":478,"filename":479,"language":97,"meta":98,"style":98},"\u003Cscript lang=\"ts\" setup>\nimport { ref } from 'vue'\n\nconst isDialogOpen = ref(false)\n\ndefineShortcuts({\n  meta_k: () => {\n    isDialogOpen.value = !isDialogOpen.value\n  }\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003CModal\n      v-model:open=\"isDialogOpen\"\n      title=\"Boîte de Dialogue\"\n      description=\"Ceci est une boîte de dialogue\"\n    \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n","src\u002Fcomponents\u002FInlineModal.vue",[100,481,482,506,533,538,561,565,575,592,617,623,631,640,645,654,663,670,689,703,717,723,732],{"__ignoreMap":98},[103,483,484,486,489,492,494,496,499,501,504],{"class":105,"line":106},[103,485,110],{"class":109},[103,487,488],{"class":113},"script",[103,490,491],{"class":132}," lang",[103,493,136],{"class":109},[103,495,139],{"class":109},[103,497,498],{"class":142},"ts",[103,500,139],{"class":109},[103,502,503],{"class":132}," setup",[103,505,117],{"class":109},[103,507,508,512,515,519,522,525,528,530],{"class":105,"line":120},[103,509,511],{"class":510},"s7zQu","import",[103,513,514],{"class":109}," {",[103,516,518],{"class":517},"sTEyZ"," ref",[103,520,521],{"class":109}," }",[103,523,524],{"class":510}," from",[103,526,527],{"class":109}," '",[103,529,97],{"class":142},[103,531,532],{"class":109},"'\n",[103,534,535],{"class":105,"line":129},[103,536,537],{"emptyLinePlaceholder":360},"\n",[103,539,540,543,546,548,551,554,558],{"class":105,"line":148},[103,541,542],{"class":132},"const",[103,544,545],{"class":517}," isDialogOpen ",[103,547,136],{"class":109},[103,549,518],{"class":550},"s2Zo4",[103,552,553],{"class":517},"(",[103,555,557],{"class":556},"sfNiH","false",[103,559,560],{"class":517},")\n",[103,562,563],{"class":105,"line":162},[103,564,537],{"emptyLinePlaceholder":360},[103,566,567,570,572],{"class":105,"line":168},[103,568,569],{"class":550},"defineShortcuts",[103,571,553],{"class":517},[103,573,574],{"class":109},"{\n",[103,576,577,580,583,586,589],{"class":105,"line":191},[103,578,579],{"class":550},"  meta_k",[103,581,582],{"class":109},":",[103,584,585],{"class":109}," ()",[103,587,588],{"class":132}," =>",[103,590,591],{"class":109}," {\n",[103,593,594,597,600,603,606,609,612,614],{"class":105,"line":202},[103,595,596],{"class":517},"    isDialogOpen",[103,598,599],{"class":109},".",[103,601,602],{"class":517},"value",[103,604,605],{"class":109}," =",[103,607,608],{"class":109}," !",[103,610,611],{"class":517},"isDialogOpen",[103,613,599],{"class":109},[103,615,616],{"class":517},"value\n",[103,618,620],{"class":105,"line":619},9,[103,621,622],{"class":109},"  }\n",[103,624,626,629],{"class":105,"line":625},10,[103,627,628],{"class":109},"}",[103,630,560],{"class":517},[103,632,634,636,638],{"class":105,"line":633},11,[103,635,205],{"class":109},[103,637,488],{"class":113},[103,639,117],{"class":109},[103,641,643],{"class":105,"line":642},12,[103,644,537],{"emptyLinePlaceholder":360},[103,646,648,650,652],{"class":105,"line":647},13,[103,649,110],{"class":109},[103,651,114],{"class":113},[103,653,117],{"class":109},[103,655,657,659,661],{"class":105,"line":656},14,[103,658,123],{"class":109},[103,660,54],{"class":113},[103,662,117],{"class":109},[103,664,666,668],{"class":105,"line":665},15,[103,667,171],{"class":109},[103,669,126],{"class":113},[103,671,673,676,678,681,683,685,687],{"class":105,"line":672},16,[103,674,675],{"class":132},"      v-model",[103,677,582],{"class":109},[103,679,680],{"class":132},"open",[103,682,136],{"class":109},[103,684,139],{"class":109},[103,686,611],{"class":517},[103,688,145],{"class":109},[103,690,692,695,697,699,701],{"class":105,"line":691},17,[103,693,694],{"class":132},"      title",[103,696,136],{"class":109},[103,698,139],{"class":109},[103,700,63],{"class":142},[103,702,145],{"class":109},[103,704,706,709,711,713,715],{"class":105,"line":705},18,[103,707,708],{"class":132},"      description",[103,710,136],{"class":109},[103,712,139],{"class":109},[103,714,64],{"class":142},[103,716,145],{"class":109},[103,718,720],{"class":105,"line":719},19,[103,721,722],{"class":109},"    \u002F>\n",[103,724,726,728,730],{"class":105,"line":725},20,[103,727,194],{"class":109},[103,729,54],{"class":113},[103,731,117],{"class":109},[103,733,735,737,739],{"class":105,"line":734},21,[103,736,205],{"class":109},[103,738,114],{"class":113},[103,740,117],{"class":109},[10,742,743],{},"Cette méthode devient rapidement encombrante et difficile à maintenir au fil du temps. Plusieurs boîtes de dialogue intensifient le code répétitif, nécessitant des ajouts répétés dans chaque composant utilisant la boîte de dialogue.",[31,745,747],{"id":746},"boîtes-de-dialogue-globales","Boîtes de dialogue globales",[10,749,750],{},"Améliorer la gestion des boîtes de dialogue implique d'en ouvrir programatiquement de manière universelle, découplant leur invocation de toute relation directe avec un composant. Ce modèle facilite le refactoring du code en séparant physiquement les boîtes de dialogue des composants, améliorant la réutilisabilité à travers plusieurs composants sans incorporations redondantes.",[10,752,753,754,757],{},"Un fichier ",[100,755,756],{},"App.vue"," pourrait ressembler à ceci :",[92,759,763],{"className":760,"code":761,"filename":762,"language":97,"meta":98,"style":98},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight has-highlighted","\u003Ctemplate>\n  \u003Cdiv>\n    \u003CLayout>\n      \u003CRouterView \u002F>\n    \u003C\u002FLayout>\n\n    \u003CModalFeature1 \u002F> \u003C!-- [!code highlight] -->\n    \u003CModalFeature2 \u002F> \u003C!-- [!code highlight] -->\n    \u003CModalFeature3 \u002F> \u003C!-- [!code highlight] -->\n\n    \u003C!-- Et ainsi de suite... -->\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n","src\u002FApp.vue",[100,764,765,773,781,790,800,809,813,825,835,845,849,855,863],{"__ignoreMap":98},[103,766,767,769,771],{"class":105,"line":106},[103,768,110],{"class":109},[103,770,114],{"class":113},[103,772,117],{"class":109},[103,774,775,777,779],{"class":105,"line":120},[103,776,123],{"class":109},[103,778,54],{"class":113},[103,780,117],{"class":109},[103,782,783,785,788],{"class":105,"line":129},[103,784,171],{"class":109},[103,786,787],{"class":113},"Layout",[103,789,117],{"class":109},[103,791,792,795,798],{"class":105,"line":148},[103,793,794],{"class":109},"      \u003C",[103,796,797],{"class":113},"RouterView",[103,799,188],{"class":109},[103,801,802,805,807],{"class":105,"line":162},[103,803,804],{"class":109},"    \u003C\u002F",[103,806,787],{"class":113},[103,808,117],{"class":109},[103,810,811],{"class":105,"line":168},[103,812,537],{"emptyLinePlaceholder":360},[103,814,817,819,822],{"class":815,"line":191},[105,816],"highlighted",[103,818,171],{"class":109},[103,820,821],{"class":113},"ModalFeature1",[103,823,824],{"class":109}," \u002F>",[103,826,828,830,833],{"class":827,"line":202},[105,816],[103,829,171],{"class":109},[103,831,832],{"class":113},"ModalFeature2",[103,834,824],{"class":109},[103,836,838,840,843],{"class":837,"line":619},[105,816],[103,839,171],{"class":109},[103,841,842],{"class":113},"ModalFeature3",[103,844,824],{"class":109},[103,846,847],{"class":105,"line":625},[103,848,537],{"emptyLinePlaceholder":360},[103,850,851],{"class":105,"line":633},[103,852,854],{"class":853},"sHwdD","    \u003C!-- Et ainsi de suite... -->\n",[103,856,857,859,861],{"class":105,"line":642},[103,858,194],{"class":109},[103,860,54],{"class":113},[103,862,117],{"class":109},[103,864,865,867,869],{"class":105,"line":647},[103,866,205],{"class":109},[103,868,114],{"class":113},[103,870,117],{"class":109},[10,872,873],{},"Atteindre cette configuration exige de partager une variable entre le composant ouvrant la boîte de dialogue et la boîte de dialogue ell-même. Dans Vue, une approche simple, bien que pas idéale, est de créer une composable externalisant son état.",[92,875,879],{"className":876,"code":877,"filename":878,"language":498,"meta":98,"style":98},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight has-highlighted","import { ref } from 'vue'\n\nconst isOpen = ref(false) \u002F\u002F [!code highlight]\n\nexport function useDialog() {\n  function open() {\n    isOpen.value = true\n  }\n\n  function close() {\n    isOpen.value = false\n  }\n\n  return {\n    isOpen,\n\n    open,\n    close\n  }\n}\n","src\u002Fcomposables\u002FuseDialog.ts",[100,880,881,899,903,922,926,942,954,968,972,976,987,1000,1004,1008,1015,1022,1026,1033,1038,1042],{"__ignoreMap":98},[103,882,883,885,887,889,891,893,895,897],{"class":105,"line":106},[103,884,511],{"class":510},[103,886,514],{"class":109},[103,888,518],{"class":517},[103,890,521],{"class":109},[103,892,524],{"class":510},[103,894,527],{"class":109},[103,896,97],{"class":142},[103,898,532],{"class":109},[103,900,901],{"class":105,"line":120},[103,902,537],{"emptyLinePlaceholder":360},[103,904,906,908,911,913,915,917,919],{"class":905,"line":129},[105,816],[103,907,542],{"class":132},[103,909,910],{"class":517}," isOpen ",[103,912,136],{"class":109},[103,914,518],{"class":550},[103,916,553],{"class":517},[103,918,557],{"class":556},[103,920,921],{"class":517},") ",[103,923,924],{"class":105,"line":148},[103,925,537],{"emptyLinePlaceholder":360},[103,927,928,931,934,937,940],{"class":105,"line":162},[103,929,930],{"class":510},"export",[103,932,933],{"class":132}," function",[103,935,936],{"class":550}," useDialog",[103,938,939],{"class":109},"()",[103,941,591],{"class":109},[103,943,944,947,950,952],{"class":105,"line":168},[103,945,946],{"class":132},"  function",[103,948,949],{"class":550}," open",[103,951,939],{"class":109},[103,953,591],{"class":109},[103,955,956,959,961,963,965],{"class":105,"line":191},[103,957,958],{"class":517},"    isOpen",[103,960,599],{"class":109},[103,962,602],{"class":517},[103,964,605],{"class":109},[103,966,967],{"class":556}," true\n",[103,969,970],{"class":105,"line":202},[103,971,622],{"class":109},[103,973,974],{"class":105,"line":619},[103,975,537],{"emptyLinePlaceholder":360},[103,977,978,980,983,985],{"class":105,"line":625},[103,979,946],{"class":132},[103,981,982],{"class":550}," close",[103,984,939],{"class":109},[103,986,591],{"class":109},[103,988,989,991,993,995,997],{"class":105,"line":633},[103,990,958],{"class":517},[103,992,599],{"class":109},[103,994,602],{"class":517},[103,996,605],{"class":109},[103,998,999],{"class":556}," false\n",[103,1001,1002],{"class":105,"line":642},[103,1003,622],{"class":109},[103,1005,1006],{"class":105,"line":647},[103,1007,537],{"emptyLinePlaceholder":360},[103,1009,1010,1013],{"class":105,"line":656},[103,1011,1012],{"class":510},"  return",[103,1014,591],{"class":109},[103,1016,1017,1019],{"class":105,"line":665},[103,1018,958],{"class":517},[103,1020,1021],{"class":109},",\n",[103,1023,1024],{"class":105,"line":672},[103,1025,537],{"emptyLinePlaceholder":360},[103,1027,1028,1031],{"class":105,"line":691},[103,1029,1030],{"class":517},"    open",[103,1032,1021],{"class":109},[103,1034,1035],{"class":105,"line":705},[103,1036,1037],{"class":517},"    close\n",[103,1039,1040],{"class":105,"line":719},[103,1041,622],{"class":109},[103,1043,1044],{"class":105,"line":725},[103,1045,1046],{"class":109},"}\n",[10,1048,1049],{},"Utilisez ce composable pour le composant ouvrant la boîte de dialogue et la boîte de dialogue elle-même.",[92,1051,1053],{"className":760,"code":1052,"filename":762,"language":97,"meta":98,"style":98},"\u003Cscript lang=\"ts\" setup>\nimport { useDialog } from '@\u002Fcomposables\u002FuseDialog'\n\nconst { open } = useDialog() \u002F\u002F [!code highlight]\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CButton label=\"Ouvrir la Boîte de Dialogue\" @click=\"open\" \u002F>\n\u003C\u002Ftemplate>\n",[100,1054,1055,1075,1094,1098,1117,1125,1129,1137,1169],{"__ignoreMap":98},[103,1056,1057,1059,1061,1063,1065,1067,1069,1071,1073],{"class":105,"line":106},[103,1058,110],{"class":109},[103,1060,488],{"class":113},[103,1062,491],{"class":132},[103,1064,136],{"class":109},[103,1066,139],{"class":109},[103,1068,498],{"class":142},[103,1070,139],{"class":109},[103,1072,503],{"class":132},[103,1074,117],{"class":109},[103,1076,1077,1079,1081,1083,1085,1087,1089,1092],{"class":105,"line":120},[103,1078,511],{"class":510},[103,1080,514],{"class":109},[103,1082,936],{"class":517},[103,1084,521],{"class":109},[103,1086,524],{"class":510},[103,1088,527],{"class":109},[103,1090,1091],{"class":142},"@\u002Fcomposables\u002FuseDialog",[103,1093,532],{"class":109},[103,1095,1096],{"class":105,"line":129},[103,1097,537],{"emptyLinePlaceholder":360},[103,1099,1101,1103,1105,1108,1110,1112,1114],{"class":1100,"line":148},[105,816],[103,1102,542],{"class":132},[103,1104,514],{"class":109},[103,1106,1107],{"class":517}," open ",[103,1109,628],{"class":109},[103,1111,605],{"class":109},[103,1113,936],{"class":550},[103,1115,1116],{"class":517},"() ",[103,1118,1119,1121,1123],{"class":105,"line":162},[103,1120,205],{"class":109},[103,1122,488],{"class":113},[103,1124,117],{"class":109},[103,1126,1127],{"class":105,"line":168},[103,1128,537],{"emptyLinePlaceholder":360},[103,1130,1131,1133,1135],{"class":105,"line":191},[103,1132,110],{"class":109},[103,1134,114],{"class":113},[103,1136,117],{"class":109},[103,1138,1139,1141,1143,1145,1147,1149,1151,1153,1156,1159,1161,1163,1165,1167],{"class":105,"line":202},[103,1140,123],{"class":109},[103,1142,174],{"class":113},[103,1144,177],{"class":132},[103,1146,136],{"class":109},[103,1148,139],{"class":109},[103,1150,70],{"class":142},[103,1152,139],{"class":109},[103,1154,1155],{"class":109}," @",[103,1157,1158],{"class":132},"click",[103,1160,136],{"class":109},[103,1162,139],{"class":109},[103,1164,680],{"class":517},[103,1166,139],{"class":109},[103,1168,188],{"class":109},[103,1170,1171,1173,1175],{"class":105,"line":619},[103,1172,205],{"class":109},[103,1174,114],{"class":113},[103,1176,117],{"class":109},[92,1178,1181],{"className":760,"code":1179,"filename":1180,"language":97,"meta":98,"style":98},"\u003Cscript lang=\"ts\" setup>\nimport { useDialog } from '@\u002Fcomposables\u002FuseDialog'\n\nconst { isOpen, close } = useDialog() \u002F\u002F [!code highlight]\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CModal\n    v-model:open=\"isOpen\"\n    title=\"Boîte de Dialogue\"\n    description=\"Ceci est une boîte de dialogue\"\n  >\n    \u003CButton label=\"Fermer\" @click=\"close\" \u002F>\n  \u003C\u002FModal>\n\u003C\u002Ftemplate>\n","src\u002Fcomponents\u002FModalFeature1.vue",[100,1182,1183,1203,1221,1225,1249,1257,1261,1269,1275,1293,1305,1317,1321,1353,1361],{"__ignoreMap":98},[103,1184,1185,1187,1189,1191,1193,1195,1197,1199,1201],{"class":105,"line":106},[103,1186,110],{"class":109},[103,1188,488],{"class":113},[103,1190,491],{"class":132},[103,1192,136],{"class":109},[103,1194,139],{"class":109},[103,1196,498],{"class":142},[103,1198,139],{"class":109},[103,1200,503],{"class":132},[103,1202,117],{"class":109},[103,1204,1205,1207,1209,1211,1213,1215,1217,1219],{"class":105,"line":120},[103,1206,511],{"class":510},[103,1208,514],{"class":109},[103,1210,936],{"class":517},[103,1212,521],{"class":109},[103,1214,524],{"class":510},[103,1216,527],{"class":109},[103,1218,1091],{"class":142},[103,1220,532],{"class":109},[103,1222,1223],{"class":105,"line":129},[103,1224,537],{"emptyLinePlaceholder":360},[103,1226,1228,1230,1232,1235,1238,1241,1243,1245,1247],{"class":1227,"line":148},[105,816],[103,1229,542],{"class":132},[103,1231,514],{"class":109},[103,1233,1234],{"class":517}," isOpen",[103,1236,1237],{"class":109},",",[103,1239,1240],{"class":517}," close ",[103,1242,628],{"class":109},[103,1244,605],{"class":109},[103,1246,936],{"class":550},[103,1248,1116],{"class":517},[103,1250,1251,1253,1255],{"class":105,"line":162},[103,1252,205],{"class":109},[103,1254,488],{"class":113},[103,1256,117],{"class":109},[103,1258,1259],{"class":105,"line":168},[103,1260,537],{"emptyLinePlaceholder":360},[103,1262,1263,1265,1267],{"class":105,"line":191},[103,1264,110],{"class":109},[103,1266,114],{"class":113},[103,1268,117],{"class":109},[103,1270,1271,1273],{"class":105,"line":202},[103,1272,123],{"class":109},[103,1274,126],{"class":113},[103,1276,1277,1280,1282,1284,1286,1288,1291],{"class":105,"line":619},[103,1278,1279],{"class":132},"    v-model",[103,1281,582],{"class":109},[103,1283,680],{"class":132},[103,1285,136],{"class":109},[103,1287,139],{"class":109},[103,1289,1290],{"class":517},"isOpen",[103,1292,145],{"class":109},[103,1294,1295,1297,1299,1301,1303],{"class":105,"line":625},[103,1296,133],{"class":132},[103,1298,136],{"class":109},[103,1300,139],{"class":109},[103,1302,63],{"class":142},[103,1304,145],{"class":109},[103,1306,1307,1309,1311,1313,1315],{"class":105,"line":633},[103,1308,151],{"class":132},[103,1310,136],{"class":109},[103,1312,139],{"class":109},[103,1314,64],{"class":142},[103,1316,145],{"class":109},[103,1318,1319],{"class":105,"line":642},[103,1320,165],{"class":109},[103,1322,1323,1325,1327,1329,1331,1333,1336,1338,1340,1342,1344,1346,1349,1351],{"class":105,"line":647},[103,1324,171],{"class":109},[103,1326,174],{"class":113},[103,1328,177],{"class":132},[103,1330,136],{"class":109},[103,1332,139],{"class":109},[103,1334,1335],{"class":142},"Fermer",[103,1337,139],{"class":109},[103,1339,1155],{"class":109},[103,1341,1158],{"class":132},[103,1343,136],{"class":109},[103,1345,139],{"class":109},[103,1347,1348],{"class":517},"close",[103,1350,139],{"class":109},[103,1352,188],{"class":109},[103,1354,1355,1357,1359],{"class":105,"line":656},[103,1356,194],{"class":109},[103,1358,197],{"class":113},[103,1360,117],{"class":109},[103,1362,1363,1365,1367],{"class":105,"line":665},[103,1364,205],{"class":109},[103,1366,114],{"class":113},[103,1368,117],{"class":109},[10,1370,1371],{},"Cette approche est certainement supérieure à la méthode en ligne mais présente tout de même des inconvénients :",[303,1373,1374,1377],{},[306,1375,1376],{},"L'inclusion de boîtes de dialogue au niveau de l'application (ou de la route) crée une redondance de code.",[306,1378,1379,1380],{},"La gestion des boîtes de dialogue devient complexe, particulièrement avec de nombreuses boîtes de dialogue ou propriétés, chacune nécessitant un passage à travers la composable.",[92,1381,1383],{"className":876,"code":1382,"language":498,"meta":98,"style":98},"import { ref } from 'vue'\n\nconst isOpen = ref(false)\nconst title = ref('') \u002F\u002F [!code highlight]\nconst description = ref('') \u002F\u002F [!code highlight]\n\nexport function useDialog() {\n  function open(newTitle: string, newDescription: string) {\n    title.value = newTitle\n    description.value = newDescription\n    isOpen.value = true\n  }\n\n  function close() {\n    isOpen.value = false\n  }\n\n  return {\n    isOpen,\n    title,\n    description,\n    open,\n    close\n  }\n}\n",[100,1384,1385,1403,1407,1423,1442,1460,1464,1476,1508,1521,1534,1546,1550,1554,1564,1576,1580,1584,1590,1596,1602,1608,1615,1620,1625],{"__ignoreMap":98},[103,1386,1387,1389,1391,1393,1395,1397,1399,1401],{"class":105,"line":106},[103,1388,511],{"class":510},[103,1390,514],{"class":109},[103,1392,518],{"class":517},[103,1394,521],{"class":109},[103,1396,524],{"class":510},[103,1398,527],{"class":109},[103,1400,97],{"class":142},[103,1402,532],{"class":109},[103,1404,1405],{"class":105,"line":120},[103,1406,537],{"emptyLinePlaceholder":360},[103,1408,1409,1411,1413,1415,1417,1419,1421],{"class":105,"line":129},[103,1410,542],{"class":132},[103,1412,910],{"class":517},[103,1414,136],{"class":109},[103,1416,518],{"class":550},[103,1418,553],{"class":517},[103,1420,557],{"class":556},[103,1422,560],{"class":517},[103,1424,1426,1428,1431,1433,1435,1437,1440],{"class":1425,"line":148},[105,816],[103,1427,542],{"class":132},[103,1429,1430],{"class":517}," title ",[103,1432,136],{"class":109},[103,1434,518],{"class":550},[103,1436,553],{"class":517},[103,1438,1439],{"class":109},"''",[103,1441,921],{"class":517},[103,1443,1445,1447,1450,1452,1454,1456,1458],{"class":1444,"line":162},[105,816],[103,1446,542],{"class":132},[103,1448,1449],{"class":517}," description ",[103,1451,136],{"class":109},[103,1453,518],{"class":550},[103,1455,553],{"class":517},[103,1457,1439],{"class":109},[103,1459,921],{"class":517},[103,1461,1462],{"class":105,"line":168},[103,1463,537],{"emptyLinePlaceholder":360},[103,1465,1466,1468,1470,1472,1474],{"class":105,"line":191},[103,1467,930],{"class":510},[103,1469,933],{"class":132},[103,1471,936],{"class":550},[103,1473,939],{"class":109},[103,1475,591],{"class":109},[103,1477,1478,1480,1482,1484,1488,1490,1494,1496,1499,1501,1503,1506],{"class":105,"line":202},[103,1479,946],{"class":132},[103,1481,949],{"class":550},[103,1483,553],{"class":109},[103,1485,1487],{"class":1486},"sHdIc","newTitle",[103,1489,582],{"class":109},[103,1491,1493],{"class":1492},"sBMFI"," string",[103,1495,1237],{"class":109},[103,1497,1498],{"class":1486}," newDescription",[103,1500,582],{"class":109},[103,1502,1493],{"class":1492},[103,1504,1505],{"class":109},")",[103,1507,591],{"class":109},[103,1509,1510,1512,1514,1516,1518],{"class":105,"line":619},[103,1511,133],{"class":517},[103,1513,599],{"class":109},[103,1515,602],{"class":517},[103,1517,605],{"class":109},[103,1519,1520],{"class":517}," newTitle\n",[103,1522,1523,1525,1527,1529,1531],{"class":105,"line":625},[103,1524,151],{"class":517},[103,1526,599],{"class":109},[103,1528,602],{"class":517},[103,1530,605],{"class":109},[103,1532,1533],{"class":517}," newDescription\n",[103,1535,1536,1538,1540,1542,1544],{"class":105,"line":633},[103,1537,958],{"class":517},[103,1539,599],{"class":109},[103,1541,602],{"class":517},[103,1543,605],{"class":109},[103,1545,967],{"class":556},[103,1547,1548],{"class":105,"line":642},[103,1549,622],{"class":109},[103,1551,1552],{"class":105,"line":647},[103,1553,537],{"emptyLinePlaceholder":360},[103,1555,1556,1558,1560,1562],{"class":105,"line":656},[103,1557,946],{"class":132},[103,1559,982],{"class":550},[103,1561,939],{"class":109},[103,1563,591],{"class":109},[103,1565,1566,1568,1570,1572,1574],{"class":105,"line":665},[103,1567,958],{"class":517},[103,1569,599],{"class":109},[103,1571,602],{"class":517},[103,1573,605],{"class":109},[103,1575,999],{"class":556},[103,1577,1578],{"class":105,"line":672},[103,1579,622],{"class":109},[103,1581,1582],{"class":105,"line":691},[103,1583,537],{"emptyLinePlaceholder":360},[103,1585,1586,1588],{"class":105,"line":705},[103,1587,1012],{"class":510},[103,1589,591],{"class":109},[103,1591,1592,1594],{"class":105,"line":719},[103,1593,958],{"class":517},[103,1595,1021],{"class":109},[103,1597,1598,1600],{"class":105,"line":725},[103,1599,133],{"class":517},[103,1601,1021],{"class":109},[103,1603,1604,1606],{"class":105,"line":734},[103,1605,151],{"class":517},[103,1607,1021],{"class":109},[103,1609,1611,1613],{"class":105,"line":1610},22,[103,1612,1030],{"class":517},[103,1614,1021],{"class":109},[103,1616,1618],{"class":105,"line":1617},23,[103,1619,1037],{"class":517},[103,1621,1623],{"class":105,"line":1622},24,[103,1624,622],{"class":109},[103,1626,1628],{"class":105,"line":1627},25,[103,1629,1046],{"class":109},[1631,1632,1633],"blockquote",{},[10,1634,1635,1638,1639,1648,1649,1656],{},[103,1636,1637],{},"!NOTE","\nSimplifiez l'écriture des composables avec ",[1640,1641,1645],"a",{"href":1642,"rel":1643},"https:\u002F\u002Fvueuse.org\u002Fshared\u002FcreateSharedComposable\u002F",[1644],"nofollow",[100,1646,1647],{},"createSharedComposable"," de ",[1640,1650,1653],{"href":1651,"rel":1652},"https:\u002F\u002Fvueuse.org\u002F",[1644],[100,1654,1655],{},"@vueuse\u002Fcore"," pour créer un composable singleton, partageant automatiquement le même état entre tous les composants qui l'utilisent.",[31,1658,1660],{"id":1659},"dialogues-programmatiques","Dialogues programmatiques",[10,1662,1663,1664,1667],{},"La méthode la plus récente et profondément efficace que j'ai découverte implique ",[14,1665,1666],{},"la gestion programmatique des boîtes de dialogue",". Cette méthode résout les difficultés mentionnées plus tôt en permettant un contrôle complet des boîtes de dialogue par le biais du code plutôt que des templates.",[10,1669,1670,1671,1674],{},"Ayant utilisé des boîtes de dialogue programmatiques dans ",[14,1672,1673],{},"Angular"," avec Angular Material, j'ai apprécié sa capacité à simplifier le code.",[92,1676,1680],{"className":1677,"code":1678,"filename":1679,"language":498,"meta":98,"style":98},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const dialogRef = dialog.open(UserProfileComponent, {\n  height: '400px',\n  width: '600px',\n})\n\ndialogRef.afterClosed().subscribe((result) => {\n  console.log(`Résultat du dialogue : ${result}`) \u002F\u002F Pizza !\n})\n\ndialogRef.close('Pizza !')\n","Angular Material Dialog",[100,1681,1682,1705,1722,1738,1744,1748,1778,1809,1815,1819],{"__ignoreMap":98},[103,1683,1684,1686,1689,1691,1694,1696,1698,1701,1703],{"class":105,"line":106},[103,1685,542],{"class":132},[103,1687,1688],{"class":517}," dialogRef ",[103,1690,136],{"class":109},[103,1692,1693],{"class":517}," dialog",[103,1695,599],{"class":109},[103,1697,680],{"class":550},[103,1699,1700],{"class":517},"(UserProfileComponent",[103,1702,1237],{"class":109},[103,1704,591],{"class":109},[103,1706,1707,1710,1712,1714,1717,1720],{"class":105,"line":120},[103,1708,1709],{"class":113},"  height",[103,1711,582],{"class":109},[103,1713,527],{"class":109},[103,1715,1716],{"class":142},"400px",[103,1718,1719],{"class":109},"'",[103,1721,1021],{"class":109},[103,1723,1724,1727,1729,1731,1734,1736],{"class":105,"line":129},[103,1725,1726],{"class":113},"  width",[103,1728,582],{"class":109},[103,1730,527],{"class":109},[103,1732,1733],{"class":142},"600px",[103,1735,1719],{"class":109},[103,1737,1021],{"class":109},[103,1739,1740,1742],{"class":105,"line":148},[103,1741,628],{"class":109},[103,1743,560],{"class":517},[103,1745,1746],{"class":105,"line":162},[103,1747,537],{"emptyLinePlaceholder":360},[103,1749,1750,1753,1755,1758,1760,1762,1765,1767,1769,1772,1774,1776],{"class":105,"line":168},[103,1751,1752],{"class":517},"dialogRef",[103,1754,599],{"class":109},[103,1756,1757],{"class":550},"afterClosed",[103,1759,939],{"class":517},[103,1761,599],{"class":109},[103,1763,1764],{"class":550},"subscribe",[103,1766,553],{"class":517},[103,1768,553],{"class":109},[103,1770,1771],{"class":1486},"result",[103,1773,1505],{"class":109},[103,1775,588],{"class":132},[103,1777,591],{"class":109},[103,1779,1780,1783,1785,1788,1790,1793,1796,1799,1801,1804,1806],{"class":105,"line":191},[103,1781,1782],{"class":517},"  console",[103,1784,599],{"class":109},[103,1786,1787],{"class":550},"log",[103,1789,553],{"class":113},[103,1791,1792],{"class":109},"`",[103,1794,1795],{"class":142},"Résultat du dialogue : ",[103,1797,1798],{"class":109},"${",[103,1800,1771],{"class":517},[103,1802,1803],{"class":109},"}`",[103,1805,921],{"class":113},[103,1807,1808],{"class":853},"\u002F\u002F Pizza !\n",[103,1810,1811,1813],{"class":105,"line":202},[103,1812,628],{"class":109},[103,1814,560],{"class":517},[103,1816,1817],{"class":105,"line":619},[103,1818,537],{"emptyLinePlaceholder":360},[103,1820,1821,1823,1825,1827,1829,1831,1834,1836],{"class":105,"line":625},[103,1822,1752],{"class":517},[103,1824,599],{"class":109},[103,1826,1348],{"class":550},[103,1828,553],{"class":517},[103,1830,1719],{"class":109},[103,1832,1833],{"class":142},"Pizza !",[103,1835,1719],{"class":109},[103,1837,560],{"class":517},[10,1839,1840],{},"Pendant longtemps, j'ai espéré d'une fonctionnalité similaire dans Vue.",[10,1842,1843,1844,1849,1850,1855,1856,1859],{},"Ce temps est désormais arrivé, lorsque ",[1640,1845,1848],{"href":1846,"rel":1847},"https:\u002F\u002Fnuxt.com\u002F",[1644],"Nuxt"," a sorti la version 3 de ",[1640,1851,1854],{"href":1852,"rel":1853},"https:\u002F\u002Fui.nuxt.com\u002F",[1644],"Nuxt UI"," avec la composable ",[100,1857,1858],{},"useOverlay",". Ce composable partagé par l'application permet d'ouvrir des boîtes de dialogue de n'importe quel endroit, imitant la fonctionnalité d'Angular Material.",[92,1861,1863],{"className":94,"code":1862,"filename":762,"language":97,"meta":98,"style":98},"\u003Cscript setup lang=\"ts\">\nconst overlay = useOverlay()\n\nasync function openModal() {\n  overlay\n    .create(MyModal)\n    .open()\n}\n\u003C\u002Fscript>\n",[100,1864,1865,1885,1900,1904,1918,1923,1938,1946,1950],{"__ignoreMap":98},[103,1866,1867,1869,1871,1873,1875,1877,1879,1881,1883],{"class":105,"line":106},[103,1868,110],{"class":109},[103,1870,488],{"class":113},[103,1872,503],{"class":132},[103,1874,491],{"class":132},[103,1876,136],{"class":109},[103,1878,139],{"class":109},[103,1880,498],{"class":142},[103,1882,139],{"class":109},[103,1884,117],{"class":109},[103,1886,1887,1889,1892,1894,1897],{"class":105,"line":120},[103,1888,542],{"class":132},[103,1890,1891],{"class":517}," overlay ",[103,1893,136],{"class":109},[103,1895,1896],{"class":550}," useOverlay",[103,1898,1899],{"class":517},"()\n",[103,1901,1902],{"class":105,"line":129},[103,1903,537],{"emptyLinePlaceholder":360},[103,1905,1906,1909,1911,1914,1916],{"class":105,"line":148},[103,1907,1908],{"class":132},"async",[103,1910,933],{"class":132},[103,1912,1913],{"class":550}," openModal",[103,1915,939],{"class":109},[103,1917,591],{"class":109},[103,1919,1920],{"class":105,"line":162},[103,1921,1922],{"class":517},"  overlay\n",[103,1924,1925,1928,1931,1933,1936],{"class":105,"line":168},[103,1926,1927],{"class":109},"    .",[103,1929,1930],{"class":550},"create",[103,1932,553],{"class":113},[103,1934,1935],{"class":517},"MyModal",[103,1937,560],{"class":113},[103,1939,1940,1942,1944],{"class":105,"line":191},[103,1941,1927],{"class":109},[103,1943,680],{"class":550},[103,1945,1899],{"class":113},[103,1947,1948],{"class":105,"line":202},[103,1949,1046],{"class":109},[103,1951,1952,1954,1956],{"class":105,"line":619},[103,1953,205],{"class":109},[103,1955,488],{"class":113},[103,1957,117],{"class":109},[10,1959,1960],{},"Magnifique, n'est-ce pas ? 😍",[10,1962,1963,1964,1971,1972,1979],{},"Sous le capot, cette méthode utilise la directive ",[1640,1965,1968],{"href":1966,"rel":1967},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fessentials\u002Fcomponent-basics.html#dynamic-components",[1644],[100,1969,1970],{},"component"," avec une simple boucle ",[1640,1973,1976],{"href":1974,"rel":1975},"https:\u002F\u002Fvuejs.org\u002Fapi\u002Fbuilt-in-directives.html#v-for",[1644],[100,1977,1978],{},"v-for"," pour rendre les boîtes de dialogue.",[92,1981,1984],{"className":94,"code":1982,"filename":1983,"language":97,"meta":98,"style":98},"\u003Cscript lang=\"ts\" setup>\nconst { overlays, close } = useOverlay()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Ccomponent\n    :is=\"overlay.component\"\n    v-for=\"overlay in overlays\"\n    :key=\"overlay.id\"\n    v-bind=\"overlay.props\"\n    v-model:open=\"overlay.modelValue\"\n  \u002F>\n\u003C\u002Ftemplate>\n","OverlayProvider.vue",[100,1985,1986,2006,2027,2035,2039,2047,2054,2068,2082,2096,2110,2124,2129],{"__ignoreMap":98},[103,1987,1988,1990,1992,1994,1996,1998,2000,2002,2004],{"class":105,"line":106},[103,1989,110],{"class":109},[103,1991,488],{"class":113},[103,1993,491],{"class":132},[103,1995,136],{"class":109},[103,1997,139],{"class":109},[103,1999,498],{"class":142},[103,2001,139],{"class":109},[103,2003,503],{"class":132},[103,2005,117],{"class":109},[103,2007,2008,2010,2012,2015,2017,2019,2021,2023,2025],{"class":105,"line":120},[103,2009,542],{"class":132},[103,2011,514],{"class":109},[103,2013,2014],{"class":517}," overlays",[103,2016,1237],{"class":109},[103,2018,1240],{"class":517},[103,2020,628],{"class":109},[103,2022,605],{"class":109},[103,2024,1896],{"class":550},[103,2026,1899],{"class":517},[103,2028,2029,2031,2033],{"class":105,"line":129},[103,2030,205],{"class":109},[103,2032,488],{"class":113},[103,2034,117],{"class":109},[103,2036,2037],{"class":105,"line":148},[103,2038,537],{"emptyLinePlaceholder":360},[103,2040,2041,2043,2045],{"class":105,"line":162},[103,2042,110],{"class":109},[103,2044,114],{"class":113},[103,2046,117],{"class":109},[103,2048,2049,2051],{"class":105,"line":168},[103,2050,123],{"class":109},[103,2052,2053],{"class":113},"component\n",[103,2055,2056,2059,2061,2063,2066],{"class":105,"line":191},[103,2057,2058],{"class":132},"    :is",[103,2060,136],{"class":109},[103,2062,139],{"class":109},[103,2064,2065],{"class":142},"overlay.component",[103,2067,145],{"class":109},[103,2069,2070,2073,2075,2077,2080],{"class":105,"line":202},[103,2071,2072],{"class":132},"    v-for",[103,2074,136],{"class":109},[103,2076,139],{"class":109},[103,2078,2079],{"class":142},"overlay in overlays",[103,2081,145],{"class":109},[103,2083,2084,2087,2089,2091,2094],{"class":105,"line":619},[103,2085,2086],{"class":132},"    :key",[103,2088,136],{"class":109},[103,2090,139],{"class":109},[103,2092,2093],{"class":142},"overlay.id",[103,2095,145],{"class":109},[103,2097,2098,2101,2103,2105,2108],{"class":105,"line":625},[103,2099,2100],{"class":132},"    v-bind",[103,2102,136],{"class":109},[103,2104,139],{"class":109},[103,2106,2107],{"class":142},"overlay.props",[103,2109,145],{"class":109},[103,2111,2112,2115,2117,2119,2122],{"class":105,"line":633},[103,2113,2114],{"class":132},"    v-model:open",[103,2116,136],{"class":109},[103,2118,139],{"class":109},[103,2120,2121],{"class":142},"overlay.modelValue",[103,2123,145],{"class":109},[103,2125,2126],{"class":105,"line":642},[103,2127,2128],{"class":109},"  \u002F>\n",[103,2130,2131,2133,2135],{"class":105,"line":647},[103,2132,205],{"class":109},[103,2134,114],{"class":113},[103,2136,117],{"class":109},[1631,2138,2139],{},[10,2140,2141,2143],{},[103,2142,1637],{},"\nIl s'agit d'une version simplifiée du code.",[10,2145,2146],{},"Avec cette méthode, les coûts de la gestion d'état sont éliminés, ce qui supprime le besoin de composables partagés ou de boîtes de dialogue globales. Cela améliore considérablement la lisibilité, la maintenabilité et la création d'expériences UI interactives.",[31,2148,2150],{"id":2149},"empilement-des-boîtes-de-dialogue-sans-effort","Empilement des boîtes de dialogue sans effort",[10,2152,2153],{},"Le point culminant de cette approche est l'empilement de boîtes de dialogue sans effort. Il suffit de rendre un tableau de boîtes de dialogue via une boucle pour permettre l'ouverture simultanée de boîtes de dialogue, facilitant un empilement élégant avec des animations fluides.",[10,2155,90],{},[92,2157,2159],{"className":94,"code":2158,"filename":762,"language":97,"meta":98,"style":98},"\u003Cscript lang=\"ts\" setup>\nimport DialogFeature1 from '.\u002Fcomponents\u002FDialogFeature1.vue'\n\nconst overlay = useOverlay()\nfunction openOverlay() {\n  overlay\n    .create(DialogFeature1, {\n      props: {\n        title: 'Premier Modal',\n        description: 'Ceci est le premier modal'\n      },\n      destroyOnClose: true\n    })\n    .open()\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003CButton label=\"Ouvrir le Premier Overlay\" @click=\"openOverlay\" \u002F>\n\n    \u003COverlayProvider \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[100,2160,2161,2181,2198,2202,2214,2226,2230,2245,2254,2270,2284,2289,2298,2305,2313,2317,2325,2329,2337,2345,2377,2381,2390,2398],{"__ignoreMap":98},[103,2162,2163,2165,2167,2169,2171,2173,2175,2177,2179],{"class":105,"line":106},[103,2164,110],{"class":109},[103,2166,488],{"class":113},[103,2168,491],{"class":132},[103,2170,136],{"class":109},[103,2172,139],{"class":109},[103,2174,498],{"class":142},[103,2176,139],{"class":109},[103,2178,503],{"class":132},[103,2180,117],{"class":109},[103,2182,2183,2185,2188,2191,2193,2196],{"class":105,"line":120},[103,2184,511],{"class":510},[103,2186,2187],{"class":517}," DialogFeature1 ",[103,2189,2190],{"class":510},"from",[103,2192,527],{"class":109},[103,2194,2195],{"class":142},".\u002Fcomponents\u002FDialogFeature1.vue",[103,2197,532],{"class":109},[103,2199,2200],{"class":105,"line":129},[103,2201,537],{"emptyLinePlaceholder":360},[103,2203,2204,2206,2208,2210,2212],{"class":105,"line":148},[103,2205,542],{"class":132},[103,2207,1891],{"class":517},[103,2209,136],{"class":109},[103,2211,1896],{"class":550},[103,2213,1899],{"class":517},[103,2215,2216,2219,2222,2224],{"class":105,"line":162},[103,2217,2218],{"class":132},"function",[103,2220,2221],{"class":550}," openOverlay",[103,2223,939],{"class":109},[103,2225,591],{"class":109},[103,2227,2228],{"class":105,"line":168},[103,2229,1922],{"class":517},[103,2231,2232,2234,2236,2238,2241,2243],{"class":105,"line":191},[103,2233,1927],{"class":109},[103,2235,1930],{"class":550},[103,2237,553],{"class":113},[103,2239,2240],{"class":517},"DialogFeature1",[103,2242,1237],{"class":109},[103,2244,591],{"class":109},[103,2246,2247,2250,2252],{"class":105,"line":202},[103,2248,2249],{"class":113},"      props",[103,2251,582],{"class":109},[103,2253,591],{"class":109},[103,2255,2256,2259,2261,2263,2266,2268],{"class":105,"line":619},[103,2257,2258],{"class":113},"        title",[103,2260,582],{"class":109},[103,2262,527],{"class":109},[103,2264,2265],{"class":142},"Premier Modal",[103,2267,1719],{"class":109},[103,2269,1021],{"class":109},[103,2271,2272,2275,2277,2279,2282],{"class":105,"line":625},[103,2273,2274],{"class":113},"        description",[103,2276,582],{"class":109},[103,2278,527],{"class":109},[103,2280,2281],{"class":142},"Ceci est le premier modal",[103,2283,532],{"class":109},[103,2285,2286],{"class":105,"line":633},[103,2287,2288],{"class":109},"      },\n",[103,2290,2291,2294,2296],{"class":105,"line":642},[103,2292,2293],{"class":113},"      destroyOnClose",[103,2295,582],{"class":109},[103,2297,967],{"class":556},[103,2299,2300,2303],{"class":105,"line":647},[103,2301,2302],{"class":109},"    }",[103,2304,560],{"class":113},[103,2306,2307,2309,2311],{"class":105,"line":656},[103,2308,1927],{"class":109},[103,2310,680],{"class":550},[103,2312,1899],{"class":113},[103,2314,2315],{"class":105,"line":665},[103,2316,1046],{"class":109},[103,2318,2319,2321,2323],{"class":105,"line":672},[103,2320,205],{"class":109},[103,2322,488],{"class":113},[103,2324,117],{"class":109},[103,2326,2327],{"class":105,"line":691},[103,2328,537],{"emptyLinePlaceholder":360},[103,2330,2331,2333,2335],{"class":105,"line":705},[103,2332,110],{"class":109},[103,2334,114],{"class":113},[103,2336,117],{"class":109},[103,2338,2339,2341,2343],{"class":105,"line":719},[103,2340,123],{"class":109},[103,2342,54],{"class":113},[103,2344,117],{"class":109},[103,2346,2347,2349,2351,2353,2355,2357,2360,2362,2364,2366,2368,2370,2373,2375],{"class":105,"line":725},[103,2348,171],{"class":109},[103,2350,174],{"class":113},[103,2352,177],{"class":132},[103,2354,136],{"class":109},[103,2356,139],{"class":109},[103,2358,2359],{"class":142},"Ouvrir le Premier Overlay",[103,2361,139],{"class":109},[103,2363,1155],{"class":109},[103,2365,1158],{"class":132},[103,2367,136],{"class":109},[103,2369,139],{"class":109},[103,2371,2372],{"class":517},"openOverlay",[103,2374,139],{"class":109},[103,2376,188],{"class":109},[103,2378,2379],{"class":105,"line":734},[103,2380,537],{"emptyLinePlaceholder":360},[103,2382,2383,2385,2388],{"class":105,"line":1610},[103,2384,171],{"class":109},[103,2386,2387],{"class":113},"OverlayProvider",[103,2389,188],{"class":109},[103,2391,2392,2394,2396],{"class":105,"line":1617},[103,2393,194],{"class":109},[103,2395,54],{"class":113},[103,2397,117],{"class":109},[103,2399,2400,2402,2404],{"class":105,"line":1622},[103,2401,205],{"class":109},[103,2403,114],{"class":113},[103,2405,117],{"class":109},[92,2407,2410],{"className":94,"code":2408,"filename":2409,"language":97,"meta":98,"style":98},"\u003Cscript lang=\"ts\" setup>\nimport DialogFeature2 from '.\u002FDialogFeature2.vue'\n\nconst props = defineProps\u003C{\n  title: string\n  description: string\n}>()\n\nconst overlay = useOverlay()\nfunction openOverlay() {\n  overlay\n    .create(DialogFeature2, {\n      props: {\n        title: 'Deuxième Modal',\n        description: 'Ceci est le deuxième modal'\n      },\n      destroyOnClose: true\n    })\n    .open()\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CModal\n    :title=\"props.title\"\n    :description=\"props.description\"\n  >\n    \u003Ctemplate #body>\n      \u003CButton\n        label=\"Ouvrir le Deuxième Modal\"\n        @click=\"openOverlay\"\n      \u002F>\n    \u003C\u002Ftemplate>\n  \u003C\u002FModal>\n\u003C\u002Ftemplate>\n","src\u002Fcomponents\u002FDialogFeature1.vue",[100,2411,2412,2432,2448,2452,2467,2477,2486,2493,2497,2509,2519,2523,2538,2546,2561,2574,2578,2586,2592,2600,2604,2612,2616,2624,2630,2651,2671,2676,2691,2699,2714,2730,2736,2745,2754],{"__ignoreMap":98},[103,2413,2414,2416,2418,2420,2422,2424,2426,2428,2430],{"class":105,"line":106},[103,2415,110],{"class":109},[103,2417,488],{"class":113},[103,2419,491],{"class":132},[103,2421,136],{"class":109},[103,2423,139],{"class":109},[103,2425,498],{"class":142},[103,2427,139],{"class":109},[103,2429,503],{"class":132},[103,2431,117],{"class":109},[103,2433,2434,2436,2439,2441,2443,2446],{"class":105,"line":120},[103,2435,511],{"class":510},[103,2437,2438],{"class":517}," DialogFeature2 ",[103,2440,2190],{"class":510},[103,2442,527],{"class":109},[103,2444,2445],{"class":142},".\u002FDialogFeature2.vue",[103,2447,532],{"class":109},[103,2449,2450],{"class":105,"line":129},[103,2451,537],{"emptyLinePlaceholder":360},[103,2453,2454,2456,2459,2461,2464],{"class":105,"line":148},[103,2455,542],{"class":132},[103,2457,2458],{"class":517}," props ",[103,2460,136],{"class":109},[103,2462,2463],{"class":550}," defineProps",[103,2465,2466],{"class":109},"\u003C{\n",[103,2468,2469,2472,2474],{"class":105,"line":162},[103,2470,2471],{"class":113},"  title",[103,2473,582],{"class":109},[103,2475,2476],{"class":1492}," string\n",[103,2478,2479,2482,2484],{"class":105,"line":168},[103,2480,2481],{"class":113},"  description",[103,2483,582],{"class":109},[103,2485,2476],{"class":1492},[103,2487,2488,2491],{"class":105,"line":191},[103,2489,2490],{"class":109},"}>",[103,2492,1899],{"class":517},[103,2494,2495],{"class":105,"line":202},[103,2496,537],{"emptyLinePlaceholder":360},[103,2498,2499,2501,2503,2505,2507],{"class":105,"line":619},[103,2500,542],{"class":132},[103,2502,1891],{"class":517},[103,2504,136],{"class":109},[103,2506,1896],{"class":550},[103,2508,1899],{"class":517},[103,2510,2511,2513,2515,2517],{"class":105,"line":625},[103,2512,2218],{"class":132},[103,2514,2221],{"class":550},[103,2516,939],{"class":109},[103,2518,591],{"class":109},[103,2520,2521],{"class":105,"line":633},[103,2522,1922],{"class":517},[103,2524,2525,2527,2529,2531,2534,2536],{"class":105,"line":642},[103,2526,1927],{"class":109},[103,2528,1930],{"class":550},[103,2530,553],{"class":113},[103,2532,2533],{"class":517},"DialogFeature2",[103,2535,1237],{"class":109},[103,2537,591],{"class":109},[103,2539,2540,2542,2544],{"class":105,"line":647},[103,2541,2249],{"class":113},[103,2543,582],{"class":109},[103,2545,591],{"class":109},[103,2547,2548,2550,2552,2554,2557,2559],{"class":105,"line":656},[103,2549,2258],{"class":113},[103,2551,582],{"class":109},[103,2553,527],{"class":109},[103,2555,2556],{"class":142},"Deuxième Modal",[103,2558,1719],{"class":109},[103,2560,1021],{"class":109},[103,2562,2563,2565,2567,2569,2572],{"class":105,"line":665},[103,2564,2274],{"class":113},[103,2566,582],{"class":109},[103,2568,527],{"class":109},[103,2570,2571],{"class":142},"Ceci est le deuxième modal",[103,2573,532],{"class":109},[103,2575,2576],{"class":105,"line":672},[103,2577,2288],{"class":109},[103,2579,2580,2582,2584],{"class":105,"line":691},[103,2581,2293],{"class":113},[103,2583,582],{"class":109},[103,2585,967],{"class":556},[103,2587,2588,2590],{"class":105,"line":705},[103,2589,2302],{"class":109},[103,2591,560],{"class":113},[103,2593,2594,2596,2598],{"class":105,"line":719},[103,2595,1927],{"class":109},[103,2597,680],{"class":550},[103,2599,1899],{"class":113},[103,2601,2602],{"class":105,"line":725},[103,2603,1046],{"class":109},[103,2605,2606,2608,2610],{"class":105,"line":734},[103,2607,205],{"class":109},[103,2609,488],{"class":113},[103,2611,117],{"class":109},[103,2613,2614],{"class":105,"line":1610},[103,2615,537],{"emptyLinePlaceholder":360},[103,2617,2618,2620,2622],{"class":105,"line":1617},[103,2619,110],{"class":109},[103,2621,114],{"class":113},[103,2623,117],{"class":109},[103,2625,2626,2628],{"class":105,"line":1622},[103,2627,123],{"class":109},[103,2629,126],{"class":113},[103,2631,2632,2635,2638,2640,2642,2645,2647,2649],{"class":105,"line":1627},[103,2633,2634],{"class":109},"    :",[103,2636,2637],{"class":132},"title",[103,2639,136],{"class":109},[103,2641,139],{"class":109},[103,2643,2644],{"class":517},"props",[103,2646,599],{"class":109},[103,2648,2637],{"class":517},[103,2650,145],{"class":109},[103,2652,2654,2656,2659,2661,2663,2665,2667,2669],{"class":105,"line":2653},26,[103,2655,2634],{"class":109},[103,2657,2658],{"class":132},"description",[103,2660,136],{"class":109},[103,2662,139],{"class":109},[103,2664,2644],{"class":517},[103,2666,599],{"class":109},[103,2668,2658],{"class":517},[103,2670,145],{"class":109},[103,2672,2674],{"class":105,"line":2673},27,[103,2675,165],{"class":109},[103,2677,2679,2681,2683,2686,2689],{"class":105,"line":2678},28,[103,2680,171],{"class":109},[103,2682,114],{"class":113},[103,2684,2685],{"class":109}," #",[103,2687,2688],{"class":132},"body",[103,2690,117],{"class":109},[103,2692,2694,2696],{"class":105,"line":2693},29,[103,2695,794],{"class":109},[103,2697,2698],{"class":113},"Button\n",[103,2700,2702,2705,2707,2709,2712],{"class":105,"line":2701},30,[103,2703,2704],{"class":132},"        label",[103,2706,136],{"class":109},[103,2708,139],{"class":109},[103,2710,2711],{"class":142},"Ouvrir le Deuxième Modal",[103,2713,145],{"class":109},[103,2715,2717,2720,2722,2724,2726,2728],{"class":105,"line":2716},31,[103,2718,2719],{"class":109},"        @",[103,2721,1158],{"class":132},[103,2723,136],{"class":109},[103,2725,139],{"class":109},[103,2727,2372],{"class":517},[103,2729,145],{"class":109},[103,2731,2733],{"class":105,"line":2732},32,[103,2734,2735],{"class":109},"      \u002F>\n",[103,2737,2739,2741,2743],{"class":105,"line":2738},33,[103,2740,804],{"class":109},[103,2742,114],{"class":113},[103,2744,117],{"class":109},[103,2746,2748,2750,2752],{"class":105,"line":2747},34,[103,2749,194],{"class":109},[103,2751,197],{"class":113},[103,2753,117],{"class":109},[103,2755,2757,2759,2761],{"class":105,"line":2756},35,[103,2758,205],{"class":109},[103,2760,114],{"class":113},[103,2762,117],{"class":109},[92,2764,2767],{"className":94,"code":2765,"filename":2766,"language":97,"meta":98,"style":98},"\u003Cscript lang=\"ts\" setup>\nconst props = defineProps\u003C{\n  title: string\n  description: string\n}>()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CModal\n    :title=\"props.title\"\n    :description=\"props.description\"\n  \u002F>\n\u003C\u002Ftemplate>\n","src\u002Fcomponents\u002FDialogFeature2.vue",[100,2768,2769,2789,2801,2809,2817,2823,2831,2835,2843,2849,2867,2885,2889],{"__ignoreMap":98},[103,2770,2771,2773,2775,2777,2779,2781,2783,2785,2787],{"class":105,"line":106},[103,2772,110],{"class":109},[103,2774,488],{"class":113},[103,2776,491],{"class":132},[103,2778,136],{"class":109},[103,2780,139],{"class":109},[103,2782,498],{"class":142},[103,2784,139],{"class":109},[103,2786,503],{"class":132},[103,2788,117],{"class":109},[103,2790,2791,2793,2795,2797,2799],{"class":105,"line":120},[103,2792,542],{"class":132},[103,2794,2458],{"class":517},[103,2796,136],{"class":109},[103,2798,2463],{"class":550},[103,2800,2466],{"class":109},[103,2802,2803,2805,2807],{"class":105,"line":129},[103,2804,2471],{"class":113},[103,2806,582],{"class":109},[103,2808,2476],{"class":1492},[103,2810,2811,2813,2815],{"class":105,"line":148},[103,2812,2481],{"class":113},[103,2814,582],{"class":109},[103,2816,2476],{"class":1492},[103,2818,2819,2821],{"class":105,"line":162},[103,2820,2490],{"class":109},[103,2822,1899],{"class":517},[103,2824,2825,2827,2829],{"class":105,"line":168},[103,2826,205],{"class":109},[103,2828,488],{"class":113},[103,2830,117],{"class":109},[103,2832,2833],{"class":105,"line":191},[103,2834,537],{"emptyLinePlaceholder":360},[103,2836,2837,2839,2841],{"class":105,"line":202},[103,2838,110],{"class":109},[103,2840,114],{"class":113},[103,2842,117],{"class":109},[103,2844,2845,2847],{"class":105,"line":619},[103,2846,123],{"class":109},[103,2848,126],{"class":113},[103,2850,2851,2853,2855,2857,2859,2861,2863,2865],{"class":105,"line":625},[103,2852,2634],{"class":109},[103,2854,2637],{"class":132},[103,2856,136],{"class":109},[103,2858,139],{"class":109},[103,2860,2644],{"class":517},[103,2862,599],{"class":109},[103,2864,2637],{"class":517},[103,2866,145],{"class":109},[103,2868,2869,2871,2873,2875,2877,2879,2881,2883],{"class":105,"line":633},[103,2870,2634],{"class":109},[103,2872,2658],{"class":132},[103,2874,136],{"class":109},[103,2876,139],{"class":109},[103,2878,2644],{"class":517},[103,2880,599],{"class":109},[103,2882,2658],{"class":517},[103,2884,145],{"class":109},[103,2886,2887],{"class":105,"line":642},[103,2888,2128],{"class":109},[103,2890,2891,2893,2895],{"class":105,"line":647},[103,2892,205],{"class":109},[103,2894,114],{"class":113},[103,2896,117],{"class":109},[10,2898,295],{},[10,2900,2901,2902],{},"Explorez le code complet sur ",[2903,2904,2906,2907,599],"git-hub-link",{"repo":2905},"barbapapazes\u002Fvue-dialog-stacking"," et même ",[1640,2908,2911],{"href":2909,"rel":2910},"https:\u002F\u002Fvue-dialog-stacking.barbapapazes.dev\u002F",[1644],"une démo en direct",[355,2913,59,2914,59,2923],{},[2915,2916],"iframe",{"src":2909,"width":2917,"height":2918,"frameBorder":2919,"scrolling":2920,"allowFullScreen":360,"className":2921},"100%",600,"0","no",[2922],"bg-white",[367,2924,2925],{},"Démo en direct de l'empilement de boîtes de dialogue",[31,2927,2929],{"id":2928},"conclusion","Conclusion",[10,2931,2932],{},"Après des années de lutte avec les boîtes de dialogue et les modaux et d'essai de plusieurs approches, j'ai enfin trouvé une méthode qui fonctionne pour moi et qui peut fonctionner pour vous.",[10,2934,2935,2936,2941,2942,2949],{},"J'ai d'abord rencontré cette approche avec Angular Material et j'ai espéré quelque chose de similaire dans Vue. ",[1640,2937,2940],{"href":2938,"rel":2939},"https:\u002F\u002Fgithub.com\u002Fantfu",[1644],"Anthony Fu"," a proposé un début prometteur avec ",[1640,2943,2946],{"href":2944,"rel":2945},"https:\u002F\u002Fgithub.com\u002Fantfu\u002Fvue-template-promise",[1644],[100,2947,2948],{},"useTemplatePromise",", mais cela est resté complexe uniquement pour l'ouverture de boîtes de dialogue.",[10,2951,2952,2953,2958,2959,2964,2965,2970,2971,2973],{},"Enfin, je remercie ",[1640,2954,2957],{"href":2955,"rel":2956},"https:\u002F\u002Fgithub.com\u002Fzernonia",[1644],"zernonia"," pour ",[1640,2960,2963],{"href":2961,"rel":2962},"https:\u002F\u002Freka-ui.com\u002F",[1644],"Reka UI"," et ",[1640,2966,2969],{"href":2967,"rel":2968},"https:\u002F\u002Fgithub.com\u002Fgenu",[1644],"Eugen Istoc"," pour la composable ",[100,2972,1858],{}," dans Nuxt UI, je peux désormais gérer les boîtes de dialogue de manière appropriée dans Vue.",[10,2975,2976],{},"Je suis convaincu que cette approche est un changement majeur pour les expériences UI interactives dans Vue. N'hésitez pas à l'essayer !",[2978,2979,2980],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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 .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":98,"searchDepth":120,"depth":120,"links":2982},[2983,2984,2985,2986,2987,2988],{"id":33,"depth":120,"text":34},{"id":372,"depth":120,"text":373},{"id":746,"depth":120,"text":747},{"id":1659,"depth":120,"text":1660},{"id":2149,"depth":120,"text":2150},{"id":2928,"depth":120,"text":2929},"fe3fd9b9-481e-4d38-9cea-969595fc913a","2025-03-29","Après des années de recherche d'une approche efficace pour gérer les boîtes de dialogue, j'ai enfin découvert une technique fiable et efficace.","md",{},"\u002Ffr\u002Fposts\u002Fa-journey-to-craft-interactive-ui-experiences-with-dialogs","---\ncontentId: fe3fd9b9-481e-4d38-9cea-969595fc913a\ntitle: \"Les boîtes de dialogue dans l'expérience utilisateur\"\ndescription: \"Après des années de recherche d'une approche efficace pour gérer les boîtes de dialogue, j'ai enfin découvert une technique fiable et efficace.\"\ndate: 2025-03-29\n---\n\nAutant que je me souvienne, ce défi a été le plus persistant depuis que j'ai commencé le **développement web**. Récemment, j'ai découvert une méthode pour construire une bibliothèque de composants, ce qui a été un long processus. Cependant, résoudre les subtilités de la gestion des boîtes de dialogue s'est avéré encore plus vaste, complexe, et crucial en raison de leur présence omniprésente dans chaque application.\n\n\u003CCalloutReadMore id=\"75df6bff-de40-4056-af23-fae2401cd21a\" \u002F>\n\nAujourd'hui, je suis ravi de partager la solution que j'ai trouvée, _je ne l'ai pas développée_, pour gérer efficacement les boîtes de dialogue au sein de vos applications. Cette solution découle de plusieurs années d'expérience, et j'espère qu'elle vous sera aussi bénéfique qu'elle l'a été pour moi.\n\n## Comprendre le problème\n\nAvant de plonger dans le problème, alignons-nous sur ce dont nous parlons. Cet article se concentre sur les **boîtes de dialogue**, **modaux**, et **slideover**. Chacun sert à afficher des informations ou à solliciter une confirmation, différant principalement en termes de présentation et d'utilisation.\n\nEssayez d'ouvrir la boîte de dialogue en cliquant sur le bouton ci-dessous.\n\n\u003Cdiv class=\"flex justify-center\">\n  \u003CModal\n    title=\"Boîte de Dialogue\"\n    description=\"Ceci est une boîte de dialogue\"\n    :ui=\"{ content: 'min-w-[280px]' }\"\n  >\n    \u003CButton label=\"Ouvrir la Boîte de Dialogue\" \u002F>\n  \u003C\u002FModal>\n\u003C\u002Fdiv>\n\nDe même, vous pouvez ouvrir un slideover en cliquant sur le bouton.\n\n\u003Cdiv class=\"flex justify-center\">\n  \u003CSlideover\n    title=\"Slideover\"\n    description=\"Ceci est un slideover\"\n    :ui=\"{ content: 'min-w-[280px]' }\"\n  >\n    \u003CButton label=\"Ouvrir le Slideover\" \u002F>\n  \u003C\u002FSlideover>\n\u003C\u002Fdiv>\n\nLe design et les types de dialogues peuvent sembler secondaires ; l'aspect important réside dans le code sous-jacent. Examinons-le :\n\n::: code-group\n\n```vue [src\u002Fcomponents\u002FModal.vue]\n\u003Ctemplate>\n  \u003CModal\n    title=\"Boîte de Dialogue\"\n    description=\"Ceci est une boîte de dialogue\"\n  >\n    \u003CButton label=\"Ouvrir la Boîte de Dialogue\" \u002F>\n  \u003C\u002FModal>\n\u003C\u002Ftemplate>\n```\n\n```vue [src\u002Fcomponents\u002FSlideover.vue]\n\u003Ctemplate>\n  \u003CSlideover\n    title=\"Slideover\"\n    description=\"Ceci est un slideover\"\n  >\n    \u003CButton label=\"Ouvrir le Slideover\" \u002F>\n  \u003C\u002FSlideover>\n\u003C\u002Ftemplate>\n```\n\n:::\n\nIls sont très similaires, reposant sur le même composant en coulisses. La véritable intrigue réside dans la manière dont ce code est utilisé. Ces exemples illustrent que les boîtes de dialogue s'intègrent parfaitement comme tout élément HTML, ce qui est le cœur du problème.\n\nLes boîtes de dialogue sont un type particulier de composant dans une application :\n\n1. Ils peuvent être invoqués de n'importe où dans l'application.\n2. Ils s'ouvrent par divers moyens, tels que :\n    - Clics de bouton\n    - Pressions de touches\n    - Navigations dans les routes\n3. La nature omniprésente rend des applications sans boîtes de dialogue presque impraticables sans sacrifier l'expérience utilisateur :\n    - Création d'éléments\n    - Modification d'éléments\n    - Confirmations d'action\n    - Visualisations d'éléments\n    - Etc.\n\n    Chaque action peut mener à des conséquences immédiates ou attendre une réponse du serveur, ce qui complique les possibilités d'utilisation des boîtes de dialogue.\n\nEn raison de ces facteurs, les boîtes de dialogue nécessitent une attention minutieuse lors du développement et de l'utilisation ; sinon, ils et leur logique peuvent rapidement devenir chaotiques.\n\nDe plus, j'ai toujours eu une préférence personnelle pour permettre l'empilement de boîtes de dialogue avec des animations fluides, comme ceci :\n\n\u003Cfigure>\n  \u003Cvideo autoplay loop muted playsinline>\n    \u003Csource src=\"https:\u002F\u002Fimages.soubiran.dev\u002Fposts\u002Fa-journey-to-craft-interactive-ui-experiences-with-dialogs\u002Fmodal-stacking.mp4\" type=\"video\u002Fmp4\">\n  \u003C\u002Fvideo>\n  \u003Cfigcaption>Empiler des boîtes de dialogue avec une animation élégante.\u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\n\n## Boîte de dialogue en ligne\n\nUne première tentative pour relever ce défi est via des **boîtes de dialogue en ligne**. C'est une approche répandue et qui convient aux cas simples. La stratégie consiste à incorporer un composant modal directement dans le template nécessitant celui-ci, comme vu précédemment.\n\n```vue [src\u002Fcomponents\u002FModal.vue]\n\u003Ctemplate>\n  \u003CModal\n    title=\"Dialogue\"\n    description=\"Ceci est un dialogue\"\n  >\n    \u003CButton label=\"Ouvrir le Dialogue\" \u002F>\n  \u003C\u002FModal>\n\u003C\u002Ftemplate>\n```\n\nBien que l'implémentation soit simple, cette approche présente au moins deux inconvénients significatifs :\n\n1. Les composants de boîte de dialogue deviennent étroitement couplés avec le composant qui les utilise, compliquant la compréhension et le refactoring.\n2. Il devient coûteux, en terme de code, d'ajouter une nouvelle boîte de dialogue, surtout lorsqu'on ouvre les ouvre sans boutons ou en découplant les templates d'une boîte de dialogue. Un exemple d'ouverture d'une boîte dialogue par pression de touche suit :\n\n```vue [src\u002Fcomponents\u002FInlineModal.vue]\n\u003Cscript lang=\"ts\" setup>\nimport { ref } from 'vue'\n\nconst isDialogOpen = ref(false)\n\ndefineShortcuts({\n  meta_k: () => {\n    isDialogOpen.value = !isDialogOpen.value\n  }\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003CModal\n      v-model:open=\"isDialogOpen\"\n      title=\"Boîte de Dialogue\"\n      description=\"Ceci est une boîte de dialogue\"\n    \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n```\n\nCette méthode devient rapidement encombrante et difficile à maintenir au fil du temps. Plusieurs boîtes de dialogue intensifient le code répétitif, nécessitant des ajouts répétés dans chaque composant utilisant la boîte de dialogue.\n\n## Boîtes de dialogue globales\n\nAméliorer la gestion des boîtes de dialogue implique d'en ouvrir programatiquement de manière universelle, découplant leur invocation de toute relation directe avec un composant. Ce modèle facilite le refactoring du code en séparant physiquement les boîtes de dialogue des composants, améliorant la réutilisabilité à travers plusieurs composants sans incorporations redondantes.\n\nUn fichier `App.vue` pourrait ressembler à ceci :\n\n```vue [src\u002FApp.vue]\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003CLayout>\n      \u003CRouterView \u002F>\n    \u003C\u002FLayout>\n\n    \u003CModalFeature1 \u002F> \u003C!-- [!code highlight] -->\n    \u003CModalFeature2 \u002F> \u003C!-- [!code highlight] -->\n    \u003CModalFeature3 \u002F> \u003C!-- [!code highlight] -->\n\n    \u003C!-- Et ainsi de suite... -->\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n```\n\nAtteindre cette configuration exige de partager une variable entre le composant ouvrant la boîte de dialogue et la boîte de dialogue ell-même. Dans Vue, une approche simple, bien que pas idéale, est de créer une composable externalisant son état.\n\n```ts [src\u002Fcomposables\u002FuseDialog.ts]\nimport { ref } from 'vue'\n\nconst isOpen = ref(false) \u002F\u002F [!code highlight]\n\nexport function useDialog() {\n  function open() {\n    isOpen.value = true\n  }\n\n  function close() {\n    isOpen.value = false\n  }\n\n  return {\n    isOpen,\n\n    open,\n    close\n  }\n}\n```\n\nUtilisez ce composable pour le composant ouvrant la boîte de dialogue et la boîte de dialogue elle-même.\n\n```vue [src\u002FApp.vue]\n\u003Cscript lang=\"ts\" setup>\nimport { useDialog } from '@\u002Fcomposables\u002FuseDialog'\n\nconst { open } = useDialog() \u002F\u002F [!code highlight]\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CButton label=\"Ouvrir la Boîte de Dialogue\" @click=\"open\" \u002F>\n\u003C\u002Ftemplate>\n```\n\n```vue [src\u002Fcomponents\u002FModalFeature1.vue]\n\u003Cscript lang=\"ts\" setup>\nimport { useDialog } from '@\u002Fcomposables\u002FuseDialog'\n\nconst { isOpen, close } = useDialog() \u002F\u002F [!code highlight]\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CModal\n    v-model:open=\"isOpen\"\n    title=\"Boîte de Dialogue\"\n    description=\"Ceci est une boîte de dialogue\"\n  >\n    \u003CButton label=\"Fermer\" @click=\"close\" \u002F>\n  \u003C\u002FModal>\n\u003C\u002Ftemplate>\n```\n\nCette approche est certainement supérieure à la méthode en ligne mais présente tout de même des inconvénients :\n\n1. L'inclusion de boîtes de dialogue au niveau de l'application (ou de la route) crée une redondance de code.\n2. La gestion des boîtes de dialogue devient complexe, particulièrement avec de nombreuses boîtes de dialogue ou propriétés, chacune nécessitant un passage à travers la composable.\n\n    ```ts\n    import { ref } from 'vue'\n\n    const isOpen = ref(false)\n    const title = ref('') \u002F\u002F [!code highlight]\n    const description = ref('') \u002F\u002F [!code highlight]\n\n    export function useDialog() {\n      function open(newTitle: string, newDescription: string) {\n        title.value = newTitle\n        description.value = newDescription\n        isOpen.value = true\n      }\n\n      function close() {\n        isOpen.value = false\n      }\n\n      return {\n        isOpen,\n        title,\n        description,\n        open,\n        close\n      }\n    }\n    ```\n\n> [!NOTE]\n> Simplifiez l'écriture des composables avec [`createSharedComposable`](https:\u002F\u002Fvueuse.org\u002Fshared\u002FcreateSharedComposable\u002F) de [`@vueuse\u002Fcore`](https:\u002F\u002Fvueuse.org\u002F) pour créer un composable singleton, partageant automatiquement le même état entre tous les composants qui l'utilisent.\n\n## Dialogues programmatiques\n\nLa méthode la plus récente et profondément efficace que j'ai découverte implique **la gestion programmatique des boîtes de dialogue**. Cette méthode résout les difficultés mentionnées plus tôt en permettant un contrôle complet des boîtes de dialogue par le biais du code plutôt que des templates.\n\nAyant utilisé des boîtes de dialogue programmatiques dans **Angular** avec Angular Material, j'ai apprécié sa capacité à simplifier le code.\n\n```ts [Angular Material Dialog]\nconst dialogRef = dialog.open(UserProfileComponent, {\n  height: '400px',\n  width: '600px',\n})\n\ndialogRef.afterClosed().subscribe((result) => {\n  console.log(`Résultat du dialogue : ${result}`) \u002F\u002F Pizza !\n})\n\ndialogRef.close('Pizza !')\n```\n\nPendant longtemps, j'ai espéré d'une fonctionnalité similaire dans Vue.\n\nCe temps est désormais arrivé, lorsque [Nuxt](https:\u002F\u002Fnuxt.com\u002F) a sorti la version 3 de [Nuxt UI](https:\u002F\u002Fui.nuxt.com\u002F) avec la composable `useOverlay`. Ce composable partagé par l'application permet d'ouvrir des boîtes de dialogue de n'importe quel endroit, imitant la fonctionnalité d'Angular Material.\n\n```vue [src\u002FApp.vue]\n\u003Cscript setup lang=\"ts\">\nconst overlay = useOverlay()\n\nasync function openModal() {\n  overlay\n    .create(MyModal)\n    .open()\n}\n\u003C\u002Fscript>\n```\n\nMagnifique, n'est-ce pas ? 😍\n\nSous le capot, cette méthode utilise la directive [`component`](https:\u002F\u002Fvuejs.org\u002Fguide\u002Fessentials\u002Fcomponent-basics.html#dynamic-components) avec une simple boucle [`v-for`](https:\u002F\u002Fvuejs.org\u002Fapi\u002Fbuilt-in-directives.html#v-for) pour rendre les boîtes de dialogue.\n\n```vue [OverlayProvider.vue]\n\u003Cscript lang=\"ts\" setup>\nconst { overlays, close } = useOverlay()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Ccomponent\n    :is=\"overlay.component\"\n    v-for=\"overlay in overlays\"\n    :key=\"overlay.id\"\n    v-bind=\"overlay.props\"\n    v-model:open=\"overlay.modelValue\"\n  \u002F>\n\u003C\u002Ftemplate>\n```\n\n> [!NOTE]\n> Il s'agit d'une version simplifiée du code.\n\nAvec cette méthode, les coûts de la gestion d'état sont éliminés, ce qui supprime le besoin de composables partagés ou de boîtes de dialogue globales. Cela améliore considérablement la lisibilité, la maintenabilité et la création d'expériences UI interactives.\n\n## Empilement des boîtes de dialogue sans effort\n\nLe point culminant de cette approche est l'empilement de boîtes de dialogue sans effort. Il suffit de rendre un tableau de boîtes de dialogue via une boucle pour permettre l'ouverture simultanée de boîtes de dialogue, facilitant un empilement élégant avec des animations fluides.\n\n::: code-group\n\n```vue [src\u002FApp.vue]\n\u003Cscript lang=\"ts\" setup>\nimport DialogFeature1 from '.\u002Fcomponents\u002FDialogFeature1.vue'\n\nconst overlay = useOverlay()\nfunction openOverlay() {\n  overlay\n    .create(DialogFeature1, {\n      props: {\n        title: 'Premier Modal',\n        description: 'Ceci est le premier modal'\n      },\n      destroyOnClose: true\n    })\n    .open()\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003CButton label=\"Ouvrir le Premier Overlay\" @click=\"openOverlay\" \u002F>\n\n    \u003COverlayProvider \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n```\n\n```vue [src\u002Fcomponents\u002FDialogFeature1.vue]\n\u003Cscript lang=\"ts\" setup>\nimport DialogFeature2 from '.\u002FDialogFeature2.vue'\n\nconst props = defineProps\u003C{\n  title: string\n  description: string\n}>()\n\nconst overlay = useOverlay()\nfunction openOverlay() {\n  overlay\n    .create(DialogFeature2, {\n      props: {\n        title: 'Deuxième Modal',\n        description: 'Ceci est le deuxième modal'\n      },\n      destroyOnClose: true\n    })\n    .open()\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CModal\n    :title=\"props.title\"\n    :description=\"props.description\"\n  >\n    \u003Ctemplate #body>\n      \u003CButton\n        label=\"Ouvrir le Deuxième Modal\"\n        @click=\"openOverlay\"\n      \u002F>\n    \u003C\u002Ftemplate>\n  \u003C\u002FModal>\n\u003C\u002Ftemplate>\n```\n\n```vue [src\u002Fcomponents\u002FDialogFeature2.vue]\n\u003Cscript lang=\"ts\" setup>\nconst props = defineProps\u003C{\n  title: string\n  description: string\n}>()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CModal\n    :title=\"props.title\"\n    :description=\"props.description\"\n  \u002F>\n\u003C\u002Ftemplate>\n```\n\n:::\n\nExplorez le code complet sur \u003CGitHubLink repo=\"barbapapazes\u002Fvue-dialog-stacking\" \u002F> et même [une démo en direct](https:\u002F\u002Fvue-dialog-stacking.barbapapazes.dev\u002F).\n\n\u003Cfigure>\n  \u003Ciframe\n    src=\"https:\u002F\u002Fvue-dialog-stacking.barbapapazes.dev\u002F\"\n    width=\"100%\"\n    height=\"600\"\n    frameborder=\"0\"\n    scrolling=\"no\"\n    allowfullscreen\n    class=\"bg-white\"\n  >\u003C\u002Fiframe>\n  \u003Cfigcaption>Démo en direct de l'empilement de boîtes de dialogue\u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\n\n## Conclusion\n\nAprès des années de lutte avec les boîtes de dialogue et les modaux et d'essai de plusieurs approches, j'ai enfin trouvé une méthode qui fonctionne pour moi et qui peut fonctionner pour vous.\n\nJ'ai d'abord rencontré cette approche avec Angular Material et j'ai espéré quelque chose de similaire dans Vue. [Anthony Fu](https:\u002F\u002Fgithub.com\u002Fantfu) a proposé un début prometteur avec [`useTemplatePromise`](https:\u002F\u002Fgithub.com\u002Fantfu\u002Fvue-template-promise), mais cela est resté complexe uniquement pour l'ouverture de boîtes de dialogue.\n\nEnfin, je remercie [zernonia](https:\u002F\u002Fgithub.com\u002Fzernonia) pour [Reka UI](https:\u002F\u002Freka-ui.com\u002F) et [Eugen Istoc](https:\u002F\u002Fgithub.com\u002Fgenu) pour la composable `useOverlay` dans Nuxt UI, je peux désormais gérer les boîtes de dialogue de manière appropriée dans Vue.\n\nJe suis convaincu que cette approche est un changement majeur pour les expériences UI interactives dans Vue. N'hésitez pas à l'essayer !\n",null,{"title":5,"description":2991},{"loc":2994},"fr\u002Fposts\u002Fa-journey-to-craft-interactive-ui-experiences-with-dialogs","YbUj3IsSqTKsSlTsBFoOIF5l5gNuSQEO4HYb2HC-2WE",{"id":3002,"title":3003,"body":3004,"contentId":21,"date":5062,"description":5063,"extension":2992,"meta":5064,"navigation":360,"path":5065,"rawbody":5066,"robots":2996,"schemaOrg":2996,"seo":5067,"sitemap":5068,"stem":5069,"__hash__":5070},"posts\u002Ffr\u002Fposts\u002Fthe-simplest-method-to-create-a-vue-js-component-library.md","Créer une Bibliothèque de Composants Vue.js Simplement",{"type":7,"value":3005,"toc":5051},[3006,3016,3022,3048,5048],[10,3007,3008,3009],{},"TL;DR: ",[2903,3010,3012,3013],{"repo":3011},"barbapapazes\u002Fvue-library"," ",[26,3014,3015],{},"Continuez à lire pour comprendre le processus derrière cela et les raisons de mes choix.",[10,3017,3018,3019],{},"Une bibliothèque de composants est une collection de morceaux réutilisables qui peuvent être utilisés dans divers projets. Elle facilite le partage de ressources entre différents projets et équipes. Ces composants peuvent être basiques et génériques comme des boutons, des champs de saisie et des modales, ou des modules plus spécifiques au métier. ",[26,3020,3021],{},"En fin de compte, c'est une méthode pour partager du code entre les projets, une compétence cruciale qui peut faire économiser beaucoup de temps.",[10,3023,3024,3025,3030,3031,3036,3037,3042,3043,599],{},"Cependant, construire une bibliothèque de composants avec ",[1640,3026,3029],{"href":3027,"rel":3028},"https:\u002F\u002Fvuejs.org",[1644],"Vue.js"," est plus difficile qu'il n'y paraît en raison des ",[1640,3032,3035],{"href":3033,"rel":3034},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fscaling-up\u002Fsfc.html",[1644],"Single File Components"," (SFC). Dans un projet TypeScript standard, vous transpilez généralement en JavaScript et regroupez vos fichiers avec un outil comme ",[1640,3038,3041],{"href":3039,"rel":3040},"https:\u002F\u002Ftsup.egoist.dev\u002F",[1644],"tsup"," ou ",[1640,3044,3047],{"href":3045,"rel":3046},"https:\u002F\u002Fvitejs.dev\u002F",[1644],"Vite",[19,3049,3051,3065,3068,3071,3198,3201,3296,3302,3305,3313,3320,3380,3391,3539,3543,3550,3557,3563,3592,3599,3611,3625,3629,3647,3650,3653,3668,3671,3682,3687,3693,3716,3723,3869,3878,3882,3898,3916,3925,4091,4098,4102,4108,4159,4165,4171,4174,4185,4189,4192,4217,4220,4232,4258,4262,4265,4275,4356,4389,4399,4405,4556,4562,4568,4581,4585,4592,4595,4598,4606,4612,4642,4648,4669,4672,4685,4696,4702,4711,4791,4794,4815,4824,4828,4831,4838,4850,4863,4881,4886,4953,4965,4976,4979,4994,4997,5001,5004,5024,5033,5041,5046],{"id":3050},"1fafed86-05bb-4de4-b7d0-bb0bd6abc9d2",[1631,3052,3053],{},[10,3054,3055,3057,3058,3061,3062],{},[103,3056,1637],{},"\nN'oubliez pas de ",[14,3059,3060],{},"livrer le code du paquet aussi natif que possible"," et de laisser les outils de l'utilisateur gérer la transpilation et l'optimisation. C'est un principe que je garde toujours à l'esprit lorsque je crée des bibliothèques, ce qui simplifie considérablement le processus. ",[26,3063,3064],{},"Chaque cas est unique, mais c'est une bonne ligne directrice à suivre.",[10,3066,3067],{},"L'utilisation des SFC de Vue.js complique ce processus. Examinons pourquoi en explorant les défis que vous rencontrerez lors de la construction d'une bibliothèque de composants Vue.js.",[10,3069,3070],{},"Considérons le composant suivant :",[92,3072,3074],{"className":94,"code":3073,"language":97,"meta":98,"style":98},"\u003Cscript lang=\"ts\" setup>\nimport { useUser } from '..\u002Fcomposables'\n\nconst { user } = useUser()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv v-if=\"user\">\n    {{ user.name }}\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[100,3075,3076,3096,3116,3120,3137,3145,3149,3157,3177,3182,3190],{"__ignoreMap":98},[103,3077,3078,3080,3082,3084,3086,3088,3090,3092,3094],{"class":105,"line":106},[103,3079,110],{"class":109},[103,3081,488],{"class":113},[103,3083,491],{"class":132},[103,3085,136],{"class":109},[103,3087,139],{"class":109},[103,3089,498],{"class":142},[103,3091,139],{"class":109},[103,3093,503],{"class":132},[103,3095,117],{"class":109},[103,3097,3098,3100,3102,3105,3107,3109,3111,3114],{"class":105,"line":120},[103,3099,511],{"class":510},[103,3101,514],{"class":109},[103,3103,3104],{"class":517}," useUser",[103,3106,521],{"class":109},[103,3108,524],{"class":510},[103,3110,527],{"class":109},[103,3112,3113],{"class":142},"..\u002Fcomposables",[103,3115,532],{"class":109},[103,3117,3118],{"class":105,"line":129},[103,3119,537],{"emptyLinePlaceholder":360},[103,3121,3122,3124,3126,3129,3131,3133,3135],{"class":105,"line":148},[103,3123,542],{"class":132},[103,3125,514],{"class":109},[103,3127,3128],{"class":517}," user ",[103,3130,628],{"class":109},[103,3132,605],{"class":109},[103,3134,3104],{"class":550},[103,3136,1899],{"class":517},[103,3138,3139,3141,3143],{"class":105,"line":162},[103,3140,205],{"class":109},[103,3142,488],{"class":113},[103,3144,117],{"class":109},[103,3146,3147],{"class":105,"line":168},[103,3148,537],{"emptyLinePlaceholder":360},[103,3150,3151,3153,3155],{"class":105,"line":191},[103,3152,110],{"class":109},[103,3154,114],{"class":113},[103,3156,117],{"class":109},[103,3158,3159,3161,3163,3166,3168,3170,3173,3175],{"class":105,"line":202},[103,3160,123],{"class":109},[103,3162,54],{"class":113},[103,3164,3165],{"class":132}," v-if",[103,3167,136],{"class":109},[103,3169,139],{"class":109},[103,3171,3172],{"class":142},"user",[103,3174,139],{"class":109},[103,3176,117],{"class":109},[103,3178,3179],{"class":105,"line":619},[103,3180,3181],{"class":517},"    {{ user.name }}\n",[103,3183,3184,3186,3188],{"class":105,"line":625},[103,3185,194],{"class":109},[103,3187,54],{"class":113},[103,3189,117],{"class":109},[103,3191,3192,3194,3196],{"class":105,"line":633},[103,3193,205],{"class":109},[103,3195,114],{"class":113},[103,3197,117],{"class":109},[10,3199,3200],{},"Le composable est le suivant :",[92,3202,3204],{"className":1677,"code":3203,"language":498,"meta":98,"style":98},"import { ref } from 'vue'\n\nexport function useUser() {\n  const user = ref({ name: 'John Doe' })\n\n  return {\n    user\n  }\n}\n",[100,3205,3206,3224,3228,3240,3273,3277,3283,3288,3292],{"__ignoreMap":98},[103,3207,3208,3210,3212,3214,3216,3218,3220,3222],{"class":105,"line":106},[103,3209,511],{"class":510},[103,3211,514],{"class":109},[103,3213,518],{"class":517},[103,3215,521],{"class":109},[103,3217,524],{"class":510},[103,3219,527],{"class":109},[103,3221,97],{"class":142},[103,3223,532],{"class":109},[103,3225,3226],{"class":105,"line":120},[103,3227,537],{"emptyLinePlaceholder":360},[103,3229,3230,3232,3234,3236,3238],{"class":105,"line":129},[103,3231,930],{"class":510},[103,3233,933],{"class":132},[103,3235,3104],{"class":550},[103,3237,939],{"class":109},[103,3239,591],{"class":109},[103,3241,3242,3245,3248,3250,3252,3254,3257,3260,3262,3264,3267,3269,3271],{"class":105,"line":148},[103,3243,3244],{"class":132},"  const",[103,3246,3247],{"class":517}," user",[103,3249,605],{"class":109},[103,3251,518],{"class":550},[103,3253,553],{"class":113},[103,3255,3256],{"class":109},"{",[103,3258,3259],{"class":113}," name",[103,3261,582],{"class":109},[103,3263,527],{"class":109},[103,3265,3266],{"class":142},"John Doe",[103,3268,1719],{"class":109},[103,3270,521],{"class":109},[103,3272,560],{"class":113},[103,3274,3275],{"class":105,"line":162},[103,3276,537],{"emptyLinePlaceholder":360},[103,3278,3279,3281],{"class":105,"line":168},[103,3280,1012],{"class":510},[103,3282,591],{"class":109},[103,3284,3285],{"class":105,"line":191},[103,3286,3287],{"class":517},"    user\n",[103,3289,3290],{"class":105,"line":202},[103,3291,622],{"class":109},[103,3293,3294],{"class":105,"line":619},[103,3295,1046],{"class":109},[10,3297,3298,3299],{},"Le composant utilise un composable importé d'un autre fichier. ",[26,3300,3301],{},"Cela peut sembler trivial, mais cela pose un problème significatif.",[10,3303,3304],{},"Tout cela suit l'architecture ci-dessous :",[92,3306,3311],{"className":3307,"code":3309,"language":3310},[3308],"language-text","src\u002F\n  components\u002F\n    User.vue\n  composables\u002F\n    useUser.ts\n  index.ts\n","text",[100,3312,3309],{"__ignoreMap":98},[10,3314,3315,3316,3319],{},"Le fichier ",[100,3317,3318],{},"index.ts"," sert de point d'entrée de la bibliothèque et ressemble à ceci :",[92,3321,3323],{"className":1677,"code":3322,"language":498,"meta":98,"style":98},"import User from '.\u002Fcomponents\u002FUser.vue'\n\nexport { User }\n\nexport { useUser } from '.\u002Fcomposables\u002FuseUser'\n",[100,3324,3325,3341,3345,3357,3361],{"__ignoreMap":98},[103,3326,3327,3329,3332,3334,3336,3339],{"class":105,"line":106},[103,3328,511],{"class":510},[103,3330,3331],{"class":517}," User ",[103,3333,2190],{"class":510},[103,3335,527],{"class":109},[103,3337,3338],{"class":142},".\u002Fcomponents\u002FUser.vue",[103,3340,532],{"class":109},[103,3342,3343],{"class":105,"line":120},[103,3344,537],{"emptyLinePlaceholder":360},[103,3346,3347,3349,3351,3354],{"class":105,"line":129},[103,3348,930],{"class":510},[103,3350,514],{"class":109},[103,3352,3353],{"class":517}," User",[103,3355,3356],{"class":109}," }\n",[103,3358,3359],{"class":105,"line":148},[103,3360,537],{"emptyLinePlaceholder":360},[103,3362,3363,3365,3367,3369,3371,3373,3375,3378],{"class":105,"line":162},[103,3364,930],{"class":510},[103,3366,514],{"class":109},[103,3368,3104],{"class":517},[103,3370,521],{"class":109},[103,3372,524],{"class":510},[103,3374,527],{"class":109},[103,3376,3377],{"class":142},".\u002Fcomposables\u002FuseUser",[103,3379,532],{"class":109},[10,3381,3382,3383,3386,3387,3390],{},"De plus, le ",[100,3384,3385],{},"package.json"," inclut quelques ",[100,3388,3389],{},"exports"," :",[92,3392,3396],{"className":3393,"code":3394,"language":3395,"meta":98,"style":98},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \".\u002Fdist\u002Findex.d.ts\",\n      \"import\": \".\u002Fdist\u002Findex.mjs\"\n    }\n  },\n  \"main\": \"dist\u002Findex.mjs\",\n  \"types\": \"dist\u002Findex.d.ts\"\n}\n","json",[100,3397,3398,3402,3424,3436,3449,3471,3488,3493,3498,3518,3535],{"__ignoreMap":98},[103,3399,3400],{"class":105,"line":106},[103,3401,574],{"class":109},[103,3403,3404,3407,3410,3412,3414,3417,3420,3422],{"class":105,"line":120},[103,3405,3406],{"class":109},"  \"",[103,3408,3409],{"class":132},"type",[103,3411,139],{"class":109},[103,3413,582],{"class":109},[103,3415,3416],{"class":109}," \"",[103,3418,3419],{"class":142},"module",[103,3421,139],{"class":109},[103,3423,1021],{"class":109},[103,3425,3426,3428,3430,3432,3434],{"class":105,"line":129},[103,3427,3406],{"class":109},[103,3429,3389],{"class":132},[103,3431,139],{"class":109},[103,3433,582],{"class":109},[103,3435,591],{"class":109},[103,3437,3438,3441,3443,3445,3447],{"class":105,"line":148},[103,3439,3440],{"class":109},"    \"",[103,3442,599],{"class":1492},[103,3444,139],{"class":109},[103,3446,582],{"class":109},[103,3448,591],{"class":109},[103,3450,3451,3454,3458,3460,3462,3464,3467,3469],{"class":105,"line":162},[103,3452,3453],{"class":109},"      \"",[103,3455,3457],{"class":3456},"sbssI","types",[103,3459,139],{"class":109},[103,3461,582],{"class":109},[103,3463,3416],{"class":109},[103,3465,3466],{"class":142},".\u002Fdist\u002Findex.d.ts",[103,3468,139],{"class":109},[103,3470,1021],{"class":109},[103,3472,3473,3475,3477,3479,3481,3483,3486],{"class":105,"line":168},[103,3474,3453],{"class":109},[103,3476,511],{"class":3456},[103,3478,139],{"class":109},[103,3480,582],{"class":109},[103,3482,3416],{"class":109},[103,3484,3485],{"class":142},".\u002Fdist\u002Findex.mjs",[103,3487,145],{"class":109},[103,3489,3490],{"class":105,"line":191},[103,3491,3492],{"class":109},"    }\n",[103,3494,3495],{"class":105,"line":202},[103,3496,3497],{"class":109},"  },\n",[103,3499,3500,3502,3505,3507,3509,3511,3514,3516],{"class":105,"line":619},[103,3501,3406],{"class":109},[103,3503,3504],{"class":132},"main",[103,3506,139],{"class":109},[103,3508,582],{"class":109},[103,3510,3416],{"class":109},[103,3512,3513],{"class":142},"dist\u002Findex.mjs",[103,3515,139],{"class":109},[103,3517,1021],{"class":109},[103,3519,3520,3522,3524,3526,3528,3530,3533],{"class":105,"line":625},[103,3521,3406],{"class":109},[103,3523,3457],{"class":132},[103,3525,139],{"class":109},[103,3527,582],{"class":109},[103,3529,3416],{"class":109},[103,3531,3532],{"class":142},"dist\u002Findex.d.ts",[103,3534,145],{"class":109},[103,3536,3537],{"class":105,"line":633},[103,3538,1046],{"class":109},[31,3540,3542],{"id":3541},"similaire-à-un-projet-typescript-standard","Similaire à un projet TypeScript standard",[1631,3544,3545],{},[10,3546,3547,3549],{},[103,3548,1637],{},"\nUn bundle est un seul fichier encapsulant tout le code de votre projet. Il optimise le chargement du projet en minimisant le nombre de fichiers à charger. Ce bundle n'est pas transpile (sauf TypeScript vers JavaScript) ni minifié lors de la création d'un package npm.",[10,3551,3552,3553,3556],{},"Maintenant, si nous tentons de regrouper ce projet, notre dossier ",[100,3554,3555],{},"dist"," apparaîtra comme ceci :",[92,3558,3561],{"className":3559,"code":3560,"language":3310},[3308],"dist\u002F\n  index.cjs\n",[100,3562,3560],{"__ignoreMap":98},[10,3564,3565,3566,3569,3570,3573,3574],{},"En inspectant ",[100,3567,3568],{},"index.cjs",", vous trouverez une surprise : le composable ",[100,3571,3572],{},"useUser"," est intégré, mais le composant est complètement ignoré. ",[26,3575,3576,3577,3580,3581,3042,3586,3591],{},"S'il fonctionne, l'outil pourrait déclencher une erreur du type ",[100,3578,3579],{},"No loader is configured for \".vue\" files",". Essentiellement, des outils comme ",[1640,3582,3585],{"href":3583,"rel":3584},"https:\u002F\u002Fesbuild.github.io",[1644],"esbuild",[1640,3587,3590],{"href":3588,"rel":3589},"https:\u002F\u002Fwww.typescriptlang.org\u002Fdocs\u002Fhandbook\u002Fcompiler-options.html",[1644],"tsc"," ne savent pas comment gérer les fichiers Vue. C'est logique, car les fichiers Vue ne sont pas des fichiers JavaScript.",[10,3593,3594,3595,3598],{},"Le problème devient évident. Nous avons besoin d'une configuration pour gérer les fichiers Vue. Jetons un coup d'œil rapide sur Internet pour quelque chose comme ",[100,3596,3597],{},"vue loader",", car nous utilisons des fichiers Vue et devons les transpiler en JavaScript.",[10,3600,3601,3602,3607,3608],{},"Le premier résultat est ",[1640,3603,3606],{"href":3604,"rel":3605},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fvue-loader",[1644],"Vue Loader",", qui est un loader Webpack. ",[26,3609,3610],{},"Non. Je refuse d'utiliser Webpack. Vite est maintenant un standard, et il n'y a aucune justification pour employer Webpack dans ce contexte.",[10,3612,3613,3614,3012,3616,3624],{},"Mais avant de plonger plus loin, rappelons-nous notre mantra : ",[14,3615,3060],{},[26,3617,3618,3619],{},"et ",[1640,3620,3623],{"href":3621,"rel":3622},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FKISS_principle",[1644],"keep it simple, stupid!"," La transpilation des fichiers SFC Vue avec Webpack semble contraire à ce principe.",[31,3626,3628],{"id":3627},"ignorer-les-fichiers-vue","Ignorer les fichiers Vue",[10,3630,3631,3632,3635,3636,3641,3642,599],{},"Pour livrer des fichiers ",[100,3633,3634],{},".vue"," sans les transpiler, nous demandons à notre outil de les ignorer. Cela est réalisé à l'aide d'un outil appelé ",[1640,3637,3640],{"href":3638,"rel":3639},"https:\u002F\u002Funbuild.unjs.io",[1644],"unbuild"," provenant de l'écosystème ",[1640,3643,3646],{"href":3644,"rel":3645},"https:\u002F\u002Funjs.io",[1644],"UnJS",[10,3648,3649],{},"Unbuild est un outil simple mais hautement configurable car il est construit sur Rollup.",[10,3651,3652],{},"Pour notre petit projet, nous pouvons l'expérimenter directement :",[92,3654,3658],{"className":3655,"code":3656,"language":3657,"meta":98,"style":98},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","npx unbuild\n","sh",[100,3659,3660],{"__ignoreMap":98},[103,3661,3662,3665],{"class":105,"line":106},[103,3663,3664],{"class":1492},"npx",[103,3666,3667],{"class":142}," unbuild\n",[10,3669,3670],{},"Malheureusement, cela échoue pour les mêmes raisons que précédemment :",[92,3672,3676],{"className":3673,"code":3674,"language":3675,"meta":98,"style":98},"language-txt shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","src\u002Fcomponents\u002FShowGitHubUser.vue (1:0): Expression expected (Note that you need plugins to import files that are not JavaScript)\n","txt",[100,3677,3678],{"__ignoreMap":98},[103,3679,3680],{"class":105,"line":106},[103,3681,3674],{},[10,3683,3684],{},[26,3685,3686],{},"Mais unbuild a le potentiel pour bien plus.",[10,3688,3689,3690,3692],{},"Pour résoudre ce problème, essayons la solution évidente. Dans le fichier ",[100,3691,3318],{},", supprimons l'exportation du composant et exportons uniquement les fichiers TypeScript :",[92,3694,3696],{"className":1677,"code":3695,"language":498,"meta":98,"style":98},"export { useUser } from '.\u002Fcomposables\u002FuseUser'\n",[100,3697,3698],{"__ignoreMap":98},[103,3699,3700,3702,3704,3706,3708,3710,3712,3714],{"class":105,"line":106},[103,3701,930],{"class":510},[103,3703,514],{"class":109},[103,3705,3104],{"class":517},[103,3707,521],{"class":109},[103,3709,524],{"class":510},[103,3711,527],{"class":109},[103,3713,3377],{"class":142},[103,3715,532],{"class":109},[10,3717,3718,3719,3722],{},"Maintenant, la commande ",[100,3720,3721],{},"npx unbuild"," fonctionne correctement.",[92,3724,3726],{"className":3655,"code":3725,"language":3657,"meta":98,"style":98},"➜ npx unbuild\nℹ Automatically detected entries: src\u002Findex [esm] [dts]\nℹ Building vue-library\nℹ Cleaning dist directory: .\u002Fdist\n✔ Build succeeded for vue-library\n  dist\u002Findex.mjs (total size: 139 B, chunk size: 139 B, exports: useUser)\n\nΣ Total dist size (byte size): 531 B\n",[100,3727,3728,3738,3767,3777,3793,3809,3843,3847],{"__ignoreMap":98},[103,3729,3730,3733,3736],{"class":105,"line":106},[103,3731,3732],{"class":1492},"➜",[103,3734,3735],{"class":142}," npx",[103,3737,3667],{"class":142},[103,3739,3740,3743,3746,3749,3752,3755,3758,3761,3764],{"class":105,"line":120},[103,3741,3742],{"class":1492},"ℹ",[103,3744,3745],{"class":142}," Automatically",[103,3747,3748],{"class":142}," detected",[103,3750,3751],{"class":142}," entries:",[103,3753,3754],{"class":142}," src\u002Findex",[103,3756,3757],{"class":517}," [esm] ",[103,3759,3760],{"class":109},"[",[103,3762,3763],{"class":517},"dts",[103,3765,3766],{"class":109},"]\n",[103,3768,3769,3771,3774],{"class":105,"line":129},[103,3770,3742],{"class":1492},[103,3772,3773],{"class":142}," Building",[103,3775,3776],{"class":142}," vue-library\n",[103,3778,3779,3781,3784,3787,3790],{"class":105,"line":148},[103,3780,3742],{"class":1492},[103,3782,3783],{"class":142}," Cleaning",[103,3785,3786],{"class":142}," dist",[103,3788,3789],{"class":142}," directory:",[103,3791,3792],{"class":142}," .\u002Fdist\n",[103,3794,3795,3798,3801,3804,3807],{"class":105,"line":162},[103,3796,3797],{"class":1492},"✔",[103,3799,3800],{"class":142}," Build",[103,3802,3803],{"class":142}," succeeded",[103,3805,3806],{"class":142}," for",[103,3808,3776],{"class":142},[103,3810,3811,3814,3817,3820,3823,3826,3829,3832,3834,3836,3839,3841],{"class":105,"line":168},[103,3812,3813],{"class":1492},"  dist\u002Findex.mjs",[103,3815,3816],{"class":517}," (total ",[103,3818,3819],{"class":142},"size:",[103,3821,3822],{"class":3456}," 139",[103,3824,3825],{"class":142}," B,",[103,3827,3828],{"class":142}," chunk",[103,3830,3831],{"class":142}," size:",[103,3833,3822],{"class":3456},[103,3835,3825],{"class":142},[103,3837,3838],{"class":142}," exports:",[103,3840,3104],{"class":142},[103,3842,560],{"class":517},[103,3844,3845],{"class":105,"line":191},[103,3846,537],{"emptyLinePlaceholder":360},[103,3848,3849,3852,3855,3857,3860,3863,3866],{"class":105,"line":202},[103,3850,3851],{"class":1492},"Σ",[103,3853,3854],{"class":142}," Total",[103,3856,3786],{"class":142},[103,3858,3859],{"class":142}," size",[103,3861,3862],{"class":517}," (byte ",[103,3864,3865],{"class":142},"size",[103,3867,3868],{"class":517},"): 531 B\n",[10,3870,3871,3872,3875,3876,599],{},"C'est un bon début, mais nos composants restent dans le dossier ",[100,3873,3874],{},"src"," et peu importe combien vous cherchez ardemment, ils sont absents du dossier ",[100,3877,3555],{},[31,3879,3881],{"id":3880},"copier-les-fichiers-vue","Copier les fichiers Vue",[10,3883,3884,3885,3888,3889,3891,3892,3894,3895,599],{},"Maintenant que les fichiers ",[100,3886,3887],{},".ts"," sont traités correctement, nous pouvons tenter de copier les fichiers ",[100,3890,3634],{}," dans le dossier ",[100,3893,3555],{}," avec une simple commande ",[100,3896,3897],{},"cp",[92,3899,3901],{"className":3655,"code":3900,"language":3657,"meta":98,"style":98},"cp -r src\u002Fcomponents\u002F dist\u002Fcomponents\u002F\n",[100,3902,3903],{"__ignoreMap":98},[103,3904,3905,3907,3910,3913],{"class":105,"line":106},[103,3906,3897],{"class":1492},[103,3908,3909],{"class":142}," -r",[103,3911,3912],{"class":142}," src\u002Fcomponents\u002F",[103,3914,3915],{"class":142}," dist\u002Fcomponents\u002F\n",[10,3917,3918,3919,3921,3922,3924],{},"Ensuite, nous ajoutons un champ ",[100,3920,3389],{}," dans le ",[100,3923,3385],{}," pour informer l'utilisateur où localiser les composants :",[92,3926,3928],{"className":3393,"code":3927,"language":3395,"meta":98,"style":98},"{\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \".\u002Fdist\u002Findex.d.ts\",\n      \"import\": \".\u002Fdist\u002Findex.mjs\"\n    },\n    \".\u002Fcomponents\u002F*\": {\n      \"import\": \".\u002Fdist\u002Fcomponents\u002F*.vue\"\n    }\n  },\n  \"main\": \"dist\u002Findex.mjs\",\n  \"types\": \"dist\u002Findex.d.ts\"\n}\n",[100,3929,3930,3934,3952,3964,3976,3994,4010,4015,4028,4045,4049,4053,4071,4087],{"__ignoreMap":98},[103,3931,3932],{"class":105,"line":106},[103,3933,574],{"class":109},[103,3935,3936,3938,3940,3942,3944,3946,3948,3950],{"class":105,"line":120},[103,3937,3406],{"class":109},[103,3939,3409],{"class":132},[103,3941,139],{"class":109},[103,3943,582],{"class":109},[103,3945,3416],{"class":109},[103,3947,3419],{"class":142},[103,3949,139],{"class":109},[103,3951,1021],{"class":109},[103,3953,3954,3956,3958,3960,3962],{"class":105,"line":129},[103,3955,3406],{"class":109},[103,3957,3389],{"class":132},[103,3959,139],{"class":109},[103,3961,582],{"class":109},[103,3963,591],{"class":109},[103,3965,3966,3968,3970,3972,3974],{"class":105,"line":148},[103,3967,3440],{"class":109},[103,3969,599],{"class":1492},[103,3971,139],{"class":109},[103,3973,582],{"class":109},[103,3975,591],{"class":109},[103,3977,3978,3980,3982,3984,3986,3988,3990,3992],{"class":105,"line":162},[103,3979,3453],{"class":109},[103,3981,3457],{"class":3456},[103,3983,139],{"class":109},[103,3985,582],{"class":109},[103,3987,3416],{"class":109},[103,3989,3466],{"class":142},[103,3991,139],{"class":109},[103,3993,1021],{"class":109},[103,3995,3996,3998,4000,4002,4004,4006,4008],{"class":105,"line":168},[103,3997,3453],{"class":109},[103,3999,511],{"class":3456},[103,4001,139],{"class":109},[103,4003,582],{"class":109},[103,4005,3416],{"class":109},[103,4007,3485],{"class":142},[103,4009,145],{"class":109},[103,4011,4012],{"class":105,"line":191},[103,4013,4014],{"class":109},"    },\n",[103,4016,4017,4019,4022,4024,4026],{"class":105,"line":202},[103,4018,3440],{"class":109},[103,4020,4021],{"class":1492},".\u002Fcomponents\u002F*",[103,4023,139],{"class":109},[103,4025,582],{"class":109},[103,4027,591],{"class":109},[103,4029,4030,4032,4034,4036,4038,4040,4043],{"class":105,"line":619},[103,4031,3453],{"class":109},[103,4033,511],{"class":3456},[103,4035,139],{"class":109},[103,4037,582],{"class":109},[103,4039,3416],{"class":109},[103,4041,4042],{"class":142},".\u002Fdist\u002Fcomponents\u002F*.vue",[103,4044,145],{"class":109},[103,4046,4047],{"class":105,"line":625},[103,4048,3492],{"class":109},[103,4050,4051],{"class":105,"line":633},[103,4052,3497],{"class":109},[103,4054,4055,4057,4059,4061,4063,4065,4067,4069],{"class":105,"line":642},[103,4056,3406],{"class":109},[103,4058,3504],{"class":132},[103,4060,139],{"class":109},[103,4062,582],{"class":109},[103,4064,3416],{"class":109},[103,4066,3513],{"class":142},[103,4068,139],{"class":109},[103,4070,1021],{"class":109},[103,4072,4073,4075,4077,4079,4081,4083,4085],{"class":105,"line":647},[103,4074,3406],{"class":109},[103,4076,3457],{"class":132},[103,4078,139],{"class":109},[103,4080,582],{"class":109},[103,4082,3416],{"class":109},[103,4084,3532],{"class":142},[103,4086,145],{"class":109},[103,4088,4089],{"class":105,"line":656},[103,4090,1046],{"class":109},[10,4092,4093,4094,4097],{},"Ça a l'air prometteur, n'est-ce pas ? ",[26,4095,4096],{},"Mais ce n'est pas le cas."," 😔",[31,4099,4101],{"id":4100},"le-dilemme-du-chemin-dimportation","Le dilemme du chemin d'importation",[10,4103,4104,4105,4107],{},"Dans notre composant Vue, le composable ",[100,4106,3572],{}," est importé comme ceci :",[92,4109,4111],{"className":94,"code":4110,"language":97,"meta":98,"style":98},"\u003Cscript lang=\"ts\" setup>\nimport { useUser } from '..\u002Fcomposables'\n\u003C\u002Fscript>\n",[100,4112,4113,4133,4151],{"__ignoreMap":98},[103,4114,4115,4117,4119,4121,4123,4125,4127,4129,4131],{"class":105,"line":106},[103,4116,110],{"class":109},[103,4118,488],{"class":113},[103,4120,491],{"class":132},[103,4122,136],{"class":109},[103,4124,139],{"class":109},[103,4126,498],{"class":142},[103,4128,139],{"class":109},[103,4130,503],{"class":132},[103,4132,117],{"class":109},[103,4134,4135,4137,4139,4141,4143,4145,4147,4149],{"class":105,"line":120},[103,4136,511],{"class":510},[103,4138,514],{"class":109},[103,4140,3104],{"class":517},[103,4142,521],{"class":109},[103,4144,524],{"class":510},[103,4146,527],{"class":109},[103,4148,3113],{"class":142},[103,4150,532],{"class":109},[103,4152,4153,4155,4157],{"class":105,"line":129},[103,4154,205],{"class":109},[103,4156,488],{"class":113},[103,4158,117],{"class":109},[10,4160,4161,4162,4164],{},"Pendant ce temps, notre dossier ",[100,4163,3555],{}," est structuré comme suit :",[92,4166,4169],{"className":4167,"code":4168,"language":3310},[3308],"dist\u002F\n  components\u002F\n    User.vue\n  index.d.mts\n  index.d.ts\n  index.mjs\n",[100,4170,4168],{"__ignoreMap":98},[10,4172,4173],{},"Pouvez-vous identifier le problème ? 👀",[10,4175,4176,4177,4180,4181,4184],{},"Le composant est inutilisable et produira une erreur du type ",[100,4178,4179],{},"Cannot find module '..\u002Fcomposables' or its corresponding type declarations",", car le chemin mène nulle part. Nous avons regroupé tous nos fichiers TypeScript dans un seul fichier ",[100,4182,4183],{},"index.mjs",", perdant complètement la structure du projet.",[31,4186,4188],{"id":4187},"regrouper-ou-ne-pas-regrouper","Regrouper ou ne pas regrouper ?",[10,4190,4191],{},"À partir de là, nous avons deux choix :",[303,4193,4194,4205],{},[306,4195,4196,4199,4200,4204],{},[14,4197,4198],{},"Regrouper les fichiers Vue"," en utilisant ",[1640,4201,3047],{"href":4202,"rel":4203},"https:\u002F\u002Fvitejs.dev",[1644],", des plugins et une configuration extensive.",[306,4206,4207,4210,4211,4216],{},[14,4208,4209],{},"Conserver la structure de répertoires"," et utiliser un outil comme ",[1640,4212,4215],{"href":4213,"rel":4214},"https:\u002F\u002Fgithub.com\u002Funjs\u002Fmkdist",[1644],"mkdist"," pour une transpilation fichier à fichier (construction sans bundle) sur les fichiers TypeScript.",[10,4218,4219],{},"La décision dépendra largement de vos besoins spécifiques, mais je suis convaincu que la simplicité est la meilleure approche. Donc, explorons la deuxième option.",[10,4221,4222,4223,3891,4225,4227,4228,4231],{},"L'objectif est de transformer les fichiers TypeScript en fichiers JavaScript, tout en préservant la structure d'origine du dossier ",[100,4224,3874],{},[100,4226,3555],{}," et en ",[26,4229,4230],{},"ignorant"," les fichiers Vue.",[1631,4233,4234],{},[10,4235,4236,4238,4239,4242,4243,4246,4247,4252,4253,599],{},[103,4237,1637],{},"\nEn utilisant un bloc ",[100,4240,4241],{},"\u003Cscript setup lang=\"ts\">",", mkdist ignorera les fichiers Vue pour permettre au compilateur Vue de générer des props d'exécution. Sinon, il générera un fichier ",[100,4244,4245],{},".vue.d.ts"," pour offrir des types pour le composant Vue. Rappelez-vous que le ",[1640,4248,4251],{"href":4249,"rel":4250},"https:\u002F\u002Fgithub.com\u002Fvitejs\u002Fvite-plugin-vue\u002Ftree\u002Fmain\u002Fpackages\u002Fplugin-vue",[1644],"@vitejs\u002Fplugin-vue"," et Vite comprennent intrinsèquement les blocs de script TypeScript. Pour plus de détails, consultez ",[1640,4254,4257],{"href":4255,"rel":4256},"https:\u002F\u002Fgithub.com\u002Funjs\u002Fmkdist\u002Fissues\u002F14",[1644],"issue mkdist#14",[31,4259,4261],{"id":4260},"configurer-unbuild","Configurer Unbuild",[10,4263,4264],{},"Oui, nous allons configurer unbuild car il intègre mkdist, ce qui le rend aggréable à utiliser.",[10,4266,4267,4268,4271,4272,4274],{},"Commencez par créer un fichier ",[100,4269,4270],{},"build.config.ts"," à la racine du projet. Unbuild lira ce fichier de configuration pour comprendre comment traiter le projet. Il suppose des valeurs par défaut et infère de nombreux aspects à partir du ",[100,4273,3385],{},", mais nous devons lui dire d'utiliser mkdist.",[92,4276,4278],{"className":1677,"code":4277,"language":498,"meta":98,"style":98},"import { defineBuildConfig } from 'unbuild'\n\nexport default defineBuildConfig({\n  entries: ['.\u002Fsrc\u002F'],\n  declaration: true,\n})\n",[100,4279,4280,4299,4303,4316,4338,4350],{"__ignoreMap":98},[103,4281,4282,4284,4286,4289,4291,4293,4295,4297],{"class":105,"line":106},[103,4283,511],{"class":510},[103,4285,514],{"class":109},[103,4287,4288],{"class":517}," defineBuildConfig",[103,4290,521],{"class":109},[103,4292,524],{"class":510},[103,4294,527],{"class":109},[103,4296,3640],{"class":142},[103,4298,532],{"class":109},[103,4300,4301],{"class":105,"line":120},[103,4302,537],{"emptyLinePlaceholder":360},[103,4304,4305,4307,4310,4312,4314],{"class":105,"line":129},[103,4306,930],{"class":510},[103,4308,4309],{"class":510}," default",[103,4311,4288],{"class":550},[103,4313,553],{"class":517},[103,4315,574],{"class":109},[103,4317,4318,4321,4323,4326,4328,4331,4333,4336],{"class":105,"line":148},[103,4319,4320],{"class":113},"  entries",[103,4322,582],{"class":109},[103,4324,4325],{"class":517}," [",[103,4327,1719],{"class":109},[103,4329,4330],{"class":142},".\u002Fsrc\u002F",[103,4332,1719],{"class":109},[103,4334,4335],{"class":517},"]",[103,4337,1021],{"class":109},[103,4339,4340,4343,4345,4348],{"class":105,"line":162},[103,4341,4342],{"class":113},"  declaration",[103,4344,582],{"class":109},[103,4346,4347],{"class":556}," true",[103,4349,1021],{"class":109},[103,4351,4352,4354],{"class":105,"line":168},[103,4353,628],{"class":109},[103,4355,560],{"class":517},[10,4357,4358,4359,4362,4363,4366,4367,4369,4370,4373,4374,4377,4378,4380,4381,4384,4385,4388],{},"C'est un fichier de configuration très simple. ",[26,4360,4361],{},"Et je l'adore."," La clé ",[100,4364,4365],{},"entries"," informe unbuild du fichier initial à commencer à transpiler. Cependant, ",[100,4368,4330],{}," est un répertoire, indiqué par le ",[100,4371,4372],{},"\u002F"," à la fin. Avec cet indice, unbuild contourne le regroupement par défaut ",[100,4375,4376],{},"rollup"," en faveur de ",[100,4379,4215],{},". La clé ",[100,4382,4383],{},"declaration"," incite unbuild à créer des fichiers de déclaration TypeScript (",[100,4386,4387],{},".d.ts",").",[10,4390,4391,4392,3921,4394,4396,4397,599],{},"Nous pouvons également supprimer le champ ",[100,4393,3389],{},[100,4395,3385],{}," puisque nous ne regrouperons plus les fichiers et restaurer l'exportation du composant dans le fichier ",[100,4398,3318],{},[10,4400,4401,4402,4404],{},"Relaçons la commande ",[100,4403,3721],{}," et magie :",[92,4406,4408],{"className":3655,"code":4407,"language":3657,"meta":98,"style":98},"➜ npx unbuild\nℹ Building vue-library\nℹ Cleaning dist directory: .\u002Fdist\n✔ Build succeeded for vue-library\n  dist (total size: 600 B)\n  └─ dist\u002Findex.d.ts (108 B)\n  └─ dist\u002Findex.mjs (112 B)\n  └─ dist\u002Fcomposables\u002FuseUser.d.ts (79 B)\n  └─ dist\u002Fcomposables\u002FuseUser.mjs (124 B)\n  └─ dist\u002Fcomponents\u002FUser.vue (177 B)\nΣ Total dist size (byte size): 600 B\n",[100,4409,4410,4418,4426,4438,4450,4467,4483,4497,4511,4525,4539],{"__ignoreMap":98},[103,4411,4412,4414,4416],{"class":105,"line":106},[103,4413,3732],{"class":1492},[103,4415,3735],{"class":142},[103,4417,3667],{"class":142},[103,4419,4420,4422,4424],{"class":105,"line":120},[103,4421,3742],{"class":1492},[103,4423,3773],{"class":142},[103,4425,3776],{"class":142},[103,4427,4428,4430,4432,4434,4436],{"class":105,"line":129},[103,4429,3742],{"class":1492},[103,4431,3783],{"class":142},[103,4433,3786],{"class":142},[103,4435,3789],{"class":142},[103,4437,3792],{"class":142},[103,4439,4440,4442,4444,4446,4448],{"class":105,"line":148},[103,4441,3797],{"class":1492},[103,4443,3800],{"class":142},[103,4445,3803],{"class":142},[103,4447,3806],{"class":142},[103,4449,3776],{"class":142},[103,4451,4452,4455,4457,4459,4462,4465],{"class":105,"line":162},[103,4453,4454],{"class":1492},"  dist",[103,4456,3816],{"class":517},[103,4458,3819],{"class":142},[103,4460,4461],{"class":3456}," 600",[103,4463,4464],{"class":142}," B",[103,4466,560],{"class":517},[103,4468,4469,4472,4475,4478,4481],{"class":105,"line":168},[103,4470,4471],{"class":1492},"  └─",[103,4473,4474],{"class":142}," dist\u002Findex.d.ts",[103,4476,4477],{"class":517}," (108 ",[103,4479,4480],{"class":142},"B",[103,4482,560],{"class":517},[103,4484,4485,4487,4490,4493,4495],{"class":105,"line":191},[103,4486,4471],{"class":1492},[103,4488,4489],{"class":142}," dist\u002Findex.mjs",[103,4491,4492],{"class":517}," (112 ",[103,4494,4480],{"class":142},[103,4496,560],{"class":517},[103,4498,4499,4501,4504,4507,4509],{"class":105,"line":202},[103,4500,4471],{"class":1492},[103,4502,4503],{"class":142}," dist\u002Fcomposables\u002FuseUser.d.ts",[103,4505,4506],{"class":517}," (79 ",[103,4508,4480],{"class":142},[103,4510,560],{"class":517},[103,4512,4513,4515,4518,4521,4523],{"class":105,"line":619},[103,4514,4471],{"class":1492},[103,4516,4517],{"class":142}," dist\u002Fcomposables\u002FuseUser.mjs",[103,4519,4520],{"class":517}," (124 ",[103,4522,4480],{"class":142},[103,4524,560],{"class":517},[103,4526,4527,4529,4532,4535,4537],{"class":105,"line":625},[103,4528,4471],{"class":1492},[103,4530,4531],{"class":142}," dist\u002Fcomponents\u002FUser.vue",[103,4533,4534],{"class":517}," (177 ",[103,4536,4480],{"class":142},[103,4538,560],{"class":517},[103,4540,4541,4543,4545,4547,4549,4551,4553],{"class":105,"line":633},[103,4542,3851],{"class":1492},[103,4544,3854],{"class":142},[103,4546,3786],{"class":142},[103,4548,3859],{"class":142},[103,4550,3862],{"class":517},[103,4552,3865],{"class":142},[103,4554,4555],{"class":517},"): 600 B\n",[10,4557,4558,4559,4561],{},"Maintenant, le dossier ",[100,4560,3555],{}," apparaît comme ceci :",[92,4563,4566],{"className":4564,"code":4565,"language":3310},[3308],"dist\u002F\n  components\u002F\n    User.vue\n  composables\u002F\n    useUser.d.ts\n    useUser.mjs\n  index.d.ts\n  index.mjs\n",[100,4567,4565],{"__ignoreMap":98},[10,4569,4570,4571,4573,4574,4577,4578,4580],{},"Cela reflète la structure du dossier ",[100,4572,3874],{},", et le composant ",[100,4575,4576],{},"User.vue"," est maintenant utilisable dans le dossier ",[100,4579,3555],{},". Le chemin d'importation relatif reste intact, et le composant peut être utilisé dans n'importe quel projet Vue. :partying_face:",[31,4582,4584],{"id":4583},"développement-local","Développement local",[10,4586,4587,4588,4591],{},"De nombreux tutoriels s'arrêtent ici, suggérant que vous êtes prêt à publier votre package sur npm. Mais comment pouvez-vous créer une bibliothèque complexe si vous ne pouvez pas ",[14,4589,4590],{},"voir le résultat de votre travail"," ? Si vous ne pouvez pas la tester tout en la construisant ?",[10,4593,4594],{},"Vous ne pouvez pas.",[10,4596,4597],{},"Examinons comment utiliser la bibliothèque dans un projet local, au sein du même dépôt pour plus de simplicité.",[10,4599,4600,4601,599],{},"Je vais expliquer la méthode la plus simple en utilisant un ",[1640,4602,4605],{"href":4603,"rel":4604},"https:\u002F\u002Fpnpm.io\u002F",[1644],"pnpm workspace",[10,4607,4267,4608,4611],{},[100,4609,4610],{},"pnpm-workspace.yaml"," à la racine du projet :",[92,4613,4617],{"className":4614,"code":4615,"language":4616,"meta":98,"style":98},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","packages:\n  - .\n  - playground\n","yaml",[100,4618,4619,4627,4635],{"__ignoreMap":98},[103,4620,4621,4624],{"class":105,"line":106},[103,4622,4623],{"class":113},"packages",[103,4625,4626],{"class":109},":\n",[103,4628,4629,4632],{"class":105,"line":120},[103,4630,4631],{"class":109},"  -",[103,4633,4634],{"class":3456}," .\n",[103,4636,4637,4639],{"class":105,"line":129},[103,4638,4631],{"class":109},[103,4640,4641],{"class":142}," playground\n",[10,4643,4644,4645,3390],{},"Ensuite, créez un nouveau projet Vite dans un dossier ",[100,4646,4647],{},"playground",[92,4649,4651],{"className":3655,"code":4650,"language":3657,"meta":98,"style":98},"npx create-vite playground --template vue-ts\n",[100,4652,4653],{"__ignoreMap":98},[103,4654,4655,4657,4660,4663,4666],{"class":105,"line":106},[103,4656,3664],{"class":1492},[103,4658,4659],{"class":142}," create-vite",[103,4661,4662],{"class":142}," playground",[103,4664,4665],{"class":142}," --template",[103,4667,4668],{"class":142}," vue-ts\n",[10,4670,4671],{},"Puisque nous utilisons un pnpm workspace, installez les dépendances à partir de la racine du projet :",[92,4673,4675],{"className":3655,"code":4674,"language":3657,"meta":98,"style":98},"pnpm install\n",[100,4676,4677],{"__ignoreMap":98},[103,4678,4679,4682],{"class":105,"line":106},[103,4680,4681],{"class":1492},"pnpm",[103,4683,4684],{"class":142}," install\n",[1631,4686,4687],{},[10,4688,4689,4691,4692,4695],{},[103,4690,1637],{},"\nCette étape n'est pas obligatoire. Vous pouvez créer un nouveau projet Vite et installer les dépendances à l'intérieur. Pnpm simplifie cela en installant les dépendances pour tous les espaces de travail à la racine. Une simple commande ",[100,4693,4694],{},"pnpm install"," installera les dépendances de tous les espaces de travail.",[10,4697,4698,4699,4701],{},"Maintenant, nous pouvons utiliser cette bibliothèque ",[100,4700,4647],{}," dans un projet \"réel\" pour tester notre bibliothèque.",[10,4703,4704,4705,4707,4708,3390],{},"Par exemple, ouvrez le fichier ",[100,4706,762],{}," et importez le composant ",[100,4709,4710],{},"User",[92,4712,4714],{"className":94,"code":4713,"language":97,"meta":98,"style":98},"\u003Cscript setup lang=\"ts\">\nimport { User } from '..\u002F..\u002Fsrc'\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CUser \u002F>\n\u003C\u002Ftemplate>\n",[100,4715,4716,4736,4755,4763,4767,4775,4783],{"__ignoreMap":98},[103,4717,4718,4720,4722,4724,4726,4728,4730,4732,4734],{"class":105,"line":106},[103,4719,110],{"class":109},[103,4721,488],{"class":113},[103,4723,503],{"class":132},[103,4725,491],{"class":132},[103,4727,136],{"class":109},[103,4729,139],{"class":109},[103,4731,498],{"class":142},[103,4733,139],{"class":109},[103,4735,117],{"class":109},[103,4737,4738,4740,4742,4744,4746,4748,4750,4753],{"class":105,"line":120},[103,4739,511],{"class":510},[103,4741,514],{"class":109},[103,4743,3353],{"class":517},[103,4745,521],{"class":109},[103,4747,524],{"class":510},[103,4749,527],{"class":109},[103,4751,4752],{"class":142},"..\u002F..\u002Fsrc",[103,4754,532],{"class":109},[103,4756,4757,4759,4761],{"class":105,"line":129},[103,4758,205],{"class":109},[103,4760,488],{"class":113},[103,4762,117],{"class":109},[103,4764,4765],{"class":105,"line":148},[103,4766,537],{"emptyLinePlaceholder":360},[103,4768,4769,4771,4773],{"class":105,"line":162},[103,4770,110],{"class":109},[103,4772,114],{"class":113},[103,4774,117],{"class":109},[103,4776,4777,4779,4781],{"class":105,"line":168},[103,4778,123],{"class":109},[103,4780,4710],{"class":113},[103,4782,188],{"class":109},[103,4784,4785,4787,4789],{"class":105,"line":191},[103,4786,205],{"class":109},[103,4788,114],{"class":113},[103,4790,117],{"class":109},[10,4792,4793],{},"Et exécutez le projet :",[92,4795,4797],{"className":3655,"code":4796,"language":3657,"meta":98,"style":98},"cd playground && pnpm dev\n",[100,4798,4799],{"__ignoreMap":98},[103,4800,4801,4804,4806,4809,4812],{"class":105,"line":106},[103,4802,4803],{"class":550},"cd",[103,4805,4662],{"class":142},[103,4807,4808],{"class":109}," &&",[103,4810,4811],{"class":1492}," pnpm",[103,4813,4814],{"class":142}," dev\n",[10,4816,4817,4818,4820,4821],{},"Vous verrez le composant ",[100,4819,4710],{}," s'afficher dans le navigateur. 🎉 Si simple, mais si puissant. ",[26,4822,4823],{},"J'utilise cette méthode pour presque toutes les bibliothèques que je construis, et cela fonctionne parfaitement.",[31,4825,4827],{"id":4826},"publication-sur-npm","Publication sur npm",[10,4829,4830],{},"Cette partie est simple une fois que vous avez compris le flux de travail.",[10,4832,4833,4834,4837],{},"Commencez par nommer votre bibliothèque. J'utiliserai ",[100,4835,4836],{},"@barbapapazes\u002Fvue-library"," pour cet exemple.",[10,4839,4840,4841,4846,4847,599],{},"Ensuite, créez un compte sur ",[1640,4842,4845],{"href":4843,"rel":4844},"https:\u002F\u002Fwww.npmjs.com",[1644],"npm"," et connectez-vous en utilisant la commande ",[100,4848,4849],{},"npm login",[10,4851,4852,4853,4858,4859,4862],{},"Puis, installez ",[1640,4854,4857],{"href":4855,"rel":4856},"https:\u002F\u002Fchangelogen.unjs.io",[1644],"changelogen"," pour générer un changelog, mettre à jour le numéro de version selon la gestion sémantique des versions et les ",[26,4860,4861],{},"conventional commits",", et pré-remplir la version sur GitHub.",[92,4864,4866],{"className":3655,"code":4865,"language":3657,"meta":98,"style":98},"pnpm i -D changelogen\n",[100,4867,4868],{"__ignoreMap":98},[103,4869,4870,4872,4875,4878],{"class":105,"line":106},[103,4871,4681],{"class":1492},[103,4873,4874],{"class":142}," i",[103,4876,4877],{"class":142}," -D",[103,4879,4880],{"class":142}," changelogen\n",[10,4882,4883,4884,3390],{},"Ensuite, ajoutez deux scripts à votre ",[100,4885,3385],{},[92,4887,4889],{"className":3393,"code":4888,"language":3395,"meta":98,"style":98},"{\n  \"scripts\": {\n    \"prepack\": \"unbuild\",\n    \"release\": \"changelogen --release && npm publish --access public && git push --follow-tags\"\n  }\n}\n",[100,4890,4891,4895,4908,4927,4945,4949],{"__ignoreMap":98},[103,4892,4893],{"class":105,"line":106},[103,4894,574],{"class":109},[103,4896,4897,4899,4902,4904,4906],{"class":105,"line":120},[103,4898,3406],{"class":109},[103,4900,4901],{"class":132},"scripts",[103,4903,139],{"class":109},[103,4905,582],{"class":109},[103,4907,591],{"class":109},[103,4909,4910,4912,4915,4917,4919,4921,4923,4925],{"class":105,"line":129},[103,4911,3440],{"class":109},[103,4913,4914],{"class":1492},"prepack",[103,4916,139],{"class":109},[103,4918,582],{"class":109},[103,4920,3416],{"class":109},[103,4922,3640],{"class":142},[103,4924,139],{"class":109},[103,4926,1021],{"class":109},[103,4928,4929,4931,4934,4936,4938,4940,4943],{"class":105,"line":148},[103,4930,3440],{"class":109},[103,4932,4933],{"class":1492},"release",[103,4935,139],{"class":109},[103,4937,582],{"class":109},[103,4939,3416],{"class":109},[103,4941,4942],{"class":142},"changelogen --release && npm publish --access public && git push --follow-tags",[103,4944,145],{"class":109},[103,4946,4947],{"class":105,"line":162},[103,4948,622],{"class":109},[103,4950,4951],{"class":105,"line":168},[103,4952,1046],{"class":109},[10,4954,4955,4956,4958,4959,4961,4962,4964],{},"Le script ",[100,4957,4914],{}," exécute la commande ",[100,4960,3640],{}," avant de publier le package sur npm. Le script ",[100,4963,4933],{}," génère un changelog, publie le package sur npm, et pousse les tags sur GitHub.",[1631,4966,4967],{},[10,4968,4969,4971,4972,4975],{},[103,4970,1637],{},"\nVotre projet doit être sur GitHub pour que l'option ",[100,4973,4974],{},"--release"," fonctionne. Sinon, omettez-là et créez la version manuellement sur votre dépôt.",[10,4977,4978],{},"Maintenant, publiez votre package sur npm en utilisant la commande suivante :",[92,4980,4982],{"className":3655,"code":4981,"language":3657,"meta":98,"style":98},"npm run release\n",[100,4983,4984],{"__ignoreMap":98},[103,4985,4986,4988,4991],{"class":105,"line":106},[103,4987,4845],{"class":1492},[103,4989,4990],{"class":142}," run",[103,4992,4993],{"class":142}," release\n",[10,4995,4996],{},"Et c'est tout ! Vous venez de publier votre première bibliothèque de composants Vue.js sur npm. 🚀",[31,4998,5000],{"id":4999},"tout-est-prêt","Tout est prêt",[10,5002,5003],{},"Nous avons terminé pour aujourd'hui. Nous avons couvert de nombreux sujets :",[313,5005,5006,5009,5012,5015,5018,5021],{},[306,5007,5008],{},"Comment construire une bibliothèque de composants Vue.js avec TypeScript.",[306,5010,5011],{},"Comment gérer les fichiers Vue dans un projet TypeScript.",[306,5013,5014],{},"Comment utiliser unbuild pour transpiler les fichiers TypeScript sans regrouper les fichiers Vue.",[306,5016,5017],{},"Comment tirer parti de mkdist pour maintenir la structure du projet.",[306,5019,5020],{},"Comment utiliser un pnpm workspace pour tester la bibliothèque dans un projet local.",[306,5022,5023],{},"Comment publier la bibliothèque sur npm.",[10,5025,5026,5027],{},"Pour un exemple plus détaillé et complexe, consultez GitHub : ",[2903,5028,5029,5030,599],{"repo":3011}," mais tout ce qui a été expliqué ici provient de ",[14,5031,5032],{},"mon expérience dans le monde réel",[10,5034,5035,5036,599],{},"J'espère que vous avez apprécié ce tutoriel et qu'il vous aidera à construire votre propre bibliothèque de composants Vue.js. Si vous avez des questions, n'hésitez pas à me contacter sur ",[1640,5037,5040],{"href":5038,"rel":5039},"https:\u002F\u002Fx.com\u002Fsoubiran_",[1644],"X (Twitter)",[10,5042,5043],{},[26,5044,5045],{},"Vous pouvez aller encore plus loin en utilisant Vite pour construire votre bibliothèque de composants :",[19,5047],{"id":3050},[2978,5049,5050],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":98,"searchDepth":120,"depth":120,"links":5052},[5053,5054,5055,5056,5057,5058,5059,5060,5061],{"id":3541,"depth":120,"text":3542},{"id":3627,"depth":120,"text":3628},{"id":3880,"depth":120,"text":3881},{"id":4100,"depth":120,"text":4101},{"id":4187,"depth":120,"text":4188},{"id":4260,"depth":120,"text":4261},{"id":4583,"depth":120,"text":4584},{"id":4826,"depth":120,"text":4827},{"id":4999,"depth":120,"text":5000},"2024-09-24","Gérer les complexités des SFC de Vue avec des outils puissants comme unbuild et mkdist pour créer une bibliothèque de composants.",{},"\u002Ffr\u002Fposts\u002Fthe-simplest-method-to-create-a-vue-js-component-library","---\ncontentId: 75df6bff-de40-4056-af23-fae2401cd21a\ntitle: Créer une Bibliothèque de Composants Vue.js Simplement\ndescription: Gérer les complexités des SFC de Vue avec des outils puissants comme unbuild et mkdist pour créer une bibliothèque de composants.\ndate: 2024-09-24\n---\n\nTL;DR: \u003CGitHubLink repo=\"barbapapazes\u002Fvue-library\" \u002F> _Continuez à lire pour comprendre le processus derrière cela et les raisons de mes choix._\n\nUne bibliothèque de composants est une collection de morceaux réutilisables qui peuvent être utilisés dans divers projets. Elle facilite le partage de ressources entre différents projets et équipes. Ces composants peuvent être basiques et génériques comme des boutons, des champs de saisie et des modales, ou des modules plus spécifiques au métier. _En fin de compte, c'est une méthode pour partager du code entre les projets, une compétence cruciale qui peut faire économiser beaucoup de temps._\n\nCependant, construire une bibliothèque de composants avec [Vue.js](https:\u002F\u002Fvuejs.org) est plus difficile qu'il n'y paraît en raison des [Single File Components](https:\u002F\u002Fvuejs.org\u002Fguide\u002Fscaling-up\u002Fsfc.html) (SFC). Dans un projet TypeScript standard, vous transpilez généralement en JavaScript et regroupez vos fichiers avec un outil comme [tsup](https:\u002F\u002Ftsup.egoist.dev\u002F) ou [Vite](https:\u002F\u002Fvitejs.dev\u002F).\n\n\u003CCalloutReadMore id=\"1fafed86-05bb-4de4-b7d0-bb0bd6abc9d2\" \u002F>\n\n> [!NOTE]\n> N'oubliez pas de **livrer le code du paquet aussi natif que possible** et de laisser les outils de l'utilisateur gérer la transpilation et l'optimisation. C'est un principe que je garde toujours à l'esprit lorsque je crée des bibliothèques, ce qui simplifie considérablement le processus. _Chaque cas est unique, mais c'est une bonne ligne directrice à suivre._\n\nL'utilisation des SFC de Vue.js complique ce processus. Examinons pourquoi en explorant les défis que vous rencontrerez lors de la construction d'une bibliothèque de composants Vue.js.\n\nConsidérons le composant suivant :\n\n```vue\n\u003Cscript lang=\"ts\" setup>\nimport { useUser } from '..\u002Fcomposables'\n\nconst { user } = useUser()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv v-if=\"user\">\n    {{ user.name }}\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n```\n\nLe composable est le suivant :\n\n```ts\nimport { ref } from 'vue'\n\nexport function useUser() {\n  const user = ref({ name: 'John Doe' })\n\n  return {\n    user\n  }\n}\n```\n\nLe composant utilise un composable importé d'un autre fichier. _Cela peut sembler trivial, mais cela pose un problème significatif._\n\nTout cela suit l'architecture ci-dessous :\n\n```\nsrc\u002F\n  components\u002F\n    User.vue\n  composables\u002F\n    useUser.ts\n  index.ts\n```\n\nLe fichier `index.ts` sert de point d'entrée de la bibliothèque et ressemble à ceci :\n\n```ts\nimport User from '.\u002Fcomponents\u002FUser.vue'\n\nexport { User }\n\nexport { useUser } from '.\u002Fcomposables\u002FuseUser'\n```\n\nDe plus, le `package.json` inclut quelques `exports` :\n\n```json\n{\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \".\u002Fdist\u002Findex.d.ts\",\n      \"import\": \".\u002Fdist\u002Findex.mjs\"\n    }\n  },\n  \"main\": \"dist\u002Findex.mjs\",\n  \"types\": \"dist\u002Findex.d.ts\"\n}\n```\n\n## Similaire à un projet TypeScript standard\n\n> [!NOTE]\n> Un bundle est un seul fichier encapsulant tout le code de votre projet. Il optimise le chargement du projet en minimisant le nombre de fichiers à charger. Ce bundle n'est pas transpile (sauf TypeScript vers JavaScript) ni minifié lors de la création d'un package npm.\n\nMaintenant, si nous tentons de regrouper ce projet, notre dossier `dist` apparaîtra comme ceci :\n\n```\ndist\u002F\n  index.cjs\n```\n\nEn inspectant `index.cjs`, vous trouverez une surprise : le composable `useUser` est intégré, mais le composant est complètement ignoré. _S'il fonctionne, l'outil pourrait déclencher une erreur du type `No loader is configured for \".vue\" files`. Essentiellement, des outils comme [esbuild](https:\u002F\u002Fesbuild.github.io) ou [tsc](https:\u002F\u002Fwww.typescriptlang.org\u002Fdocs\u002Fhandbook\u002Fcompiler-options.html) ne savent pas comment gérer les fichiers Vue. C'est logique, car les fichiers Vue ne sont pas des fichiers JavaScript._\n\nLe problème devient évident. Nous avons besoin d'une configuration pour gérer les fichiers Vue. Jetons un coup d'œil rapide sur Internet pour quelque chose comme `vue loader`, car nous utilisons des fichiers Vue et devons les transpiler en JavaScript.\n\nLe premier résultat est [Vue Loader](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fvue-loader), qui est un loader Webpack. _Non. Je refuse d'utiliser Webpack. Vite est maintenant un standard, et il n'y a aucune justification pour employer Webpack dans ce contexte._\n\nMais avant de plonger plus loin, rappelons-nous notre mantra : **livrer le code du paquet aussi natif que possible** _et [keep it simple, stupid!](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FKISS_principle)_ La transpilation des fichiers SFC Vue avec Webpack semble contraire à ce principe.\n\n## Ignorer les fichiers Vue\n\nPour livrer des fichiers `.vue` sans les transpiler, nous demandons à notre outil de les ignorer. Cela est réalisé à l'aide d'un outil appelé [unbuild](https:\u002F\u002Funbuild.unjs.io) provenant de l'écosystème [UnJS](https:\u002F\u002Funjs.io).\n\nUnbuild est un outil simple mais hautement configurable car il est construit sur Rollup.\n\nPour notre petit projet, nous pouvons l'expérimenter directement :\n\n```sh\nnpx unbuild\n```\n\nMalheureusement, cela échoue pour les mêmes raisons que précédemment :\n\n```txt\nsrc\u002Fcomponents\u002FShowGitHubUser.vue (1:0): Expression expected (Note that you need plugins to import files that are not JavaScript)\n```\n\n_Mais unbuild a le potentiel pour bien plus._\n\nPour résoudre ce problème, essayons la solution évidente. Dans le fichier `index.ts`, supprimons l'exportation du composant et exportons uniquement les fichiers TypeScript :\n\n```ts\nexport { useUser } from '.\u002Fcomposables\u002FuseUser'\n```\n\nMaintenant, la commande `npx unbuild` fonctionne correctement.\n\n```sh\n➜ npx unbuild\nℹ Automatically detected entries: src\u002Findex [esm] [dts]\nℹ Building vue-library\nℹ Cleaning dist directory: .\u002Fdist\n✔ Build succeeded for vue-library\n  dist\u002Findex.mjs (total size: 139 B, chunk size: 139 B, exports: useUser)\n\nΣ Total dist size (byte size): 531 B\n```\n\nC'est un bon début, mais nos composants restent dans le dossier `src` et peu importe combien vous cherchez ardemment, ils sont absents du dossier `dist`.\n\n## Copier les fichiers Vue\n\nMaintenant que les fichiers `.ts` sont traités correctement, nous pouvons tenter de copier les fichiers `.vue` dans le dossier `dist` avec une simple commande `cp`.\n\n```sh\ncp -r src\u002Fcomponents\u002F dist\u002Fcomponents\u002F\n```\n\nEnsuite, nous ajoutons un champ `exports` dans le `package.json` pour informer l'utilisateur où localiser les composants :\n\n```json\n{\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \".\u002Fdist\u002Findex.d.ts\",\n      \"import\": \".\u002Fdist\u002Findex.mjs\"\n    },\n    \".\u002Fcomponents\u002F*\": {\n      \"import\": \".\u002Fdist\u002Fcomponents\u002F*.vue\"\n    }\n  },\n  \"main\": \"dist\u002Findex.mjs\",\n  \"types\": \"dist\u002Findex.d.ts\"\n}\n```\n\nÇa a l'air prometteur, n'est-ce pas ? _Mais ce n'est pas le cas._ :pensive:\n\n## Le dilemme du chemin d'importation\n\nDans notre composant Vue, le composable `useUser` est importé comme ceci :\n\n```vue\n\u003Cscript lang=\"ts\" setup>\nimport { useUser } from '..\u002Fcomposables'\n\u003C\u002Fscript>\n```\n\nPendant ce temps, notre dossier `dist` est structuré comme suit :\n\n```\ndist\u002F\n  components\u002F\n    User.vue\n  index.d.mts\n  index.d.ts\n  index.mjs\n```\n\nPouvez-vous identifier le problème ? :eyes:\n\nLe composant est inutilisable et produira une erreur du type `Cannot find module '..\u002Fcomposables' or its corresponding type declarations`, car le chemin mène nulle part. Nous avons regroupé tous nos fichiers TypeScript dans un seul fichier `index.mjs`, perdant complètement la structure du projet.\n\n## Regrouper ou ne pas regrouper ?\n\nÀ partir de là, nous avons deux choix :\n\n1. **Regrouper les fichiers Vue** en utilisant [Vite](https:\u002F\u002Fvitejs.dev), des plugins et une configuration extensive.\n2. **Conserver la structure de répertoires** et utiliser un outil comme [mkdist](https:\u002F\u002Fgithub.com\u002Funjs\u002Fmkdist) pour une transpilation fichier à fichier (construction sans bundle) sur les fichiers TypeScript.\n\nLa décision dépendra largement de vos besoins spécifiques, mais je suis convaincu que la simplicité est la meilleure approche. Donc, explorons la deuxième option.\n\nL'objectif est de transformer les fichiers TypeScript en fichiers JavaScript, tout en préservant la structure d'origine du dossier `src` dans le dossier `dist` et en _ignorant_ les fichiers Vue.\n\n> [!NOTE]\n> En utilisant un bloc `\u003Cscript setup lang=\"ts\">`, mkdist ignorera les fichiers Vue pour permettre au compilateur Vue de générer des props d'exécution. Sinon, il générera un fichier `.vue.d.ts` pour offrir des types pour le composant Vue. Rappelez-vous que le [@vitejs\u002Fplugin-vue](https:\u002F\u002Fgithub.com\u002Fvitejs\u002Fvite-plugin-vue\u002Ftree\u002Fmain\u002Fpackages\u002Fplugin-vue) et Vite comprennent intrinsèquement les blocs de script TypeScript. Pour plus de détails, consultez [issue mkdist#14](https:\u002F\u002Fgithub.com\u002Funjs\u002Fmkdist\u002Fissues\u002F14).\n\n## Configurer Unbuild\n\nOui, nous allons configurer unbuild car il intègre mkdist, ce qui le rend aggréable à utiliser.\n\nCommencez par créer un fichier `build.config.ts` à la racine du projet. Unbuild lira ce fichier de configuration pour comprendre comment traiter le projet. Il suppose des valeurs par défaut et infère de nombreux aspects à partir du `package.json`, mais nous devons lui dire d'utiliser mkdist.\n\n```ts\nimport { defineBuildConfig } from 'unbuild'\n\nexport default defineBuildConfig({\n  entries: ['.\u002Fsrc\u002F'],\n  declaration: true,\n})\n```\n\nC'est un fichier de configuration très simple. _Et je l'adore._ La clé `entries` informe unbuild du fichier initial à commencer à transpiler. Cependant, `.\u002Fsrc\u002F` est un répertoire, indiqué par le `\u002F` à la fin. Avec cet indice, unbuild contourne le regroupement par défaut `rollup` en faveur de `mkdist`. La clé `declaration` incite unbuild à créer des fichiers de déclaration TypeScript (`.d.ts`).\n\nNous pouvons également supprimer le champ `exports` dans le `package.json` puisque nous ne regrouperons plus les fichiers et restaurer l'exportation du composant dans le fichier `index.ts`.\n\nRelaçons la commande `npx unbuild` et magie :\n\n```sh\n➜ npx unbuild\nℹ Building vue-library\nℹ Cleaning dist directory: .\u002Fdist\n✔ Build succeeded for vue-library\n  dist (total size: 600 B)\n  └─ dist\u002Findex.d.ts (108 B)\n  └─ dist\u002Findex.mjs (112 B)\n  └─ dist\u002Fcomposables\u002FuseUser.d.ts (79 B)\n  └─ dist\u002Fcomposables\u002FuseUser.mjs (124 B)\n  └─ dist\u002Fcomponents\u002FUser.vue (177 B)\nΣ Total dist size (byte size): 600 B\n```\n\nMaintenant, le dossier `dist` apparaît comme ceci :\n\n```\ndist\u002F\n  components\u002F\n    User.vue\n  composables\u002F\n    useUser.d.ts\n    useUser.mjs\n  index.d.ts\n  index.mjs\n```\n\nCela reflète la structure du dossier `src`, et le composant `User.vue` est maintenant utilisable dans le dossier `dist`. Le chemin d'importation relatif reste intact, et le composant peut être utilisé dans n'importe quel projet Vue. :partying_face:\n\n## Développement local\n\nDe nombreux tutoriels s'arrêtent ici, suggérant que vous êtes prêt à publier votre package sur npm. Mais comment pouvez-vous créer une bibliothèque complexe si vous ne pouvez pas **voir le résultat de votre travail** ? Si vous ne pouvez pas la tester tout en la construisant ?\n\nVous ne pouvez pas.\n\nExaminons comment utiliser la bibliothèque dans un projet local, au sein du même dépôt pour plus de simplicité.\n\nJe vais expliquer la méthode la plus simple en utilisant un [pnpm workspace](https:\u002F\u002Fpnpm.io\u002F).\n\nCommencez par créer un fichier `pnpm-workspace.yaml` à la racine du projet :\n\n```yaml\npackages:\n  - .\n  - playground\n```\n\nEnsuite, créez un nouveau projet Vite dans un dossier `playground` :\n\n```sh\nnpx create-vite playground --template vue-ts\n```\n\nPuisque nous utilisons un pnpm workspace, installez les dépendances à partir de la racine du projet :\n\n```sh\npnpm install\n```\n\n> [!NOTE]\n> Cette étape n'est pas obligatoire. Vous pouvez créer un nouveau projet Vite et installer les dépendances à l'intérieur. Pnpm simplifie cela en installant les dépendances pour tous les espaces de travail à la racine. Une simple commande `pnpm install` installera les dépendances de tous les espaces de travail.\n\nMaintenant, nous pouvons utiliser cette bibliothèque `playground` dans un projet \"réel\" pour tester notre bibliothèque.\n\nPar exemple, ouvrez le fichier `src\u002FApp.vue` et importez le composant `User` :\n\n```vue\n\u003Cscript setup lang=\"ts\">\nimport { User } from '..\u002F..\u002Fsrc'\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CUser \u002F>\n\u003C\u002Ftemplate>\n```\n\nEt exécutez le projet :\n\n```sh\ncd playground && pnpm dev\n```\n\nVous verrez le composant `User` s'afficher dans le navigateur. :tada: Si simple, mais si puissant. _J'utilise cette méthode pour presque toutes les bibliothèques que je construis, et cela fonctionne parfaitement._\n\n## Publication sur npm\n\nCette partie est simple une fois que vous avez compris le flux de travail.\n\nCommencez par nommer votre bibliothèque. J'utiliserai `@barbapapazes\u002Fvue-library` pour cet exemple.\n\nEnsuite, créez un compte sur [npm](https:\u002F\u002Fwww.npmjs.com) et connectez-vous en utilisant la commande `npm login`.\n\nPuis, installez [changelogen](https:\u002F\u002Fchangelogen.unjs.io) pour générer un changelog, mettre à jour le numéro de version selon la gestion sémantique des versions et les _conventional commits_, et pré-remplir la version sur GitHub.\n\n```sh\npnpm i -D changelogen\n```\n\nEnsuite, ajoutez deux scripts à votre `package.json` :\n\n```json\n{\n  \"scripts\": {\n    \"prepack\": \"unbuild\",\n    \"release\": \"changelogen --release && npm publish --access public && git push --follow-tags\"\n  }\n}\n```\n\nLe script `prepack` exécute la commande `unbuild` avant de publier le package sur npm. Le script `release` génère un changelog, publie le package sur npm, et pousse les tags sur GitHub.\n\n> [!NOTE]\n> Votre projet doit être sur GitHub pour que l'option `--release` fonctionne. Sinon, omettez-là et créez la version manuellement sur votre dépôt.\n\nMaintenant, publiez votre package sur npm en utilisant la commande suivante :\n\n```sh\nnpm run release\n```\n\nEt c'est tout ! Vous venez de publier votre première bibliothèque de composants Vue.js sur npm. :rocket:\n\n## Tout est prêt\n\nNous avons terminé pour aujourd'hui. Nous avons couvert de nombreux sujets :\n\n- Comment construire une bibliothèque de composants Vue.js avec TypeScript.\n- Comment gérer les fichiers Vue dans un projet TypeScript.\n- Comment utiliser unbuild pour transpiler les fichiers TypeScript sans regrouper les fichiers Vue.\n- Comment tirer parti de mkdist pour maintenir la structure du projet.\n- Comment utiliser un pnpm workspace pour tester la bibliothèque dans un projet local.\n- Comment publier la bibliothèque sur npm.\n\nPour un exemple plus détaillé et complexe, consultez GitHub : \u003CGitHubLink repo=\"barbapapazes\u002Fvue-library\" \u002F> mais tout ce qui a été expliqué ici provient de **mon expérience dans le monde réel**.\n\nJ'espère que vous avez apprécié ce tutoriel et qu'il vous aidera à construire votre propre bibliothèque de composants Vue.js. Si vous avez des questions, n'hésitez pas à me contacter sur [X (Twitter)](https:\u002F\u002Fx.com\u002Fsoubiran_).\n\n_Vous pouvez aller encore plus loin en utilisant Vite pour construire votre bibliothèque de composants :_\n\n\u003CCalloutReadMore id=\"1fafed86-05bb-4de4-b7d0-bb0bd6abc9d2\" \u002F>\n",{"title":3003,"description":5063},{"loc":5065},"fr\u002Fposts\u002Fthe-simplest-method-to-create-a-vue-js-component-library","leiu4ugGnVZbNxLN54wTrqoPqm1ZAPeO0Ix356BJhgc",1790086903639]