[{"data":1,"prerenderedAt":2196},["ShallowReactive",2],{"page-\u002Fvue\u002F21-build-tooling-and-vite":3},{"id":4,"title":5,"body":6,"description":2189,"extension":2190,"meta":2191,"navigation":89,"path":2192,"seo":2193,"stem":2194,"__hash__":2195},"content\u002Fvue\u002F21-build-tooling-and-vite.md","Vue 3 Engineering Reference — Build Tooling & Vite",{"type":7,"value":8,"toc":2179},"minimark",[9,14,19,287,291,715,719,890,894,1254,1258,2176],[10,11,13],"h1",{"id":12},"_21-build-tooling-vite","21 — Build Tooling & Vite",[15,16,18],"h2",{"id":17},"vite-dev-server-hmr-hot-module-replacement","Vite Dev Server — HMR (Hot Module Replacement)",[20,21,24],"code-wrapper",{"filename":22,"language":23},"hmr-internals.ts","typescript",[25,26,30],"pre",{"className":27,"code":28,"language":23,"meta":29,"style":29},"language-typescript shiki shiki-themes github-light github-dark","\u002F\u002F ── Vite dev server architecture: ──────────────────────\n\u002F\u002F 1. Native ES modules: browser loads modules via \u003Cscript type=\"module\">\n\u002F\u002F    No bundling in dev — each .vue\u002F.ts\u002F.css file is served as a separate module.\n\u002F\u002F 2. On file change: Vite notifies the browser via WebSocket.\n\u002F\u002F 3. Browser re-fetches ONLY the changed module + its dependencies.\n\u002F\u002F 4. Vue SFC HMR: if only \u003Ctemplate> changes → re-render only (preserve state).\n\u002F\u002F    If \u003Cscript> changes → full component reload (state lost).\n\u002F\u002F    If \u003Cstyle> changes → CSS hot-swap (no JS reload at all).\n\n\u002F\u002F ── HMR API for composables: accept module replacement ──\nimport { defineComponent, ref } from 'vue'\n\nif (import.meta.hot) {\n  \u002F\u002F ── import.meta.hot.accept: handle this module's own replacement ──\n  import.meta.hot.accept((newModule) => {\n    \u002F\u002F This callback runs when THIS module is hot-updated.\n    \u002F\u002F newModule is the new version of this module's exports.\n    \u002F\u002F Use to: re-run initialization, migrate state to new module.\n  })\n\n  \u002F\u002F ── import.meta.hot.dispose: cleanup before replacement ──\n  import.meta.hot.dispose((data) => {\n    \u002F\u002F `data` is an object passed to the new module's accept callback.\n    \u002F\u002F Use to: save state, clear timers, remove event listeners.\n    clearInterval(myTimer)\n    data.savedState = myRef.value  \u002F\u002F pass to new module\n  })\n}\n","",[31,32,33,42,48,54,60,66,72,78,84,91,97,115,120,141,147,180,186,192,198,204,209,215,240,246,252,261,276,281],"code",{"__ignoreMap":29},[34,35,38],"span",{"class":36,"line":37},"line",1,[34,39,41],{"class":40},"sdCPZ","\u002F\u002F ── Vite dev server architecture: ──────────────────────\n",[34,43,45],{"class":36,"line":44},2,[34,46,47],{"class":40},"\u002F\u002F 1. Native ES modules: browser loads modules via \u003Cscript type=\"module\">\n",[34,49,51],{"class":36,"line":50},3,[34,52,53],{"class":40},"\u002F\u002F    No bundling in dev — each .vue\u002F.ts\u002F.css file is served as a separate module.\n",[34,55,57],{"class":36,"line":56},4,[34,58,59],{"class":40},"\u002F\u002F 2. On file change: Vite notifies the browser via WebSocket.\n",[34,61,63],{"class":36,"line":62},5,[34,64,65],{"class":40},"\u002F\u002F 3. Browser re-fetches ONLY the changed module + its dependencies.\n",[34,67,69],{"class":36,"line":68},6,[34,70,71],{"class":40},"\u002F\u002F 4. Vue SFC HMR: if only \u003Ctemplate> changes → re-render only (preserve state).\n",[34,73,75],{"class":36,"line":74},7,[34,76,77],{"class":40},"\u002F\u002F    If \u003Cscript> changes → full component reload (state lost).\n",[34,79,81],{"class":36,"line":80},8,[34,82,83],{"class":40},"\u002F\u002F    If \u003Cstyle> changes → CSS hot-swap (no JS reload at all).\n",[34,85,87],{"class":36,"line":86},9,[34,88,90],{"emptyLinePlaceholder":89},true,"\n",[34,92,94],{"class":36,"line":93},10,[34,95,96],{"class":40},"\u002F\u002F ── HMR API for composables: accept module replacement ──\n",[34,98,100,104,108,111],{"class":36,"line":99},11,[34,101,103],{"class":102},"svdQ7","import",[34,105,107],{"class":106},"ssxIu"," { defineComponent, ref } ",[34,109,110],{"class":102},"from",[34,112,114],{"class":113},"sJ6F3"," 'vue'\n",[34,116,118],{"class":36,"line":117},12,[34,119,90],{"emptyLinePlaceholder":89},[34,121,123,126,129,131,134,138],{"class":36,"line":122},13,[34,124,125],{"class":102},"if",[34,127,128],{"class":106}," (",[34,130,103],{"class":102},[34,132,133],{"class":106},".",[34,135,137],{"class":136},"snvgF","meta",[34,139,140],{"class":106},".hot) {\n",[34,142,144],{"class":36,"line":143},14,[34,145,146],{"class":40},"  \u002F\u002F ── import.meta.hot.accept: handle this module's own replacement ──\n",[34,148,150,153,155,157,160,164,167,171,174,177],{"class":36,"line":149},15,[34,151,152],{"class":102},"  import",[34,154,133],{"class":106},[34,156,137],{"class":136},[34,158,159],{"class":106},".hot.",[34,161,163],{"class":162},"sIsaT","accept",[34,165,166],{"class":106},"((",[34,168,170],{"class":169},"sCrzJ","newModule",[34,172,173],{"class":106},") ",[34,175,176],{"class":102},"=>",[34,178,179],{"class":106}," {\n",[34,181,183],{"class":36,"line":182},16,[34,184,185],{"class":40},"    \u002F\u002F This callback runs when THIS module is hot-updated.\n",[34,187,189],{"class":36,"line":188},17,[34,190,191],{"class":40},"    \u002F\u002F newModule is the new version of this module's exports.\n",[34,193,195],{"class":36,"line":194},18,[34,196,197],{"class":40},"    \u002F\u002F Use to: re-run initialization, migrate state to new module.\n",[34,199,201],{"class":36,"line":200},19,[34,202,203],{"class":106},"  })\n",[34,205,207],{"class":36,"line":206},20,[34,208,90],{"emptyLinePlaceholder":89},[34,210,212],{"class":36,"line":211},21,[34,213,214],{"class":40},"  \u002F\u002F ── import.meta.hot.dispose: cleanup before replacement ──\n",[34,216,218,220,222,224,226,229,231,234,236,238],{"class":36,"line":217},22,[34,219,152],{"class":102},[34,221,133],{"class":106},[34,223,137],{"class":136},[34,225,159],{"class":106},[34,227,228],{"class":162},"dispose",[34,230,166],{"class":106},[34,232,233],{"class":169},"data",[34,235,173],{"class":106},[34,237,176],{"class":102},[34,239,179],{"class":106},[34,241,243],{"class":36,"line":242},23,[34,244,245],{"class":40},"    \u002F\u002F `data` is an object passed to the new module's accept callback.\n",[34,247,249],{"class":36,"line":248},24,[34,250,251],{"class":40},"    \u002F\u002F Use to: save state, clear timers, remove event listeners.\n",[34,253,255,258],{"class":36,"line":254},25,[34,256,257],{"class":162},"    clearInterval",[34,259,260],{"class":106},"(myTimer)\n",[34,262,264,267,270,273],{"class":36,"line":263},26,[34,265,266],{"class":106},"    data.savedState ",[34,268,269],{"class":102},"=",[34,271,272],{"class":106}," myRef.value  ",[34,274,275],{"class":40},"\u002F\u002F pass to new module\n",[34,277,279],{"class":36,"line":278},27,[34,280,203],{"class":106},[34,282,284],{"class":36,"line":283},28,[34,285,286],{"class":106},"}\n",[15,288,290],{"id":289},"vite-plugin-pipeline-vue-sfc-compilation","Vite Plugin Pipeline — Vue SFC Compilation",[20,292,294],{"filename":293,"language":23},"vite.config.ts",[25,295,297],{"className":27,"code":296,"language":23,"meta":29,"style":29},"import { defineConfig } from 'vite'\nimport vue from '@vitejs\u002Fplugin-vue'\nimport vueJsx from '@vitejs\u002Fplugin-vue-jsx'\nimport vueDevTools from 'vite-plugin-vue-devtools'\nimport AutoImport from 'unplugin-auto-import\u002Fvite'\nimport Components from 'unplugin-vue-components\u002Fvite'\nimport { fileURLToPath, URL } from 'node:url'\n\nexport default defineConfig({\n  plugins: [\n    \u002F\u002F ── @vitejs\u002Fplugin-vue: compiles .vue files to JS ──\n    \u002F\u002F Handles: \u003Ctemplate> → render function, \u003Cscript setup> → setup(),\n    \u002F\u002F \u003Cstyle scoped> → CSS with data-v attributes.\n    vue(),\n\n    \u002F\u002F ── @vitejs\u002Fplugin-vue-jsx: enables JSX in .tsx files ──\n    \u002F\u002F Uses @vue\u002Fbabel-plugin-jsx under the hood.\n    vueJsx(),\n\n    \u002F\u002F ── vite-plugin-vue-devtools: in-browser DevTools ──\n    vueDevTools(),\n\n    \u002F\u002F ── unplugin-auto-import: auto-import Vue APIs ──\n    \u002F\u002F No need for: import { ref, computed } from 'vue'\n    AutoImport({\n      imports: ['vue', 'vue-router', 'pinia'],\n      dts: 'src\u002Ftypes\u002Fauto-imports.d.ts',  \u002F\u002F TS declaration for intellisense\n    }),\n\n    \u002F\u002F ── unplugin-vue-components: auto-import components ──\n    \u002F\u002F No need for: import MyComponent from '.\u002FMyComponent.vue'\n    Components({\n      dirs: ['src\u002Fcomponents'],\n      extensions: ['vue'],\n      dts: 'src\u002Ftypes\u002Fcomponents.d.ts',\n      \u002F\u002F ── resolvers: auto-import from UI libraries ──\n      \u002F\u002F resolvers: [ElementPlusResolver()],\n    }),\n  ],\n\n  resolve: {\n    alias: {\n      \u002F\u002F ── @ → \u002Fsrc: enables import Foo from '@\u002Fcomponents\u002FFoo.vue' ──\n      '@': fileURLToPath(new URL('.\u002Fsrc', import.meta.url)),\n    },\n  },\n\n  \u002F\u002F ── Environment variables: prefixed with VITE_ ──\n  \u002F\u002F Access via: import.meta.env.VITE_API_BASE\n  \u002F\u002F Only VITE_-prefixed vars are exposed to the client (security: other vars stay server-side).\n  \u002F\u002F Defined in .env, .env.development, .env.production (loaded based on mode).\n})\n",[31,298,299,311,323,335,347,359,371,383,387,401,406,411,416,421,429,433,438,443,450,454,459,466,470,475,480,487,509,523,528,533,539,545,553,564,574,585,591,597,602,608,613,619,625,631,668,674,680,685,691,697,703,709],{"__ignoreMap":29},[34,300,301,303,306,308],{"class":36,"line":37},[34,302,103],{"class":102},[34,304,305],{"class":106}," { defineConfig } ",[34,307,110],{"class":102},[34,309,310],{"class":113}," 'vite'\n",[34,312,313,315,318,320],{"class":36,"line":44},[34,314,103],{"class":102},[34,316,317],{"class":106}," vue ",[34,319,110],{"class":102},[34,321,322],{"class":113}," '@vitejs\u002Fplugin-vue'\n",[34,324,325,327,330,332],{"class":36,"line":50},[34,326,103],{"class":102},[34,328,329],{"class":106}," vueJsx ",[34,331,110],{"class":102},[34,333,334],{"class":113}," '@vitejs\u002Fplugin-vue-jsx'\n",[34,336,337,339,342,344],{"class":36,"line":56},[34,338,103],{"class":102},[34,340,341],{"class":106}," vueDevTools ",[34,343,110],{"class":102},[34,345,346],{"class":113}," 'vite-plugin-vue-devtools'\n",[34,348,349,351,354,356],{"class":36,"line":62},[34,350,103],{"class":102},[34,352,353],{"class":106}," AutoImport ",[34,355,110],{"class":102},[34,357,358],{"class":113}," 'unplugin-auto-import\u002Fvite'\n",[34,360,361,363,366,368],{"class":36,"line":68},[34,362,103],{"class":102},[34,364,365],{"class":106}," Components ",[34,367,110],{"class":102},[34,369,370],{"class":113}," 'unplugin-vue-components\u002Fvite'\n",[34,372,373,375,378,380],{"class":36,"line":74},[34,374,103],{"class":102},[34,376,377],{"class":106}," { fileURLToPath, URL } ",[34,379,110],{"class":102},[34,381,382],{"class":113}," 'node:url'\n",[34,384,385],{"class":36,"line":80},[34,386,90],{"emptyLinePlaceholder":89},[34,388,389,392,395,398],{"class":36,"line":86},[34,390,391],{"class":102},"export",[34,393,394],{"class":102}," default",[34,396,397],{"class":162}," defineConfig",[34,399,400],{"class":106},"({\n",[34,402,403],{"class":36,"line":93},[34,404,405],{"class":106},"  plugins: [\n",[34,407,408],{"class":36,"line":99},[34,409,410],{"class":40},"    \u002F\u002F ── @vitejs\u002Fplugin-vue: compiles .vue files to JS ──\n",[34,412,413],{"class":36,"line":117},[34,414,415],{"class":40},"    \u002F\u002F Handles: \u003Ctemplate> → render function, \u003Cscript setup> → setup(),\n",[34,417,418],{"class":36,"line":122},[34,419,420],{"class":40},"    \u002F\u002F \u003Cstyle scoped> → CSS with data-v attributes.\n",[34,422,423,426],{"class":36,"line":143},[34,424,425],{"class":162},"    vue",[34,427,428],{"class":106},"(),\n",[34,430,431],{"class":36,"line":149},[34,432,90],{"emptyLinePlaceholder":89},[34,434,435],{"class":36,"line":182},[34,436,437],{"class":40},"    \u002F\u002F ── @vitejs\u002Fplugin-vue-jsx: enables JSX in .tsx files ──\n",[34,439,440],{"class":36,"line":188},[34,441,442],{"class":40},"    \u002F\u002F Uses @vue\u002Fbabel-plugin-jsx under the hood.\n",[34,444,445,448],{"class":36,"line":194},[34,446,447],{"class":162},"    vueJsx",[34,449,428],{"class":106},[34,451,452],{"class":36,"line":200},[34,453,90],{"emptyLinePlaceholder":89},[34,455,456],{"class":36,"line":206},[34,457,458],{"class":40},"    \u002F\u002F ── vite-plugin-vue-devtools: in-browser DevTools ──\n",[34,460,461,464],{"class":36,"line":211},[34,462,463],{"class":162},"    vueDevTools",[34,465,428],{"class":106},[34,467,468],{"class":36,"line":217},[34,469,90],{"emptyLinePlaceholder":89},[34,471,472],{"class":36,"line":242},[34,473,474],{"class":40},"    \u002F\u002F ── unplugin-auto-import: auto-import Vue APIs ──\n",[34,476,477],{"class":36,"line":248},[34,478,479],{"class":40},"    \u002F\u002F No need for: import { ref, computed } from 'vue'\n",[34,481,482,485],{"class":36,"line":254},[34,483,484],{"class":162},"    AutoImport",[34,486,400],{"class":106},[34,488,489,492,495,498,501,503,506],{"class":36,"line":263},[34,490,491],{"class":106},"      imports: [",[34,493,494],{"class":113},"'vue'",[34,496,497],{"class":106},", ",[34,499,500],{"class":113},"'vue-router'",[34,502,497],{"class":106},[34,504,505],{"class":113},"'pinia'",[34,507,508],{"class":106},"],\n",[34,510,511,514,517,520],{"class":36,"line":278},[34,512,513],{"class":106},"      dts: ",[34,515,516],{"class":113},"'src\u002Ftypes\u002Fauto-imports.d.ts'",[34,518,519],{"class":106},",  ",[34,521,522],{"class":40},"\u002F\u002F TS declaration for intellisense\n",[34,524,525],{"class":36,"line":283},[34,526,527],{"class":106},"    }),\n",[34,529,531],{"class":36,"line":530},29,[34,532,90],{"emptyLinePlaceholder":89},[34,534,536],{"class":36,"line":535},30,[34,537,538],{"class":40},"    \u002F\u002F ── unplugin-vue-components: auto-import components ──\n",[34,540,542],{"class":36,"line":541},31,[34,543,544],{"class":40},"    \u002F\u002F No need for: import MyComponent from '.\u002FMyComponent.vue'\n",[34,546,548,551],{"class":36,"line":547},32,[34,549,550],{"class":162},"    Components",[34,552,400],{"class":106},[34,554,556,559,562],{"class":36,"line":555},33,[34,557,558],{"class":106},"      dirs: [",[34,560,561],{"class":113},"'src\u002Fcomponents'",[34,563,508],{"class":106},[34,565,567,570,572],{"class":36,"line":566},34,[34,568,569],{"class":106},"      extensions: [",[34,571,494],{"class":113},[34,573,508],{"class":106},[34,575,577,579,582],{"class":36,"line":576},35,[34,578,513],{"class":106},[34,580,581],{"class":113},"'src\u002Ftypes\u002Fcomponents.d.ts'",[34,583,584],{"class":106},",\n",[34,586,588],{"class":36,"line":587},36,[34,589,590],{"class":40},"      \u002F\u002F ── resolvers: auto-import from UI libraries ──\n",[34,592,594],{"class":36,"line":593},37,[34,595,596],{"class":40},"      \u002F\u002F resolvers: [ElementPlusResolver()],\n",[34,598,600],{"class":36,"line":599},38,[34,601,527],{"class":106},[34,603,605],{"class":36,"line":604},39,[34,606,607],{"class":106},"  ],\n",[34,609,611],{"class":36,"line":610},40,[34,612,90],{"emptyLinePlaceholder":89},[34,614,616],{"class":36,"line":615},41,[34,617,618],{"class":106},"  resolve: {\n",[34,620,622],{"class":36,"line":621},42,[34,623,624],{"class":106},"    alias: {\n",[34,626,628],{"class":36,"line":627},43,[34,629,630],{"class":40},"      \u002F\u002F ── @ → \u002Fsrc: enables import Foo from '@\u002Fcomponents\u002FFoo.vue' ──\n",[34,632,634,637,640,643,646,649,652,654,657,659,661,663,665],{"class":36,"line":633},44,[34,635,636],{"class":113},"      '@'",[34,638,639],{"class":106},": ",[34,641,642],{"class":162},"fileURLToPath",[34,644,645],{"class":106},"(",[34,647,648],{"class":102},"new",[34,650,651],{"class":162}," URL",[34,653,645],{"class":106},[34,655,656],{"class":113},"'.\u002Fsrc'",[34,658,497],{"class":106},[34,660,103],{"class":102},[34,662,133],{"class":106},[34,664,137],{"class":136},[34,666,667],{"class":106},".url)),\n",[34,669,671],{"class":36,"line":670},45,[34,672,673],{"class":106},"    },\n",[34,675,677],{"class":36,"line":676},46,[34,678,679],{"class":106},"  },\n",[34,681,683],{"class":36,"line":682},47,[34,684,90],{"emptyLinePlaceholder":89},[34,686,688],{"class":36,"line":687},48,[34,689,690],{"class":40},"  \u002F\u002F ── Environment variables: prefixed with VITE_ ──\n",[34,692,694],{"class":36,"line":693},49,[34,695,696],{"class":40},"  \u002F\u002F Access via: import.meta.env.VITE_API_BASE\n",[34,698,700],{"class":36,"line":699},50,[34,701,702],{"class":40},"  \u002F\u002F Only VITE_-prefixed vars are exposed to the client (security: other vars stay server-side).\n",[34,704,706],{"class":36,"line":705},51,[34,707,708],{"class":40},"  \u002F\u002F Defined in .env, .env.development, .env.production (loaded based on mode).\n",[34,710,712],{"class":36,"line":711},52,[34,713,714],{"class":106},"})\n",[15,716,718],{"id":717},"environment-variables-env-files","Environment Variables — .env Files",[20,720,723],{"filename":721,"language":722},".env","bash",[25,724,727],{"className":725,"code":726,"language":722,"meta":29,"style":29},"language-bash shiki shiki-themes github-light github-dark","# ── .env: loaded in all modes ──────────────────────────\nVITE_API_BASE=http:\u002F\u002Flocalhost:3000\u002Fapi\nVITE_APP_TITLE=My Vue App\n\n# ── .env.development: dev mode only (npm run dev) ───────\n# .env.development\nVITE_API_BASE=http:\u002F\u002Flocalhost:3000\u002Fapi\nVITE_DEBUG=true\n\n# ── .env.production: production build only ──────────────\n# .env.production\nVITE_API_BASE=https:\u002F\u002Fapi.myapp.com\nVITE_DEBUG=false\n\n# ── .env.local: local overrides, gitignored ─────────────\n# .env.local — loaded in ALL modes, highest priority\nVITE_API_KEY=my-secret-key  # never committed to git\n\n# ── Access in code: ────────────────────────────────────\n# import.meta.env.VITE_API_BASE  → string (always string, even if \"true\")\n# import.meta.env.DEV           → boolean (true in dev mode)\n# import.meta.env.PROD          → boolean (true in production)\n# import.meta.env.MODE          → string ('development' | 'production' | 'test')\n# import.meta.env.BASE_URL      → string (from vite.config base option)\n# import.meta.env.SSR           → boolean (true during SSR build)\n",[31,728,729,734,744,760,764,769,774,782,792,796,801,806,815,824,828,833,838,851,855,860,865,870,875,880,885],{"__ignoreMap":29},[34,730,731],{"class":36,"line":37},[34,732,733],{"class":40},"# ── .env: loaded in all modes ──────────────────────────\n",[34,735,736,739,741],{"class":36,"line":44},[34,737,738],{"class":106},"VITE_API_BASE",[34,740,269],{"class":102},[34,742,743],{"class":113},"http:\u002F\u002Flocalhost:3000\u002Fapi\n",[34,745,746,749,751,754,757],{"class":36,"line":50},[34,747,748],{"class":106},"VITE_APP_TITLE",[34,750,269],{"class":102},[34,752,753],{"class":113},"My",[34,755,756],{"class":162}," Vue",[34,758,759],{"class":113}," App\n",[34,761,762],{"class":36,"line":56},[34,763,90],{"emptyLinePlaceholder":89},[34,765,766],{"class":36,"line":62},[34,767,768],{"class":40},"# ── .env.development: dev mode only (npm run dev) ───────\n",[34,770,771],{"class":36,"line":68},[34,772,773],{"class":40},"# .env.development\n",[34,775,776,778,780],{"class":36,"line":74},[34,777,738],{"class":106},[34,779,269],{"class":102},[34,781,743],{"class":113},[34,783,784,787,789],{"class":36,"line":80},[34,785,786],{"class":106},"VITE_DEBUG",[34,788,269],{"class":102},[34,790,791],{"class":113},"true\n",[34,793,794],{"class":36,"line":86},[34,795,90],{"emptyLinePlaceholder":89},[34,797,798],{"class":36,"line":93},[34,799,800],{"class":40},"# ── .env.production: production build only ──────────────\n",[34,802,803],{"class":36,"line":99},[34,804,805],{"class":40},"# .env.production\n",[34,807,808,810,812],{"class":36,"line":117},[34,809,738],{"class":106},[34,811,269],{"class":102},[34,813,814],{"class":113},"https:\u002F\u002Fapi.myapp.com\n",[34,816,817,819,821],{"class":36,"line":122},[34,818,786],{"class":106},[34,820,269],{"class":102},[34,822,823],{"class":113},"false\n",[34,825,826],{"class":36,"line":143},[34,827,90],{"emptyLinePlaceholder":89},[34,829,830],{"class":36,"line":149},[34,831,832],{"class":40},"# ── .env.local: local overrides, gitignored ─────────────\n",[34,834,835],{"class":36,"line":182},[34,836,837],{"class":40},"# .env.local — loaded in ALL modes, highest priority\n",[34,839,840,843,845,848],{"class":36,"line":188},[34,841,842],{"class":106},"VITE_API_KEY",[34,844,269],{"class":102},[34,846,847],{"class":113},"my-secret-key",[34,849,850],{"class":40},"  # never committed to git\n",[34,852,853],{"class":36,"line":194},[34,854,90],{"emptyLinePlaceholder":89},[34,856,857],{"class":36,"line":200},[34,858,859],{"class":40},"# ── Access in code: ────────────────────────────────────\n",[34,861,862],{"class":36,"line":206},[34,863,864],{"class":40},"# import.meta.env.VITE_API_BASE  → string (always string, even if \"true\")\n",[34,866,867],{"class":36,"line":211},[34,868,869],{"class":40},"# import.meta.env.DEV           → boolean (true in dev mode)\n",[34,871,872],{"class":36,"line":217},[34,873,874],{"class":40},"# import.meta.env.PROD          → boolean (true in production)\n",[34,876,877],{"class":36,"line":242},[34,878,879],{"class":40},"# import.meta.env.MODE          → string ('development' | 'production' | 'test')\n",[34,881,882],{"class":36,"line":248},[34,883,884],{"class":40},"# import.meta.env.BASE_URL      → string (from vite.config base option)\n",[34,886,887],{"class":36,"line":254},[34,888,889],{"class":40},"# import.meta.env.SSR           → boolean (true during SSR build)\n",[15,891,893],{"id":892},"production-build-optimization-configuration","Production Build — Optimization Configuration",[20,895,897],{"filename":896,"language":23},"build-optimization.ts",[25,898,900],{"className":27,"code":899,"language":23,"meta":29,"style":29},"import { defineConfig } from 'vite'\n\nexport default defineConfig({\n  build: {\n    \u002F\u002F ── target: browser feature level for output JS ──\n    \u002F\u002F 'esnext': modern browsers only (smaller, no polyfills)\n    \u002F\u002F 'es2015': broader support (larger, includes polyfills)\n    target: 'esnext',\n\n    \u002F\u002F ── outDir: output directory (default: 'dist') ──\n    outDir: 'dist',\n\n    \u002F\u002F ── sourcemap: production source maps ──\n    \u002F\u002F true: full sourcemaps (large, for error tracking)\n    \u002F\u002F 'hidden': sourcemaps generated but not linked (upload to Sentry, etc.)\n    \u002F\u002F false: no sourcemaps (smallest, hardest to debug)\n    sourcemap: 'hidden',\n\n    \u002F\u002F ── minify: minification tool ──\n    \u002F\u002F 'esbuild': fast, good default (Vite's default)\n    \u002F\u002F 'terser': slower, slightly smaller output\n    \u002F\u002F false: no minification (debug builds)\n    minify: 'esbuild',\n\n    \u002F\u002F ── cssCodeSplit: split CSS per-chunk ──\n    \u002F\u002F true (default): each async chunk gets its own CSS file (lazy-loaded)\n    \u002F\u002F false: all CSS in one file (fewer requests, larger initial load)\n    cssCodeSplit: true,\n\n    \u002F\u002F ── rollupOptions: advanced build customization ──\n    rollupOptions: {\n      output: {\n        \u002F\u002F ── manualChunks: split vendor code for caching ──\n        manualChunks: {\n          'vendor-vue': ['vue', 'vue-router', 'pinia'],\n          'vendor-ui': ['@vueuse\u002Fcore'],\n        },\n        \u002F\u002F ── Asset naming: deterministic for cache stability ──\n        chunkFileNames: 'assets\u002Fjs\u002F[name]-[hash].js',\n        assetFileNames: 'assets\u002F[ext]\u002F[name]-[hash].[ext]',\n      },\n      \u002F\u002F ── external: don't bundle these (load via CDN or other means) ──\n      \u002F\u002F external: ['vue', 'vue-router'],\n    },\n\n    \u002F\u002F ── chunkSizeWarningLimit: raise if vendor chunks are large ──\n    chunkSizeWarningLimit: 600,\n  },\n\n  \u002F\u002F ── esbuild: esbuild-specific options ──\n  esbuild: {\n    \u002F\u002F ── drop: strip console.log and debugger in production ──\n    drop: ['console', 'debugger'],  \u002F\u002F only in production builds\n  },\n})\n",[31,901,902,912,916,926,931,936,941,946,956,960,965,975,979,984,989,994,999,1009,1013,1018,1023,1028,1033,1043,1047,1052,1057,1062,1072,1076,1081,1086,1091,1096,1101,1121,1133,1138,1143,1153,1163,1168,1173,1178,1182,1186,1191,1201,1205,1209,1214,1219,1224,1244,1249],{"__ignoreMap":29},[34,903,904,906,908,910],{"class":36,"line":37},[34,905,103],{"class":102},[34,907,305],{"class":106},[34,909,110],{"class":102},[34,911,310],{"class":113},[34,913,914],{"class":36,"line":44},[34,915,90],{"emptyLinePlaceholder":89},[34,917,918,920,922,924],{"class":36,"line":50},[34,919,391],{"class":102},[34,921,394],{"class":102},[34,923,397],{"class":162},[34,925,400],{"class":106},[34,927,928],{"class":36,"line":56},[34,929,930],{"class":106},"  build: {\n",[34,932,933],{"class":36,"line":62},[34,934,935],{"class":40},"    \u002F\u002F ── target: browser feature level for output JS ──\n",[34,937,938],{"class":36,"line":68},[34,939,940],{"class":40},"    \u002F\u002F 'esnext': modern browsers only (smaller, no polyfills)\n",[34,942,943],{"class":36,"line":74},[34,944,945],{"class":40},"    \u002F\u002F 'es2015': broader support (larger, includes polyfills)\n",[34,947,948,951,954],{"class":36,"line":80},[34,949,950],{"class":106},"    target: ",[34,952,953],{"class":113},"'esnext'",[34,955,584],{"class":106},[34,957,958],{"class":36,"line":86},[34,959,90],{"emptyLinePlaceholder":89},[34,961,962],{"class":36,"line":93},[34,963,964],{"class":40},"    \u002F\u002F ── outDir: output directory (default: 'dist') ──\n",[34,966,967,970,973],{"class":36,"line":99},[34,968,969],{"class":106},"    outDir: ",[34,971,972],{"class":113},"'dist'",[34,974,584],{"class":106},[34,976,977],{"class":36,"line":117},[34,978,90],{"emptyLinePlaceholder":89},[34,980,981],{"class":36,"line":122},[34,982,983],{"class":40},"    \u002F\u002F ── sourcemap: production source maps ──\n",[34,985,986],{"class":36,"line":143},[34,987,988],{"class":40},"    \u002F\u002F true: full sourcemaps (large, for error tracking)\n",[34,990,991],{"class":36,"line":149},[34,992,993],{"class":40},"    \u002F\u002F 'hidden': sourcemaps generated but not linked (upload to Sentry, etc.)\n",[34,995,996],{"class":36,"line":182},[34,997,998],{"class":40},"    \u002F\u002F false: no sourcemaps (smallest, hardest to debug)\n",[34,1000,1001,1004,1007],{"class":36,"line":188},[34,1002,1003],{"class":106},"    sourcemap: ",[34,1005,1006],{"class":113},"'hidden'",[34,1008,584],{"class":106},[34,1010,1011],{"class":36,"line":194},[34,1012,90],{"emptyLinePlaceholder":89},[34,1014,1015],{"class":36,"line":200},[34,1016,1017],{"class":40},"    \u002F\u002F ── minify: minification tool ──\n",[34,1019,1020],{"class":36,"line":206},[34,1021,1022],{"class":40},"    \u002F\u002F 'esbuild': fast, good default (Vite's default)\n",[34,1024,1025],{"class":36,"line":211},[34,1026,1027],{"class":40},"    \u002F\u002F 'terser': slower, slightly smaller output\n",[34,1029,1030],{"class":36,"line":217},[34,1031,1032],{"class":40},"    \u002F\u002F false: no minification (debug builds)\n",[34,1034,1035,1038,1041],{"class":36,"line":242},[34,1036,1037],{"class":106},"    minify: ",[34,1039,1040],{"class":113},"'esbuild'",[34,1042,584],{"class":106},[34,1044,1045],{"class":36,"line":248},[34,1046,90],{"emptyLinePlaceholder":89},[34,1048,1049],{"class":36,"line":254},[34,1050,1051],{"class":40},"    \u002F\u002F ── cssCodeSplit: split CSS per-chunk ──\n",[34,1053,1054],{"class":36,"line":263},[34,1055,1056],{"class":40},"    \u002F\u002F true (default): each async chunk gets its own CSS file (lazy-loaded)\n",[34,1058,1059],{"class":36,"line":278},[34,1060,1061],{"class":40},"    \u002F\u002F false: all CSS in one file (fewer requests, larger initial load)\n",[34,1063,1064,1067,1070],{"class":36,"line":283},[34,1065,1066],{"class":106},"    cssCodeSplit: ",[34,1068,1069],{"class":136},"true",[34,1071,584],{"class":106},[34,1073,1074],{"class":36,"line":530},[34,1075,90],{"emptyLinePlaceholder":89},[34,1077,1078],{"class":36,"line":535},[34,1079,1080],{"class":40},"    \u002F\u002F ── rollupOptions: advanced build customization ──\n",[34,1082,1083],{"class":36,"line":541},[34,1084,1085],{"class":106},"    rollupOptions: {\n",[34,1087,1088],{"class":36,"line":547},[34,1089,1090],{"class":106},"      output: {\n",[34,1092,1093],{"class":36,"line":555},[34,1094,1095],{"class":40},"        \u002F\u002F ── manualChunks: split vendor code for caching ──\n",[34,1097,1098],{"class":36,"line":566},[34,1099,1100],{"class":106},"        manualChunks: {\n",[34,1102,1103,1106,1109,1111,1113,1115,1117,1119],{"class":36,"line":576},[34,1104,1105],{"class":113},"          'vendor-vue'",[34,1107,1108],{"class":106},": [",[34,1110,494],{"class":113},[34,1112,497],{"class":106},[34,1114,500],{"class":113},[34,1116,497],{"class":106},[34,1118,505],{"class":113},[34,1120,508],{"class":106},[34,1122,1123,1126,1128,1131],{"class":36,"line":587},[34,1124,1125],{"class":113},"          'vendor-ui'",[34,1127,1108],{"class":106},[34,1129,1130],{"class":113},"'@vueuse\u002Fcore'",[34,1132,508],{"class":106},[34,1134,1135],{"class":36,"line":593},[34,1136,1137],{"class":106},"        },\n",[34,1139,1140],{"class":36,"line":599},[34,1141,1142],{"class":40},"        \u002F\u002F ── Asset naming: deterministic for cache stability ──\n",[34,1144,1145,1148,1151],{"class":36,"line":604},[34,1146,1147],{"class":106},"        chunkFileNames: ",[34,1149,1150],{"class":113},"'assets\u002Fjs\u002F[name]-[hash].js'",[34,1152,584],{"class":106},[34,1154,1155,1158,1161],{"class":36,"line":610},[34,1156,1157],{"class":106},"        assetFileNames: ",[34,1159,1160],{"class":113},"'assets\u002F[ext]\u002F[name]-[hash].[ext]'",[34,1162,584],{"class":106},[34,1164,1165],{"class":36,"line":615},[34,1166,1167],{"class":106},"      },\n",[34,1169,1170],{"class":36,"line":621},[34,1171,1172],{"class":40},"      \u002F\u002F ── external: don't bundle these (load via CDN or other means) ──\n",[34,1174,1175],{"class":36,"line":627},[34,1176,1177],{"class":40},"      \u002F\u002F external: ['vue', 'vue-router'],\n",[34,1179,1180],{"class":36,"line":633},[34,1181,673],{"class":106},[34,1183,1184],{"class":36,"line":670},[34,1185,90],{"emptyLinePlaceholder":89},[34,1187,1188],{"class":36,"line":676},[34,1189,1190],{"class":40},"    \u002F\u002F ── chunkSizeWarningLimit: raise if vendor chunks are large ──\n",[34,1192,1193,1196,1199],{"class":36,"line":682},[34,1194,1195],{"class":106},"    chunkSizeWarningLimit: ",[34,1197,1198],{"class":136},"600",[34,1200,584],{"class":106},[34,1202,1203],{"class":36,"line":687},[34,1204,679],{"class":106},[34,1206,1207],{"class":36,"line":693},[34,1208,90],{"emptyLinePlaceholder":89},[34,1210,1211],{"class":36,"line":699},[34,1212,1213],{"class":40},"  \u002F\u002F ── esbuild: esbuild-specific options ──\n",[34,1215,1216],{"class":36,"line":705},[34,1217,1218],{"class":106},"  esbuild: {\n",[34,1220,1221],{"class":36,"line":711},[34,1222,1223],{"class":40},"    \u002F\u002F ── drop: strip console.log and debugger in production ──\n",[34,1225,1227,1230,1233,1235,1238,1241],{"class":36,"line":1226},53,[34,1228,1229],{"class":106},"    drop: [",[34,1231,1232],{"class":113},"'console'",[34,1234,497],{"class":106},[34,1236,1237],{"class":113},"'debugger'",[34,1239,1240],{"class":106},"],  ",[34,1242,1243],{"class":40},"\u002F\u002F only in production builds\n",[34,1245,1247],{"class":36,"line":1246},54,[34,1248,679],{"class":106},[34,1250,1252],{"class":36,"line":1251},55,[34,1253,714],{"class":106},[15,1255,1257],{"id":1256},"css-processing-scoped-modules-and-preprocessors","CSS Processing — Scoped, Modules, and Preprocessors",[20,1259,1262,1499,1641,1645,1865,1869,1987,1991,1999,2081],{"filename":1260,"language":1261},"CssPatterns.vue","vue",[25,1263,1266],{"className":1264,"code":1265,"language":1261,"meta":29,"style":29},"language-vue shiki shiki-themes github-light github-dark","\u003C!-- ── 1. Scoped CSS: component-scoped via data-v-\u003Chash> ── -->\n\u003C!-- Compiler rewrites .btn → .btn[data-v-abc123] at build time. -->\n\u003C!-- No runtime cost — pure build-time transformation. -->\n\u003Cstyle scoped>\n.btn { color: red; }\n\u002F* Deep selector: ::v-deep (or :deep()) pierces scoped boundary *\u002F\n:deep(.child-class) { font-weight: bold; }\n\u002F* ⚠️ :deep() is an escape hatch — breaks encapsulation. Use sparingly. *\u002F\n\u003C\u002Fstyle>\n\n\u003C!-- ── 2. CSS Modules: class names become camelCase JS properties ── -->\n\u003Cstyle module>\n.btn { color: red; }\n.container { max-width: 1200px; }\n\u003C\u002Fstyle>\n\u003C!-- Access: $style.btn, $style.container in template -->\n\u003C!-- \u003Cdiv :class=\"$style.btn\"> — hashed class names, no conflicts -->\n\n\u003C!-- ── 3. v-bind in CSS: reactive CSS values ── -->\n\u003Cstyle scoped>\n.header {\n  color: v-bind('themeColor');  \u002F* reads themeColor from \u003Cscript setup> *\u002F\n  font-size: v-bind('fontSize + \"px\"');  \u002F* arbitrary JS expression *\u002F\n}\n\u003C\u002Fstyle>\n",[31,1267,1268,1273,1278,1283,1298,1317,1322,1343,1348,1357,1361,1366,1377,1391,1411,1419,1424,1429,1433,1438,1448,1455,1472,1487,1491],{"__ignoreMap":29},[34,1269,1270],{"class":36,"line":37},[34,1271,1272],{"class":40},"\u003C!-- ── 1. Scoped CSS: component-scoped via data-v-\u003Chash> ── -->\n",[34,1274,1275],{"class":36,"line":44},[34,1276,1277],{"class":40},"\u003C!-- Compiler rewrites .btn → .btn[data-v-abc123] at build time. -->\n",[34,1279,1280],{"class":36,"line":50},[34,1281,1282],{"class":40},"\u003C!-- No runtime cost — pure build-time transformation. -->\n",[34,1284,1285,1288,1292,1295],{"class":36,"line":56},[34,1286,1287],{"class":106},"\u003C",[34,1289,1291],{"class":1290},"sk71V","style",[34,1293,1294],{"class":162}," scoped",[34,1296,1297],{"class":106},">\n",[34,1299,1300,1303,1306,1309,1311,1314],{"class":36,"line":62},[34,1301,1302],{"class":162},".btn",[34,1304,1305],{"class":106}," { ",[34,1307,1308],{"class":136},"color",[34,1310,639],{"class":106},[34,1312,1313],{"class":136},"red",[34,1315,1316],{"class":106},"; }\n",[34,1318,1319],{"class":36,"line":68},[34,1320,1321],{"class":40},"\u002F* Deep selector: ::v-deep (or :deep()) pierces scoped boundary *\u002F\n",[34,1323,1324,1327,1330,1333,1336,1338,1341],{"class":36,"line":74},[34,1325,1326],{"class":106},":deep(",[34,1328,1329],{"class":162},".child-class",[34,1331,1332],{"class":106},") { ",[34,1334,1335],{"class":136},"font-weight",[34,1337,639],{"class":106},[34,1339,1340],{"class":136},"bold",[34,1342,1316],{"class":106},[34,1344,1345],{"class":36,"line":80},[34,1346,1347],{"class":40},"\u002F* ⚠️ :deep() is an escape hatch — breaks encapsulation. Use sparingly. *\u002F\n",[34,1349,1350,1353,1355],{"class":36,"line":86},[34,1351,1352],{"class":106},"\u003C\u002F",[34,1354,1291],{"class":1290},[34,1356,1297],{"class":106},[34,1358,1359],{"class":36,"line":93},[34,1360,90],{"emptyLinePlaceholder":89},[34,1362,1363],{"class":36,"line":99},[34,1364,1365],{"class":40},"\u003C!-- ── 2. CSS Modules: class names become camelCase JS properties ── -->\n",[34,1367,1368,1370,1372,1375],{"class":36,"line":117},[34,1369,1287],{"class":106},[34,1371,1291],{"class":1290},[34,1373,1374],{"class":162}," module",[34,1376,1297],{"class":106},[34,1378,1379,1381,1383,1385,1387,1389],{"class":36,"line":122},[34,1380,1302],{"class":162},[34,1382,1305],{"class":106},[34,1384,1308],{"class":136},[34,1386,639],{"class":106},[34,1388,1313],{"class":136},[34,1390,1316],{"class":106},[34,1392,1393,1396,1398,1401,1403,1406,1409],{"class":36,"line":143},[34,1394,1395],{"class":162},".container",[34,1397,1305],{"class":106},[34,1399,1400],{"class":136},"max-width",[34,1402,639],{"class":106},[34,1404,1405],{"class":136},"1200",[34,1407,1408],{"class":102},"px",[34,1410,1316],{"class":106},[34,1412,1413,1415,1417],{"class":36,"line":149},[34,1414,1352],{"class":106},[34,1416,1291],{"class":1290},[34,1418,1297],{"class":106},[34,1420,1421],{"class":36,"line":182},[34,1422,1423],{"class":40},"\u003C!-- Access: $style.btn, $style.container in template -->\n",[34,1425,1426],{"class":36,"line":188},[34,1427,1428],{"class":40},"\u003C!-- \u003Cdiv :class=\"$style.btn\"> — hashed class names, no conflicts -->\n",[34,1430,1431],{"class":36,"line":194},[34,1432,90],{"emptyLinePlaceholder":89},[34,1434,1435],{"class":36,"line":200},[34,1436,1437],{"class":40},"\u003C!-- ── 3. v-bind in CSS: reactive CSS values ── -->\n",[34,1439,1440,1442,1444,1446],{"class":36,"line":206},[34,1441,1287],{"class":106},[34,1443,1291],{"class":1290},[34,1445,1294],{"class":162},[34,1447,1297],{"class":106},[34,1449,1450,1453],{"class":36,"line":211},[34,1451,1452],{"class":162},".header",[34,1454,179],{"class":106},[34,1456,1457,1460,1463,1466,1469],{"class":36,"line":217},[34,1458,1459],{"class":136},"  color",[34,1461,1462],{"class":106},": v-bind(",[34,1464,1465],{"class":113},"'themeColor'",[34,1467,1468],{"class":106},");  ",[34,1470,1471],{"class":40},"\u002F* reads themeColor from \u003Cscript setup> *\u002F\n",[34,1473,1474,1477,1479,1482,1484],{"class":36,"line":242},[34,1475,1476],{"class":136},"  font-size",[34,1478,1462],{"class":106},[34,1480,1481],{"class":113},"'fontSize + \"px\"'",[34,1483,1468],{"class":106},[34,1485,1486],{"class":40},"\u002F* arbitrary JS expression *\u002F\n",[34,1488,1489],{"class":36,"line":248},[34,1490,286],{"class":106},[34,1492,1493,1495,1497],{"class":36,"line":254},[34,1494,1352],{"class":106},[34,1496,1291],{"class":1290},[34,1498,1297],{"class":106},[20,1500,1503],{"filename":1501,"language":1502},"preprocessor.scss","scss",[25,1504,1507],{"className":1505,"code":1506,"language":1502,"meta":29,"style":29},"language-scss shiki shiki-themes github-light github-dark","\u002F* ── Sass\u002FSCSS: use lang=\"scss\" on \u003Cstyle> ── *\u002F\n\u002F* vite.config: no extra config needed — Vite auto-detects .scss *\u002F\n\u003Cstyle lang=\"scss\" scoped>\n$primary: #42b883;\n\n.btn {\n  color: $primary;\n  &:hover { color: darken($primary, 10%); }\n}\n\n\u002F* ── CSS variables with SCSS: use :root for global, scoped for local ── *\u002F\n:root {\n  --primary: #{$primary};  \u002F* SCSS variable → CSS custom property *\u002F\n}\n\u003C\u002Fstyle>\n",[31,1508,1509,1514,1519,1528,1541,1545,1551,1561,1593,1597,1601,1606,1613,1629,1633],{"__ignoreMap":29},[34,1510,1511],{"class":36,"line":37},[34,1512,1513],{"class":40},"\u002F* ── Sass\u002FSCSS: use lang=\"scss\" on \u003Cstyle> ── *\u002F\n",[34,1515,1516],{"class":36,"line":44},[34,1517,1518],{"class":40},"\u002F* vite.config: no extra config needed — Vite auto-detects .scss *\u002F\n",[34,1520,1521,1523,1525],{"class":36,"line":50},[34,1522,1287],{"class":106},[34,1524,1291],{"class":1290},[34,1526,1527],{"class":106}," lang=\"scss\" scoped>\n",[34,1529,1530,1533,1535,1538],{"class":36,"line":56},[34,1531,1532],{"class":169},"$primary",[34,1534,639],{"class":106},[34,1536,1537],{"class":136},"#42b883",[34,1539,1540],{"class":106},";\n",[34,1542,1543],{"class":36,"line":62},[34,1544,90],{"emptyLinePlaceholder":89},[34,1546,1547,1549],{"class":36,"line":68},[34,1548,1302],{"class":162},[34,1550,179],{"class":106},[34,1552,1553,1555,1557,1559],{"class":36,"line":74},[34,1554,1459],{"class":136},[34,1556,639],{"class":106},[34,1558,1532],{"class":169},[34,1560,1540],{"class":106},[34,1562,1563,1566,1569,1571,1573,1575,1578,1580,1582,1584,1587,1590],{"class":36,"line":80},[34,1564,1565],{"class":1290},"  &",[34,1567,1568],{"class":162},":hover",[34,1570,1305],{"class":106},[34,1572,1308],{"class":136},[34,1574,639],{"class":106},[34,1576,1577],{"class":136},"darken",[34,1579,645],{"class":106},[34,1581,1532],{"class":169},[34,1583,497],{"class":106},[34,1585,1586],{"class":136},"10",[34,1588,1589],{"class":102},"%",[34,1591,1592],{"class":106},"); }\n",[34,1594,1595],{"class":36,"line":86},[34,1596,286],{"class":106},[34,1598,1599],{"class":36,"line":93},[34,1600,90],{"emptyLinePlaceholder":89},[34,1602,1603],{"class":36,"line":99},[34,1604,1605],{"class":40},"\u002F* ── CSS variables with SCSS: use :root for global, scoped for local ── *\u002F\n",[34,1607,1608,1611],{"class":36,"line":117},[34,1609,1610],{"class":162},":root",[34,1612,179],{"class":106},[34,1614,1615,1618,1620,1623,1626],{"class":36,"line":122},[34,1616,1617],{"class":169},"  --primary",[34,1619,639],{"class":106},[34,1621,1622],{"class":169},"#{$primary}",[34,1624,1625],{"class":106},";  ",[34,1627,1628],{"class":40},"\u002F* SCSS variable → CSS custom property *\u002F\n",[34,1630,1631],{"class":36,"line":143},[34,1632,286],{"class":106},[34,1634,1635,1637,1639],{"class":36,"line":149},[34,1636,1352],{"class":106},[34,1638,1291],{"class":1290},[34,1640,1297],{"class":106},[15,1642,1644],{"id":1643},"tips-tricks","💡 Tips & Tricks",[20,1646,1648],{"filename":1647,"language":23},"tips.ts",[25,1649,1651],{"className":27,"code":1650,"language":23,"meta":29,"style":29},"\u002F\u002F ── 1. import.meta.glob: lazy-load all files matching a pattern ──\nconst modules = import.meta.glob('.\u002Fviews\u002F*.vue')\n\u002F\u002F Returns: { '.\u002Fviews\u002FHome.vue': () => import('.\u002Fviews\u002FHome.vue'), ... }\n\u002F\u002F Useful for: dynamic route generation, auto-registering all components.\n\n\u002F\u002F ── 2. import.meta.glob with eager option ──\nconst modules = import.meta.glob('.\u002Futils\u002F*.ts', { eager: true })\n\u002F\u002F All modules loaded synchronously (no lazy import), default exports available.\n\n\u002F\u002F ── 3. Vite define: compile-time constants ──\ndefine: { __APP_VERSION__: JSON.stringify(pkg.version) }\n\u002F\u002F Access: __APP_VERSION__ in code — replaced at build time, no runtime cost.\n\n\u002F\u002F ── 4. resolve.dedupe: deduplicate shared deps ──\nresolve: { dedupe: ['vue'] }\n\u002F\u002F If multiple versions of 'vue' are in node_modules (monorepo), dedupe to one.\n\n\u002F\u002F ── 5. server.proxy: proxy API requests in dev ──\nserver: {\n  proxy: {\n    '\u002Fapi': 'http:\u002F\u002Flocalhost:3000',  \u002F\u002F forward \u002Fapi to backend\n  },\n}\n\u002F\u002F Avoids CORS in dev — requests go through Vite dev server to backend.\n",[31,1652,1653,1658,1689,1694,1699,1703,1708,1739,1744,1748,1753,1777,1782,1786,1791,1808,1813,1817,1822,1830,1837,1852,1856,1860],{"__ignoreMap":29},[34,1654,1655],{"class":36,"line":37},[34,1656,1657],{"class":40},"\u002F\u002F ── 1. import.meta.glob: lazy-load all files matching a pattern ──\n",[34,1659,1660,1663,1666,1669,1672,1674,1676,1678,1681,1683,1686],{"class":36,"line":44},[34,1661,1662],{"class":102},"const",[34,1664,1665],{"class":136}," modules",[34,1667,1668],{"class":102}," =",[34,1670,1671],{"class":102}," import",[34,1673,133],{"class":106},[34,1675,137],{"class":136},[34,1677,133],{"class":106},[34,1679,1680],{"class":162},"glob",[34,1682,645],{"class":106},[34,1684,1685],{"class":113},"'.\u002Fviews\u002F*.vue'",[34,1687,1688],{"class":106},")\n",[34,1690,1691],{"class":36,"line":50},[34,1692,1693],{"class":40},"\u002F\u002F Returns: { '.\u002Fviews\u002FHome.vue': () => import('.\u002Fviews\u002FHome.vue'), ... }\n",[34,1695,1696],{"class":36,"line":56},[34,1697,1698],{"class":40},"\u002F\u002F Useful for: dynamic route generation, auto-registering all components.\n",[34,1700,1701],{"class":36,"line":62},[34,1702,90],{"emptyLinePlaceholder":89},[34,1704,1705],{"class":36,"line":68},[34,1706,1707],{"class":40},"\u002F\u002F ── 2. import.meta.glob with eager option ──\n",[34,1709,1710,1712,1714,1716,1718,1720,1722,1724,1726,1728,1731,1734,1736],{"class":36,"line":74},[34,1711,1662],{"class":102},[34,1713,1665],{"class":136},[34,1715,1668],{"class":102},[34,1717,1671],{"class":102},[34,1719,133],{"class":106},[34,1721,137],{"class":136},[34,1723,133],{"class":106},[34,1725,1680],{"class":162},[34,1727,645],{"class":106},[34,1729,1730],{"class":113},"'.\u002Futils\u002F*.ts'",[34,1732,1733],{"class":106},", { eager: ",[34,1735,1069],{"class":136},[34,1737,1738],{"class":106}," })\n",[34,1740,1741],{"class":36,"line":80},[34,1742,1743],{"class":40},"\u002F\u002F All modules loaded synchronously (no lazy import), default exports available.\n",[34,1745,1746],{"class":36,"line":86},[34,1747,90],{"emptyLinePlaceholder":89},[34,1749,1750],{"class":36,"line":93},[34,1751,1752],{"class":40},"\u002F\u002F ── 3. Vite define: compile-time constants ──\n",[34,1754,1755,1758,1761,1764,1766,1769,1771,1774],{"class":36,"line":99},[34,1756,1757],{"class":162},"define",[34,1759,1760],{"class":106},": { ",[34,1762,1763],{"class":162},"__APP_VERSION__",[34,1765,639],{"class":106},[34,1767,1768],{"class":136},"JSON",[34,1770,133],{"class":106},[34,1772,1773],{"class":162},"stringify",[34,1775,1776],{"class":106},"(pkg.version) }\n",[34,1778,1779],{"class":36,"line":117},[34,1780,1781],{"class":40},"\u002F\u002F Access: __APP_VERSION__ in code — replaced at build time, no runtime cost.\n",[34,1783,1784],{"class":36,"line":122},[34,1785,90],{"emptyLinePlaceholder":89},[34,1787,1788],{"class":36,"line":143},[34,1789,1790],{"class":40},"\u002F\u002F ── 4. resolve.dedupe: deduplicate shared deps ──\n",[34,1792,1793,1796,1798,1801,1803,1805],{"class":36,"line":149},[34,1794,1795],{"class":162},"resolve",[34,1797,1760],{"class":106},[34,1799,1800],{"class":162},"dedupe",[34,1802,1108],{"class":106},[34,1804,494],{"class":113},[34,1806,1807],{"class":106},"] }\n",[34,1809,1810],{"class":36,"line":182},[34,1811,1812],{"class":40},"\u002F\u002F If multiple versions of 'vue' are in node_modules (monorepo), dedupe to one.\n",[34,1814,1815],{"class":36,"line":188},[34,1816,90],{"emptyLinePlaceholder":89},[34,1818,1819],{"class":36,"line":194},[34,1820,1821],{"class":40},"\u002F\u002F ── 5. server.proxy: proxy API requests in dev ──\n",[34,1823,1824,1827],{"class":36,"line":200},[34,1825,1826],{"class":162},"server",[34,1828,1829],{"class":106},": {\n",[34,1831,1832,1835],{"class":36,"line":206},[34,1833,1834],{"class":162},"  proxy",[34,1836,1829],{"class":106},[34,1838,1839,1842,1844,1847,1849],{"class":36,"line":211},[34,1840,1841],{"class":113},"    '\u002Fapi'",[34,1843,639],{"class":106},[34,1845,1846],{"class":113},"'http:\u002F\u002Flocalhost:3000'",[34,1848,519],{"class":106},[34,1850,1851],{"class":40},"\u002F\u002F forward \u002Fapi to backend\n",[34,1853,1854],{"class":36,"line":217},[34,1855,679],{"class":106},[34,1857,1858],{"class":36,"line":242},[34,1859,286],{"class":106},[34,1861,1862],{"class":36,"line":248},[34,1863,1864],{"class":40},"\u002F\u002F Avoids CORS in dev — requests go through Vite dev server to backend.\n",[15,1866,1868],{"id":1867},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,1870,1872],{"filename":1871,"language":23},"edge-cases.ts",[25,1873,1875],{"className":27,"code":1874,"language":23,"meta":29,"style":29},"\u002F\u002F ── 1. Only VITE_-prefixed env vars are exposed to client ──\n\u002F\u002F DB_PASSWORD=secret in .env → import.meta.env.DB_PASSWORD is undefined.\n\u002F\u002F Vite strips non-VITE_ vars for security. Rename: VITE_DB_PASSWORD.\n\n\u002F\u002F ── 2. import.meta.env values are ALWAYS strings ──\n\u002F\u002F VITE_DEBUG=true → \"true\" (string), not boolean.\n\u002F\u002F Must parse: const debug = import.meta.env.VITE_DEBUG === 'true'\n\n\u002F\u002F ── 3. :deep() breaks scoping — use only when necessary ──\n\u002F\u002F Parent styling child internals couples components. Prefer props\u002Femit.\n\u002F\u002F If needed, scope tightly: :deep(.specific-class) not :deep(*).\n\n\u002F\u002F ── 4. v-bind() in CSS creates a reactive dependency ──\n\u002F\u002F Changing the bound value re-generates the CSS (inline style on element).\n\u002F\u002F Not free — don't use for frequently-changing values (perf cost).\n\n\u002F\u002F ── 5. manualChunks with shared deps can cause circular chunks ──\n\u002F\u002F If chunk A and B both depend on C, C becomes its own chunk.\n\u002F\u002F Plan chunks carefully — misaligned splits cause extra requests.\n\n\u002F\u002F ── 6. Vite dev (unbundled) vs prod (bundled) can mask issues ──\n\u002F\u002F Code that works in dev (native ESM, no tree-shaking) may break in prod\n\u002F\u002F (aggressive tree-shaking, minification). Test both modes regularly.\n",[31,1876,1877,1882,1887,1892,1896,1901,1906,1911,1915,1920,1925,1930,1934,1939,1944,1949,1953,1958,1963,1968,1972,1977,1982],{"__ignoreMap":29},[34,1878,1879],{"class":36,"line":37},[34,1880,1881],{"class":40},"\u002F\u002F ── 1. Only VITE_-prefixed env vars are exposed to client ──\n",[34,1883,1884],{"class":36,"line":44},[34,1885,1886],{"class":40},"\u002F\u002F DB_PASSWORD=secret in .env → import.meta.env.DB_PASSWORD is undefined.\n",[34,1888,1889],{"class":36,"line":50},[34,1890,1891],{"class":40},"\u002F\u002F Vite strips non-VITE_ vars for security. Rename: VITE_DB_PASSWORD.\n",[34,1893,1894],{"class":36,"line":56},[34,1895,90],{"emptyLinePlaceholder":89},[34,1897,1898],{"class":36,"line":62},[34,1899,1900],{"class":40},"\u002F\u002F ── 2. import.meta.env values are ALWAYS strings ──\n",[34,1902,1903],{"class":36,"line":68},[34,1904,1905],{"class":40},"\u002F\u002F VITE_DEBUG=true → \"true\" (string), not boolean.\n",[34,1907,1908],{"class":36,"line":74},[34,1909,1910],{"class":40},"\u002F\u002F Must parse: const debug = import.meta.env.VITE_DEBUG === 'true'\n",[34,1912,1913],{"class":36,"line":80},[34,1914,90],{"emptyLinePlaceholder":89},[34,1916,1917],{"class":36,"line":86},[34,1918,1919],{"class":40},"\u002F\u002F ── 3. :deep() breaks scoping — use only when necessary ──\n",[34,1921,1922],{"class":36,"line":93},[34,1923,1924],{"class":40},"\u002F\u002F Parent styling child internals couples components. Prefer props\u002Femit.\n",[34,1926,1927],{"class":36,"line":99},[34,1928,1929],{"class":40},"\u002F\u002F If needed, scope tightly: :deep(.specific-class) not :deep(*).\n",[34,1931,1932],{"class":36,"line":117},[34,1933,90],{"emptyLinePlaceholder":89},[34,1935,1936],{"class":36,"line":122},[34,1937,1938],{"class":40},"\u002F\u002F ── 4. v-bind() in CSS creates a reactive dependency ──\n",[34,1940,1941],{"class":36,"line":143},[34,1942,1943],{"class":40},"\u002F\u002F Changing the bound value re-generates the CSS (inline style on element).\n",[34,1945,1946],{"class":36,"line":149},[34,1947,1948],{"class":40},"\u002F\u002F Not free — don't use for frequently-changing values (perf cost).\n",[34,1950,1951],{"class":36,"line":182},[34,1952,90],{"emptyLinePlaceholder":89},[34,1954,1955],{"class":36,"line":188},[34,1956,1957],{"class":40},"\u002F\u002F ── 5. manualChunks with shared deps can cause circular chunks ──\n",[34,1959,1960],{"class":36,"line":194},[34,1961,1962],{"class":40},"\u002F\u002F If chunk A and B both depend on C, C becomes its own chunk.\n",[34,1964,1965],{"class":36,"line":200},[34,1966,1967],{"class":40},"\u002F\u002F Plan chunks carefully — misaligned splits cause extra requests.\n",[34,1969,1970],{"class":36,"line":206},[34,1971,90],{"emptyLinePlaceholder":89},[34,1973,1974],{"class":36,"line":211},[34,1975,1976],{"class":40},"\u002F\u002F ── 6. Vite dev (unbundled) vs prod (bundled) can mask issues ──\n",[34,1978,1979],{"class":36,"line":217},[34,1980,1981],{"class":40},"\u002F\u002F Code that works in dev (native ESM, no tree-shaking) may break in prod\n",[34,1983,1984],{"class":36,"line":242},[34,1985,1986],{"class":40},"\u002F\u002F (aggressive tree-shaking, minification). Test both modes regularly.\n",[15,1988,1990],{"id":1989},"spot-the-bug","🧠 Spot the Bug",[1992,1993,1994,1995,1998],"p",{},"A component reads an environment variable but gets ",[31,1996,1997],{},"undefined"," in the browser.",[20,2000,2002],{"filename":2001,"language":23},"EnvBug.ts",[25,2003,2005],{"className":27,"code":2004,"language":23,"meta":29,"style":29},"\u002F\u002F .env\n\u002F\u002F API_KEY=my-secret-key\n\n\u002F\u002F Component\nconst apiKey = import.meta.env.API_KEY  \u002F\u002F undefined in browser\nfetch(`${apiBase}\u002Fdata`, { headers: { Authorization: `Bearer ${apiKey}` } })\n",[31,2006,2007,2012,2017,2021,2026,2050],{"__ignoreMap":29},[34,2008,2009],{"class":36,"line":37},[34,2010,2011],{"class":40},"\u002F\u002F .env\n",[34,2013,2014],{"class":36,"line":44},[34,2015,2016],{"class":40},"\u002F\u002F API_KEY=my-secret-key\n",[34,2018,2019],{"class":36,"line":50},[34,2020,90],{"emptyLinePlaceholder":89},[34,2022,2023],{"class":36,"line":56},[34,2024,2025],{"class":40},"\u002F\u002F Component\n",[34,2027,2028,2030,2033,2035,2037,2039,2041,2044,2047],{"class":36,"line":62},[34,2029,1662],{"class":102},[34,2031,2032],{"class":136}," apiKey",[34,2034,1668],{"class":102},[34,2036,1671],{"class":102},[34,2038,133],{"class":106},[34,2040,137],{"class":136},[34,2042,2043],{"class":106},".env.",[34,2045,2046],{"class":136},"API_KEY",[34,2048,2049],{"class":40},"  \u002F\u002F undefined in browser\n",[34,2051,2052,2055,2057,2060,2063,2066,2069,2072,2075,2078],{"class":36,"line":68},[34,2053,2054],{"class":162},"fetch",[34,2056,645],{"class":106},[34,2058,2059],{"class":113},"`${",[34,2061,2062],{"class":106},"apiBase",[34,2064,2065],{"class":113},"}\u002Fdata`",[34,2067,2068],{"class":106},", { headers: { Authorization: ",[34,2070,2071],{"class":113},"`Bearer ${",[34,2073,2074],{"class":106},"apiKey",[34,2076,2077],{"class":113},"}`",[34,2079,2080],{"class":106}," } })\n",[2082,2083,2084,2088,2101,2111,2163],"details",{},[2085,2086,2087],"summary",{},"Answer",[1992,2089,2090,2091,2093,2094,2097,2098,2100],{},"The env variable is named ",[31,2092,2046],{},", but Vite only exposes variables prefixed with ",[31,2095,2096],{},"VITE_"," to client-side code. ",[31,2099,2046],{}," is stripped for security — it could be a server secret that shouldn't be in the browser bundle.",[1992,2102,2103,2107,2108,2110],{},[2104,2105,2106],"strong",{},"Fix"," — rename to ",[31,2109,842],{},":",[20,2112,2113,2134],{"filename":721,"language":722},[25,2114,2116],{"className":725,"code":2115,"language":722,"meta":29,"style":29},"# .env\nVITE_API_KEY=my-secret-key  # VITE_ prefix → exposed to client\n",[31,2117,2118,2123],{"__ignoreMap":29},[34,2119,2120],{"class":36,"line":37},[34,2121,2122],{"class":40},"# .env\n",[34,2124,2125,2127,2129,2131],{"class":36,"line":44},[34,2126,842],{"class":106},[34,2128,269],{"class":102},[34,2130,847],{"class":113},[34,2132,2133],{"class":40},"  # VITE_ prefix → exposed to client\n",[20,2135,2137],{"filename":2136,"language":23},"EnvFixed.ts",[25,2138,2140],{"className":27,"code":2139,"language":23,"meta":29,"style":29},"const apiKey = import.meta.env.VITE_API_KEY  \u002F\u002F \"my-secret-key\"\n",[31,2141,2142],{"__ignoreMap":29},[34,2143,2144,2146,2148,2150,2152,2154,2156,2158,2160],{"class":36,"line":37},[34,2145,1662],{"class":102},[34,2147,2032],{"class":136},[34,2149,1668],{"class":102},[34,2151,1671],{"class":102},[34,2153,133],{"class":106},[34,2155,137],{"class":136},[34,2157,2043],{"class":106},[34,2159,842],{"class":136},[34,2161,2162],{"class":40},"  \u002F\u002F \"my-secret-key\"\n",[1992,2164,2165,2168,2169,2171,2172,2175],{},[2104,2166,2167],{},"The lesson",": only ",[31,2170,2096],{},"-prefixed environment variables are injected into client-side code via ",[31,2173,2174],{},"import.meta.env",". Non-prefixed variables are intentionally excluded to prevent accidentally leaking server secrets to the browser.",[1291,2177,2178],{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}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 .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}",{"title":29,"searchDepth":44,"depth":44,"links":2180},[2181,2182,2183,2184,2185,2186,2187,2188],{"id":17,"depth":44,"text":18},{"id":289,"depth":44,"text":290},{"id":717,"depth":44,"text":718},{"id":892,"depth":44,"text":893},{"id":1256,"depth":44,"text":1257},{"id":1643,"depth":44,"text":1644},{"id":1867,"depth":44,"text":1868},{"id":1989,"depth":44,"text":1990},"Vite dev server HMR internals, plugin pipeline, production build optimization, environment variables, CSS code splitting, and alias configuration.","md",{},"\u002Fvue\u002F21-build-tooling-and-vite",{"title":5,"description":2189},"vue\u002F21-build-tooling-and-vite","MDMCB4PMXD7BXK5QNv4D9Bs0oQkvoeHtftK4ccvBSYU",1789924655847]