[{"data":1,"prerenderedAt":5273},["ShallowReactive",2],{"navigation_docs":3,"-tu-hoc-nuxt-4-data-server":420,"-tu-hoc-nuxt-4-data-server-surround":5268},[4,153],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":26},"Phỏng vấn .NET","i-lucide-briefcase","\u002Fphong-van","1.phong-van",[10,27,66,111,128],{"title":11,"icon":12,"path":13,"stem":14,"children":15,"page":26},"Tổng quan","i-lucide-compass","\u002Fphong-van\u002Ftong-quan","1.phong-van\u002F1.tong-quan",[16,21],{"title":17,"path":18,"stem":19,"icon":20},"Thị trường HCM","\u002Fphong-van\u002Ftong-quan\u002Fthi-truong","1.phong-van\u002F1.tong-quan\u002F1.thi-truong","i-lucide-trending-up",{"title":22,"path":23,"stem":24,"icon":25},"Lộ trình 7 ngày","\u002Fphong-van\u002Ftong-quan\u002Flo-trinh","1.phong-van\u002F1.tong-quan\u002F2.lo-trinh","i-lucide-calendar-days",false,{"title":22,"icon":25,"path":28,"stem":29,"children":30,"page":26},"\u002Fphong-van\u002Flo-trinh","1.phong-van\u002F2.lo-trinh",[31,36,41,46,51,56,61],{"title":32,"path":33,"stem":34,"icon":35},"Ngày 1 — C# cơ bản","\u002Fphong-van\u002Flo-trinh\u002Fngay-1-csharp-co-ban","1.phong-van\u002F2.lo-trinh\u002F1.ngay-1-csharp-co-ban","i-lucide-1",{"title":37,"path":38,"stem":39,"icon":40},"Ngày 2 — OOP và SOLID","\u002Fphong-van\u002Flo-trinh\u002Fngay-2-oop-solid","1.phong-van\u002F2.lo-trinh\u002F2.ngay-2-oop-solid","i-lucide-2",{"title":42,"path":43,"stem":44,"icon":45},"Ngày 3 — C# nâng cao","\u002Fphong-van\u002Flo-trinh\u002Fngay-3-csharp-nang-cao","1.phong-van\u002F2.lo-trinh\u002F3.ngay-3-csharp-nang-cao","i-lucide-3",{"title":47,"path":48,"stem":49,"icon":50},"Ngày 4 — Database và SQL","\u002Fphong-van\u002Flo-trinh\u002Fngay-4-database-sql","1.phong-van\u002F2.lo-trinh\u002F4.ngay-4-database-sql","i-lucide-4",{"title":52,"path":53,"stem":54,"icon":55},"Ngày 5 — ASP.NET Core và EF Core","\u002Fphong-van\u002Flo-trinh\u002Fngay-5-aspnet-efcore","1.phong-van\u002F2.lo-trinh\u002F5.ngay-5-aspnet-efcore","i-lucide-5",{"title":57,"path":58,"stem":59,"icon":60},"Ngày 6 — DSA coding","\u002Fphong-van\u002Flo-trinh\u002Fngay-6-dsa","1.phong-van\u002F2.lo-trinh\u002F6.ngay-6-dsa","i-lucide-6",{"title":62,"path":63,"stem":64,"icon":65},"Ngày 7 — Mock và HR","\u002Fphong-van\u002Flo-trinh\u002Fngay-7-mock-interview","1.phong-van\u002F2.lo-trinh\u002F7.ngay-7-mock-interview","i-lucide-7",{"title":67,"icon":68,"path":69,"stem":70,"children":71,"page":26},"Ngân hàng câu hỏi","i-lucide-database","\u002Fphong-van\u002Fngan-hang-cau-hoi","1.phong-van\u002F3.ngan-hang-cau-hoi",[72,77,82,87,92,97,102,107],{"title":73,"path":74,"stem":75,"icon":76},"C# (60+ câu)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fcsharp","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F1.csharp","i-lucide-hash",{"title":78,"path":79,"stem":80,"icon":81},"OOP và SOLID (40+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Foop","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F2.oop","i-lucide-shapes",{"title":83,"path":84,"stem":85,"icon":86},"SQL (40+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fsql","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F3.sql","i-lucide-database-zap",{"title":88,"path":89,"stem":90,"icon":91},"ASP.NET và EF (50+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Faspnet-ef","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F4.aspnet-ef","i-lucide-server",{"title":93,"path":94,"stem":95,"icon":96},"DSA và Coding (30+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fdsa-coding","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F5.dsa-coding","i-lucide-binary",{"title":98,"path":99,"stem":100,"icon":101},"HR và Soft skill (25+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fhr-behavior","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F6.hr-behavior","i-lucide-users",{"title":103,"path":104,"stem":105,"icon":106},"Design Pattern (55+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fdesign-pattern","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F7.design-pattern","i-lucide-puzzle",{"title":108,"path":109,"stem":110,"icon":91},"ASP.NET MVC và Web API","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Faspnet-mvc-webapi","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F8.aspnet-mvc-webapi",{"title":112,"itle":113,"icon":114,"path":115,"stem":116,"children":117,"page":26},"Tai Lieu","Tài liệu và Cheat sheet","i-lucide-book-marked","\u002Fphong-van\u002Ftai-lieu","1.phong-van\u002F4.tai-lieu",[118,123],{"title":119,"path":120,"stem":121,"icon":122},"Cheat Sheet","\u002Fphong-van\u002Ftai-lieu\u002Fcheatsheet","1.phong-van\u002F4.tai-lieu\u002F1.cheatsheet","i-lucide-clipboard-list",{"title":124,"path":125,"stem":126,"icon":127},"Nguồn tham khảo","\u002Fphong-van\u002Ftai-lieu\u002Fnguon","1.phong-van\u002F4.tai-lieu\u002F2.nguon","i-lucide-link",{"title":129,"icon":130,"path":131,"stem":132,"children":133,"page":26},"Phỏng vấn English","i-lucide-globe","\u002Fphong-van\u002Fenglish-interview","1.phong-van\u002F5.english-interview",[134,138,143,148],{"title":135,"path":136,"stem":137,"icon":130},"Tổng quan English","\u002Fphong-van\u002Fenglish-interview\u002Ftong-quan","1.phong-van\u002F5.english-interview\u002F1.tong-quan",{"title":139,"path":140,"stem":141,"icon":142},"Vocabulary","\u002Fphong-van\u002Fenglish-interview\u002Fvocabulary","1.phong-van\u002F5.english-interview\u002F2.vocabulary","i-lucide-book-a",{"title":144,"path":145,"stem":146,"icon":147},"Technical Q và A (EN)","\u002Fphong-van\u002Fenglish-interview\u002Ftechnical-en","1.phong-van\u002F5.english-interview\u002F3.technical-en","i-lucide-code",{"title":149,"path":150,"stem":151,"icon":152},"Behavioral (EN)","\u002Fphong-van\u002Fenglish-interview\u002Fbehavioral-en","1.phong-van\u002F5.english-interview\u002F4.behavioral-en","i-lucide-message-square-text",{"title":154,"icon":155,"path":156,"stem":157,"children":158},"Tự học","i-lucide-graduation-cap","\u002Ftu-hoc","2.tu-hoc",[159,161,287,310,333,357,384],{"title":154,"path":156,"stem":160,"icon":155},"2.tu-hoc\u002Findex",{"title":162,"icon":68,"path":163,"stem":164,"children":165},"Database","\u002Ftu-hoc\u002Fdatabase","2.tu-hoc\u002F1.database\u002Findex",[166,167,197,222,243,248],{"title":162,"path":163,"stem":164,"icon":68},{"title":168,"icon":91,"path":169,"stem":170,"children":171,"page":26},"SQL Server","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server","2.tu-hoc\u002F1.database\u002F2.sql-server",[172,177,182,187,192],{"title":173,"path":174,"stem":175,"icon":176},"1. Giới thiệu và Cài đặt","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F1.gioi-thieu-cai-dat","i-lucide-play-circle",{"title":178,"path":179,"stem":180,"icon":181},"2. Cú pháp cơ bản","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F2.cu-phap-co-ban","i-lucide-code-2",{"title":183,"path":184,"stem":185,"icon":186},"3. Truy vấn nâng cao","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F3.truy-van-nang-cao","i-lucide-git-merge",{"title":188,"path":189,"stem":190,"icon":191},"4. Đặc thù và Performance","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F4.dac-thu-performance","i-lucide-gauge",{"title":193,"path":194,"stem":195,"icon":196},"5. Q và A phỏng vấn","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F5.cau-hoi-phong-van","i-lucide-message-circle-question",{"title":198,"icon":199,"path":200,"stem":201,"children":202,"page":26},"PostgreSQL","i-simple-icons-postgresql","\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql","2.tu-hoc\u002F1.database\u002F3.postgresql",[203,206,209,212,215,218],{"title":173,"path":204,"stem":205,"icon":176},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F1.gioi-thieu-cai-dat",{"title":178,"path":207,"stem":208,"icon":181},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F2.cu-phap-co-ban",{"title":183,"path":210,"stem":211,"icon":186},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F3.truy-van-nang-cao",{"title":188,"path":213,"stem":214,"icon":191},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F4.dac-thu-performance",{"title":193,"path":216,"stem":217,"icon":196},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F5.cau-hoi-phong-van",{"title":219,"path":220,"stem":221,"icon":96},"6. Tích hợp C# và Postgres","\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Ftich-hop-csharp","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F6.tich-hop-csharp",{"title":223,"icon":224,"path":225,"stem":226,"children":227,"page":26},"MySQL","i-simple-icons-mysql","\u002Ftu-hoc\u002Fdatabase\u002Fmysql","2.tu-hoc\u002F1.database\u002F4.mysql",[228,231,234,237,240],{"title":173,"path":229,"stem":230,"icon":176},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F4.mysql\u002F1.gioi-thieu-cai-dat",{"title":178,"path":232,"stem":233,"icon":181},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F4.mysql\u002F2.cu-phap-co-ban",{"title":183,"path":235,"stem":236,"icon":186},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F4.mysql\u002F3.truy-van-nang-cao",{"title":188,"path":238,"stem":239,"icon":191},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F4.mysql\u002F4.dac-thu-performance",{"title":193,"path":241,"stem":242,"icon":196},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F4.mysql\u002F5.cau-hoi-phong-van",{"title":244,"path":245,"stem":246,"icon":247},"So sánh 3 RDBMS","\u002Ftu-hoc\u002Fdatabase\u002Fso-sanh","2.tu-hoc\u002F1.database\u002F5.so-sanh","i-lucide-columns-3",{"title":249,"path":250,"stem":251,"children":252,"icon":254},"Supabase (BaaS)","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase","2.tu-hoc\u002F1.database\u002F6.supabase\u002Findex",[253,255,259,264,269,274,278,283],{"title":249,"path":250,"stem":251,"icon":254},"i-simple-icons-supabase",{"title":256,"path":257,"stem":258,"icon":68},"1. Database","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fdatabase","2.tu-hoc\u002F1.database\u002F6.supabase\u002F1.database",{"title":260,"path":261,"stem":262,"icon":263},"2. Authentication","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fauthentication","2.tu-hoc\u002F1.database\u002F6.supabase\u002F2.authentication","i-lucide-key",{"title":265,"path":266,"stem":267,"icon":268},"3. Storage & Realtime","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fstorage-realtime","2.tu-hoc\u002F1.database\u002F6.supabase\u002F3.storage-realtime","i-lucide-zap",{"title":270,"path":271,"stem":272,"icon":273},"4. Edge Functions & AI","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Ffunctions-ai","2.tu-hoc\u002F1.database\u002F6.supabase\u002F4.functions-ai","i-lucide-brain",{"title":275,"path":276,"stem":277,"icon":181},"5. API & SDKs","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fapi-sdk","2.tu-hoc\u002F1.database\u002F6.supabase\u002F5.api-sdk",{"title":279,"path":280,"stem":281,"icon":282},"6. Platform & Deploy","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fplatform-deploy","2.tu-hoc\u002F1.database\u002F6.supabase\u002F6.platform-deploy","i-lucide-cloud",{"title":284,"path":285,"stem":286,"icon":196},"7. Interview & Q&A","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Finterview","2.tu-hoc\u002F1.database\u002F6.supabase\u002F7.interview",{"title":288,"icon":289,"path":290,"stem":291,"children":292},"TypeScript","i-simple-icons-typescript","\u002Ftu-hoc\u002Ftypescript","2.tu-hoc\u002F2.typescript\u002Findex",[293,294,297,301,306],{"title":288,"path":290,"stem":291,"icon":289},{"title":173,"path":295,"stem":296,"icon":176},"\u002Ftu-hoc\u002Ftypescript\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F2.typescript\u002F1.gioi-thieu-cai-dat",{"title":298,"path":299,"stem":300,"icon":81},"2. Type System","\u002Ftu-hoc\u002Ftypescript\u002Ftype-system","2.tu-hoc\u002F2.typescript\u002F2.type-system",{"title":302,"path":303,"stem":304,"icon":305},"3. Advanced types","\u002Ftu-hoc\u002Ftypescript\u002Fadvanced-types","2.tu-hoc\u002F2.typescript\u002F3.advanced-types","i-lucide-braces",{"title":307,"path":308,"stem":309,"icon":196},"4. Q và A phỏng vấn","\u002Ftu-hoc\u002Ftypescript\u002Fcau-hoi-phong-van","2.tu-hoc\u002F2.typescript\u002F4.cau-hoi-phong-van",{"title":311,"icon":81,"path":312,"stem":313,"children":314},"OOP","\u002Ftu-hoc\u002Foop","2.tu-hoc\u002F3.oop\u002Findex",[315,316,321,326,330],{"title":311,"path":312,"stem":313,"icon":81},{"title":317,"path":318,"stem":319,"icon":320},"1. Bốn tính chất","\u002Ftu-hoc\u002Foop\u002F4-tinh-chat","2.tu-hoc\u002F3.oop\u002F1.4-tinh-chat","i-lucide-circle-dot",{"title":322,"path":323,"stem":324,"icon":325},"2. SOLID","\u002Ftu-hoc\u002Foop\u002Fsolid","2.tu-hoc\u002F3.oop\u002F2.solid","i-lucide-shield",{"title":327,"path":328,"stem":329,"icon":106},"3. Design Pattern","\u002Ftu-hoc\u002Foop\u002Fdesign-pattern","2.tu-hoc\u002F3.oop\u002F3.design-pattern",{"title":307,"path":331,"stem":332,"icon":196},"\u002Ftu-hoc\u002Foop\u002Fcau-hoi-phong-van","2.tu-hoc\u002F3.oop\u002F4.cau-hoi-phong-van",{"title":334,"icon":305,"path":335,"stem":336,"children":337},"Generic","\u002Ftu-hoc\u002Fgeneric","2.tu-hoc\u002F4.generic\u002Findex",[338,339,344,349,354],{"title":334,"path":335,"stem":336,"icon":305},{"title":340,"path":341,"stem":342,"icon":343},"1. Khái niệm và cú pháp","\u002Ftu-hoc\u002Fgeneric\u002Fkhai-niem","2.tu-hoc\u002F4.generic\u002F1.khai-niem","i-lucide-book",{"title":345,"path":346,"stem":347,"icon":348},"2. Constraint và Variance","\u002Ftu-hoc\u002Fgeneric\u002Fconstraint-variance","2.tu-hoc\u002F4.generic\u002F2.constraint-variance","i-lucide-filter",{"title":350,"path":351,"stem":352,"icon":353},"3. C# vs TS","\u002Ftu-hoc\u002Fgeneric\u002Fso-sanh","2.tu-hoc\u002F4.generic\u002F3.so-sanh","i-lucide-git-compare",{"title":307,"path":355,"stem":356,"icon":196},"\u002Ftu-hoc\u002Fgeneric\u002Fcau-hoi-phong-van","2.tu-hoc\u002F4.generic\u002F4.cau-hoi-phong-van",{"title":358,"icon":359,"path":360,"stem":361,"children":362},"Nuxt 4","i-simple-icons-nuxt","\u002Ftu-hoc\u002Fnuxt-4","2.tu-hoc\u002F5.nuxt-4\u002Findex",[363,364,367,372,376,379],{"title":358,"path":360,"stem":361,"icon":359},{"title":173,"path":365,"stem":366,"icon":176},"\u002Ftu-hoc\u002Fnuxt-4\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F5.nuxt-4\u002F1.gioi-thieu-cai-dat",{"title":368,"path":369,"stem":370,"icon":371},"2. Routing và Rendering","\u002Ftu-hoc\u002Fnuxt-4\u002Frouting-rendering","2.tu-hoc\u002F5.nuxt-4\u002F2.routing-rendering","i-lucide-route",{"title":373,"path":374,"stem":375,"icon":91},"3. Data và Server","\u002Ftu-hoc\u002Fnuxt-4\u002Fdata-server","2.tu-hoc\u002F5.nuxt-4\u002F3.data-server",{"title":307,"path":377,"stem":378,"icon":196},"\u002Ftu-hoc\u002Fnuxt-4\u002Fcau-hoi-phong-van","2.tu-hoc\u002F5.nuxt-4\u002F4.cau-hoi-phong-van",{"title":380,"path":381,"stem":382,"icon":383},"5. Practical Todo App","\u002Ftu-hoc\u002Fnuxt-4\u002Fpractical-todo-app","2.tu-hoc\u002F5.nuxt-4\u002F5.practical-todo-app","i-lucide-check-square",{"title":385,"path":386,"stem":387,"children":388,"icon":390},"Tailwind CSS","\u002Ftu-hoc\u002Ftailwindcss","2.tu-hoc\u002F6.tailwindcss\u002Findex",[389,391,396,401,406,411,416],{"title":385,"path":386,"stem":387,"icon":390},"i-simple-icons-tailwindcss",{"title":392,"path":393,"stem":394,"icon":395},"1. Layout & Spacing","\u002Ftu-hoc\u002Ftailwindcss\u002Flayout-spacing","2.tu-hoc\u002F6.tailwindcss\u002F1.layout-spacing","i-lucide-layout",{"title":397,"path":398,"stem":399,"icon":400},"2. Typography & Colors","\u002Ftu-hoc\u002Ftailwindcss\u002Ftypography-colors","2.tu-hoc\u002F6.tailwindcss\u002F2.typography-colors","i-lucide-type",{"title":402,"path":403,"stem":404,"icon":405},"3. Interactive & Effects","\u002Ftu-hoc\u002Ftailwindcss\u002Finteractive-effects","2.tu-hoc\u002F6.tailwindcss\u002F3.interactive-effects","i-lucide-sparkles",{"title":407,"path":408,"stem":409,"icon":410},"4. Responsive & Dark Mode","\u002Ftu-hoc\u002Ftailwindcss\u002Fresponsive-darkmode","2.tu-hoc\u002F6.tailwindcss\u002F4.responsive-darkmode","i-lucide-monitor-smartphone",{"title":412,"path":413,"stem":414,"icon":415},"5. Customization & Theme","\u002Ftu-hoc\u002Ftailwindcss\u002Fcustomization","2.tu-hoc\u002F6.tailwindcss\u002F5.customization","i-lucide-settings-2",{"title":417,"path":418,"stem":419,"icon":181},"6. Practical Projects","\u002Ftu-hoc\u002Ftailwindcss\u002Fpractical-projects","2.tu-hoc\u002F6.tailwindcss\u002F6.practical-projects",{"id":421,"title":422,"body":423,"description":5261,"extension":5262,"links":5263,"meta":5264,"navigation":5265,"path":374,"seo":5266,"stem":375,"__hash__":5267},"docs\u002F2.tu-hoc\u002F5.nuxt-4\u002F3.data-server.md","Data Fetching và Server Routes",{"type":424,"value":425,"toc":5230},"minimark",[426,442,466,470,1041,1045,1245,1249,1460,1495,1499,1503,1662,1666,1669,1673,1750,1753,1853,1857,1919,1923,1930,2027,2094,2125,2129,2147,2446,2485,2489,2499,2503,3099,3113,3117,3237,3241,3431,3435,3720,3724,3902,4088,4092,4426,4706,4710,4946,4950,5052,5056,5059,5138,5144,5158,5162,5218,5226],[427,428,430,431,435,436,435,439],"h2",{"id":429},"_1-data-fetching-usefetch-vs-useasyncdata-vs-fetch","1. Data fetching — ",[432,433,434],"code",{},"useFetch"," vs ",[432,437,438],{},"useAsyncData",[432,440,441],{},"$fetch",[443,444,445,457,461],"field-group",{},[446,447,450,451,453,454,456],"field",{"name":448,"type":449},"useFetch(url)","High-level","Wrapper convenience cho ",[432,452,438],{}," + ",[432,455,441],{},". Dùng cho 95% case fetch URL.",[446,458,460],{"name":459,"type":334},"useAsyncData(key, fn)","Async function bất kỳ với cache key. Linh hoạt hơn — fetch nhiều endpoint, transform...",[446,462,465],{"name":463,"type":464},"$fetch(url)","Imperative","Promise raw, không reactive, không SSR-aware. Dùng trong event handler, form submit.",[467,468,434],"h3",{"id":469},"usefetch",[471,472,477],"pre",{"className":473,"code":474,"language":475,"meta":476,"style":476},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript setup lang=\"ts\">\nconst { data, pending, error, refresh } = await useFetch(\"\u002Fapi\u002Fusers\", {\n  query: { city: \"HCM\" },\n  default: () => [],\n  transform: (rows: User[]) => rows.map(r => ({ ...r, fullName: `${r.first} ${r.last}` })),\n  pick: [\"id\", \"name\"],        \u002F\u002F chỉ giữ fields này\n  watch: [filter],             \u002F\u002F refetch khi filter đổi\n  lazy: false,                 \u002F\u002F false = block navigation tới khi xong (default trong setup)\n  server: true,                \u002F\u002F run trên server (default)\n  immediate: true,             \u002F\u002F gọi luôn (default)\n  key: \"users-hcm\",            \u002F\u002F custom cache key\n});\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv v-if=\"pending\">Loading...\u003C\u002Fdiv>\n  \u003Cdiv v-else-if=\"error\">Error: {{ error.message }}\u003C\u002Fdiv>\n  \u003Cul v-else>\n    \u003Cli v-for=\"u in data\" :key=\"u.id\">{{ u.name }}\u003C\u002Fli>\n  \u003C\u002Ful>\n  \u003Cbutton @click=\"refresh()\">Reload\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n","vue","",[432,478,479,514,572,599,619,714,750,766,783,799,814,834,844,854,861,871,904,934,947,991,1001,1032],{"__ignoreMap":476},[480,481,484,488,492,496,499,502,505,509,511],"span",{"class":482,"line":483},"line",1,[480,485,487],{"class":486},"sMK4o","\u003C",[480,489,491],{"class":490},"swJcz","script",[480,493,495],{"class":494},"spNyl"," setup",[480,497,498],{"class":494}," lang",[480,500,501],{"class":486},"=",[480,503,504],{"class":486},"\"",[480,506,508],{"class":507},"sfazB","ts",[480,510,504],{"class":486},[480,512,513],{"class":486},">\n",[480,515,517,520,523,527,530,533,535,538,540,543,546,549,553,557,560,562,565,567,569],{"class":482,"line":516},2,[480,518,519],{"class":494},"const",[480,521,522],{"class":486}," {",[480,524,526],{"class":525},"sTEyZ"," data",[480,528,529],{"class":486},",",[480,531,532],{"class":525}," pending",[480,534,529],{"class":486},[480,536,537],{"class":525}," error",[480,539,529],{"class":486},[480,541,542],{"class":525}," refresh ",[480,544,545],{"class":486},"}",[480,547,548],{"class":486}," =",[480,550,552],{"class":551},"s7zQu"," await",[480,554,556],{"class":555},"s2Zo4"," useFetch",[480,558,559],{"class":525},"(",[480,561,504],{"class":486},[480,563,564],{"class":507},"\u002Fapi\u002Fusers",[480,566,504],{"class":486},[480,568,529],{"class":486},[480,570,571],{"class":486}," {\n",[480,573,575,578,581,583,586,588,591,594,596],{"class":482,"line":574},3,[480,576,577],{"class":490},"  query",[480,579,580],{"class":486},":",[480,582,522],{"class":486},[480,584,585],{"class":490}," city",[480,587,580],{"class":486},[480,589,590],{"class":486}," \"",[480,592,593],{"class":507},"HCM",[480,595,504],{"class":486},[480,597,598],{"class":486}," },\n",[480,600,602,605,607,610,613,616],{"class":482,"line":601},4,[480,603,604],{"class":555},"  default",[480,606,580],{"class":486},[480,608,609],{"class":486}," ()",[480,611,612],{"class":494}," =>",[480,614,615],{"class":525}," []",[480,617,618],{"class":486},",\n",[480,620,622,625,627,630,634,636,640,643,646,648,651,654,657,659,662,664,666,669,672,674,676,679,681,684,686,688,691,693,696,698,700,703,706,709,712],{"class":482,"line":621},5,[480,623,624],{"class":555},"  transform",[480,626,580],{"class":486},[480,628,629],{"class":486}," (",[480,631,633],{"class":632},"sHdIc","rows",[480,635,580],{"class":486},[480,637,639],{"class":638},"sBMFI"," User",[480,641,642],{"class":525},"[]",[480,644,645],{"class":486},")",[480,647,612],{"class":494},[480,649,650],{"class":525}," rows",[480,652,653],{"class":486},".",[480,655,656],{"class":555},"map",[480,658,559],{"class":525},[480,660,661],{"class":632},"r",[480,663,612],{"class":494},[480,665,629],{"class":525},[480,667,668],{"class":486},"{",[480,670,671],{"class":486}," ...",[480,673,661],{"class":525},[480,675,529],{"class":486},[480,677,678],{"class":490}," fullName",[480,680,580],{"class":486},[480,682,683],{"class":486}," `${",[480,685,661],{"class":525},[480,687,653],{"class":486},[480,689,690],{"class":525},"first",[480,692,545],{"class":486},[480,694,695],{"class":486}," ${",[480,697,661],{"class":525},[480,699,653],{"class":486},[480,701,702],{"class":525},"last",[480,704,705],{"class":486},"}`",[480,707,708],{"class":486}," }",[480,710,711],{"class":525},"))",[480,713,618],{"class":486},[480,715,717,720,722,725,727,730,732,734,736,739,741,744,746],{"class":482,"line":716},6,[480,718,719],{"class":490},"  pick",[480,721,580],{"class":486},[480,723,724],{"class":525}," [",[480,726,504],{"class":486},[480,728,729],{"class":507},"id",[480,731,504],{"class":486},[480,733,529],{"class":486},[480,735,590],{"class":486},[480,737,738],{"class":507},"name",[480,740,504],{"class":486},[480,742,743],{"class":525},"]",[480,745,529],{"class":486},[480,747,749],{"class":748},"sHwdD","        \u002F\u002F chỉ giữ fields này\n",[480,751,753,756,758,761,763],{"class":482,"line":752},7,[480,754,755],{"class":490},"  watch",[480,757,580],{"class":486},[480,759,760],{"class":525}," [filter]",[480,762,529],{"class":486},[480,764,765],{"class":748},"             \u002F\u002F refetch khi filter đổi\n",[480,767,769,772,774,778,780],{"class":482,"line":768},8,[480,770,771],{"class":490},"  lazy",[480,773,580],{"class":486},[480,775,777],{"class":776},"sfNiH"," false",[480,779,529],{"class":486},[480,781,782],{"class":748},"                 \u002F\u002F false = block navigation tới khi xong (default trong setup)\n",[480,784,786,789,791,794,796],{"class":482,"line":785},9,[480,787,788],{"class":490},"  server",[480,790,580],{"class":486},[480,792,793],{"class":776}," true",[480,795,529],{"class":486},[480,797,798],{"class":748},"                \u002F\u002F run trên server (default)\n",[480,800,802,805,807,809,811],{"class":482,"line":801},10,[480,803,804],{"class":490},"  immediate",[480,806,580],{"class":486},[480,808,793],{"class":776},[480,810,529],{"class":486},[480,812,813],{"class":748},"             \u002F\u002F gọi luôn (default)\n",[480,815,817,820,822,824,827,829,831],{"class":482,"line":816},11,[480,818,819],{"class":490},"  key",[480,821,580],{"class":486},[480,823,590],{"class":486},[480,825,826],{"class":507},"users-hcm",[480,828,504],{"class":486},[480,830,529],{"class":486},[480,832,833],{"class":748},"            \u002F\u002F custom cache key\n",[480,835,837,839,841],{"class":482,"line":836},12,[480,838,545],{"class":486},[480,840,645],{"class":525},[480,842,843],{"class":486},";\n",[480,845,847,850,852],{"class":482,"line":846},13,[480,848,849],{"class":486},"\u003C\u002F",[480,851,491],{"class":490},[480,853,513],{"class":486},[480,855,857],{"class":482,"line":856},14,[480,858,860],{"emptyLinePlaceholder":859},true,"\n",[480,862,864,866,869],{"class":482,"line":863},15,[480,865,487],{"class":486},[480,867,868],{"class":490},"template",[480,870,513],{"class":486},[480,872,874,877,880,883,885,887,890,892,895,898,900,902],{"class":482,"line":873},16,[480,875,876],{"class":486},"  \u003C",[480,878,879],{"class":490},"div",[480,881,882],{"class":494}," v-if",[480,884,501],{"class":486},[480,886,504],{"class":486},[480,888,889],{"class":507},"pending",[480,891,504],{"class":486},[480,893,894],{"class":486},">",[480,896,897],{"class":525},"Loading...",[480,899,849],{"class":486},[480,901,879],{"class":490},[480,903,513],{"class":486},[480,905,907,909,911,914,916,918,921,923,925,928,930,932],{"class":482,"line":906},17,[480,908,876],{"class":486},[480,910,879],{"class":490},[480,912,913],{"class":494}," v-else-if",[480,915,501],{"class":486},[480,917,504],{"class":486},[480,919,920],{"class":507},"error",[480,922,504],{"class":486},[480,924,894],{"class":486},[480,926,927],{"class":525},"Error: {{ error.message }}",[480,929,849],{"class":486},[480,931,879],{"class":490},[480,933,513],{"class":486},[480,935,937,939,942,945],{"class":482,"line":936},18,[480,938,876],{"class":486},[480,940,941],{"class":490},"ul",[480,943,944],{"class":494}," v-else",[480,946,513],{"class":486},[480,948,950,953,956,959,961,963,966,968,971,973,975,978,980,982,985,987,989],{"class":482,"line":949},19,[480,951,952],{"class":486},"    \u003C",[480,954,955],{"class":490},"li",[480,957,958],{"class":494}," v-for",[480,960,501],{"class":486},[480,962,504],{"class":486},[480,964,965],{"class":507},"u in data",[480,967,504],{"class":486},[480,969,970],{"class":494}," :key",[480,972,501],{"class":486},[480,974,504],{"class":486},[480,976,977],{"class":507},"u.id",[480,979,504],{"class":486},[480,981,894],{"class":486},[480,983,984],{"class":525},"{{ u.name }}",[480,986,849],{"class":486},[480,988,955],{"class":490},[480,990,513],{"class":486},[480,992,994,997,999],{"class":482,"line":993},20,[480,995,996],{"class":486},"  \u003C\u002F",[480,998,941],{"class":490},[480,1000,513],{"class":486},[480,1002,1004,1006,1009,1012,1014,1016,1019,1021,1023,1026,1028,1030],{"class":482,"line":1003},21,[480,1005,876],{"class":486},[480,1007,1008],{"class":490},"button",[480,1010,1011],{"class":494}," @click",[480,1013,501],{"class":486},[480,1015,504],{"class":486},[480,1017,1018],{"class":507},"refresh()",[480,1020,504],{"class":486},[480,1022,894],{"class":486},[480,1024,1025],{"class":525},"Reload",[480,1027,849],{"class":486},[480,1029,1008],{"class":490},[480,1031,513],{"class":486},[480,1033,1035,1037,1039],{"class":482,"line":1034},22,[480,1036,849],{"class":486},[480,1038,868],{"class":490},[480,1040,513],{"class":486},[467,1042,1044],{"id":1043},"useasyncdata-linh-hoạt-hơn","useAsyncData — linh hoạt hơn",[471,1046,1048],{"className":473,"code":1047,"language":475,"meta":476,"style":476},"\u003Cscript setup lang=\"ts\">\n\u002F\u002F Combine nhiều fetch\nconst { data } = await useAsyncData(\"dashboard\", async () => {\n  const [users, orders, stats] = await Promise.all([\n    $fetch(\"\u002Fapi\u002Fusers\"),\n    $fetch(\"\u002Fapi\u002Forders\"),\n    $fetch(\"\u002Fapi\u002Fstats\"),\n  ]);\n  return { users, orders, stats };\n});\n\u003C\u002Fscript>\n",[432,1049,1050,1070,1075,1113,1150,1167,1184,1201,1208,1229,1237],{"__ignoreMap":476},[480,1051,1052,1054,1056,1058,1060,1062,1064,1066,1068],{"class":482,"line":483},[480,1053,487],{"class":486},[480,1055,491],{"class":490},[480,1057,495],{"class":494},[480,1059,498],{"class":494},[480,1061,501],{"class":486},[480,1063,504],{"class":486},[480,1065,508],{"class":507},[480,1067,504],{"class":486},[480,1069,513],{"class":486},[480,1071,1072],{"class":482,"line":516},[480,1073,1074],{"class":748},"\u002F\u002F Combine nhiều fetch\n",[480,1076,1077,1079,1081,1084,1086,1088,1090,1093,1095,1097,1100,1102,1104,1107,1109,1111],{"class":482,"line":574},[480,1078,519],{"class":494},[480,1080,522],{"class":486},[480,1082,1083],{"class":525}," data ",[480,1085,545],{"class":486},[480,1087,548],{"class":486},[480,1089,552],{"class":551},[480,1091,1092],{"class":555}," useAsyncData",[480,1094,559],{"class":525},[480,1096,504],{"class":486},[480,1098,1099],{"class":507},"dashboard",[480,1101,504],{"class":486},[480,1103,529],{"class":486},[480,1105,1106],{"class":494}," async",[480,1108,609],{"class":486},[480,1110,612],{"class":494},[480,1112,571],{"class":486},[480,1114,1115,1118,1120,1123,1125,1128,1130,1133,1135,1137,1139,1142,1144,1147],{"class":482,"line":601},[480,1116,1117],{"class":494},"  const",[480,1119,724],{"class":486},[480,1121,1122],{"class":525},"users",[480,1124,529],{"class":486},[480,1126,1127],{"class":525}," orders",[480,1129,529],{"class":486},[480,1131,1132],{"class":525}," stats",[480,1134,743],{"class":486},[480,1136,548],{"class":486},[480,1138,552],{"class":551},[480,1140,1141],{"class":638}," Promise",[480,1143,653],{"class":486},[480,1145,1146],{"class":555},"all",[480,1148,1149],{"class":490},"([\n",[480,1151,1152,1155,1157,1159,1161,1163,1165],{"class":482,"line":621},[480,1153,1154],{"class":555},"    $fetch",[480,1156,559],{"class":490},[480,1158,504],{"class":486},[480,1160,564],{"class":507},[480,1162,504],{"class":486},[480,1164,645],{"class":490},[480,1166,618],{"class":486},[480,1168,1169,1171,1173,1175,1178,1180,1182],{"class":482,"line":716},[480,1170,1154],{"class":555},[480,1172,559],{"class":490},[480,1174,504],{"class":486},[480,1176,1177],{"class":507},"\u002Fapi\u002Forders",[480,1179,504],{"class":486},[480,1181,645],{"class":490},[480,1183,618],{"class":486},[480,1185,1186,1188,1190,1192,1195,1197,1199],{"class":482,"line":752},[480,1187,1154],{"class":555},[480,1189,559],{"class":490},[480,1191,504],{"class":486},[480,1193,1194],{"class":507},"\u002Fapi\u002Fstats",[480,1196,504],{"class":486},[480,1198,645],{"class":490},[480,1200,618],{"class":486},[480,1202,1203,1206],{"class":482,"line":768},[480,1204,1205],{"class":490},"  ])",[480,1207,843],{"class":486},[480,1209,1210,1213,1215,1218,1220,1222,1224,1226],{"class":482,"line":785},[480,1211,1212],{"class":551},"  return",[480,1214,522],{"class":486},[480,1216,1217],{"class":525}," users",[480,1219,529],{"class":486},[480,1221,1127],{"class":525},[480,1223,529],{"class":486},[480,1225,1132],{"class":525},[480,1227,1228],{"class":486}," };\n",[480,1230,1231,1233,1235],{"class":482,"line":801},[480,1232,545],{"class":486},[480,1234,645],{"class":525},[480,1236,843],{"class":486},[480,1238,1239,1241,1243],{"class":482,"line":816},[480,1240,849],{"class":486},[480,1242,491],{"class":490},[480,1244,513],{"class":486},[467,1246,1248],{"id":1247},"fetch-imperative","$fetch — imperative",[471,1250,1252],{"className":473,"code":1251,"language":475,"meta":476,"style":476},"\u003Cscript setup lang=\"ts\">\nasync function deleteUser(id: number) {\n  await $fetch(`\u002Fapi\u002Fusers\u002F${id}`, { method: \"DELETE\" });\n  await refresh();              \u002F\u002F refetch useFetch caller\n}\n\nasync function createUser(form: UserForm) {\n  return await $fetch(\"\u002Fapi\u002Fusers\", {\n    method: \"POST\",\n    body: form,\n  });\n}\n\u003C\u002Fscript>\n",[432,1253,1254,1274,1298,1343,1359,1364,1368,1391,1411,1427,1439,1448,1452],{"__ignoreMap":476},[480,1255,1256,1258,1260,1262,1264,1266,1268,1270,1272],{"class":482,"line":483},[480,1257,487],{"class":486},[480,1259,491],{"class":490},[480,1261,495],{"class":494},[480,1263,498],{"class":494},[480,1265,501],{"class":486},[480,1267,504],{"class":486},[480,1269,508],{"class":507},[480,1271,504],{"class":486},[480,1273,513],{"class":486},[480,1275,1276,1279,1282,1285,1287,1289,1291,1294,1296],{"class":482,"line":516},[480,1277,1278],{"class":494},"async",[480,1280,1281],{"class":494}," function",[480,1283,1284],{"class":555}," deleteUser",[480,1286,559],{"class":486},[480,1288,729],{"class":632},[480,1290,580],{"class":486},[480,1292,1293],{"class":638}," number",[480,1295,645],{"class":486},[480,1297,571],{"class":486},[480,1299,1300,1303,1306,1308,1311,1314,1317,1319,1321,1323,1325,1328,1330,1332,1335,1337,1339,1341],{"class":482,"line":574},[480,1301,1302],{"class":551},"  await",[480,1304,1305],{"class":555}," $fetch",[480,1307,559],{"class":490},[480,1309,1310],{"class":486},"`",[480,1312,1313],{"class":507},"\u002Fapi\u002Fusers\u002F",[480,1315,1316],{"class":486},"${",[480,1318,729],{"class":525},[480,1320,705],{"class":486},[480,1322,529],{"class":486},[480,1324,522],{"class":486},[480,1326,1327],{"class":490}," method",[480,1329,580],{"class":486},[480,1331,590],{"class":486},[480,1333,1334],{"class":507},"DELETE",[480,1336,504],{"class":486},[480,1338,708],{"class":486},[480,1340,645],{"class":490},[480,1342,843],{"class":486},[480,1344,1345,1347,1350,1353,1356],{"class":482,"line":601},[480,1346,1302],{"class":551},[480,1348,1349],{"class":555}," refresh",[480,1351,1352],{"class":490},"()",[480,1354,1355],{"class":486},";",[480,1357,1358],{"class":748},"              \u002F\u002F refetch useFetch caller\n",[480,1360,1361],{"class":482,"line":621},[480,1362,1363],{"class":486},"}\n",[480,1365,1366],{"class":482,"line":716},[480,1367,860],{"emptyLinePlaceholder":859},[480,1369,1370,1372,1374,1377,1379,1382,1384,1387,1389],{"class":482,"line":752},[480,1371,1278],{"class":494},[480,1373,1281],{"class":494},[480,1375,1376],{"class":555}," createUser",[480,1378,559],{"class":486},[480,1380,1381],{"class":632},"form",[480,1383,580],{"class":486},[480,1385,1386],{"class":638}," UserForm",[480,1388,645],{"class":486},[480,1390,571],{"class":486},[480,1392,1393,1395,1397,1399,1401,1403,1405,1407,1409],{"class":482,"line":768},[480,1394,1212],{"class":551},[480,1396,552],{"class":551},[480,1398,1305],{"class":555},[480,1400,559],{"class":490},[480,1402,504],{"class":486},[480,1404,564],{"class":507},[480,1406,504],{"class":486},[480,1408,529],{"class":486},[480,1410,571],{"class":486},[480,1412,1413,1416,1418,1420,1423,1425],{"class":482,"line":785},[480,1414,1415],{"class":490},"    method",[480,1417,580],{"class":486},[480,1419,590],{"class":486},[480,1421,1422],{"class":507},"POST",[480,1424,504],{"class":486},[480,1426,618],{"class":486},[480,1428,1429,1432,1434,1437],{"class":482,"line":801},[480,1430,1431],{"class":490},"    body",[480,1433,580],{"class":486},[480,1435,1436],{"class":525}," form",[480,1438,618],{"class":486},[480,1440,1441,1444,1446],{"class":482,"line":816},[480,1442,1443],{"class":486},"  }",[480,1445,645],{"class":490},[480,1447,843],{"class":486},[480,1449,1450],{"class":482,"line":836},[480,1451,1363],{"class":486},[480,1453,1454,1456,1458],{"class":482,"line":846},[480,1455,849],{"class":486},[480,1457,491],{"class":490},[480,1459,513],{"class":486},[1461,1462,1465,1482],"callout",{"color":1463,"icon":1464},"warning","i-lucide-star",[1466,1467,1468,1469,1473,1474],"p",{},"⭐ ",[1470,1471,1472],"strong",{},"Câu PV cốt lõi:"," ",[1475,1476,504,1477,435,1479,1481],"em",{},[432,1478,434],{},[432,1480,441],{}," khác gì?\"",[1483,1484,1485,1490],"blockquote",{},[1466,1486,504,1487,1489],{},[432,1488,434],{}," SSR-aware — chạy 1 lần ở server, kết quả serialize vào HTML, client hydrate KHÔNG chạy lại. Reactive (data, pending, error). Có cache theo key.",[1466,1491,1492,1494],{},[432,1493,441],{}," là Promise thường — chạy mọi nơi gọi, không cache, không SSR-aware. Dùng cho event handler (button click, form submit). Cả 2 dùng ofetch underneath.\"",[427,1496,1498],{"id":1497},"_2-nuxt-4-data-fetching-changes","2. Nuxt 4 — Data fetching changes",[467,1500,1502],{"id":1501},"auto-sharing-same-key","Auto sharing same key",[471,1504,1506],{"className":473,"code":1505,"language":475,"meta":476,"style":476},"\u003C!-- ComponentA.vue -->\n\u003Cscript setup>\nconst { data: users } = await useFetch(\"\u002Fapi\u002Fusers\", { key: \"users-list\" });\n\u003C\u002Fscript>\n\n\u003C!-- ComponentB.vue -->\n\u003Cscript setup>\nconst { data: users } = await useFetch(\"\u002Fapi\u002Fusers\", { key: \"users-list\" });\n\u002F\u002F Cùng key → chia sẻ data + cache, chỉ fetch 1 lần\n\u003C\u002Fscript>\n",[432,1507,1508,1513,1523,1574,1582,1586,1591,1601,1649,1654],{"__ignoreMap":476},[480,1509,1510],{"class":482,"line":483},[480,1511,1512],{"class":748},"\u003C!-- ComponentA.vue -->\n",[480,1514,1515,1517,1519,1521],{"class":482,"line":516},[480,1516,487],{"class":486},[480,1518,491],{"class":490},[480,1520,495],{"class":494},[480,1522,513],{"class":486},[480,1524,1525,1527,1529,1531,1533,1536,1538,1540,1542,1544,1546,1548,1550,1552,1554,1556,1559,1561,1563,1566,1568,1570,1572],{"class":482,"line":574},[480,1526,519],{"class":494},[480,1528,522],{"class":486},[480,1530,526],{"class":490},[480,1532,580],{"class":486},[480,1534,1535],{"class":525}," users ",[480,1537,545],{"class":486},[480,1539,548],{"class":486},[480,1541,552],{"class":551},[480,1543,556],{"class":555},[480,1545,559],{"class":525},[480,1547,504],{"class":486},[480,1549,564],{"class":507},[480,1551,504],{"class":486},[480,1553,529],{"class":486},[480,1555,522],{"class":486},[480,1557,1558],{"class":490}," key",[480,1560,580],{"class":486},[480,1562,590],{"class":486},[480,1564,1565],{"class":507},"users-list",[480,1567,504],{"class":486},[480,1569,708],{"class":486},[480,1571,645],{"class":525},[480,1573,843],{"class":486},[480,1575,1576,1578,1580],{"class":482,"line":601},[480,1577,849],{"class":486},[480,1579,491],{"class":490},[480,1581,513],{"class":486},[480,1583,1584],{"class":482,"line":621},[480,1585,860],{"emptyLinePlaceholder":859},[480,1587,1588],{"class":482,"line":716},[480,1589,1590],{"class":748},"\u003C!-- ComponentB.vue -->\n",[480,1592,1593,1595,1597,1599],{"class":482,"line":752},[480,1594,487],{"class":486},[480,1596,491],{"class":490},[480,1598,495],{"class":494},[480,1600,513],{"class":486},[480,1602,1603,1605,1607,1609,1611,1613,1615,1617,1619,1621,1623,1625,1627,1629,1631,1633,1635,1637,1639,1641,1643,1645,1647],{"class":482,"line":768},[480,1604,519],{"class":494},[480,1606,522],{"class":486},[480,1608,526],{"class":490},[480,1610,580],{"class":486},[480,1612,1535],{"class":525},[480,1614,545],{"class":486},[480,1616,548],{"class":486},[480,1618,552],{"class":551},[480,1620,556],{"class":555},[480,1622,559],{"class":525},[480,1624,504],{"class":486},[480,1626,564],{"class":507},[480,1628,504],{"class":486},[480,1630,529],{"class":486},[480,1632,522],{"class":486},[480,1634,1558],{"class":490},[480,1636,580],{"class":486},[480,1638,590],{"class":486},[480,1640,1565],{"class":507},[480,1642,504],{"class":486},[480,1644,708],{"class":486},[480,1646,645],{"class":525},[480,1648,843],{"class":486},[480,1650,1651],{"class":482,"line":785},[480,1652,1653],{"class":748},"\u002F\u002F Cùng key → chia sẻ data + cache, chỉ fetch 1 lần\n",[480,1655,1656,1658,1660],{"class":482,"line":801},[480,1657,849],{"class":486},[480,1659,491],{"class":490},[480,1661,513],{"class":486},[467,1663,1665],{"id":1664},"auto-cleanup","Auto cleanup",[1466,1667,1668],{},"Component unmount → data cleanup khỏi memory. Trong Nuxt 3 phải tự lo.",[467,1670,1672],{"id":1671},"clear-on-refetch-vs-swr-cũ","Clear on refetch (vs SWR cũ)",[471,1674,1678],{"className":1675,"code":1676,"language":1677,"meta":476,"style":476},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F Nuxt 4\nconst { data, pending } = useFetch(\"\u002Fapi\u002Fx\");\nawait refresh();          \u002F\u002F pending = true, data = null\n\u002F\u002F → Sau khi xong: data = new value\n\n\u002F\u002F Nuxt 3 behavior:\n\u002F\u002F pending = true, data = OLD value vẫn giữ\n","typescript",[432,1679,1680,1685,1717,1731,1736,1740,1745],{"__ignoreMap":476},[480,1681,1682],{"class":482,"line":483},[480,1683,1684],{"class":748},"\u002F\u002F Nuxt 4\n",[480,1686,1687,1689,1691,1693,1695,1698,1700,1702,1704,1706,1708,1711,1713,1715],{"class":482,"line":516},[480,1688,519],{"class":494},[480,1690,522],{"class":486},[480,1692,526],{"class":525},[480,1694,529],{"class":486},[480,1696,1697],{"class":525}," pending ",[480,1699,545],{"class":486},[480,1701,548],{"class":486},[480,1703,556],{"class":555},[480,1705,559],{"class":525},[480,1707,504],{"class":486},[480,1709,1710],{"class":507},"\u002Fapi\u002Fx",[480,1712,504],{"class":486},[480,1714,645],{"class":525},[480,1716,843],{"class":486},[480,1718,1719,1722,1724,1726,1728],{"class":482,"line":574},[480,1720,1721],{"class":551},"await",[480,1723,1349],{"class":555},[480,1725,1352],{"class":525},[480,1727,1355],{"class":486},[480,1729,1730],{"class":748},"          \u002F\u002F pending = true, data = null\n",[480,1732,1733],{"class":482,"line":601},[480,1734,1735],{"class":748},"\u002F\u002F → Sau khi xong: data = new value\n",[480,1737,1738],{"class":482,"line":621},[480,1739,860],{"emptyLinePlaceholder":859},[480,1741,1742],{"class":482,"line":716},[480,1743,1744],{"class":748},"\u002F\u002F Nuxt 3 behavior:\n",[480,1746,1747],{"class":482,"line":752},[480,1748,1749],{"class":748},"\u002F\u002F pending = true, data = OLD value vẫn giữ\n",[1466,1751,1752],{},"Muốn giữ data cũ trong Nuxt 4:",[471,1754,1756],{"className":1675,"code":1755,"language":1677,"meta":476,"style":476},"useFetch(\"\u002Fapi\u002Fx\", {\n  getCachedData(key) {\n    return useNuxtApp().payload.data[key] ?? useNuxtApp().static.data[key];\n  }\n});\n",[432,1757,1758,1774,1788,1840,1845],{"__ignoreMap":476},[480,1759,1760,1762,1764,1766,1768,1770,1772],{"class":482,"line":483},[480,1761,434],{"class":555},[480,1763,559],{"class":525},[480,1765,504],{"class":486},[480,1767,1710],{"class":507},[480,1769,504],{"class":486},[480,1771,529],{"class":486},[480,1773,571],{"class":486},[480,1775,1776,1779,1781,1784,1786],{"class":482,"line":516},[480,1777,1778],{"class":490},"  getCachedData",[480,1780,559],{"class":486},[480,1782,1783],{"class":632},"key",[480,1785,645],{"class":486},[480,1787,571],{"class":486},[480,1789,1790,1793,1796,1798,1800,1803,1805,1808,1811,1813,1816,1819,1821,1823,1825,1828,1830,1832,1834,1836,1838],{"class":482,"line":574},[480,1791,1792],{"class":551},"    return",[480,1794,1795],{"class":555}," useNuxtApp",[480,1797,1352],{"class":490},[480,1799,653],{"class":486},[480,1801,1802],{"class":525},"payload",[480,1804,653],{"class":486},[480,1806,1807],{"class":525},"data",[480,1809,1810],{"class":490},"[",[480,1812,1783],{"class":525},[480,1814,1815],{"class":490},"] ",[480,1817,1818],{"class":486},"??",[480,1820,1795],{"class":555},[480,1822,1352],{"class":490},[480,1824,653],{"class":486},[480,1826,1827],{"class":525},"static",[480,1829,653],{"class":486},[480,1831,1807],{"class":525},[480,1833,1810],{"class":490},[480,1835,1783],{"class":525},[480,1837,743],{"class":490},[480,1839,843],{"class":486},[480,1841,1842],{"class":482,"line":601},[480,1843,1844],{"class":486},"  }\n",[480,1846,1847,1849,1851],{"class":482,"line":621},[480,1848,545],{"class":486},[480,1850,645],{"class":525},[480,1852,843],{"class":486},[467,1854,1856],{"id":1855},"abort-control-42","Abort control (4.2+)",[471,1858,1860],{"className":1675,"code":1859,"language":1677,"meta":476,"style":476},"const { data } = useFetch(\"\u002Fapi\u002Fheavy\", {\n  signal: controller.signal,\n});\n\u002F\u002F Component unmount → tự abort request\n",[432,1861,1862,1889,1906,1914],{"__ignoreMap":476},[480,1863,1864,1866,1868,1870,1872,1874,1876,1878,1880,1883,1885,1887],{"class":482,"line":483},[480,1865,519],{"class":494},[480,1867,522],{"class":486},[480,1869,1083],{"class":525},[480,1871,545],{"class":486},[480,1873,548],{"class":486},[480,1875,556],{"class":555},[480,1877,559],{"class":525},[480,1879,504],{"class":486},[480,1881,1882],{"class":507},"\u002Fapi\u002Fheavy",[480,1884,504],{"class":486},[480,1886,529],{"class":486},[480,1888,571],{"class":486},[480,1890,1891,1894,1896,1899,1901,1904],{"class":482,"line":516},[480,1892,1893],{"class":490},"  signal",[480,1895,580],{"class":486},[480,1897,1898],{"class":525}," controller",[480,1900,653],{"class":486},[480,1902,1903],{"class":525},"signal",[480,1905,618],{"class":486},[480,1907,1908,1910,1912],{"class":482,"line":574},[480,1909,545],{"class":486},[480,1911,645],{"class":525},[480,1913,843],{"class":486},[480,1915,1916],{"class":482,"line":601},[480,1917,1918],{"class":748},"\u002F\u002F Component unmount → tự abort request\n",[427,1920,1922],{"id":1921},"_3-state-management","3. State management",[467,1924,1926,1929],{"id":1925},"usestate-ssr-safe-global-state",[432,1927,1928],{},"useState"," — SSR-safe global state",[471,1931,1933],{"className":1675,"code":1932,"language":1677,"meta":476,"style":476},"\u002F\u002F app\u002Fcomposables\u002FuseAuth.ts\nexport const useAuth = () => useState(\"auth\", () => ({\n  user: null as User | null,\n  token: \"\" as string,\n}));\n",[432,1934,1935,1940,1980,2002,2019],{"__ignoreMap":476},[480,1936,1937],{"class":482,"line":483},[480,1938,1939],{"class":748},"\u002F\u002F app\u002Fcomposables\u002FuseAuth.ts\n",[480,1941,1942,1945,1948,1951,1953,1955,1957,1960,1962,1964,1967,1969,1971,1973,1975,1977],{"class":482,"line":516},[480,1943,1944],{"class":551},"export",[480,1946,1947],{"class":494}," const",[480,1949,1950],{"class":525}," useAuth ",[480,1952,501],{"class":486},[480,1954,609],{"class":486},[480,1956,612],{"class":494},[480,1958,1959],{"class":555}," useState",[480,1961,559],{"class":525},[480,1963,504],{"class":486},[480,1965,1966],{"class":507},"auth",[480,1968,504],{"class":486},[480,1970,529],{"class":486},[480,1972,609],{"class":486},[480,1974,612],{"class":494},[480,1976,629],{"class":525},[480,1978,1979],{"class":486},"{\n",[480,1981,1982,1985,1987,1990,1993,1995,1998,2000],{"class":482,"line":574},[480,1983,1984],{"class":490},"  user",[480,1986,580],{"class":486},[480,1988,1989],{"class":486}," null",[480,1991,1992],{"class":551}," as",[480,1994,639],{"class":638},[480,1996,1997],{"class":486}," |",[480,1999,1989],{"class":638},[480,2001,618],{"class":486},[480,2003,2004,2007,2009,2012,2014,2017],{"class":482,"line":601},[480,2005,2006],{"class":490},"  token",[480,2008,580],{"class":486},[480,2010,2011],{"class":486}," \"\"",[480,2013,1992],{"class":551},[480,2015,2016],{"class":638}," string",[480,2018,618],{"class":486},[480,2020,2021,2023,2025],{"class":482,"line":621},[480,2022,545],{"class":486},[480,2024,711],{"class":525},[480,2026,843],{"class":486},[471,2028,2030],{"className":473,"code":2029,"language":475,"meta":476,"style":476},"\u003Cscript setup>\nconst auth = useAuth();\nconsole.log(auth.value.user);    \u002F\u002F shared across components, SSR-safe\n\u003C\u002Fscript>\n",[432,2031,2032,2042,2058,2086],{"__ignoreMap":476},[480,2033,2034,2036,2038,2040],{"class":482,"line":483},[480,2035,487],{"class":486},[480,2037,491],{"class":490},[480,2039,495],{"class":494},[480,2041,513],{"class":486},[480,2043,2044,2046,2049,2051,2054,2056],{"class":482,"line":516},[480,2045,519],{"class":494},[480,2047,2048],{"class":525}," auth ",[480,2050,501],{"class":486},[480,2052,2053],{"class":555}," useAuth",[480,2055,1352],{"class":525},[480,2057,843],{"class":486},[480,2059,2060,2063,2065,2068,2071,2073,2076,2078,2081,2083],{"class":482,"line":574},[480,2061,2062],{"class":525},"console",[480,2064,653],{"class":486},[480,2066,2067],{"class":555},"log",[480,2069,2070],{"class":525},"(auth",[480,2072,653],{"class":486},[480,2074,2075],{"class":525},"value",[480,2077,653],{"class":486},[480,2079,2080],{"class":525},"user)",[480,2082,1355],{"class":486},[480,2084,2085],{"class":748},"    \u002F\u002F shared across components, SSR-safe\n",[480,2087,2088,2090,2092],{"class":482,"line":601},[480,2089,849],{"class":486},[480,2091,491],{"class":490},[480,2093,513],{"class":486},[1461,2095,2098,2109],{"color":2096,"icon":2097},"primary","i-lucide-info",[1466,2099,2100],{},[1470,2101,2102,2104,2105,2108],{},[432,2103,1928],{}," khác ",[432,2106,2107],{},"ref"," ngoài composable?",[941,2110,2111,2120],{},[955,2112,2113,2115,2116,2119],{},[432,2114,2107],{}," ngoài composable: shared module-level — ",[1470,2117,2118],{},"không SSR-safe"," (request này thấy state request trước).",[955,2121,2122,2124],{},[432,2123,1928],{},": scope theo request ở SSR, share trong client navigation.",[467,2126,2128],{"id":2127},"pinia-store-lớn","Pinia — store lớn",[471,2130,2134],{"className":2131,"code":2132,"language":2133,"meta":476,"style":476},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm add @pinia\u002Fnuxt\n","bash",[432,2135,2136],{"__ignoreMap":476},[480,2137,2138,2141,2144],{"class":482,"line":483},[480,2139,2140],{"class":638},"pnpm",[480,2142,2143],{"class":507}," add",[480,2145,2146],{"class":507}," @pinia\u002Fnuxt\n",[471,2148,2150],{"className":1675,"code":2149,"language":1677,"meta":476,"style":476},"\u002F\u002F nuxt.config.ts\nmodules: ['@pinia\u002Fnuxt']\n\n\u002F\u002F app\u002Fstores\u002Fuser.ts\nexport const useUserStore = defineStore(\"user\", {\n  state: () => ({ id: 0, name: \"\" }),\n  getters: {\n    isLoggedIn: (s) => s.id > 0,\n  },\n  actions: {\n    async login(email: string, pwd: string) {\n      const res = await $fetch(\"\u002Fapi\u002Flogin\", { method: \"POST\", body: { email, pwd } });\n      this.id = res.id;\n      this.name = res.name;\n    },\n  },\n});\n",[432,2151,2152,2157,2177,2181,2186,2213,2252,2261,2291,2296,2305,2335,2396,2413,2429,2434,2438],{"__ignoreMap":476},[480,2153,2154],{"class":482,"line":483},[480,2155,2156],{"class":748},"\u002F\u002F nuxt.config.ts\n",[480,2158,2159,2162,2164,2166,2169,2172,2174],{"class":482,"line":516},[480,2160,2161],{"class":638},"modules",[480,2163,580],{"class":486},[480,2165,724],{"class":525},[480,2167,2168],{"class":486},"'",[480,2170,2171],{"class":507},"@pinia\u002Fnuxt",[480,2173,2168],{"class":486},[480,2175,2176],{"class":525},"]\n",[480,2178,2179],{"class":482,"line":574},[480,2180,860],{"emptyLinePlaceholder":859},[480,2182,2183],{"class":482,"line":601},[480,2184,2185],{"class":748},"\u002F\u002F app\u002Fstores\u002Fuser.ts\n",[480,2187,2188,2190,2192,2195,2197,2200,2202,2204,2207,2209,2211],{"class":482,"line":621},[480,2189,1944],{"class":551},[480,2191,1947],{"class":494},[480,2193,2194],{"class":525}," useUserStore ",[480,2196,501],{"class":486},[480,2198,2199],{"class":555}," defineStore",[480,2201,559],{"class":525},[480,2203,504],{"class":486},[480,2205,2206],{"class":507},"user",[480,2208,504],{"class":486},[480,2210,529],{"class":486},[480,2212,571],{"class":486},[480,2214,2215,2218,2220,2222,2224,2226,2228,2231,2233,2237,2239,2242,2244,2246,2248,2250],{"class":482,"line":716},[480,2216,2217],{"class":555},"  state",[480,2219,580],{"class":486},[480,2221,609],{"class":486},[480,2223,612],{"class":494},[480,2225,629],{"class":525},[480,2227,668],{"class":486},[480,2229,2230],{"class":490}," id",[480,2232,580],{"class":486},[480,2234,2236],{"class":2235},"sbssI"," 0",[480,2238,529],{"class":486},[480,2240,2241],{"class":490}," name",[480,2243,580],{"class":486},[480,2245,2011],{"class":486},[480,2247,708],{"class":486},[480,2249,645],{"class":525},[480,2251,618],{"class":486},[480,2253,2254,2257,2259],{"class":482,"line":752},[480,2255,2256],{"class":490},"  getters",[480,2258,580],{"class":486},[480,2260,571],{"class":486},[480,2262,2263,2266,2268,2270,2273,2275,2277,2280,2282,2285,2287,2289],{"class":482,"line":768},[480,2264,2265],{"class":555},"    isLoggedIn",[480,2267,580],{"class":486},[480,2269,629],{"class":486},[480,2271,2272],{"class":632},"s",[480,2274,645],{"class":486},[480,2276,612],{"class":494},[480,2278,2279],{"class":525}," s",[480,2281,653],{"class":486},[480,2283,2284],{"class":525},"id ",[480,2286,894],{"class":486},[480,2288,2236],{"class":2235},[480,2290,618],{"class":486},[480,2292,2293],{"class":482,"line":785},[480,2294,2295],{"class":486},"  },\n",[480,2297,2298,2301,2303],{"class":482,"line":801},[480,2299,2300],{"class":490},"  actions",[480,2302,580],{"class":486},[480,2304,571],{"class":486},[480,2306,2307,2310,2313,2315,2318,2320,2322,2324,2327,2329,2331,2333],{"class":482,"line":816},[480,2308,2309],{"class":494},"    async",[480,2311,2312],{"class":490}," login",[480,2314,559],{"class":486},[480,2316,2317],{"class":632},"email",[480,2319,580],{"class":486},[480,2321,2016],{"class":638},[480,2323,529],{"class":486},[480,2325,2326],{"class":632}," pwd",[480,2328,580],{"class":486},[480,2330,2016],{"class":638},[480,2332,645],{"class":486},[480,2334,571],{"class":486},[480,2336,2337,2340,2343,2345,2347,2349,2351,2353,2356,2358,2360,2362,2364,2366,2368,2370,2372,2374,2377,2379,2381,2384,2386,2388,2390,2392,2394],{"class":482,"line":836},[480,2338,2339],{"class":494},"      const",[480,2341,2342],{"class":525}," res",[480,2344,548],{"class":486},[480,2346,552],{"class":551},[480,2348,1305],{"class":555},[480,2350,559],{"class":490},[480,2352,504],{"class":486},[480,2354,2355],{"class":507},"\u002Fapi\u002Flogin",[480,2357,504],{"class":486},[480,2359,529],{"class":486},[480,2361,522],{"class":486},[480,2363,1327],{"class":490},[480,2365,580],{"class":486},[480,2367,590],{"class":486},[480,2369,1422],{"class":507},[480,2371,504],{"class":486},[480,2373,529],{"class":486},[480,2375,2376],{"class":490}," body",[480,2378,580],{"class":486},[480,2380,522],{"class":486},[480,2382,2383],{"class":525}," email",[480,2385,529],{"class":486},[480,2387,2326],{"class":525},[480,2389,708],{"class":486},[480,2391,708],{"class":486},[480,2393,645],{"class":490},[480,2395,843],{"class":486},[480,2397,2398,2401,2403,2405,2407,2409,2411],{"class":482,"line":846},[480,2399,2400],{"class":486},"      this.",[480,2402,729],{"class":525},[480,2404,548],{"class":486},[480,2406,2342],{"class":525},[480,2408,653],{"class":486},[480,2410,729],{"class":525},[480,2412,843],{"class":486},[480,2414,2415,2417,2419,2421,2423,2425,2427],{"class":482,"line":856},[480,2416,2400],{"class":486},[480,2418,738],{"class":525},[480,2420,548],{"class":486},[480,2422,2342],{"class":525},[480,2424,653],{"class":486},[480,2426,738],{"class":525},[480,2428,843],{"class":486},[480,2430,2431],{"class":482,"line":863},[480,2432,2433],{"class":486},"    },\n",[480,2435,2436],{"class":482,"line":873},[480,2437,2295],{"class":486},[480,2439,2440,2442,2444],{"class":482,"line":906},[480,2441,545],{"class":486},[480,2443,645],{"class":525},[480,2445,843],{"class":486},[471,2447,2449],{"className":473,"code":2448,"language":475,"meta":476,"style":476},"\u003Cscript setup>\nconst userStore = useUserStore();\n\u003C\u002Fscript>\n",[432,2450,2451,2461,2477],{"__ignoreMap":476},[480,2452,2453,2455,2457,2459],{"class":482,"line":483},[480,2454,487],{"class":486},[480,2456,491],{"class":490},[480,2458,495],{"class":494},[480,2460,513],{"class":486},[480,2462,2463,2465,2468,2470,2473,2475],{"class":482,"line":516},[480,2464,519],{"class":494},[480,2466,2467],{"class":525}," userStore ",[480,2469,501],{"class":486},[480,2471,2472],{"class":555}," useUserStore",[480,2474,1352],{"class":525},[480,2476,843],{"class":486},[480,2478,2479,2481,2483],{"class":482,"line":574},[480,2480,849],{"class":486},[480,2482,491],{"class":490},[480,2484,513],{"class":486},[427,2486,2488],{"id":2487},"_4-server-api-routes-nitro","4. Server API routes — Nitro",[1466,2490,2491,2494,2495,2498],{},[432,2492,2493],{},"server\u002Fapi\u002F**"," tự thành endpoint. ",[1470,2496,2497],{},"Nitro"," là engine server-side.",[467,2500,2502],{"id":2501},"rest-endpoints","REST endpoints",[471,2504,2506],{"className":1675,"code":2505,"language":1677,"meta":476,"style":476},"\u002F\u002F server\u002Fapi\u002Fusers\u002Findex.get.ts → GET \u002Fapi\u002Fusers\nexport default defineEventHandler(async (event) => {\n  const { city } = getQuery(event);\n  \u002F\u002F ... query DB\n  return await db.users.findMany({ where: { city: String(city) } });\n});\n\n\u002F\u002F server\u002Fapi\u002Fusers\u002Findex.post.ts → POST \u002Fapi\u002Fusers\nexport default defineEventHandler(async (event) => {\n  const body = await readBody(event);\n  \u002F\u002F Validation\n  if (!body.email) {\n    throw createError({ statusCode: 400, message: \"Email required\" });\n  }\n  \u002F\u002F ... insert\n  setResponseStatus(event, 201);\n  return await db.users.create({ data: body });\n});\n\n\u002F\u002F server\u002Fapi\u002Fusers\u002F[id].get.ts → GET \u002Fapi\u002Fusers\u002F123\nexport default defineEventHandler(async (event) => {\n  const id = Number(getRouterParam(event, \"id\"));\n  const user = await db.users.findUnique({ where: { id } });\n  if (!user) throw createError({ statusCode: 404 });\n  return user;\n});\n\n\u002F\u002F server\u002Fapi\u002Fusers\u002F[id].delete.ts → DELETE \u002Fapi\u002Fusers\u002F123\nexport default defineEventHandler(async (event) => {\n  const id = Number(getRouterParam(event, \"id\"));\n  await db.users.delete({ where: { id } });\n  setResponseStatus(event, 204);\n});\n",[432,2507,2508,2513,2538,2561,2566,2618,2626,2630,2635,2657,2678,2683,2704,2744,2748,2753,2771,2804,2812,2816,2821,2843,2875,2918,2953,2962,2971,2976,2982,3005,3036,3072,3090],{"__ignoreMap":476},[480,2509,2510],{"class":482,"line":483},[480,2511,2512],{"class":748},"\u002F\u002F server\u002Fapi\u002Fusers\u002Findex.get.ts → GET \u002Fapi\u002Fusers\n",[480,2514,2515,2517,2520,2523,2525,2527,2529,2532,2534,2536],{"class":482,"line":516},[480,2516,1944],{"class":551},[480,2518,2519],{"class":551}," default",[480,2521,2522],{"class":555}," defineEventHandler",[480,2524,559],{"class":525},[480,2526,1278],{"class":494},[480,2528,629],{"class":486},[480,2530,2531],{"class":632},"event",[480,2533,645],{"class":486},[480,2535,612],{"class":494},[480,2537,571],{"class":486},[480,2539,2540,2542,2544,2546,2548,2550,2553,2555,2557,2559],{"class":482,"line":574},[480,2541,1117],{"class":494},[480,2543,522],{"class":486},[480,2545,585],{"class":525},[480,2547,708],{"class":486},[480,2549,548],{"class":486},[480,2551,2552],{"class":555}," getQuery",[480,2554,559],{"class":490},[480,2556,2531],{"class":525},[480,2558,645],{"class":490},[480,2560,843],{"class":486},[480,2562,2563],{"class":482,"line":601},[480,2564,2565],{"class":748},"  \u002F\u002F ... query DB\n",[480,2567,2568,2570,2572,2575,2577,2579,2581,2584,2586,2588,2591,2593,2595,2597,2599,2602,2604,2607,2610,2612,2614,2616],{"class":482,"line":621},[480,2569,1212],{"class":551},[480,2571,552],{"class":551},[480,2573,2574],{"class":525}," db",[480,2576,653],{"class":486},[480,2578,1122],{"class":525},[480,2580,653],{"class":486},[480,2582,2583],{"class":555},"findMany",[480,2585,559],{"class":490},[480,2587,668],{"class":486},[480,2589,2590],{"class":490}," where",[480,2592,580],{"class":486},[480,2594,522],{"class":486},[480,2596,585],{"class":490},[480,2598,580],{"class":486},[480,2600,2601],{"class":555}," String",[480,2603,559],{"class":490},[480,2605,2606],{"class":525},"city",[480,2608,2609],{"class":490},") ",[480,2611,545],{"class":486},[480,2613,708],{"class":486},[480,2615,645],{"class":490},[480,2617,843],{"class":486},[480,2619,2620,2622,2624],{"class":482,"line":716},[480,2621,545],{"class":486},[480,2623,645],{"class":525},[480,2625,843],{"class":486},[480,2627,2628],{"class":482,"line":752},[480,2629,860],{"emptyLinePlaceholder":859},[480,2631,2632],{"class":482,"line":768},[480,2633,2634],{"class":748},"\u002F\u002F server\u002Fapi\u002Fusers\u002Findex.post.ts → POST \u002Fapi\u002Fusers\n",[480,2636,2637,2639,2641,2643,2645,2647,2649,2651,2653,2655],{"class":482,"line":785},[480,2638,1944],{"class":551},[480,2640,2519],{"class":551},[480,2642,2522],{"class":555},[480,2644,559],{"class":525},[480,2646,1278],{"class":494},[480,2648,629],{"class":486},[480,2650,2531],{"class":632},[480,2652,645],{"class":486},[480,2654,612],{"class":494},[480,2656,571],{"class":486},[480,2658,2659,2661,2663,2665,2667,2670,2672,2674,2676],{"class":482,"line":801},[480,2660,1117],{"class":494},[480,2662,2376],{"class":525},[480,2664,548],{"class":486},[480,2666,552],{"class":551},[480,2668,2669],{"class":555}," readBody",[480,2671,559],{"class":490},[480,2673,2531],{"class":525},[480,2675,645],{"class":490},[480,2677,843],{"class":486},[480,2679,2680],{"class":482,"line":816},[480,2681,2682],{"class":748},"  \u002F\u002F Validation\n",[480,2684,2685,2688,2690,2693,2696,2698,2700,2702],{"class":482,"line":836},[480,2686,2687],{"class":551},"  if",[480,2689,629],{"class":490},[480,2691,2692],{"class":486},"!",[480,2694,2695],{"class":525},"body",[480,2697,653],{"class":486},[480,2699,2317],{"class":525},[480,2701,2609],{"class":490},[480,2703,1979],{"class":486},[480,2705,2706,2709,2712,2714,2716,2719,2721,2724,2726,2729,2731,2733,2736,2738,2740,2742],{"class":482,"line":846},[480,2707,2708],{"class":551},"    throw",[480,2710,2711],{"class":555}," createError",[480,2713,559],{"class":490},[480,2715,668],{"class":486},[480,2717,2718],{"class":490}," statusCode",[480,2720,580],{"class":486},[480,2722,2723],{"class":2235}," 400",[480,2725,529],{"class":486},[480,2727,2728],{"class":490}," message",[480,2730,580],{"class":486},[480,2732,590],{"class":486},[480,2734,2735],{"class":507},"Email required",[480,2737,504],{"class":486},[480,2739,708],{"class":486},[480,2741,645],{"class":490},[480,2743,843],{"class":486},[480,2745,2746],{"class":482,"line":856},[480,2747,1844],{"class":486},[480,2749,2750],{"class":482,"line":863},[480,2751,2752],{"class":748},"  \u002F\u002F ... insert\n",[480,2754,2755,2758,2760,2762,2764,2767,2769],{"class":482,"line":873},[480,2756,2757],{"class":555},"  setResponseStatus",[480,2759,559],{"class":490},[480,2761,2531],{"class":525},[480,2763,529],{"class":486},[480,2765,2766],{"class":2235}," 201",[480,2768,645],{"class":490},[480,2770,843],{"class":486},[480,2772,2773,2775,2777,2779,2781,2783,2785,2788,2790,2792,2794,2796,2798,2800,2802],{"class":482,"line":906},[480,2774,1212],{"class":551},[480,2776,552],{"class":551},[480,2778,2574],{"class":525},[480,2780,653],{"class":486},[480,2782,1122],{"class":525},[480,2784,653],{"class":486},[480,2786,2787],{"class":555},"create",[480,2789,559],{"class":490},[480,2791,668],{"class":486},[480,2793,526],{"class":490},[480,2795,580],{"class":486},[480,2797,2376],{"class":525},[480,2799,708],{"class":486},[480,2801,645],{"class":490},[480,2803,843],{"class":486},[480,2805,2806,2808,2810],{"class":482,"line":936},[480,2807,545],{"class":486},[480,2809,645],{"class":525},[480,2811,843],{"class":486},[480,2813,2814],{"class":482,"line":949},[480,2815,860],{"emptyLinePlaceholder":859},[480,2817,2818],{"class":482,"line":993},[480,2819,2820],{"class":748},"\u002F\u002F server\u002Fapi\u002Fusers\u002F[id].get.ts → GET \u002Fapi\u002Fusers\u002F123\n",[480,2822,2823,2825,2827,2829,2831,2833,2835,2837,2839,2841],{"class":482,"line":1003},[480,2824,1944],{"class":551},[480,2826,2519],{"class":551},[480,2828,2522],{"class":555},[480,2830,559],{"class":525},[480,2832,1278],{"class":494},[480,2834,629],{"class":486},[480,2836,2531],{"class":632},[480,2838,645],{"class":486},[480,2840,612],{"class":494},[480,2842,571],{"class":486},[480,2844,2845,2847,2849,2851,2854,2856,2859,2861,2863,2865,2867,2869,2871,2873],{"class":482,"line":1034},[480,2846,1117],{"class":494},[480,2848,2230],{"class":525},[480,2850,548],{"class":486},[480,2852,2853],{"class":555}," Number",[480,2855,559],{"class":490},[480,2857,2858],{"class":555},"getRouterParam",[480,2860,559],{"class":490},[480,2862,2531],{"class":525},[480,2864,529],{"class":486},[480,2866,590],{"class":486},[480,2868,729],{"class":507},[480,2870,504],{"class":486},[480,2872,711],{"class":490},[480,2874,843],{"class":486},[480,2876,2878,2880,2883,2885,2887,2889,2891,2893,2895,2898,2900,2902,2904,2906,2908,2910,2912,2914,2916],{"class":482,"line":2877},23,[480,2879,1117],{"class":494},[480,2881,2882],{"class":525}," user",[480,2884,548],{"class":486},[480,2886,552],{"class":551},[480,2888,2574],{"class":525},[480,2890,653],{"class":486},[480,2892,1122],{"class":525},[480,2894,653],{"class":486},[480,2896,2897],{"class":555},"findUnique",[480,2899,559],{"class":490},[480,2901,668],{"class":486},[480,2903,2590],{"class":490},[480,2905,580],{"class":486},[480,2907,522],{"class":486},[480,2909,2230],{"class":525},[480,2911,708],{"class":486},[480,2913,708],{"class":486},[480,2915,645],{"class":490},[480,2917,843],{"class":486},[480,2919,2921,2923,2925,2927,2929,2931,2934,2936,2938,2940,2942,2944,2947,2949,2951],{"class":482,"line":2920},24,[480,2922,2687],{"class":551},[480,2924,629],{"class":490},[480,2926,2692],{"class":486},[480,2928,2206],{"class":525},[480,2930,2609],{"class":490},[480,2932,2933],{"class":551},"throw",[480,2935,2711],{"class":555},[480,2937,559],{"class":490},[480,2939,668],{"class":486},[480,2941,2718],{"class":490},[480,2943,580],{"class":486},[480,2945,2946],{"class":2235}," 404",[480,2948,708],{"class":486},[480,2950,645],{"class":490},[480,2952,843],{"class":486},[480,2954,2956,2958,2960],{"class":482,"line":2955},25,[480,2957,1212],{"class":551},[480,2959,2882],{"class":525},[480,2961,843],{"class":486},[480,2963,2965,2967,2969],{"class":482,"line":2964},26,[480,2966,545],{"class":486},[480,2968,645],{"class":525},[480,2970,843],{"class":486},[480,2972,2974],{"class":482,"line":2973},27,[480,2975,860],{"emptyLinePlaceholder":859},[480,2977,2979],{"class":482,"line":2978},28,[480,2980,2981],{"class":748},"\u002F\u002F server\u002Fapi\u002Fusers\u002F[id].delete.ts → DELETE \u002Fapi\u002Fusers\u002F123\n",[480,2983,2985,2987,2989,2991,2993,2995,2997,2999,3001,3003],{"class":482,"line":2984},29,[480,2986,1944],{"class":551},[480,2988,2519],{"class":551},[480,2990,2522],{"class":555},[480,2992,559],{"class":525},[480,2994,1278],{"class":494},[480,2996,629],{"class":486},[480,2998,2531],{"class":632},[480,3000,645],{"class":486},[480,3002,612],{"class":494},[480,3004,571],{"class":486},[480,3006,3008,3010,3012,3014,3016,3018,3020,3022,3024,3026,3028,3030,3032,3034],{"class":482,"line":3007},30,[480,3009,1117],{"class":494},[480,3011,2230],{"class":525},[480,3013,548],{"class":486},[480,3015,2853],{"class":555},[480,3017,559],{"class":490},[480,3019,2858],{"class":555},[480,3021,559],{"class":490},[480,3023,2531],{"class":525},[480,3025,529],{"class":486},[480,3027,590],{"class":486},[480,3029,729],{"class":507},[480,3031,504],{"class":486},[480,3033,711],{"class":490},[480,3035,843],{"class":486},[480,3037,3039,3041,3043,3045,3047,3049,3052,3054,3056,3058,3060,3062,3064,3066,3068,3070],{"class":482,"line":3038},31,[480,3040,1302],{"class":551},[480,3042,2574],{"class":525},[480,3044,653],{"class":486},[480,3046,1122],{"class":525},[480,3048,653],{"class":486},[480,3050,3051],{"class":555},"delete",[480,3053,559],{"class":490},[480,3055,668],{"class":486},[480,3057,2590],{"class":490},[480,3059,580],{"class":486},[480,3061,522],{"class":486},[480,3063,2230],{"class":525},[480,3065,708],{"class":486},[480,3067,708],{"class":486},[480,3069,645],{"class":490},[480,3071,843],{"class":486},[480,3073,3075,3077,3079,3081,3083,3086,3088],{"class":482,"line":3074},32,[480,3076,2757],{"class":555},[480,3078,559],{"class":490},[480,3080,2531],{"class":525},[480,3082,529],{"class":486},[480,3084,3085],{"class":2235}," 204",[480,3087,645],{"class":490},[480,3089,843],{"class":486},[480,3091,3093,3095,3097],{"class":482,"line":3092},33,[480,3094,545],{"class":486},[480,3096,645],{"class":525},[480,3098,843],{"class":486},[1461,3100,1468,3101,3104,3105,3108,3109,3112],{"color":2096,"icon":1464},[1470,3102,3103],{},"Convention Nuxt 4:"," filename ",[432,3106,3107],{},"\u003Croute>.\u003Cmethod>.ts"," → tự match HTTP method. Hoặc ",[432,3110,3111],{},"\u003Croute>.ts"," thì handler nhận mọi method, tự switch.",[467,3114,3116],{"id":3115},"helpers-thường-dùng","Helpers thường dùng",[3118,3119,3120,3133],"table",{},[3121,3122,3123],"thead",{},[3124,3125,3126,3130],"tr",{},[3127,3128,3129],"th",{},"Helper",[3127,3131,3132],{},"Tác dụng",[3134,3135,3136,3147,3157,3167,3177,3187,3197,3207,3217,3227],"tbody",{},[3124,3137,3138,3144],{},[3139,3140,3141],"td",{},[432,3142,3143],{},"getQuery(event)",[3139,3145,3146],{},"Query string params",[3124,3148,3149,3154],{},[3139,3150,3151],{},[432,3152,3153],{},"getRouterParam(event, \"id\")",[3139,3155,3156],{},"Path param",[3124,3158,3159,3164],{},[3139,3160,3161],{},[432,3162,3163],{},"readBody(event)",[3139,3165,3166],{},"Parse JSON body",[3124,3168,3169,3174],{},[3139,3170,3171],{},[432,3172,3173],{},"readMultipartFormData(event)",[3139,3175,3176],{},"Parse multipart (file upload)",[3124,3178,3179,3184],{},[3139,3180,3181],{},[432,3182,3183],{},"setResponseStatus(event, 201)",[3139,3185,3186],{},"Set status code",[3124,3188,3189,3194],{},[3139,3190,3191],{},[432,3192,3193],{},"setResponseHeader(event, \"X-Custom\", \"value\")",[3139,3195,3196],{},"Set header",[3124,3198,3199,3204],{},[3139,3200,3201],{},[432,3202,3203],{},"getHeader(event, \"authorization\")",[3139,3205,3206],{},"Get header",[3124,3208,3209,3214],{},[3139,3210,3211],{},[432,3212,3213],{},"getCookie(event, \"session\")",[3139,3215,3216],{},"Get cookie",[3124,3218,3219,3224],{},[3139,3220,3221],{},[432,3222,3223],{},"setCookie(event, \"session\", \"value\", { httpOnly: true })",[3139,3225,3226],{},"Set cookie",[3124,3228,3229,3234],{},[3139,3230,3231],{},[432,3232,3233],{},"createError({ statusCode, message })",[3139,3235,3236],{},"Throw error có status",[467,3238,3240],{"id":3239},"server-middleware","Server middleware",[471,3242,3244],{"className":1675,"code":3243,"language":1677,"meta":476,"style":476},"\u002F\u002F server\u002Fmiddleware\u002Fauth.ts — chạy mọi request\nexport default defineEventHandler(async (event) => {\n  const url = getRequestURL(event);\n  if (url.pathname.startsWith(\"\u002Fapi\u002Fadmin\")) {\n    const token = getHeader(event, \"authorization\");\n    if (!isValidAdminToken(token)) {\n      throw createError({ statusCode: 401, message: \"Unauthorized\" });\n    }\n  }\n});\n",[432,3245,3246,3251,3273,3293,3326,3356,3377,3414,3419,3423],{"__ignoreMap":476},[480,3247,3248],{"class":482,"line":483},[480,3249,3250],{"class":748},"\u002F\u002F server\u002Fmiddleware\u002Fauth.ts — chạy mọi request\n",[480,3252,3253,3255,3257,3259,3261,3263,3265,3267,3269,3271],{"class":482,"line":516},[480,3254,1944],{"class":551},[480,3256,2519],{"class":551},[480,3258,2522],{"class":555},[480,3260,559],{"class":525},[480,3262,1278],{"class":494},[480,3264,629],{"class":486},[480,3266,2531],{"class":632},[480,3268,645],{"class":486},[480,3270,612],{"class":494},[480,3272,571],{"class":486},[480,3274,3275,3277,3280,3282,3285,3287,3289,3291],{"class":482,"line":574},[480,3276,1117],{"class":494},[480,3278,3279],{"class":525}," url",[480,3281,548],{"class":486},[480,3283,3284],{"class":555}," getRequestURL",[480,3286,559],{"class":490},[480,3288,2531],{"class":525},[480,3290,645],{"class":490},[480,3292,843],{"class":486},[480,3294,3295,3297,3299,3302,3304,3307,3309,3312,3314,3316,3319,3321,3324],{"class":482,"line":601},[480,3296,2687],{"class":551},[480,3298,629],{"class":490},[480,3300,3301],{"class":525},"url",[480,3303,653],{"class":486},[480,3305,3306],{"class":525},"pathname",[480,3308,653],{"class":486},[480,3310,3311],{"class":555},"startsWith",[480,3313,559],{"class":490},[480,3315,504],{"class":486},[480,3317,3318],{"class":507},"\u002Fapi\u002Fadmin",[480,3320,504],{"class":486},[480,3322,3323],{"class":490},")) ",[480,3325,1979],{"class":486},[480,3327,3328,3331,3334,3336,3339,3341,3343,3345,3347,3350,3352,3354],{"class":482,"line":621},[480,3329,3330],{"class":494},"    const",[480,3332,3333],{"class":525}," token",[480,3335,548],{"class":486},[480,3337,3338],{"class":555}," getHeader",[480,3340,559],{"class":490},[480,3342,2531],{"class":525},[480,3344,529],{"class":486},[480,3346,590],{"class":486},[480,3348,3349],{"class":507},"authorization",[480,3351,504],{"class":486},[480,3353,645],{"class":490},[480,3355,843],{"class":486},[480,3357,3358,3361,3363,3365,3368,3370,3373,3375],{"class":482,"line":716},[480,3359,3360],{"class":551},"    if",[480,3362,629],{"class":490},[480,3364,2692],{"class":486},[480,3366,3367],{"class":555},"isValidAdminToken",[480,3369,559],{"class":490},[480,3371,3372],{"class":525},"token",[480,3374,3323],{"class":490},[480,3376,1979],{"class":486},[480,3378,3379,3382,3384,3386,3388,3390,3392,3395,3397,3399,3401,3403,3406,3408,3410,3412],{"class":482,"line":752},[480,3380,3381],{"class":551},"      throw",[480,3383,2711],{"class":555},[480,3385,559],{"class":490},[480,3387,668],{"class":486},[480,3389,2718],{"class":490},[480,3391,580],{"class":486},[480,3393,3394],{"class":2235}," 401",[480,3396,529],{"class":486},[480,3398,2728],{"class":490},[480,3400,580],{"class":486},[480,3402,590],{"class":486},[480,3404,3405],{"class":507},"Unauthorized",[480,3407,504],{"class":486},[480,3409,708],{"class":486},[480,3411,645],{"class":490},[480,3413,843],{"class":486},[480,3415,3416],{"class":482,"line":768},[480,3417,3418],{"class":486},"    }\n",[480,3420,3421],{"class":482,"line":785},[480,3422,1844],{"class":486},[480,3424,3425,3427,3429],{"class":482,"line":801},[480,3426,545],{"class":486},[480,3428,645],{"class":525},[480,3430,843],{"class":486},[467,3432,3434],{"id":3433},"storage-layer-nitro","Storage layer (Nitro)",[471,3436,3438],{"className":1675,"code":3437,"language":1677,"meta":476,"style":476},"\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  nitro: {\n    storage: {\n      cache: { driver: \"redis\", url: process.env.REDIS_URL }\n    }\n  }\n});\n\n\u002F\u002F In handler\nexport default defineEventHandler(async () => {\n  const storage = useStorage(\"cache\");\n  const cached = await storage.getItem(\"users:list\");\n  if (cached) return cached;\n  \n  const users = await db.users.findMany();\n  await storage.setItem(\"users:list\", users, { ttl: 300 });\n  return users;\n});\n",[432,3439,3440,3444,3457,3466,3475,3517,3521,3525,3533,3537,3542,3560,3585,3616,3634,3639,3663,3704,3712],{"__ignoreMap":476},[480,3441,3442],{"class":482,"line":483},[480,3443,2156],{"class":748},[480,3445,3446,3448,3450,3453,3455],{"class":482,"line":516},[480,3447,1944],{"class":551},[480,3449,2519],{"class":551},[480,3451,3452],{"class":555}," defineNuxtConfig",[480,3454,559],{"class":525},[480,3456,1979],{"class":486},[480,3458,3459,3462,3464],{"class":482,"line":574},[480,3460,3461],{"class":490},"  nitro",[480,3463,580],{"class":486},[480,3465,571],{"class":486},[480,3467,3468,3471,3473],{"class":482,"line":601},[480,3469,3470],{"class":490},"    storage",[480,3472,580],{"class":486},[480,3474,571],{"class":486},[480,3476,3477,3480,3482,3484,3487,3489,3491,3494,3496,3498,3500,3502,3505,3507,3510,3512,3515],{"class":482,"line":621},[480,3478,3479],{"class":490},"      cache",[480,3481,580],{"class":486},[480,3483,522],{"class":486},[480,3485,3486],{"class":490}," driver",[480,3488,580],{"class":486},[480,3490,590],{"class":486},[480,3492,3493],{"class":507},"redis",[480,3495,504],{"class":486},[480,3497,529],{"class":486},[480,3499,3279],{"class":490},[480,3501,580],{"class":486},[480,3503,3504],{"class":525}," process",[480,3506,653],{"class":486},[480,3508,3509],{"class":525},"env",[480,3511,653],{"class":486},[480,3513,3514],{"class":525},"REDIS_URL ",[480,3516,1363],{"class":486},[480,3518,3519],{"class":482,"line":716},[480,3520,3418],{"class":486},[480,3522,3523],{"class":482,"line":752},[480,3524,1844],{"class":486},[480,3526,3527,3529,3531],{"class":482,"line":768},[480,3528,545],{"class":486},[480,3530,645],{"class":525},[480,3532,843],{"class":486},[480,3534,3535],{"class":482,"line":785},[480,3536,860],{"emptyLinePlaceholder":859},[480,3538,3539],{"class":482,"line":801},[480,3540,3541],{"class":748},"\u002F\u002F In handler\n",[480,3543,3544,3546,3548,3550,3552,3554,3556,3558],{"class":482,"line":816},[480,3545,1944],{"class":551},[480,3547,2519],{"class":551},[480,3549,2522],{"class":555},[480,3551,559],{"class":525},[480,3553,1278],{"class":494},[480,3555,609],{"class":486},[480,3557,612],{"class":494},[480,3559,571],{"class":486},[480,3561,3562,3564,3567,3569,3572,3574,3576,3579,3581,3583],{"class":482,"line":836},[480,3563,1117],{"class":494},[480,3565,3566],{"class":525}," storage",[480,3568,548],{"class":486},[480,3570,3571],{"class":555}," useStorage",[480,3573,559],{"class":490},[480,3575,504],{"class":486},[480,3577,3578],{"class":507},"cache",[480,3580,504],{"class":486},[480,3582,645],{"class":490},[480,3584,843],{"class":486},[480,3586,3587,3589,3592,3594,3596,3598,3600,3603,3605,3607,3610,3612,3614],{"class":482,"line":846},[480,3588,1117],{"class":494},[480,3590,3591],{"class":525}," cached",[480,3593,548],{"class":486},[480,3595,552],{"class":551},[480,3597,3566],{"class":525},[480,3599,653],{"class":486},[480,3601,3602],{"class":555},"getItem",[480,3604,559],{"class":490},[480,3606,504],{"class":486},[480,3608,3609],{"class":507},"users:list",[480,3611,504],{"class":486},[480,3613,645],{"class":490},[480,3615,843],{"class":486},[480,3617,3618,3620,3622,3625,3627,3630,3632],{"class":482,"line":856},[480,3619,2687],{"class":551},[480,3621,629],{"class":490},[480,3623,3624],{"class":525},"cached",[480,3626,2609],{"class":490},[480,3628,3629],{"class":551},"return",[480,3631,3591],{"class":525},[480,3633,843],{"class":486},[480,3635,3636],{"class":482,"line":863},[480,3637,3638],{"class":490},"  \n",[480,3640,3641,3643,3645,3647,3649,3651,3653,3655,3657,3659,3661],{"class":482,"line":873},[480,3642,1117],{"class":494},[480,3644,1217],{"class":525},[480,3646,548],{"class":486},[480,3648,552],{"class":551},[480,3650,2574],{"class":525},[480,3652,653],{"class":486},[480,3654,1122],{"class":525},[480,3656,653],{"class":486},[480,3658,2583],{"class":555},[480,3660,1352],{"class":490},[480,3662,843],{"class":486},[480,3664,3665,3667,3669,3671,3674,3676,3678,3680,3682,3684,3686,3688,3690,3693,3695,3698,3700,3702],{"class":482,"line":906},[480,3666,1302],{"class":551},[480,3668,3566],{"class":525},[480,3670,653],{"class":486},[480,3672,3673],{"class":555},"setItem",[480,3675,559],{"class":490},[480,3677,504],{"class":486},[480,3679,3609],{"class":507},[480,3681,504],{"class":486},[480,3683,529],{"class":486},[480,3685,1217],{"class":525},[480,3687,529],{"class":486},[480,3689,522],{"class":486},[480,3691,3692],{"class":490}," ttl",[480,3694,580],{"class":486},[480,3696,3697],{"class":2235}," 300",[480,3699,708],{"class":486},[480,3701,645],{"class":490},[480,3703,843],{"class":486},[480,3705,3706,3708,3710],{"class":482,"line":936},[480,3707,1212],{"class":551},[480,3709,1217],{"class":525},[480,3711,843],{"class":486},[480,3713,3714,3716,3718],{"class":482,"line":949},[480,3715,545],{"class":486},[480,3717,645],{"class":525},[480,3719,843],{"class":486},[427,3721,3723],{"id":3722},"_5-websocket-sse-server-sent-events","5. WebSocket \u002F SSE (Server-Sent Events)",[471,3725,3727],{"className":1675,"code":3726,"language":1677,"meta":476,"style":476},"\u002F\u002F server\u002Fapi\u002Fsse.get.ts\nexport default defineEventHandler(async (event) => {\n  const eventStream = createEventStream(event);\n  \n  setInterval(() => {\n    eventStream.push({ event: \"tick\", data: JSON.stringify({ time: Date.now() }) });\n  }, 1000);\n  \n  return eventStream.send();\n});\n",[432,3728,3729,3734,3756,3776,3780,3793,3863,3875,3879,3894],{"__ignoreMap":476},[480,3730,3731],{"class":482,"line":483},[480,3732,3733],{"class":748},"\u002F\u002F server\u002Fapi\u002Fsse.get.ts\n",[480,3735,3736,3738,3740,3742,3744,3746,3748,3750,3752,3754],{"class":482,"line":516},[480,3737,1944],{"class":551},[480,3739,2519],{"class":551},[480,3741,2522],{"class":555},[480,3743,559],{"class":525},[480,3745,1278],{"class":494},[480,3747,629],{"class":486},[480,3749,2531],{"class":632},[480,3751,645],{"class":486},[480,3753,612],{"class":494},[480,3755,571],{"class":486},[480,3757,3758,3760,3763,3765,3768,3770,3772,3774],{"class":482,"line":574},[480,3759,1117],{"class":494},[480,3761,3762],{"class":525}," eventStream",[480,3764,548],{"class":486},[480,3766,3767],{"class":555}," createEventStream",[480,3769,559],{"class":490},[480,3771,2531],{"class":525},[480,3773,645],{"class":490},[480,3775,843],{"class":486},[480,3777,3778],{"class":482,"line":601},[480,3779,3638],{"class":490},[480,3781,3782,3785,3787,3789,3791],{"class":482,"line":621},[480,3783,3784],{"class":555},"  setInterval",[480,3786,559],{"class":490},[480,3788,1352],{"class":486},[480,3790,612],{"class":494},[480,3792,571],{"class":486},[480,3794,3795,3798,3800,3803,3805,3807,3810,3812,3814,3817,3819,3821,3823,3825,3828,3830,3833,3835,3837,3840,3842,3845,3847,3850,3853,3855,3857,3859,3861],{"class":482,"line":716},[480,3796,3797],{"class":525},"    eventStream",[480,3799,653],{"class":486},[480,3801,3802],{"class":555},"push",[480,3804,559],{"class":490},[480,3806,668],{"class":486},[480,3808,3809],{"class":490}," event",[480,3811,580],{"class":486},[480,3813,590],{"class":486},[480,3815,3816],{"class":507},"tick",[480,3818,504],{"class":486},[480,3820,529],{"class":486},[480,3822,526],{"class":490},[480,3824,580],{"class":486},[480,3826,3827],{"class":525}," JSON",[480,3829,653],{"class":486},[480,3831,3832],{"class":555},"stringify",[480,3834,559],{"class":490},[480,3836,668],{"class":486},[480,3838,3839],{"class":490}," time",[480,3841,580],{"class":486},[480,3843,3844],{"class":525}," Date",[480,3846,653],{"class":486},[480,3848,3849],{"class":555},"now",[480,3851,3852],{"class":490},"() ",[480,3854,545],{"class":486},[480,3856,2609],{"class":490},[480,3858,545],{"class":486},[480,3860,645],{"class":490},[480,3862,843],{"class":486},[480,3864,3865,3868,3871,3873],{"class":482,"line":752},[480,3866,3867],{"class":486},"  },",[480,3869,3870],{"class":2235}," 1000",[480,3872,645],{"class":490},[480,3874,843],{"class":486},[480,3876,3877],{"class":482,"line":768},[480,3878,3638],{"class":490},[480,3880,3881,3883,3885,3887,3890,3892],{"class":482,"line":785},[480,3882,1212],{"class":551},[480,3884,3762],{"class":525},[480,3886,653],{"class":486},[480,3888,3889],{"class":555},"send",[480,3891,1352],{"class":490},[480,3893,843],{"class":486},[480,3895,3896,3898,3900],{"class":482,"line":801},[480,3897,545],{"class":486},[480,3899,645],{"class":525},[480,3901,843],{"class":486},[471,3903,3905],{"className":473,"code":3904,"language":475,"meta":476,"style":476},"\u003Cscript setup>\nconst events = useState(\"events\", () => [] as any[]);\nonMounted(() => {\n  const source = new EventSource(\"\u002Fapi\u002Fsse\");\n  source.addEventListener(\"tick\", (e) => {\n    events.value.push(JSON.parse(e.data));\n  });\n});\n\u003C\u002Fscript>\n",[432,3906,3907,3917,3957,3970,3998,4029,4064,4072,4080],{"__ignoreMap":476},[480,3908,3909,3911,3913,3915],{"class":482,"line":483},[480,3910,487],{"class":486},[480,3912,491],{"class":490},[480,3914,495],{"class":494},[480,3916,513],{"class":486},[480,3918,3919,3921,3924,3926,3928,3930,3932,3935,3937,3939,3941,3943,3946,3949,3952,3955],{"class":482,"line":516},[480,3920,519],{"class":494},[480,3922,3923],{"class":525}," events ",[480,3925,501],{"class":486},[480,3927,1959],{"class":555},[480,3929,559],{"class":525},[480,3931,504],{"class":486},[480,3933,3934],{"class":507},"events",[480,3936,504],{"class":486},[480,3938,529],{"class":486},[480,3940,609],{"class":486},[480,3942,612],{"class":494},[480,3944,3945],{"class":525}," [] ",[480,3947,3948],{"class":551},"as",[480,3950,3951],{"class":638}," any",[480,3953,3954],{"class":525},"[])",[480,3956,843],{"class":486},[480,3958,3959,3962,3964,3966,3968],{"class":482,"line":574},[480,3960,3961],{"class":555},"onMounted",[480,3963,559],{"class":525},[480,3965,1352],{"class":486},[480,3967,612],{"class":494},[480,3969,571],{"class":486},[480,3971,3972,3974,3977,3979,3982,3985,3987,3989,3992,3994,3996],{"class":482,"line":601},[480,3973,1117],{"class":494},[480,3975,3976],{"class":525}," source",[480,3978,548],{"class":486},[480,3980,3981],{"class":486}," new",[480,3983,3984],{"class":555}," EventSource",[480,3986,559],{"class":490},[480,3988,504],{"class":486},[480,3990,3991],{"class":507},"\u002Fapi\u002Fsse",[480,3993,504],{"class":486},[480,3995,645],{"class":490},[480,3997,843],{"class":486},[480,3999,4000,4003,4005,4008,4010,4012,4014,4016,4018,4020,4023,4025,4027],{"class":482,"line":621},[480,4001,4002],{"class":525},"  source",[480,4004,653],{"class":486},[480,4006,4007],{"class":555},"addEventListener",[480,4009,559],{"class":490},[480,4011,504],{"class":486},[480,4013,3816],{"class":507},[480,4015,504],{"class":486},[480,4017,529],{"class":486},[480,4019,629],{"class":486},[480,4021,4022],{"class":632},"e",[480,4024,645],{"class":486},[480,4026,612],{"class":494},[480,4028,571],{"class":486},[480,4030,4031,4034,4036,4038,4040,4042,4044,4047,4049,4052,4054,4056,4058,4060,4062],{"class":482,"line":716},[480,4032,4033],{"class":525},"    events",[480,4035,653],{"class":486},[480,4037,2075],{"class":525},[480,4039,653],{"class":486},[480,4041,3802],{"class":555},[480,4043,559],{"class":490},[480,4045,4046],{"class":525},"JSON",[480,4048,653],{"class":486},[480,4050,4051],{"class":555},"parse",[480,4053,559],{"class":490},[480,4055,4022],{"class":525},[480,4057,653],{"class":486},[480,4059,1807],{"class":525},[480,4061,711],{"class":490},[480,4063,843],{"class":486},[480,4065,4066,4068,4070],{"class":482,"line":752},[480,4067,1443],{"class":486},[480,4069,645],{"class":490},[480,4071,843],{"class":486},[480,4073,4074,4076,4078],{"class":482,"line":768},[480,4075,545],{"class":486},[480,4077,645],{"class":525},[480,4079,843],{"class":486},[480,4081,4082,4084,4086],{"class":482,"line":785},[480,4083,849],{"class":486},[480,4085,491],{"class":490},[480,4087,513],{"class":486},[427,4089,4091],{"id":4090},"_6-file-upload","6. File upload",[471,4093,4095],{"className":1675,"code":4094,"language":1677,"meta":476,"style":476},"\u002F\u002F server\u002Fapi\u002Fupload.post.ts\nimport { writeFile } from \"node:fs\u002Fpromises\";\nimport { join } from \"node:path\";\n\nexport default defineEventHandler(async (event) => {\n  const formData = await readMultipartFormData(event);\n  if (!formData) throw createError({ statusCode: 400 });\n  \n  const file = formData.find(f => f.name === \"file\");\n  if (!file?.data) throw createError({ statusCode: 400 });\n  \n  const filename = `${Date.now()}-${file.filename}`;\n  await writeFile(join(\"uploads\", filename), file.data);\n  return { url: `\u002Fuploads\u002F${filename}` };\n});\n",[432,4096,4097,4102,4126,4148,4152,4174,4196,4229,4233,4277,4314,4318,4356,4394,4418],{"__ignoreMap":476},[480,4098,4099],{"class":482,"line":483},[480,4100,4101],{"class":748},"\u002F\u002F server\u002Fapi\u002Fupload.post.ts\n",[480,4103,4104,4107,4109,4112,4114,4117,4119,4122,4124],{"class":482,"line":516},[480,4105,4106],{"class":551},"import",[480,4108,522],{"class":486},[480,4110,4111],{"class":525}," writeFile",[480,4113,708],{"class":486},[480,4115,4116],{"class":551}," from",[480,4118,590],{"class":486},[480,4120,4121],{"class":507},"node:fs\u002Fpromises",[480,4123,504],{"class":486},[480,4125,843],{"class":486},[480,4127,4128,4130,4132,4135,4137,4139,4141,4144,4146],{"class":482,"line":574},[480,4129,4106],{"class":551},[480,4131,522],{"class":486},[480,4133,4134],{"class":525}," join",[480,4136,708],{"class":486},[480,4138,4116],{"class":551},[480,4140,590],{"class":486},[480,4142,4143],{"class":507},"node:path",[480,4145,504],{"class":486},[480,4147,843],{"class":486},[480,4149,4150],{"class":482,"line":601},[480,4151,860],{"emptyLinePlaceholder":859},[480,4153,4154,4156,4158,4160,4162,4164,4166,4168,4170,4172],{"class":482,"line":621},[480,4155,1944],{"class":551},[480,4157,2519],{"class":551},[480,4159,2522],{"class":555},[480,4161,559],{"class":525},[480,4163,1278],{"class":494},[480,4165,629],{"class":486},[480,4167,2531],{"class":632},[480,4169,645],{"class":486},[480,4171,612],{"class":494},[480,4173,571],{"class":486},[480,4175,4176,4178,4181,4183,4185,4188,4190,4192,4194],{"class":482,"line":716},[480,4177,1117],{"class":494},[480,4179,4180],{"class":525}," formData",[480,4182,548],{"class":486},[480,4184,552],{"class":551},[480,4186,4187],{"class":555}," readMultipartFormData",[480,4189,559],{"class":490},[480,4191,2531],{"class":525},[480,4193,645],{"class":490},[480,4195,843],{"class":486},[480,4197,4198,4200,4202,4204,4207,4209,4211,4213,4215,4217,4219,4221,4223,4225,4227],{"class":482,"line":752},[480,4199,2687],{"class":551},[480,4201,629],{"class":490},[480,4203,2692],{"class":486},[480,4205,4206],{"class":525},"formData",[480,4208,2609],{"class":490},[480,4210,2933],{"class":551},[480,4212,2711],{"class":555},[480,4214,559],{"class":490},[480,4216,668],{"class":486},[480,4218,2718],{"class":490},[480,4220,580],{"class":486},[480,4222,2723],{"class":2235},[480,4224,708],{"class":486},[480,4226,645],{"class":490},[480,4228,843],{"class":486},[480,4230,4231],{"class":482,"line":768},[480,4232,3638],{"class":490},[480,4234,4235,4237,4240,4242,4244,4246,4249,4251,4254,4256,4259,4261,4263,4266,4268,4271,4273,4275],{"class":482,"line":785},[480,4236,1117],{"class":494},[480,4238,4239],{"class":525}," file",[480,4241,548],{"class":486},[480,4243,4180],{"class":525},[480,4245,653],{"class":486},[480,4247,4248],{"class":555},"find",[480,4250,559],{"class":490},[480,4252,4253],{"class":632},"f",[480,4255,612],{"class":494},[480,4257,4258],{"class":525}," f",[480,4260,653],{"class":486},[480,4262,738],{"class":525},[480,4264,4265],{"class":486}," ===",[480,4267,590],{"class":486},[480,4269,4270],{"class":507},"file",[480,4272,504],{"class":486},[480,4274,645],{"class":490},[480,4276,843],{"class":486},[480,4278,4279,4281,4283,4285,4287,4290,4292,4294,4296,4298,4300,4302,4304,4306,4308,4310,4312],{"class":482,"line":801},[480,4280,2687],{"class":551},[480,4282,629],{"class":490},[480,4284,2692],{"class":486},[480,4286,4270],{"class":525},[480,4288,4289],{"class":486},"?.",[480,4291,1807],{"class":525},[480,4293,2609],{"class":490},[480,4295,2933],{"class":551},[480,4297,2711],{"class":555},[480,4299,559],{"class":490},[480,4301,668],{"class":486},[480,4303,2718],{"class":490},[480,4305,580],{"class":486},[480,4307,2723],{"class":2235},[480,4309,708],{"class":486},[480,4311,645],{"class":490},[480,4313,843],{"class":486},[480,4315,4316],{"class":482,"line":816},[480,4317,3638],{"class":490},[480,4319,4320,4322,4325,4327,4329,4332,4334,4336,4338,4340,4343,4345,4347,4349,4352,4354],{"class":482,"line":836},[480,4321,1117],{"class":494},[480,4323,4324],{"class":525}," filename",[480,4326,548],{"class":486},[480,4328,683],{"class":486},[480,4330,4331],{"class":525},"Date",[480,4333,653],{"class":486},[480,4335,3849],{"class":555},[480,4337,1352],{"class":525},[480,4339,545],{"class":486},[480,4341,4342],{"class":507},"-",[480,4344,1316],{"class":486},[480,4346,4270],{"class":525},[480,4348,653],{"class":486},[480,4350,4351],{"class":525},"filename",[480,4353,705],{"class":486},[480,4355,843],{"class":486},[480,4357,4358,4360,4362,4364,4367,4369,4371,4374,4376,4378,4380,4382,4384,4386,4388,4390,4392],{"class":482,"line":846},[480,4359,1302],{"class":551},[480,4361,4111],{"class":555},[480,4363,559],{"class":490},[480,4365,4366],{"class":555},"join",[480,4368,559],{"class":490},[480,4370,504],{"class":486},[480,4372,4373],{"class":507},"uploads",[480,4375,504],{"class":486},[480,4377,529],{"class":486},[480,4379,4324],{"class":525},[480,4381,645],{"class":490},[480,4383,529],{"class":486},[480,4385,4239],{"class":525},[480,4387,653],{"class":486},[480,4389,1807],{"class":525},[480,4391,645],{"class":490},[480,4393,843],{"class":486},[480,4395,4396,4398,4400,4402,4404,4407,4410,4412,4414,4416],{"class":482,"line":856},[480,4397,1212],{"class":551},[480,4399,522],{"class":486},[480,4401,3279],{"class":490},[480,4403,580],{"class":486},[480,4405,4406],{"class":486}," `",[480,4408,4409],{"class":507},"\u002Fuploads\u002F",[480,4411,1316],{"class":486},[480,4413,4351],{"class":525},[480,4415,705],{"class":486},[480,4417,1228],{"class":486},[480,4419,4420,4422,4424],{"class":482,"line":863},[480,4421,545],{"class":486},[480,4423,645],{"class":525},[480,4425,843],{"class":486},[471,4427,4429],{"className":473,"code":4428,"language":475,"meta":476,"style":476},"\u003Cscript setup>\nasync function upload(e: Event) {\n  const file = (e.target as HTMLInputElement).files?.[0];\n  if (!file) return;\n  \n  const form = new FormData();\n  form.append(\"file\", file);\n  \n  const res = await $fetch(\"\u002Fapi\u002Fupload\", { method: \"POST\", body: form });\n  console.log(res.url);\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cinput type=\"file\" @change=\"upload\" \u002F>\n\u003C\u002Ftemplate>\n",[432,4430,4431,4441,4463,4503,4519,4523,4540,4566,4570,4619,4641,4645,4653,4657,4665,4698],{"__ignoreMap":476},[480,4432,4433,4435,4437,4439],{"class":482,"line":483},[480,4434,487],{"class":486},[480,4436,491],{"class":490},[480,4438,495],{"class":494},[480,4440,513],{"class":486},[480,4442,4443,4445,4447,4450,4452,4454,4456,4459,4461],{"class":482,"line":516},[480,4444,1278],{"class":494},[480,4446,1281],{"class":494},[480,4448,4449],{"class":555}," upload",[480,4451,559],{"class":486},[480,4453,4022],{"class":632},[480,4455,580],{"class":486},[480,4457,4458],{"class":638}," Event",[480,4460,645],{"class":486},[480,4462,571],{"class":486},[480,4464,4465,4467,4469,4471,4473,4475,4477,4480,4482,4485,4487,4489,4492,4494,4496,4499,4501],{"class":482,"line":574},[480,4466,1117],{"class":494},[480,4468,4239],{"class":525},[480,4470,548],{"class":486},[480,4472,629],{"class":490},[480,4474,4022],{"class":525},[480,4476,653],{"class":486},[480,4478,4479],{"class":525},"target",[480,4481,1992],{"class":551},[480,4483,4484],{"class":638}," HTMLInputElement",[480,4486,645],{"class":490},[480,4488,653],{"class":486},[480,4490,4491],{"class":525},"files",[480,4493,4289],{"class":486},[480,4495,1810],{"class":490},[480,4497,4498],{"class":2235},"0",[480,4500,743],{"class":490},[480,4502,843],{"class":486},[480,4504,4505,4507,4509,4511,4513,4515,4517],{"class":482,"line":601},[480,4506,2687],{"class":551},[480,4508,629],{"class":490},[480,4510,2692],{"class":486},[480,4512,4270],{"class":525},[480,4514,2609],{"class":490},[480,4516,3629],{"class":551},[480,4518,843],{"class":486},[480,4520,4521],{"class":482,"line":621},[480,4522,3638],{"class":490},[480,4524,4525,4527,4529,4531,4533,4536,4538],{"class":482,"line":716},[480,4526,1117],{"class":494},[480,4528,1436],{"class":525},[480,4530,548],{"class":486},[480,4532,3981],{"class":486},[480,4534,4535],{"class":555}," FormData",[480,4537,1352],{"class":490},[480,4539,843],{"class":486},[480,4541,4542,4545,4547,4550,4552,4554,4556,4558,4560,4562,4564],{"class":482,"line":752},[480,4543,4544],{"class":525},"  form",[480,4546,653],{"class":486},[480,4548,4549],{"class":555},"append",[480,4551,559],{"class":490},[480,4553,504],{"class":486},[480,4555,4270],{"class":507},[480,4557,504],{"class":486},[480,4559,529],{"class":486},[480,4561,4239],{"class":525},[480,4563,645],{"class":490},[480,4565,843],{"class":486},[480,4567,4568],{"class":482,"line":768},[480,4569,3638],{"class":490},[480,4571,4572,4574,4576,4578,4580,4582,4584,4586,4589,4591,4593,4595,4597,4599,4601,4603,4605,4607,4609,4611,4613,4615,4617],{"class":482,"line":785},[480,4573,1117],{"class":494},[480,4575,2342],{"class":525},[480,4577,548],{"class":486},[480,4579,552],{"class":551},[480,4581,1305],{"class":555},[480,4583,559],{"class":490},[480,4585,504],{"class":486},[480,4587,4588],{"class":507},"\u002Fapi\u002Fupload",[480,4590,504],{"class":486},[480,4592,529],{"class":486},[480,4594,522],{"class":486},[480,4596,1327],{"class":490},[480,4598,580],{"class":486},[480,4600,590],{"class":486},[480,4602,1422],{"class":507},[480,4604,504],{"class":486},[480,4606,529],{"class":486},[480,4608,2376],{"class":490},[480,4610,580],{"class":486},[480,4612,1436],{"class":525},[480,4614,708],{"class":486},[480,4616,645],{"class":490},[480,4618,843],{"class":486},[480,4620,4621,4624,4626,4628,4630,4633,4635,4637,4639],{"class":482,"line":801},[480,4622,4623],{"class":525},"  console",[480,4625,653],{"class":486},[480,4627,2067],{"class":555},[480,4629,559],{"class":490},[480,4631,4632],{"class":525},"res",[480,4634,653],{"class":486},[480,4636,3301],{"class":525},[480,4638,645],{"class":490},[480,4640,843],{"class":486},[480,4642,4643],{"class":482,"line":816},[480,4644,1363],{"class":486},[480,4646,4647,4649,4651],{"class":482,"line":836},[480,4648,849],{"class":486},[480,4650,491],{"class":490},[480,4652,513],{"class":486},[480,4654,4655],{"class":482,"line":846},[480,4656,860],{"emptyLinePlaceholder":859},[480,4658,4659,4661,4663],{"class":482,"line":856},[480,4660,487],{"class":486},[480,4662,868],{"class":490},[480,4664,513],{"class":486},[480,4666,4667,4669,4672,4675,4677,4679,4681,4683,4686,4688,4690,4693,4695],{"class":482,"line":863},[480,4668,876],{"class":486},[480,4670,4671],{"class":490},"input",[480,4673,4674],{"class":494}," type",[480,4676,501],{"class":486},[480,4678,504],{"class":486},[480,4680,4270],{"class":507},[480,4682,504],{"class":486},[480,4684,4685],{"class":494}," @change",[480,4687,501],{"class":486},[480,4689,504],{"class":486},[480,4691,4692],{"class":507},"upload",[480,4694,504],{"class":486},[480,4696,4697],{"class":486}," \u002F>\n",[480,4699,4700,4702,4704],{"class":482,"line":873},[480,4701,849],{"class":486},[480,4703,868],{"class":490},[480,4705,513],{"class":486},[427,4707,4709],{"id":4708},"_7-plugins-chạy-khi-nuxt-khởi-tạo","7. Plugins — chạy khi Nuxt khởi tạo",[471,4711,4713],{"className":1675,"code":4712,"language":1677,"meta":476,"style":476},"\u002F\u002F app\u002Fplugins\u002Fanalytics.client.ts — chỉ client (suffix .client)\nexport default defineNuxtPlugin(() => {\n  \u002F\u002F setup analytics\n  if (import.meta.client) {\n    window.dataLayer = window.dataLayer || [];\n  }\n});\n\n\u002F\u002F app\u002Fplugins\u002Fdayjs.ts — cả client + server\nimport dayjs from \"dayjs\";\n\nexport default defineNuxtPlugin(() => ({\n  provide: {\n    dayjs: dayjs,\n  }\n}));\n\n\u002F\u002F Dùng\nconst { $dayjs } = useNuxtApp();\n$dayjs(new Date()).format(\"YYYY-MM-DD\");\n",[432,4714,4715,4720,4737,4742,4764,4790,4794,4802,4806,4811,4830,4834,4852,4861,4873,4877,4885,4889,4894,4913],{"__ignoreMap":476},[480,4716,4717],{"class":482,"line":483},[480,4718,4719],{"class":748},"\u002F\u002F app\u002Fplugins\u002Fanalytics.client.ts — chỉ client (suffix .client)\n",[480,4721,4722,4724,4726,4729,4731,4733,4735],{"class":482,"line":516},[480,4723,1944],{"class":551},[480,4725,2519],{"class":551},[480,4727,4728],{"class":555}," defineNuxtPlugin",[480,4730,559],{"class":525},[480,4732,1352],{"class":486},[480,4734,612],{"class":494},[480,4736,571],{"class":486},[480,4738,4739],{"class":482,"line":574},[480,4740,4741],{"class":748},"  \u002F\u002F setup analytics\n",[480,4743,4744,4746,4748,4750,4752,4755,4757,4760,4762],{"class":482,"line":601},[480,4745,2687],{"class":551},[480,4747,629],{"class":490},[480,4749,4106],{"class":551},[480,4751,653],{"class":486},[480,4753,4754],{"class":525},"meta",[480,4756,653],{"class":486},[480,4758,4759],{"class":525},"client",[480,4761,2609],{"class":490},[480,4763,1979],{"class":486},[480,4765,4766,4769,4771,4774,4776,4779,4781,4783,4786,4788],{"class":482,"line":621},[480,4767,4768],{"class":525},"    window",[480,4770,653],{"class":486},[480,4772,4773],{"class":525},"dataLayer",[480,4775,548],{"class":486},[480,4777,4778],{"class":525}," window",[480,4780,653],{"class":486},[480,4782,4773],{"class":525},[480,4784,4785],{"class":486}," ||",[480,4787,615],{"class":490},[480,4789,843],{"class":486},[480,4791,4792],{"class":482,"line":716},[480,4793,1844],{"class":486},[480,4795,4796,4798,4800],{"class":482,"line":752},[480,4797,545],{"class":486},[480,4799,645],{"class":525},[480,4801,843],{"class":486},[480,4803,4804],{"class":482,"line":768},[480,4805,860],{"emptyLinePlaceholder":859},[480,4807,4808],{"class":482,"line":785},[480,4809,4810],{"class":748},"\u002F\u002F app\u002Fplugins\u002Fdayjs.ts — cả client + server\n",[480,4812,4813,4815,4818,4821,4823,4826,4828],{"class":482,"line":801},[480,4814,4106],{"class":551},[480,4816,4817],{"class":525}," dayjs ",[480,4819,4820],{"class":551},"from",[480,4822,590],{"class":486},[480,4824,4825],{"class":507},"dayjs",[480,4827,504],{"class":486},[480,4829,843],{"class":486},[480,4831,4832],{"class":482,"line":816},[480,4833,860],{"emptyLinePlaceholder":859},[480,4835,4836,4838,4840,4842,4844,4846,4848,4850],{"class":482,"line":836},[480,4837,1944],{"class":551},[480,4839,2519],{"class":551},[480,4841,4728],{"class":555},[480,4843,559],{"class":525},[480,4845,1352],{"class":486},[480,4847,612],{"class":494},[480,4849,629],{"class":525},[480,4851,1979],{"class":486},[480,4853,4854,4857,4859],{"class":482,"line":846},[480,4855,4856],{"class":490},"  provide",[480,4858,580],{"class":486},[480,4860,571],{"class":486},[480,4862,4863,4866,4868,4871],{"class":482,"line":856},[480,4864,4865],{"class":490},"    dayjs",[480,4867,580],{"class":486},[480,4869,4870],{"class":525}," dayjs",[480,4872,618],{"class":486},[480,4874,4875],{"class":482,"line":863},[480,4876,1844],{"class":486},[480,4878,4879,4881,4883],{"class":482,"line":873},[480,4880,545],{"class":486},[480,4882,711],{"class":525},[480,4884,843],{"class":486},[480,4886,4887],{"class":482,"line":906},[480,4888,860],{"emptyLinePlaceholder":859},[480,4890,4891],{"class":482,"line":936},[480,4892,4893],{"class":748},"\u002F\u002F Dùng\n",[480,4895,4896,4898,4900,4903,4905,4907,4909,4911],{"class":482,"line":949},[480,4897,519],{"class":494},[480,4899,522],{"class":486},[480,4901,4902],{"class":525}," $dayjs ",[480,4904,545],{"class":486},[480,4906,548],{"class":486},[480,4908,1795],{"class":555},[480,4910,1352],{"class":525},[480,4912,843],{"class":486},[480,4914,4915,4918,4920,4923,4925,4928,4930,4933,4935,4937,4940,4942,4944],{"class":482,"line":993},[480,4916,4917],{"class":555},"$dayjs",[480,4919,559],{"class":525},[480,4921,4922],{"class":486},"new",[480,4924,3844],{"class":555},[480,4926,4927],{"class":525},"())",[480,4929,653],{"class":486},[480,4931,4932],{"class":555},"format",[480,4934,559],{"class":525},[480,4936,504],{"class":486},[480,4938,4939],{"class":507},"YYYY-MM-DD",[480,4941,504],{"class":486},[480,4943,645],{"class":525},[480,4945,843],{"class":486},[427,4947,4949],{"id":4948},"_8-module-ecosystem-hot","8. Module ecosystem hot",[3118,4951,4952,4961],{},[3121,4953,4954],{},[3124,4955,4956,4959],{},[3127,4957,4958],{},"Module",[3127,4960,3132],{},[3134,4962,4963,4973,4982,4992,5002,5012,5022,5032,5042],{},[3124,4964,4965,4970],{},[3139,4966,4967],{},[432,4968,4969],{},"@nuxt\u002Fui",[3139,4971,4972],{},"UI library chính chủ (button, input, modal...)",[3124,4974,4975,4979],{},[3139,4976,4977],{},[432,4978,2171],{},[3139,4980,4981],{},"Pinia state management",[3124,4983,4984,4989],{},[3139,4985,4986],{},[432,4987,4988],{},"@nuxt\u002Fimage",[3139,4990,4991],{},"Image optimization (lazy, format, srcset)",[3124,4993,4994,4999],{},[3139,4995,4996],{},[432,4997,4998],{},"@nuxt\u002Fcontent",[3139,5000,5001],{},"Markdown CMS (file-based)",[3124,5003,5004,5009],{},[3139,5005,5006],{},[432,5007,5008],{},"@nuxtjs\u002Fi18n",[3139,5010,5011],{},"i18n",[3124,5013,5014,5019],{},[3139,5015,5016],{},[432,5017,5018],{},"@vueuse\u002Fnuxt",[3139,5020,5021],{},"VueUse composables auto-import",[3124,5023,5024,5029],{},[3139,5025,5026],{},[432,5027,5028],{},"@nuxtjs\u002Ftailwindcss",[3139,5030,5031],{},"TailwindCSS",[3124,5033,5034,5039],{},[3139,5035,5036],{},[432,5037,5038],{},"@sidebase\u002Fnuxt-auth",[3139,5040,5041],{},"Auth (NextAuth-style)",[3124,5043,5044,5049],{},[3139,5045,5046],{},[432,5047,5048],{},"@nuxt\u002Ftest-utils",[3139,5050,5051],{},"Testing helpers",[427,5053,5055],{"id":5054},"_9-deploy","9. Deploy",[1466,5057,5058],{},"Nitro hỗ trợ deploy đa nền tảng:",[471,5060,5062],{"className":2131,"code":5061,"language":2133,"meta":476,"style":476},"# Build cho Node\nnpx nuxt build\n\n# Static (prerender all)\nnpx nuxt generate\n\n# Vercel — auto detect, không cần config\n# Cloudflare Workers\nNITRO_PRESET=cloudflare-workers npx nuxt build\n\n# Netlify, Deno Deploy, AWS Lambda, Bun, Edge — đều OK\n",[432,5063,5064,5069,5080,5084,5089,5098,5102,5107,5112,5129,5133],{"__ignoreMap":476},[480,5065,5066],{"class":482,"line":483},[480,5067,5068],{"class":748},"# Build cho Node\n",[480,5070,5071,5074,5077],{"class":482,"line":516},[480,5072,5073],{"class":638},"npx",[480,5075,5076],{"class":507}," nuxt",[480,5078,5079],{"class":507}," build\n",[480,5081,5082],{"class":482,"line":574},[480,5083,860],{"emptyLinePlaceholder":859},[480,5085,5086],{"class":482,"line":601},[480,5087,5088],{"class":748},"# Static (prerender all)\n",[480,5090,5091,5093,5095],{"class":482,"line":621},[480,5092,5073],{"class":638},[480,5094,5076],{"class":507},[480,5096,5097],{"class":507}," generate\n",[480,5099,5100],{"class":482,"line":716},[480,5101,860],{"emptyLinePlaceholder":859},[480,5103,5104],{"class":482,"line":752},[480,5105,5106],{"class":748},"# Vercel — auto detect, không cần config\n",[480,5108,5109],{"class":482,"line":768},[480,5110,5111],{"class":748},"# Cloudflare Workers\n",[480,5113,5114,5117,5119,5122,5125,5127],{"class":482,"line":785},[480,5115,5116],{"class":525},"NITRO_PRESET",[480,5118,501],{"class":486},[480,5120,5121],{"class":507},"cloudflare-workers",[480,5123,5124],{"class":638}," npx",[480,5126,5076],{"class":507},[480,5128,5079],{"class":507},[480,5130,5131],{"class":482,"line":801},[480,5132,860],{"emptyLinePlaceholder":859},[480,5134,5135],{"class":482,"line":816},[480,5136,5137],{"class":748},"# Netlify, Deno Deploy, AWS Lambda, Bun, Edge — đều OK\n",[1466,5139,5140,5141,580],{},"Output ở ",[432,5142,5143],{},".output\u002F",[941,5145,5146,5152],{},[955,5147,5148,5151],{},[432,5149,5150],{},".output\u002Fserver\u002F"," — server bundle (cho SSR)",[955,5153,5154,5157],{},[432,5155,5156],{},".output\u002Fpublic\u002F"," — static assets (CDN)",[427,5159,5161],{"id":5160},"_10-performance-tips","10. Performance tips",[1461,5163,5165],{"color":5164,"icon":383},"success",[5166,5167,5168,5174,5181,5190,5196,5203,5209,5212],"ol",{},[955,5169,5170,5171,5173],{},"✅ Dùng ",[432,5172,434],{}," với key — auto cache + dedupe.",[955,5175,5176,5177,5180],{},"✅ ",[432,5178,5179],{},"routeRules"," SSG\u002FISR cho trang ít đổi.",[955,5182,5176,5183,5186,5187,5189],{},[432,5184,5185],{},"\u003CNuxtImg>"," từ ",[432,5188,4988],{}," — lazy + format modern.",[955,5191,5192,5193,653],{},"✅ Lazy load component nặng: ",[432,5194,5195],{},"LazyMyComponent",[955,5197,5198,5199,5202],{},"✅ Server-only component: ",[432,5200,5201],{},"\u003CClientOnly>"," wrap nếu cần.",[955,5204,5205,5206,653],{},"✅ Bundle analyzer: ",[432,5207,5208],{},"npx nuxt analyze",[955,5210,5211],{},"✅ Nuxt 4.2 — async handler extraction tự giảm bundle.",[955,5213,5214,5215,5217],{},"✅ Tránh prop drilling sâu → dùng ",[432,5216,1928],{}," hoặc Pinia.",[1461,5219,5221,5222],{"color":2096,"icon":5220},"i-lucide-arrow-right","Tiếp theo: ",[5223,5224,5225],"a",{"href":377},"Câu hỏi phỏng vấn Nuxt 4",[5227,5228,5229],"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 pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}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 .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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}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 .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":476,"searchDepth":516,"depth":516,"links":5231},[5232,5238,5244,5249,5255,5256,5257,5258,5259,5260],{"id":429,"depth":516,"text":5233,"children":5234},"1. Data fetching — useFetch vs useAsyncData vs $fetch",[5235,5236,5237],{"id":469,"depth":574,"text":434},{"id":1043,"depth":574,"text":1044},{"id":1247,"depth":574,"text":1248},{"id":1497,"depth":516,"text":1498,"children":5239},[5240,5241,5242,5243],{"id":1501,"depth":574,"text":1502},{"id":1664,"depth":574,"text":1665},{"id":1671,"depth":574,"text":1672},{"id":1855,"depth":574,"text":1856},{"id":1921,"depth":516,"text":1922,"children":5245},[5246,5248],{"id":1925,"depth":574,"text":5247},"useState — SSR-safe global state",{"id":2127,"depth":574,"text":2128},{"id":2487,"depth":516,"text":2488,"children":5250},[5251,5252,5253,5254],{"id":2501,"depth":574,"text":2502},{"id":3115,"depth":574,"text":3116},{"id":3239,"depth":574,"text":3240},{"id":3433,"depth":574,"text":3434},{"id":3722,"depth":516,"text":3723},{"id":4090,"depth":516,"text":4091},{"id":4708,"depth":516,"text":4709},{"id":4948,"depth":516,"text":4949},{"id":5054,"depth":516,"text":5055},{"id":5160,"depth":516,"text":5161},"useFetch, useAsyncData, $fetch, state management, server API routes, Nitro engine — Nuxt 4 mới nhất.","md",null,{},{"title":373,"icon":91},{"title":422,"description":5261},"nKc8wd4BzRxIfCMD30wUeRX2mnwaC5fEJm1k-6NCA7c",[5269,5271],{"title":368,"path":369,"stem":370,"description":5270,"icon":371,"children":-1},"File-based routing, layouts, middleware, SSR \u002F SSG \u002F SPA \u002F Hybrid rendering — chọn đúng cho app.",{"title":307,"path":377,"stem":378,"description":5272,"icon":196,"children":-1},"45+ câu hỏi Nuxt 4 + Vue 3 cho dev frontend \u002F fullstack — version mới nhất.",1783490360306]