diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 1c06ae92..f522fbaa 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -16,6 +16,7 @@ const nav: ThemeConfig['nav'] = [ { text: '시작하기', link: '/guide/quick-start' }, // { text: 'Style Guide', link: '/style-guide/' }, { text: '용어 사전', link: '/glossary/' }, + { text: '애러 참고', link: '/error-reference/' }, { text: 'Vue 2 문서', link: 'https://v2.ko.vuejs.org' @@ -419,7 +420,8 @@ export const sidebar: ThemeConfig['sidebar'] = { { text: '렌더 함수', link: '/api/render-function' }, { text: '서버 사이드 렌더링', link: '/api/ssr' }, { text: 'TypeScript 유틸리티 타입', link: '/api/utility-types' }, - { text: '커스텀 렌더러', link: '/api/custom-renderer' } + { text: '커스텀 렌더러', link: '/api/custom-renderer' }, + { text: '컴파일 타임 플래그', link: '/api/compile-time-flags' } ] } ], @@ -569,15 +571,25 @@ export default defineConfigWithTheme({ head: [ ['meta', { name: 'theme-color', content: '#3c8772' }], - ['meta', { name: 'twitter:site', content: '@vuejs' }], - ['meta', { name: 'twitter:card', content: 'summary' }], + ['meta', { property: 'og:url', content: 'https://vuejs.org/' }], + ['meta', { property: 'og:type', content: 'website' }], + ['meta', { property: 'og:title', content: 'Vue.js' }], [ 'meta', { - name: 'twitter:image', + property: 'og:description', + content: 'Vue.js - The Progressive JavaScript Framework' + } + ], + [ + 'meta', + { + property: 'og:image', content: 'https://vuejs.org/images/logo.png' } ], + ['meta', { name: 'twitter:site', content: '@vuejs' }], + ['meta', { name: 'twitter:card', content: 'summary' }], [ 'link', { @@ -695,6 +707,7 @@ export default defineConfigWithTheme({ markdown: { config(md) { md.use(headerPlugin) + // .use(textAdPlugin) } }, diff --git a/package.json b/package.json index 5344c01a..d45d6086 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "@vue/theme": "^2.2.5", "dynamics.js": "^1.1.5", "gsap": "^3.9.0", - "vitepress": "1.0.0-rc.31", - "vue": "^3.4.0-beta.1" + "vitepress": "1.0.0-rc.33", + "vue": "^3.4.0" }, "devDependencies": { "@types/markdown-it": "^12.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 262bbd12..f8ee5ffe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ dependencies: version: 3.0.0 '@vue/theme': specifier: ^2.2.5 - version: 2.2.5(vitepress@1.0.0-rc.31)(vue@3.4.0-beta.3) + version: 2.2.5(vitepress@1.0.0-rc.33)(vue@3.4.3) dynamics.js: specifier: ^1.1.5 version: 1.1.5 @@ -21,11 +21,11 @@ dependencies: specifier: ^3.9.0 version: 3.12.2 vitepress: - specifier: 1.0.0-rc.31 - version: 1.0.0-rc.31(@types/node@20.10.2)(terser@5.24.0) + specifier: 1.0.0-rc.33 + version: 1.0.0-rc.33(@types/node@20.10.2)(terser@5.24.0) vue: - specifier: ^3.4.0-beta.1 - version: 3.4.0-beta.3 + specifier: ^3.4.0 + version: 3.4.3 devDependencies: '@types/markdown-it': @@ -190,16 +190,8 @@ packages: engines: {node: '>=6.9.0'} dev: false - /@babel/parser@7.23.3: - resolution: {integrity: sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.23.3 - dev: false - - /@babel/parser@7.23.5: - resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==} + /@babel/parser@7.23.6: + resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==} engines: {node: '>=6.0.0'} hasBin: true dependencies: @@ -582,12 +574,6 @@ packages: dev: false optional: true - /@types/hast@3.0.3: - resolution: {integrity: sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==} - dependencies: - '@types/unist': 3.0.2 - dev: false - /@types/linkify-it@3.0.5: resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} @@ -605,12 +591,6 @@ packages: '@types/mdurl': 1.0.5 dev: false - /@types/mdast@4.0.3: - resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} - dependencies: - '@types/unist': 3.0.2 - dev: false - /@types/mdurl@1.0.5: resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} @@ -619,10 +599,6 @@ packages: dependencies: undici-types: 5.26.5 - /@types/unist@3.0.2: - resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} - dev: false - /@types/web-bluetooth@0.0.16: resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} dev: false @@ -631,196 +607,109 @@ packages: resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} dev: false - /@ungap/structured-clone@1.2.0: - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - dev: false - - /@vitejs/plugin-vue@5.0.0-beta.1(vite@5.0.4)(vue@3.3.8): + /@vitejs/plugin-vue@5.0.0-beta.1(vite@5.0.10)(vue@3.4.3): resolution: {integrity: sha512-zFAHH6RJH2w/LQlFyqrml96yjYmT8n8e3O4esRxHzCn250uOlkuc0IAqFJWqdxLmQquEM4q5/ECnQJRGsKjoIw==} engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: vite: ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.0.4(@types/node@20.10.2)(terser@5.24.0) - vue: 3.3.8 + vite: 5.0.10(@types/node@20.10.2)(terser@5.24.0) + vue: 3.4.3 dev: false - /@vue/compiler-core@3.3.8: - resolution: {integrity: sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==} + /@vue/compiler-core@3.4.3: + resolution: {integrity: sha512-u8jzgFg0EDtSrb/hG53Wwh1bAOQFtc1ZCegBpA/glyvTlgHl+tq13o1zvRfLbegYUw/E4mSTGOiCnAJ9SJ+lsg==} dependencies: - '@babel/parser': 7.23.3 - '@vue/shared': 3.3.8 - estree-walker: 2.0.2 - source-map-js: 1.0.2 - dev: false - - /@vue/compiler-core@3.4.0-beta.3: - resolution: {integrity: sha512-rQ4u9j0iL1l1SqCJqwX0fGJm8n6lhE3QcoeGl5rydvtfri9kbL+XgQ9kzfYUaQayGGGxyfqUBBymo+f/V1FmNQ==} - dependencies: - '@babel/parser': 7.23.5 - '@vue/shared': 3.4.0-beta.3 + '@babel/parser': 7.23.6 + '@vue/shared': 3.4.3 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.0.2 dev: false - /@vue/compiler-dom@3.3.8: - resolution: {integrity: sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==} + /@vue/compiler-dom@3.4.3: + resolution: {integrity: sha512-oGF1E9/htI6JWj/lTJgr6UgxNCtNHbM6xKVreBWeZL9QhRGABRVoWGAzxmtBfSOd+w0Zi5BY0Es/tlJrN6WgEg==} dependencies: - '@vue/compiler-core': 3.3.8 - '@vue/shared': 3.3.8 + '@vue/compiler-core': 3.4.3 + '@vue/shared': 3.4.3 dev: false - /@vue/compiler-dom@3.4.0-beta.3: - resolution: {integrity: sha512-u87VfqA7yeqT5ipvMruReePHBwFi/+vraH3UQ82ihNI/e6YtZ91p1Utz75WTUjpiDyPWpbym/xJLZD9bMhWsBQ==} + /@vue/compiler-sfc@3.4.3: + resolution: {integrity: sha512-NuJqb5is9I4uzv316VRUDYgIlPZCG8D+ARt5P4t5UDShIHKL25J3TGZAUryY/Aiy0DsY7srJnZL5ryB6DD63Zw==} dependencies: - '@vue/compiler-core': 3.4.0-beta.3 - '@vue/shared': 3.4.0-beta.3 - dev: false - - /@vue/compiler-sfc@3.3.8: - resolution: {integrity: sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==} - dependencies: - '@babel/parser': 7.23.3 - '@vue/compiler-core': 3.3.8 - '@vue/compiler-dom': 3.3.8 - '@vue/compiler-ssr': 3.3.8 - '@vue/reactivity-transform': 3.3.8 - '@vue/shared': 3.3.8 - estree-walker: 2.0.2 - magic-string: 0.30.5 - postcss: 8.4.31 - source-map-js: 1.0.2 - dev: false - - /@vue/compiler-sfc@3.4.0-beta.3: - resolution: {integrity: sha512-PZVZtwO5WvL/yp+gJnbnXKPIehn6In7LWuGkGZ58w0BCv5Q5d0a0BW76kFwiJu6/do2AtEKOq23j2kdcdRJjFg==} - dependencies: - '@babel/parser': 7.23.5 - '@vue/compiler-core': 3.4.0-beta.3 - '@vue/compiler-dom': 3.4.0-beta.3 - '@vue/compiler-ssr': 3.4.0-beta.3 - '@vue/shared': 3.4.0-beta.3 + '@babel/parser': 7.23.6 + '@vue/compiler-core': 3.4.3 + '@vue/compiler-dom': 3.4.3 + '@vue/compiler-ssr': 3.4.3 + '@vue/shared': 3.4.3 estree-walker: 2.0.2 magic-string: 0.30.5 postcss: 8.4.32 source-map-js: 1.0.2 dev: false - /@vue/compiler-ssr@3.3.8: - resolution: {integrity: sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==} - dependencies: - '@vue/compiler-dom': 3.3.8 - '@vue/shared': 3.3.8 - dev: false - - /@vue/compiler-ssr@3.4.0-beta.3: - resolution: {integrity: sha512-8/2HnPWhAtG55tkSGlFTezf0LKheXoMI7KekTL86abb+KTe7WOeY2oCOjLwR0J2kyb3VK6mIO3WFwlzDHZlbkQ==} + /@vue/compiler-ssr@3.4.3: + resolution: {integrity: sha512-wnYQtMBkeFSxgSSQbYGQeXPhQacQiog2c6AlvMldQH6DB+gSXK/0F6DVXAJfEiuBSgBhUc8dwrrG5JQcqwalsA==} dependencies: - '@vue/compiler-dom': 3.4.0-beta.3 - '@vue/shared': 3.4.0-beta.3 + '@vue/compiler-dom': 3.4.3 + '@vue/shared': 3.4.3 dev: false /@vue/devtools-api@6.5.1: resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==} dev: false - /@vue/reactivity-transform@3.3.8: - resolution: {integrity: sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==} + /@vue/reactivity@3.4.3: + resolution: {integrity: sha512-q5f9HLDU+5aBKizXHAx0w4whkIANs1Muiq9R5YXm0HtorSlflqv9u/ohaMxuuhHWCji4xqpQ1eL04WvmAmGnFg==} dependencies: - '@babel/parser': 7.23.3 - '@vue/compiler-core': 3.3.8 - '@vue/shared': 3.3.8 - estree-walker: 2.0.2 - magic-string: 0.30.5 - dev: false - - /@vue/reactivity@3.3.8: - resolution: {integrity: sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==} - dependencies: - '@vue/shared': 3.3.8 - dev: false - - /@vue/reactivity@3.4.0-beta.3: - resolution: {integrity: sha512-tRmOn5kN4ItyovOizivkPuEzI1rh3bw/7+4VqNPm53QxnNjpUajD9v9x8E2sZ7kCHqy2x7aS54RtA8QfHEnZrw==} - dependencies: - '@vue/shared': 3.4.0-beta.3 + '@vue/shared': 3.4.3 dev: false /@vue/repl@3.0.0: resolution: {integrity: sha512-tGYibiftMo5yEuIKPWVsNuuNDejjJk0JQmvKtTm12KNLFqtGD7fWoGv1qUzcN9EAxwVeDgnT9ljRgqGVgZkyEg==} dev: false - /@vue/runtime-core@3.3.8: - resolution: {integrity: sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==} + /@vue/runtime-core@3.4.3: + resolution: {integrity: sha512-C1r6QhB1qY7D591RCSFhMULyzL9CuyrGc+3PpB0h7dU4Qqw6GNyo4BNFjHZVvsWncrUlKX3DIKg0Y7rNNr06NQ==} dependencies: - '@vue/reactivity': 3.3.8 - '@vue/shared': 3.3.8 + '@vue/reactivity': 3.4.3 + '@vue/shared': 3.4.3 dev: false - /@vue/runtime-core@3.4.0-beta.3: - resolution: {integrity: sha512-ALmtR7/iLm4V8KzxyYBdk6g8ktCqQQnEU27j5Gi9D+nDtLIrZoKgvYOx9QU6I92W3UhHdnqmhSXySaxYG6C+hQ==} + /@vue/runtime-dom@3.4.3: + resolution: {integrity: sha512-wrsprg7An5Ec+EhPngWdPuzkp0BEUxAKaQtN9dPU/iZctPyD9aaXmVtehPJerdQxQale6gEnhpnfywNw3zOv2A==} dependencies: - '@vue/reactivity': 3.4.0-beta.3 - '@vue/shared': 3.4.0-beta.3 - dev: false - - /@vue/runtime-dom@3.3.8: - resolution: {integrity: sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==} - dependencies: - '@vue/runtime-core': 3.3.8 - '@vue/shared': 3.3.8 - csstype: 3.1.2 - dev: false - - /@vue/runtime-dom@3.4.0-beta.3: - resolution: {integrity: sha512-cUlFmJrwV469etEhqFWf9GiXDanBu0N4Tk1OshUeBfHeQrdv2TxBQKh5/t2FJEBT4vJ0U59RhUILQW+RYvOFPA==} - dependencies: - '@vue/runtime-core': 3.4.0-beta.3 - '@vue/shared': 3.4.0-beta.3 + '@vue/runtime-core': 3.4.3 + '@vue/shared': 3.4.3 csstype: 3.1.3 dev: false - /@vue/server-renderer@3.3.8(vue@3.3.8): - resolution: {integrity: sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==} + /@vue/server-renderer@3.4.3(vue@3.4.3): + resolution: {integrity: sha512-BUxt8oVGMKKsqSkM1uU3d3Houyfy4WAc2SpSQRebNd+XJGATVkW/rO129jkyL+kpB/2VRKzE63zwf5RtJ3XuZw==} peerDependencies: - vue: 3.3.8 + vue: 3.4.3 dependencies: - '@vue/compiler-ssr': 3.3.8 - '@vue/shared': 3.3.8 - vue: 3.3.8 + '@vue/compiler-ssr': 3.4.3 + '@vue/shared': 3.4.3 + vue: 3.4.3 dev: false - /@vue/server-renderer@3.4.0-beta.3(vue@3.4.0-beta.3): - resolution: {integrity: sha512-Alf6kB6FUu0BVSI3lu8xXZ+Fz5h+XOxqKLV9y+4gPcGfLoRlKx8Vm1vLse0ITIfEKi+GD+fV5+FPCHBwobokcA==} - peerDependencies: - vue: 3.4.0-beta.3 - dependencies: - '@vue/compiler-ssr': 3.4.0-beta.3 - '@vue/shared': 3.4.0-beta.3 - vue: 3.4.0-beta.3 + /@vue/shared@3.4.3: + resolution: {integrity: sha512-rIwlkkP1n4uKrRzivAKPZIEkHiuwY5mmhMJ2nZKCBLz8lTUlE73rQh4n1OnnMurXt1vcUNyH4ZPfdh8QweTjpQ==} dev: false - /@vue/shared@3.3.8: - resolution: {integrity: sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==} - dev: false - - /@vue/shared@3.4.0-beta.3: - resolution: {integrity: sha512-ZHMNXUGBt9zauZyNQ/XXD15eJNuWTbPhhOkCj55pZvZv7ENopvjlGTTmo82oUjmC2sixyw/HknGtQM0hgQ+qEw==} - dev: false - - /@vue/theme@2.2.5(vitepress@1.0.0-rc.31)(vue@3.4.0-beta.3): + /@vue/theme@2.2.5(vitepress@1.0.0-rc.33)(vue@3.4.3): resolution: {integrity: sha512-UUPD0XxlRa69Ytely8JEU/cu8Pae5f4UqZNIXANPN8KT6j/O23dCbOfp1cKlSn+Q/xXLYp0K+vRh4IqZjt/9BQ==} peerDependencies: vitepress: ^1.0.0-alpha.60 dependencies: '@docsearch/css': 3.5.2 '@docsearch/js': 3.5.2 - '@vueuse/core': 9.13.0(vue@3.4.0-beta.3) + '@vueuse/core': 9.13.0(vue@3.4.3) body-scroll-lock: 3.1.5 normalize.css: 8.0.1 - vitepress: 1.0.0-rc.31(@types/node@20.10.2)(terser@5.24.0) + vitepress: 1.0.0-rc.33(@types/node@20.10.2)(terser@5.24.0) transitivePeerDependencies: - '@algolia/client-search' - '@types/react' @@ -831,32 +720,32 @@ packages: - vue dev: false - /@vueuse/core@10.6.1(vue@3.3.8): - resolution: {integrity: sha512-Pc26IJbqgC9VG1u6VY/xrXXfxD33hnvxBnKrLlA2LJlyHII+BSrRoTPJgGYq7qZOu61itITFUnm6QbacwZ4H8Q==} + /@vueuse/core@10.7.1(vue@3.4.3): + resolution: {integrity: sha512-74mWHlaesJSWGp1ihg76vAnfVq9NTv1YT0SYhAQ6zwFNdBkkP+CKKJmVOEHcdSnLXCXYiL5e7MaewblfiYLP7g==} dependencies: '@types/web-bluetooth': 0.0.20 - '@vueuse/metadata': 10.6.1 - '@vueuse/shared': 10.6.1(vue@3.3.8) - vue-demi: 0.14.6(vue@3.3.8) + '@vueuse/metadata': 10.7.1 + '@vueuse/shared': 10.7.1(vue@3.4.3) + vue-demi: 0.14.6(vue@3.4.3) transitivePeerDependencies: - '@vue/composition-api' - vue dev: false - /@vueuse/core@9.13.0(vue@3.4.0-beta.3): + /@vueuse/core@9.13.0(vue@3.4.3): resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} dependencies: '@types/web-bluetooth': 0.0.16 '@vueuse/metadata': 9.13.0 - '@vueuse/shared': 9.13.0(vue@3.4.0-beta.3) - vue-demi: 0.14.6(vue@3.4.0-beta.3) + '@vueuse/shared': 9.13.0(vue@3.4.3) + vue-demi: 0.14.6(vue@3.4.3) transitivePeerDependencies: - '@vue/composition-api' - vue dev: false - /@vueuse/integrations@10.6.1(focus-trap@7.5.4)(vue@3.3.8): - resolution: {integrity: sha512-mPDupuofMJ4DPmtX/FfP1MajmWRzYDv8WSaTCo8LQ5kFznjWgmUQ16ApjYqgMquqffNY6+IRMdMgosLDRZOSZA==} + /@vueuse/integrations@10.7.1(focus-trap@7.5.4)(vue@3.4.3): + resolution: {integrity: sha512-cKo5LEeKVHdBRBtMTOrDPdR0YNtrmN9IBfdcnY2P3m5LHVrsD0xiHUtAH1WKjHQRIErZG6rJUa6GA4tWZt89Og==} peerDependencies: async-validator: '*' axios: '*' @@ -896,36 +785,36 @@ packages: universal-cookie: optional: true dependencies: - '@vueuse/core': 10.6.1(vue@3.3.8) - '@vueuse/shared': 10.6.1(vue@3.3.8) + '@vueuse/core': 10.7.1(vue@3.4.3) + '@vueuse/shared': 10.7.1(vue@3.4.3) focus-trap: 7.5.4 - vue-demi: 0.14.6(vue@3.3.8) + vue-demi: 0.14.6(vue@3.4.3) transitivePeerDependencies: - '@vue/composition-api' - vue dev: false - /@vueuse/metadata@10.6.1: - resolution: {integrity: sha512-qhdwPI65Bgcj23e5lpGfQsxcy0bMjCAsUGoXkJ7DsoeDUdasbZ2DBa4dinFCOER3lF4gwUv+UD2AlA11zdzMFw==} + /@vueuse/metadata@10.7.1: + resolution: {integrity: sha512-jX8MbX5UX067DYVsbtrmKn6eG6KMcXxLRLlurGkZku5ZYT3vxgBjui2zajvUZ18QLIjrgBkFRsu7CqTAg18QFw==} dev: false /@vueuse/metadata@9.13.0: resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} dev: false - /@vueuse/shared@10.6.1(vue@3.3.8): - resolution: {integrity: sha512-TECVDTIedFlL0NUfHWncf3zF9Gc4VfdxfQc8JFwoVZQmxpONhLxFrlm0eHQeidHj4rdTPL3KXJa0TZCk1wnc5Q==} + /@vueuse/shared@10.7.1(vue@3.4.3): + resolution: {integrity: sha512-v0jbRR31LSgRY/C5i5X279A/WQjD6/JsMzGa+eqt658oJ75IvQXAeONmwvEMrvJQKnRElq/frzBR7fhmWY5uLw==} dependencies: - vue-demi: 0.14.6(vue@3.3.8) + vue-demi: 0.14.6(vue@3.4.3) transitivePeerDependencies: - '@vue/composition-api' - vue dev: false - /@vueuse/shared@9.13.0(vue@3.4.0-beta.3): + /@vueuse/shared@9.13.0(vue@3.4.3): resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} dependencies: - vue-demi: 0.14.6(vue@3.4.0-beta.3) + vue-demi: 0.14.6(vue@3.4.3) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -962,44 +851,13 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - /ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - dev: false - - /character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - dev: false - - /character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - dev: false - - /comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - dev: false - /commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - /csstype@3.1.2: - resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} - dev: false - /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} dev: false - /dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - dev: false - - /devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - dependencies: - dequal: 2.0.3 - dev: false - /dynamics.js@1.1.5: resolution: {integrity: sha512-c+LHNccaJS67T4Jfk9b/5CwYsZCHmc10+MplWB8WPFyqTMEqOf8MI56Rg0JRILWjtXnjuBO7xmrNevNnPX+NHg==} dev: false @@ -1061,92 +919,6 @@ packages: resolution: {integrity: sha512-EkYnpG8qHgYBFAwsgsGEqvT1WUidX0tt/ijepx7z8EUJHElykg91RvW1XbkT59T0gZzzszOpjQv7SE41XuIXyQ==} dev: false - /hast-util-from-parse5@8.0.1: - resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==} - dependencies: - '@types/hast': 3.0.3 - '@types/unist': 3.0.2 - devlop: 1.1.0 - hastscript: 8.0.0 - property-information: 6.4.0 - vfile: 6.0.1 - vfile-location: 5.0.2 - web-namespaces: 2.0.1 - dev: false - - /hast-util-parse-selector@4.0.0: - resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} - dependencies: - '@types/hast': 3.0.3 - dev: false - - /hast-util-raw@9.0.1: - resolution: {integrity: sha512-5m1gmba658Q+lO5uqL5YNGQWeh1MYWZbZmWrM5lncdcuiXuo5E2HT/CIOp0rLF8ksfSwiCVJ3twlgVRyTGThGA==} - dependencies: - '@types/hast': 3.0.3 - '@types/unist': 3.0.2 - '@ungap/structured-clone': 1.2.0 - hast-util-from-parse5: 8.0.1 - hast-util-to-parse5: 8.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.0.2 - parse5: 7.1.2 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.1 - web-namespaces: 2.0.1 - zwitch: 2.0.4 - dev: false - - /hast-util-to-html@9.0.0: - resolution: {integrity: sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw==} - dependencies: - '@types/hast': 3.0.3 - '@types/unist': 3.0.2 - ccount: 2.0.1 - comma-separated-tokens: 2.0.3 - hast-util-raw: 9.0.1 - hast-util-whitespace: 3.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.0.2 - property-information: 6.4.0 - space-separated-tokens: 2.0.2 - stringify-entities: 4.0.3 - zwitch: 2.0.4 - dev: false - - /hast-util-to-parse5@8.0.0: - resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} - dependencies: - '@types/hast': 3.0.3 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - property-information: 6.4.0 - space-separated-tokens: 2.0.2 - web-namespaces: 2.0.1 - zwitch: 2.0.4 - dev: false - - /hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - dependencies: - '@types/hast': 3.0.3 - dev: false - - /hastscript@8.0.0: - resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} - dependencies: - '@types/hast': 3.0.3 - comma-separated-tokens: 2.0.3 - hast-util-parse-selector: 4.0.0 - property-information: 6.4.0 - space-separated-tokens: 2.0.2 - dev: false - - /html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - dev: false - /magic-string@0.30.5: resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} engines: {node: '>=12'} @@ -1158,52 +930,12 @@ packages: resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} dev: false - /mdast-util-to-hast@13.0.2: - resolution: {integrity: sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og==} - dependencies: - '@types/hast': 3.0.3 - '@types/mdast': 4.0.3 - '@ungap/structured-clone': 1.2.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.0 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - dev: false - - /micromark-util-character@2.0.1: - resolution: {integrity: sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==} - dependencies: - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - - /micromark-util-encode@2.0.0: - resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} - dev: false - - /micromark-util-sanitize-uri@2.0.0: - resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} - dependencies: - micromark-util-character: 2.0.1 - micromark-util-encode: 2.0.0 - micromark-util-symbol: 2.0.0 - dev: false - - /micromark-util-symbol@2.0.0: - resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} - dev: false - - /micromark-util-types@2.0.0: - resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} - dev: false - /minisearch@6.3.0: resolution: {integrity: sha512-ihFnidEeU8iXzcVHy74dhkxh/dn8Dc08ERl0xwoMMGqp4+LvRSCgicb+zGqWthVokQKvCSxITlh3P08OzdTYCQ==} dev: false - /mrmime@1.0.1: - resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + /mrmime@2.0.0: + resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} engines: {node: '>=10'} dev: false @@ -1217,25 +949,10 @@ packages: resolution: {integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==} dev: false - /parse5@7.1.2: - resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} - dependencies: - entities: 4.5.0 - dev: false - /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: false - /postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.7 - picocolors: 1.0.0 - source-map-js: 1.0.2 - dev: false - /postcss@8.4.32: resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==} engines: {node: ^10 || ^12 || >=14} @@ -1249,10 +966,6 @@ packages: resolution: {integrity: sha512-UA9DX/OJwv6YwP9Vn7Ti/vF80XL+YA5H2l7BpCtUr3ya8LWHFzpiO5R+N7dN16ujpIxhekRFuOOF82bXX7K/lg==} dev: false - /property-information@6.4.0: - resolution: {integrity: sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ==} - dev: false - /rollup@4.6.1: resolution: {integrity: sha512-jZHaZotEHQaHLgKr8JnQiDT1rmatjgKlMekyksz+yk9jt/8z9quNjnKNRoaM0wd9DC2QKXjmWWuDYtM3jfF8pQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -1273,16 +986,20 @@ packages: fsevents: 2.3.3 dev: false - /shikiji-transformers@0.7.6: - resolution: {integrity: sha512-yTp+7JMD/aXbV9ndn14eo9IK/UNt8iDsLNyqlOmCtcldlkqWE9T2YKAlOHOTVaeDfYWUWZa2EgSXb/CBfepBrw==} + /shikiji-core@0.9.17: + resolution: {integrity: sha512-r1FWTXk6SO2aYqfWgcsJ11MuVQ1ymPSdXzJjK7q8EXuyqu8yc2N5qrQy5+BL6gTVOaF4yLjbxFjF+KTRM1Sp8Q==} + dev: false + + /shikiji-transformers@0.9.17: + resolution: {integrity: sha512-2CCG9qSLS6Bn/jbeUTEuvC6YSuP8gm8VyX5VjmCvDKyCPGhlLJbH1k/kg9wfRt7cJqpYjhdMDgT5rkdYrOZnsA==} dependencies: - shikiji: 0.7.6 + shikiji: 0.9.17 dev: false - /shikiji@0.7.6: - resolution: {integrity: sha512-KzEtvSGQtBvfwVIB70kOmIfl/5rz1LC8j+tvlHXsJKAIdONNQvG1at7ivUUq3xUctqgO6fsO3AGomUSh0F+wsQ==} + /shikiji@0.9.17: + resolution: {integrity: sha512-0z/1NfkhBkm3ijrfFeHg3G9yDNuHhXdAGbQm7tRxj4WQ5z2y0XDbnagFyKyuV2ebCTS1Mwy1I3n0Fzcc/4xdmw==} dependencies: - hast-util-to-html: 9.0.0 + shikiji-core: 0.9.17 dev: false /source-map-js@1.0.2: @@ -1300,17 +1017,6 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - /space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - dev: false - - /stringify-entities@4.0.3: - resolution: {integrity: sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==} - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - dev: false - /tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} dev: false @@ -1330,70 +1036,11 @@ packages: engines: {node: '>=4'} dev: false - /trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - dev: false - /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - /unist-util-is@6.0.0: - resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} - dependencies: - '@types/unist': 3.0.2 - dev: false - - /unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - dependencies: - '@types/unist': 3.0.2 - dev: false - - /unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - dependencies: - '@types/unist': 3.0.2 - dev: false - - /unist-util-visit-parents@6.0.1: - resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} - dependencies: - '@types/unist': 3.0.2 - unist-util-is: 6.0.0 - dev: false - - /unist-util-visit@5.0.0: - resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - dependencies: - '@types/unist': 3.0.2 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 - dev: false - - /vfile-location@5.0.2: - resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==} - dependencies: - '@types/unist': 3.0.2 - vfile: 6.0.1 - dev: false - - /vfile-message@4.0.2: - resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} - dependencies: - '@types/unist': 3.0.2 - unist-util-stringify-position: 4.0.0 - dev: false - - /vfile@6.0.1: - resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} - dependencies: - '@types/unist': 3.0.2 - unist-util-stringify-position: 4.0.0 - vfile-message: 4.0.2 - dev: false - - /vite@5.0.4(@types/node@20.10.2)(terser@5.24.0): - resolution: {integrity: sha512-RzAr8LSvM8lmhB4tQ5OPcBhpjOZRZjuxv9zO5UcxeoY2bd3kP3Ticd40Qma9/BqZ8JS96Ll/jeBX9u+LJZrhVg==} + /vite@5.0.10(@types/node@20.10.2)(terser@5.24.0): + resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -1422,19 +1069,19 @@ packages: dependencies: '@types/node': 20.10.2 esbuild: 0.19.8 - postcss: 8.4.31 + postcss: 8.4.32 rollup: 4.6.1 terser: 5.24.0 optionalDependencies: fsevents: 2.3.3 dev: false - /vitepress@1.0.0-rc.31(@types/node@20.10.2)(terser@5.24.0): - resolution: {integrity: sha512-ikH9pIjOOAbyoYAGBVfTz8TzuXp+UoWaIRMU4bw/oiTg8R65SbAaGKY84xx6TuL+f4VqUJ8lhzW82YyxSLvstA==} + /vitepress@1.0.0-rc.33(@types/node@20.10.2)(terser@5.24.0): + resolution: {integrity: sha512-XMwr5eXEB3KB1uCuOkojVaqsVSijmd6N9QmaM2M6aqJqzXzxNwuvWSiEGYl4qbwRAX6/nFRofhx9+FndtCNjGQ==} hasBin: true peerDependencies: markdown-it-mathjax3: ^4.3.2 - postcss: ^8.4.31 + postcss: ^8.4.32 peerDependenciesMeta: markdown-it-mathjax3: optional: true @@ -1444,18 +1091,18 @@ packages: '@docsearch/css': 3.5.2 '@docsearch/js': 3.5.2 '@types/markdown-it': 13.0.7 - '@vitejs/plugin-vue': 5.0.0-beta.1(vite@5.0.4)(vue@3.3.8) + '@vitejs/plugin-vue': 5.0.0-beta.1(vite@5.0.10)(vue@3.4.3) '@vue/devtools-api': 6.5.1 - '@vueuse/core': 10.6.1(vue@3.3.8) - '@vueuse/integrations': 10.6.1(focus-trap@7.5.4)(vue@3.3.8) + '@vueuse/core': 10.7.1(vue@3.4.3) + '@vueuse/integrations': 10.7.1(focus-trap@7.5.4)(vue@3.4.3) focus-trap: 7.5.4 mark.js: 8.11.1 minisearch: 6.3.0 - mrmime: 1.0.1 - shikiji: 0.7.6 - shikiji-transformers: 0.7.6 - vite: 5.0.4(@types/node@20.10.2)(terser@5.24.0) - vue: 3.3.8 + mrmime: 2.0.0 + shikiji: 0.9.17 + shikiji-transformers: 0.9.17 + vite: 5.0.10(@types/node@20.10.2)(terser@5.24.0) + vue: 3.4.3 transitivePeerDependencies: - '@algolia/client-search' - '@types/node' @@ -1484,22 +1131,7 @@ packages: - universal-cookie dev: false - /vue-demi@0.14.6(vue@3.3.8): - resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - peerDependencies: - '@vue/composition-api': ^1.0.0-rc.1 - vue: ^3.0.0-0 || ^2.6.0 - peerDependenciesMeta: - '@vue/composition-api': - optional: true - dependencies: - vue: 3.3.8 - dev: false - - /vue-demi@0.14.6(vue@3.4.0-beta.3): + /vue-demi@0.14.6(vue@3.4.3): resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} engines: {node: '>=12'} hasBin: true @@ -1511,43 +1143,20 @@ packages: '@vue/composition-api': optional: true dependencies: - vue: 3.4.0-beta.3 + vue: 3.4.3 dev: false - /vue@3.3.8: - resolution: {integrity: sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==} + /vue@3.4.3: + resolution: {integrity: sha512-GjN+culMAGv/mUbkIv8zMKItno8npcj5gWlXkSxf1SPTQf8eJ4A+YfHIvQFyL1IfuJcMl3soA7SmN1fRxbf/wA==} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@vue/compiler-dom': 3.3.8 - '@vue/compiler-sfc': 3.3.8 - '@vue/runtime-dom': 3.3.8 - '@vue/server-renderer': 3.3.8(vue@3.3.8) - '@vue/shared': 3.3.8 - dev: false - - /vue@3.4.0-beta.3: - resolution: {integrity: sha512-Y/JRbez2DVwJQc+dPD+spzbY3eyCKBGN31p5/IvkSaj9y7ZrWEYfSCFXPJOBC25ItKLU4iOnr2gWQoUY6GyTmA==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@vue/compiler-dom': 3.4.0-beta.3 - '@vue/compiler-sfc': 3.4.0-beta.3 - '@vue/runtime-dom': 3.4.0-beta.3 - '@vue/server-renderer': 3.4.0-beta.3(vue@3.4.0-beta.3) - '@vue/shared': 3.4.0-beta.3 - dev: false - - /web-namespaces@2.0.1: - resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - dev: false - - /zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + '@vue/compiler-dom': 3.4.3 + '@vue/compiler-sfc': 3.4.3 + '@vue/runtime-dom': 3.4.3 + '@vue/server-renderer': 3.4.3(vue@3.4.3) + '@vue/shared': 3.4.3 dev: false diff --git a/src/api/application.md b/src/api/application.md index ce7115dd..f509ce24 100644 --- a/src/api/application.md +++ b/src/api/application.md @@ -287,7 +287,7 @@ ## app.runWithContext() {#app-runwithcontext} -Execute a callback with the current app as injection context. +현재 앱을 인젝션(injection) 컨텍스트로 사용하여 콜백을 실행합니다. - **Type** @@ -299,7 +299,7 @@ Execute a callback with the current app as injection context. - **Details** - Expects a callback function and runs the callback immediately. During the synchronous call of the callback, `inject()` calls are able to look up injections from the values provided by the current app, even when there is no current active component instance. The return value of the callback will also be returned. + 콜백 함수를 기대하고 즉시 콜백을 실행합니다. 콜백의 동기 호출 중에 `inject()` 호출은 현재 활성 컴포넌트 인스턴스가 없는 경우에도 현재 앱에서 제공하는 값에서 주입(provide)을 조회할 수 있습니다. 콜백의 반환 값도 반환됩니다. - **Example** @@ -390,6 +390,10 @@ console.log(app.config) - 커스텀 디렉티브 훅 - 트랜지션 훅 + :::tip + 프로덕션에서는 3번째 인수(`info`)가 전체 정보 문자열 대신 축약된 코드로 제공됩니다. 코드와 문자열 매핑은 [프로덕션 오류 코드 참조](/error-reference/#runtime-errors)에서 확인할 수 있습니다. + ::: + - **예제** ```js diff --git a/src/api/built-in-components.md b/src/api/built-in-components.md index bea74593..eeaf1c3f 100644 --- a/src/api/built-in-components.md +++ b/src/api/built-in-components.md @@ -130,7 +130,7 @@ h(Transition, { ``` -- **참고** [가이드 - ``](/guide/built-ins/transition) +- **참고** [가이드 - Transition](/guide/built-ins/transition) ## `` {#transitiongroup} diff --git a/src/api/built-in-directives.md b/src/api/built-in-directives.md index a0325f1c..9fb24570 100644 --- a/src/api/built-in-directives.md +++ b/src/api/built-in-directives.md @@ -8,10 +8,7 @@ - **세부 사항**: - `v-text`는 엘리먼트의 [텍스트 컨텐츠](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) 속성을 설정하므로, - 엘리먼트 내부의 기존 컨텐츠를 덮어씁니다. - `텍스트 컨텐츠`의 일부를 업데이트해야 하는 경우, - [이중 중괄호](/guide/essentials/template-syntax#text-interpolation)를 사용해야 합니다. + `v-text`는 엘리먼트의 [텍스트 컨텐츠](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) 속성을 설정하므로, 엘리먼트 내부의 기존 컨텐츠를 덮어씁니다. `텍스트 컨텐츠`의 일부를 업데이트해야 하는 경우, [이중 중괄호](/guide/essentials/template-syntax#text-interpolation)를 사용해야 합니다. - **예제** @@ -31,20 +28,13 @@ - **세부 사항**: - `v-html`의 내용은 Vue 템플릿 문법을 처리하지 않고 일반 HTML로 삽입됩니다. - `v-html`을 사용하여 템플릿을 작성하려고 한다면, - 이 방법 대신 컴포넌트를 사용하여 해결하는 방법을 고민해봐야 합니다. + `v-html`의 내용은 Vue 템플릿 문법을 처리하지 않고 일반 HTML로 삽입됩니다. `v-html`을 사용하여 템플릿을 작성하려고 한다면, 이 방법 대신 컴포넌트를 사용하여 해결하는 방법을 고민해봐야 합니다. ::: warning 보안 참고 사항 - 웹사이트에서 임의의 HTML을 동적으로 렌더링하는 것은 [XSS 공격](https://en.wikipedia.org/wiki/Cross-site_scripting)으로 쉽게 이어질 수 있기 때문에 매우 위험할 수 있습니다. - 신뢰할 수 있는 컨텐츠에만 `v-html`을 사용하고, - 사용자가 제공하는 컨텐츠에는 **절대** 사용하면 안됩니다. + 웹사이트에서 임의의 HTML을 동적으로 렌더링하는 것은 [XSS 공격](https://en.wikipedia.org/wiki/Cross-site_scripting)으로 쉽게 이어질 수 있기 때문에 매우 위험할 수 있습니다. 신뢰할 수 있는 컨텐츠에만 `v-html`을 사용하고, 사용자가 제공하는 컨텐츠에는 **절대** 사용하면 안됩니다. ::: - [싱글 파일 컴포넌트(SFC)](/guide/scaling-up/sfc)에서 `scoped`(범위를 지정한) Style은 `v-html` 내부 컨텐츠에 적용되지 않습니다. - 왜냐하면 해당 HTML은 Vue의 템플릿 컴파일러에서 처리되지 않기 때문입니다. - 범위를 지정한 CSS로 `v-html` 컨텐츠를 대상으로 지정하려는 경우, - [CSS 모듈](./sfc-css-features#css-modules) 또는 BEM과 같은 수동 범위 지정 방법과 함께 전역 ` diff --git a/src/error-reference/errors.data.ts b/src/error-reference/errors.data.ts new file mode 100644 index 00000000..d8044adf --- /dev/null +++ b/src/error-reference/errors.data.ts @@ -0,0 +1,17 @@ +import { defineLoader } from 'vitepress' +import { errorMessages } from 'vue/compiler-sfc' +// @ts-expect-error internal api +import { ErrorTypeStrings } from 'vue' + +function filterEmptyMsg(data: Record) { + return Object.fromEntries(Object.entries(data).filter(([_, msg]) => msg)) +} + +export default defineLoader({ + load() { + return { + compiler: filterEmptyMsg(errorMessages), + runtime: filterEmptyMsg(ErrorTypeStrings) + } + } +}) diff --git a/src/error-reference/index.md b/src/error-reference/index.md new file mode 100644 index 00000000..9bae893d --- /dev/null +++ b/src/error-reference/index.md @@ -0,0 +1,30 @@ + + +# Production Error Code Reference {#error-reference} + +## Runtime Errors {#runtime-errors} + +In production builds, the 3rd argument passed to the following error handler APIs will be a short code instead of the full information string: + +- [`app.config.errorHandler`](/api/application#app-config-errorhandler) +- [`onErrorCaptured`](/api/composition-api-lifecycle#onerrorcaptured) (Composition API) +- [`errorCaptured`](/api/options-lifecycle#errorcaptured) (Options API) + +The following table maps the codes to their original full information strings. + + + +## Compiler Errors {#compiler-errors} + +The following table provides a mapping of the production compiler error codes to their original messages. + + diff --git a/src/examples/src/list-transition/App/composition.js b/src/examples/src/list-transition/App/composition.js index be855c2d..a69ab3f7 100644 --- a/src/examples/src/list-transition/App/composition.js +++ b/src/examples/src/list-transition/App/composition.js @@ -14,6 +14,7 @@ export default { function reset() { items.value = getInitialItems() + id = items.value.length + 1 } function shuffle() { diff --git a/src/examples/src/list-transition/App/options.js b/src/examples/src/list-transition/App/options.js index 2b09e5d2..bd31bc4d 100644 --- a/src/examples/src/list-transition/App/options.js +++ b/src/examples/src/list-transition/App/options.js @@ -16,6 +16,7 @@ export default { }, reset() { this.items = getInitialItems() + id = getInitialItems().length + 1 }, shuffle() { this.items = shuffle(this.items) diff --git a/src/guide/best-practices/accessibility.md b/src/guide/best-practices/accessibility.md index 62d2e8b6..8b99c24e 100644 --- a/src/guide/best-practices/accessibility.md +++ b/src/guide/best-practices/accessibility.md @@ -541,7 +541,7 @@ W3C의 WAI-ARIA는 동적 컨텐츠 및 고급 사용자 인터페이스 제어 - [ChromeVox](https://chrome.google.com/webstore/detail/chromevox-classic-extensi/kgejglhpjiefppelpmljglcjbhoiplfn?hl=en) - 확대/축소 도구 - [MAGic](https://www.freedomscientific.com/products/software/magic/) - - [ZoomText](https://www.zoomtext.com/) + - [ZoomText](https://www.freedomscientific.com/products/software/zoomtext/) - [Magnifier](https://support.microsoft.com/en-us/help/11542/windows-use-magnifier-to-make-things-easier-to-see) ### 테스트 {#testing} diff --git a/src/guide/best-practices/performance.md b/src/guide/best-practices/performance.md index 38d798d2..da656230 100644 --- a/src/guide/best-practices/performance.md +++ b/src/guide/best-practices/performance.md @@ -6,24 +6,15 @@ outline: deep ## 개요 {#overview} -Vue는 수동 최적화가 크게 필요하지 않은 가장 일반적인 사용 사례에 적합하도록 설계되었습니다. -그러나 추가 미세 조정이 필요한 어려운 시나리오가 항상 있습니다. -이 섹션에서는 Vue 앱 성능과 관련하여 주의해야 할 사항에 대해 설명합니다. +Vue는 수동 최적화가 크게 필요하지 않은 가장 일반적인 사용 사례에 적합하도록 설계되었습니다. 그러나 추가 미세 조정이 필요한 어려운 시나리오가 항상 있습니다. 이 섹션에서는 Vue 앱 성능과 관련하여 주의해야 할 사항에 대해 설명합니다. 먼저 웹 성능의 두 가지 주요 측면에 대해 논의해 보겠습니다: -- **페이지 로드 성능**: - 앱이 컨텐츠를 표시하고 초기 방문 시 대화형이 되는 속도. - 이는 일반적으로 [최대 컨텐츠 풀 페인트(LCP: Largest Contentful Paint)](https://web.dev/lcp/) 및 [최초 입력 지연(FID: First Input Delay)](https://web.dev/fid/)와 같은 웹 필수 측정기들을 사용하여 측정됩니다. +- **페이지 로드 성능**: 앱이 컨텐츠를 표시하고 초기 방문 시 대화형이 되는 속도. 이는 일반적으로 [최대 컨텐츠 풀 페인트(LCP: Largest Contentful Paint)](https://web.dev/lcp/) 및 [최초 입력 지연(FID: First Input Delay)](https://web.dev/fid/)와 같은 웹 필수 측정기들을 사용하여 측정됩니다. -- **업데이트 성능**: - 사용자 입력에 대한 응답으로 앱이 업데이트되는 속도. - 예를 들어 사용자가 검색 상자에 입력할 때 목록이 업데이트되는 속도 또는 사용자가 싱글 페이지 앱(SPA)에서 탐색 링크를 클릭할 때 페이지가 전환되는 속도입니다. +- **업데이트 성능**: 사용자 입력에 대한 응답으로 앱이 업데이트되는 속도. 예를 들어 사용자가 검색 상자에 입력할 때 목록이 업데이트되는 속도 또는 사용자가 싱글 페이지 앱(SPA)에서 탐색 링크를 클릭할 때 페이지가 전환되는 속도입니다. -두 가지를 모두 최대화하는 것이 이상적이지만, -서로 다른 프론트엔드 아키텍처는 성능을 얼마나 쉽게 달성할 수 있는지에 영향을 미칩니다. -또한 성능의 우선순위를 어떻게 지정했냐에 따라 구축하는 앱이 크게 영향을 받습니다. -따라서 최적의 성능을 보장하는 첫 번째 단계는 구축 중인 앱에 적합한 아키텍처를 선택하는 것입니다. +두 가지를 모두 최대화하는 것이 이상적이지만, 서로 다른 프론트엔드 아키텍처는 성능을 얼마나 쉽게 달성할 수 있는지에 영향을 미칩니다. 또한 성능의 우선순위를 어떻게 지정했냐에 따라 구축하는 앱이 크게 영향을 받습니다. 따라서 최적의 성능을 보장하는 첫 번째 단계는 구축 중인 앱에 적합한 아키텍처를 선택하는 것입니다. - Vue를 다양한 방식으로 활용하는 방법을 보려면 [Vue 사용 방법](/guide/extras/ways-of-using-vue)을 참고하세요. @@ -31,8 +22,7 @@ Vue는 수동 최적화가 크게 필요하지 않은 가장 일반적인 사용 ## 프로파일링 옵션 (profiling options) {#profiling-options} -성능을 개선하려면 먼저 측정 방법을 알아야 합니다. -이와 관련하여 도움이 될 수 있는 훌륭한 도구가 많이 있습니다. +성능을 개선하려면 먼저 측정 방법을 알아야 합니다. 이와 관련하여 도움이 될 수 있는 훌륭한 도구가 많이 있습니다. 프로덕션 배포의 로드 성능 프로파일링: @@ -47,9 +37,7 @@ Vue는 수동 최적화가 크게 필요하지 않은 가장 일반적인 사용 ## 페이지 로드 최적화 {#page-load-optimizations} -페이지 로드 성능을 최적화하는 것은 프레임워크에 구애받지 않는 경우가 많습니다. -포괄적인 정리 내용은 [web.dev 가이드](https://web.dev/fast/)를 확인하세요. -여기서는 주로 Vue에 특화된 기술에 초점을 맞출 것입니다. +페이지 로드 성능을 최적화하는 것은 프레임워크에 구애받지 않는 경우가 많습니다. 포괄적인 정리 내용은 [web.dev 가이드](https://web.dev/fast/)를 확인하세요. 여기서는 주로 Vue에 특화된 기술에 초점을 맞출 것입니다. ### 올바른 아키텍처 선택 {#choosing-the-right-architecture} @@ -57,39 +45,27 @@ Vue는 수동 최적화가 크게 필요하지 않은 가장 일반적인 사용 기본 애플리케이션이 SPA여야 하지만 마케팅 페이지(랜딩, 정보, 블로그)가 있는 경우 마케팅 페이지를 별도로 제공하세요! 마케팅 페이지는 SSG를 사용하여 최소한의 JS가 포함된 정적 HTML로 배포하는 것이 이상적입니다. - ### 번들 크기 및 트리 쉐이킹 (tree-shaking) {#bundle-size-and-tree-shaking} -페이지 로드 성능을 향상시키는 가장 효과적인 방법 중 하나는 더 작은 JavaScript 번들을 제공하는 것입니다. -Vue를 사용할 때 번들 크기를 줄이는 몇 가지 방법은 다음과 같습니다: +페이지 로드 성능을 향상시키는 가장 효과적인 방법 중 하나는 더 작은 JavaScript 번들을 제공하는 것입니다. Vue를 사용할 때 번들 크기를 줄이는 몇 가지 방법은 다음과 같습니다: - 가능하면 빌드 과정을 사용하십시오. - - Vue의 많은 API는 최신 빌드 도구를 통해 번들로 제공되는 경우, ["트리 쉐이킹"](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking)합니다. - 예를 들어 내장된 `` 컴포넌트를 사용하지 않으면, 최종 프로덕션 번들에 포함되지 않습니다. - 트리 쉐이킹은 소스 코드에서 사용하지 않는 다른 모듈도 제거할 수 있습니다. + - Vue의 많은 API는 최신 빌드 도구를 통해 번들로 제공되는 경우, ["트리 쉐이킹"](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking)합니다. 예를 들어 내장된 `` 컴포넌트를 사용하지 않으면, 최종 프로덕션 번들에 포함되지 않습니다. 트리 쉐이킹은 소스 코드에서 사용하지 않는 다른 모듈도 제거할 수 있습니다. - - 빌드 과정을 사용할 때, 템플릿이 미리 컴파일되므로 Vue 컴파일러를 브라우저에 제공할 필요가 없습니다. - 이것은 **14kb** 만큼을 절약 후 min+gzip으로 압축된 JavaScript로 런타임 컴파일 비용을 방지합니다. + - 빌드 과정을 사용할 때, 템플릿이 미리 컴파일되므로 Vue 컴파일러를 브라우저에 제공할 필요가 없습니다. 이것은 **14kb** 만큼을 절약 후 min+gzip으로 압축된 JavaScript로 런타임 컴파일 비용을 방지합니다. -- 새로운 의존성을 도입할 때 크기에 주의하십시오! - 실제 앱에서 비대해진 번들은 대부분 자신도 모르는 사이에 과도한 의존성을 도입한 결과입니다. +- 새로운 의존성을 도입할 때 크기에 주의하십시오! 실제 앱에서 비대해진 번들은 대부분 자신도 모르는 사이에 과도한 의존성을 도입한 결과입니다. - - 빌드 과정을 사용하는 경우, ES 모듈 형식을 제공하고, 트리 쉐이킹 친화적인 의존성을 선호하십시오. - 예를 들어 `lodash`보다 `lodash-es`를 선호합니다. + - 빌드 과정을 사용하는 경우, ES 모듈 형식을 제공하고, 트리 쉐이킹 친화적인 의존성을 선호하십시오. 예를 들어 `lodash`보다 `lodash-es`를 선호합니다. - - 의존성의 크기를 확인하고 그것이 제공하는 기능의 가치가 있는지 평가하십시오. - 의존성이 트리 쉐이킹에 친화적이면 실제 크기 증가는 가져오는 API에 따라 달라집니다. - [bundlejs.com](https://bundlejs.com/)과 같은 도구를 사용하여 빠르게 확인할 수 있지만, 실제 빌드 설정으로 측정하는 것이 항상 가장 정확합니다. + - 의존성의 크기를 확인하고 그것이 제공하는 기능의 가치가 있는지 평가하십시오. 의존성이 트리 쉐이킹에 친화적이면 실제 크기 증가는 가져오는 API에 따라 달라집니다. [bundlejs.com](https://bundlejs.com/)과 같은 도구를 사용하여 빠르게 확인할 수 있지만, 실제 빌드 설정으로 측정하는 것이 항상 가장 정확합니다. - 주로 빌드 과정 없이 Vue를 사용하고 있다면, [petite-vue](https://github.com/vuejs/petite-vue)(**6kb**)를 사용하는 것이 좋습니다. ### 코드 분할 {#code-splitting} -코드 분할은 빌드 도구가 앱 번들을 여러 개의 작은 청크로 분할하는 것으로, 로드 또는 요청 시 병렬로 로드할 수 있는 곳입니다. -적절한 코드 분할을 사용하면 페이지 로드 시, -필요한 기능을 즉시 다운로드할 수 있으며, -추가 청크는 필요할 때만 지연 로드되므로 성능이 향상됩니다. +코드 분할은 빌드 도구가 앱 번들을 여러 개의 작은 청크로 분할하는 것으로, 로드 또는 요청 시 병렬로 로드할 수 있는 곳입니다. 적절한 코드 분할을 사용하면 페이지 로드 시, 필요한 기능을 즉시 다운로드할 수 있으며, 추가 청크는 필요할 때만 지연 로드되므로 성능이 향상됩니다. Rollup(Vite의 기반) 또는 webpack과 같은 번들러는 ESM 동적 가져오기 문법을 감지하여 자동으로 분할 청크를 생성할 수 있습니다. @@ -107,24 +83,18 @@ function loadLazy() { import { defineAsyncComponent } from 'vue' // Foo.vue 및 해당 의존성에 대해 별도의 청크가 생성됩니다. -// 비동기 컴포넌트가 페이지에서 렌더링될 때에만 요청하여 가져옵니다. +// 비동기 컴포넌트가 페이지에서 렌더링될 때에만 +// 요청하여 가져옵니다. const Foo = defineAsyncComponent(() => import('./Foo.vue')) ``` -Vue 라우터를 통해 클라이언트 측 라우팅을 사용하는 경우, 라우트 컴포넌트를 비동기 컴포넌트로 사용하는 것이 좋습니다. -자세한 내용은 [라우트 지연 로드](https://router.vuejs.kr/guide/advanced/lazy-loading)를 참고하십시오. - -### SSR / SSG {#ssr-ssg} - Vue 라우터를 사용하는 애플리케이션의 경우 라우팅 컴포넌트에 지연 로딩을 사용할 것을 강력히 권장합니다. Vue 라우터는 `defineAsyncComponent`와는 별도로 지연 로딩을 명시적으로 지원합니다. 자세한 내용은 [지연 로딩 라우트](https://router.vuejs.org/guide/advanced/lazy-loading)를 참고하세요. - ## 업데이트 최적화 {#update-optimizations} ### Props 안정성 {#props-stability} -Vue에서 자식 컴포넌트는 수신된 props 중 하나 이상이 변경된 경우에만 업데이트됩니다. -다음 예제를 봅시다: +Vue에서 자식 컴포넌트는 수신된 props 중 하나 이상이 변경된 경우에만 업데이트됩니다. 다음 예제를 봅시다: ```vue-html ``` -`` 컴포넌트 내에서 `id` 및 `activeId` prop을 사용하여 현재 활성 아이템인지 여부를 결정합니다. -이것이 작동하는 동안 문제는 `activeId`가 변경될 때마다 목록의 **모든** ``이 업데이트되어야 한다는 것입니다! +`` 컴포넌트 내에서 `id` 및 `activeId` prop을 사용하여 현재 활성 아이템인지 여부를 결정합니다. 이것이 작동하는 동안 문제는 `activeId`가 변경될 때마다 목록의 **모든** ``이 업데이트되어야 한다는 것입니다! -이상적으로는 활성 상태가 변경된 아이템만 업데이트되어야 합니다. -활성 상태 계산을 부모로 이동하고, ``이 `active` prop을 직접 받도록 하여 이를 달성할 수 있습니다: +이상적으로는 활성 상태가 변경된 아이템만 업데이트되어야 합니다. 활성 상태 계산을 부모로 이동하고, ``이 `active` prop을 직접 받도록 하여 이를 달성할 수 있습니다: ```vue-html ``` -이제 대부분의 컴포넌트에서 `active` prop은 `activeId`가 변경될 때 동일하게 유지되므로, 더 이상 업데이트할 필요가 없습니다. -이 개념은 일반적으로 자식 컴포넌트에 전달되는 prop을 가능한 한 안정적으로 유지하는 것입니다. +이제 대부분의 컴포넌트에서 `active` prop은 `activeId`가 변경될 때 동일하게 유지되므로, 더 이상 업데이트할 필요가 없습니다. 이 개념은 일반적으로 자식 컴포넌트에 전달되는 prop을 가능한 한 안정적으로 유지하는 것입니다. ### `v-once` {#v-once} -`v-once`는 런타임 데이터에 의존하지만, 업데이트할 필요가 없는 컨텐츠를 렌더링하는 데 사용할 수 있는 내장 디렉티브입니다. -이것을 사용하는 컴포넌트 내 전체 하위 트리는 이후 모든 업데이트를 건너뜁니다. -자세한 내용은 [API 레퍼런스](/api/built-in-directives.html#v-once)를 참고하세요. +`v-once`는 런타임 데이터에 의존하지만, 업데이트할 필요가 없는 컨텐츠를 렌더링하는 데 사용할 수 있는 내장 디렉티브입니다. 이것을 사용하는 컴포넌트 내 전체 하위 트리는 이후 모든 업데이트를 건너뜁니다. 자세한 내용은 [API 레퍼런스](/api/built-in-directives.html#v-once)를 참고하세요. ### `v-memo` {#v-memo} -`v-memo`는 큰 하위 트리 또는 `v-for` 목록의 업데이트를 조건부로 건너뛰는 데 사용할 수 있는 내장 디렉티브입니다. -자세한 내용은 [API 레퍼런스](/api/built-in-directives.html#v-memo)를 참고하세요. +`v-memo`는 큰 하위 트리 또는 `v-for` 목록의 업데이트를 조건부로 건너뛰는 데 사용할 수 있는 내장 디렉티브입니다. 자세한 내용은 [API 레퍼런스](/api/built-in-directives.html#v-memo)를 참고하세요. + +### 계산된 안정성 {#computed-stability} + +3.4 버전부터, 계산된 속성은 이전과 다른 계산된 값이 나올 때만 효과를 발생시킵니다. 예를 들어, 다음과 같은 `isEven` 계산은 반환된 값이 `true`에서 `false`로, 또는 그 반대로 변경될 때만 효과를 발생시킵니다: + +```js +const count = ref(0) +const isEven = computed(() => count.value % 2 === 0) + +watchEffect(() => console.log(isEven.value)) // true + +// 계산된 값이 `true`로 유지되기 때문에 새로운 로그를 발생시키지 않습니다 +count.value = 2 +count.value = 4 +``` + +이는 불필요한 효과 발생을 줄이지만, 계산된 값이 매번 새로운 객체를 생성하는 경우에는 작동하지 않습니다: + +```js +const computedObj = computed(() => { + return { + isEven: count.value % 2 === 0 + } +}) +``` + +매번 새로운 객체가 생성되므로, 새로운 값은 기술적으로 항상 이전 값과 다릅니다. `isEven` 속성이 동일하게 유지되더라도 Vue는 오래된 값과 새로운 값을 깊은 비교(deep comparison)를 하지 않는 한 알 수 없습니다. 이러한 비교는 비용이 많이 들고 가치가 없을 수 있습니다. + +대신, 새로운 값과 오래된 값을 수동으로 비교하여, 변경되지 않았다는 것을 알고 있을 경우 오래된 값을 반환하도록 최적화할 수 있습니다: + +```js +const computedObj = computed((oldValue) => { + const newValue = { + isEven: count.value % 2 === 0 + } + if (oldValue && oldValue.isEven === newValue.isEven) { + return oldValue + } + return newValue +}) +``` + +[Playground 예제](https://play.vuejs.org/#eNqVVMtu2zAQ/JUFgSZK4UpuczMkow/40AJ9IC3aQ9mDIlG2EokUyKVt1PC/d0lKtoEminMQQC1nZ4c7S+7Yu66L11awGUtNoesOwQi03ZzLuu2URtiBFtUECtV2FkU5gU2OxWpRVaJA2EOlVQuXxHDJJZeFkgYJayVC5hKj6dUxLnzSjZXmV40rZfFrh3Vb/82xVrLH//5DCQNNKPkweNiNVFP+zBsrIJvDjksgGrRahjVAbRZrIWdBVLz2yBfwBrIsg6mD7LncPyryfIVnywupUmz68HOEEqqCI+XFBQzrOKR79MDdx66GCn1jhpQDZx8f0oZ+nBgdRVcH/aMuBt1xZ80qGvGvh/X6nlXwnGpPl6qsLLxTtitzFFTNl0oSN/79AKOCHHQuS5pw4XorbXsr9ImHZN7nHFdx1SilI78MeOJ7Ca+nbvgd+GgomQOv6CNjSQqXaRJuHd03+kHRdg3JoT+A3a7XsfcmpbcWkQS/LZq6uM84C8o5m4fFuOg0CemeOXXX2w2E6ylsgj2gTgeYio/f1l5UEqj+Z3yC7lGuNDlpApswNNTrql7Gd0ZJeqW8TZw5t+tGaMdDXnA2G4acs7xp1OaTj6G2YjLEi5Uo7h+I35mti3H2TQsj9Jp6etjDXC8Fhu3F9y9iS+vDZqtK2xB6ZPNGGNVYpzHA3ltZkuwTnFf70b+1tVz+MIstCmmGQzmh/p56PGf00H4YOfpR7nV8PTxubP8P2GAP9Q==) + +항상 오래된 값을 비교하고 반환하기 전에 전체 계산을 수행해야 하므로, 매번 실행될 때 동일한 의존성이 수집될 수 있습니다. ## 일반적인 최적화 {#general-optimizations} @@ -166,30 +175,21 @@ Vue에서 자식 컴포넌트는 수신된 props 중 하나 이상이 변경된 ### 대규모 목록 가상화 {#virtualize-large-lists} -모든 프론트엔드 앱에서 가장 일반적인 성능 문제 중 하나는, 큰 목록을 렌더링하는 것입니다. -프레임워크의 성능이 아무리 뛰어나더라도 수천 개의 아이템이 포함된 목록을 렌더링하는 것은 **브라우저가 처리해야 하는 DOM 노드의 수 때문에 느려질 것**입니다. +모든 프론트엔드 앱에서 가장 일반적인 성능 문제 중 하나는, 큰 목록을 렌더링하는 것입니다. 프레임워크의 성능이 아무리 뛰어나더라도 수천 개의 아이템이 포함된 목록을 렌더링하는 것은 **브라우저가 처리해야 하는 DOM 노드의 수 때문에 느려질 것**입니다. -그러나 모든 노드를 미리 렌더링할 필요는 없습니다. -대부분의 경우, 사용자의 화면 크기는 큰 목록의 작은 하위 집합만 표시할 수 있습니다. -큰 목록에서 현재 표시 영역에 있거나 가까운 아이템만 렌더링하는 기술인 **목록 가상화**로 성능을 크게 향상할 수 있습니다. +그러나 모든 노드를 미리 렌더링할 필요는 없습니다. 대부분의 경우, 사용자의 화면 크기는 큰 목록의 작은 하위 집합만 표시할 수 있습니다. 큰 목록에서 현재 표시 영역에 있거나 가까운 아이템만 렌더링하는 기술인 **목록 가상화**로 성능을 크게 향상할 수 있습니다. -목록 가상화를 구현하는 것은 쉽지 않습니다. -다행히 직접 사용할 수 있는 기존 커뮤니티 라이브러리가 있습니다: +목록 가상화를 구현하는 것은 쉽지 않습니다. 다행히 직접 사용할 수 있는 기존 커뮤니티 라이브러리가 있습니다: - [vue-virtual-scroller](https://github.com/Akryum/vue-virtual-scroller) - [vue-virtual-scroll-grid](https://github.com/rocwang/vue-virtual-scroll-grid) +- [vueuc/VVirtualList](https://github.com/07akioni/vueuc) ### 큰 불변 구조에 대한 반응성 오버헤드 감소 {#reduce-reactivity-overhead-for-large-immutable-structures} -Vue의 반응형 시스템은 기본적으로 깊습니다. -이러면 상태 관리가 직관적이지만, -데이터 크기가 클 경우 특정 수준의 오버헤드가 발생하는데, -이것은 모든 속성 접근이 의존성 추적을 수행하는 프락시 트랩을 트리거하기 때문입니다. -이것은 일반적으로 깊이 중첩된 객체의 큰 배열을 처리할 때, 단일 렌더가 100,000개 이상의 속성에 접근하는 경우 눈에 띄게 나타나므로, 매우 특정한 사용 사례에만 영향을 미칩니다. +Vue의 반응형 시스템은 기본적으로 깊습니다. 이러면 상태 관리가 직관적이지만, 데이터 크기가 클 경우 특정 수준의 오버헤드가 발생하는데, 이것은 모든 속성 접근이 의존성 추적을 수행하는 프락시 트랩을 트리거하기 때문입니다. 이것은 일반적으로 깊이 중첩된 객체의 큰 배열을 처리할 때, 단일 렌더가 100,000개 이상의 속성에 접근하는 경우 눈에 띄게 나타나므로, 매우 특정한 사용 사례에만 영향을 미칩니다. -Vue는 [`shallowRef()`](/api/reactivity-advanced.html#shallowref) 및 [`shallowReactive()`](/api/reactivity-advanced.html#shallowreactive)를 사용하여, 깊은 반응형을 opt-out하는 탈출구를 제공합니다. -얕은 API는 루트 수준에서만 반응하는 상태를 생성하고, 모든 중첩된 객체를 그대로 노출합니다. -이렇게 하면 중첩된 속성에 빠르게 접근할 수 있으며, 모든 중첩된 객체는 변경할 수 없는 것처럼 처리해야 하고, 루트 상태를 교체해야만 업데이트가 트리거될 수 있습니다: +Vue는 [`shallowRef()`](/api/reactivity-advanced.html#shallowref) 및 [`shallowReactive()`](/api/reactivity-advanced.html#shallowreactive)를 사용하여, 깊은 반응형을 opt-out하는 탈출구를 제공합니다. 얕은 API는 루트 수준에서만 반응하는 상태를 생성하고, 모든 중첩된 객체를 그대로 노출합니다. 이렇게 하면 중첩된 속성에 빠르게 접근할 수 있으며, 모든 중첩된 객체는 변경할 수 없는 것처럼 처리해야 하고, 루트 상태를 교체해야만 업데이트가 트리거될 수 있습니다: ```js const shallowArray = shallowRef([ @@ -217,10 +217,4 @@ shallowArray.value = [ 때때로 더 나은 추상화 또는 코드 구성을 위해 [렌더링 없는 컴포넌트](/guide/components/slots.html#renderless-components) 또는 상위 컴포넌트(즉, 추가 프로퍼티가 있는 다른 컴포넌트를 렌더링하는 컴포넌트)를 만들 수 있습니다. 이것이 잘못된 것은 아니지만 컴포넌트 인스턴스는 일반 DOM 노드보다 훨씬 비싸고 추상화 패턴으로 인해 너무 많이 생성하면 성능 비용이 발생할 수 있다는 점을 명심하세요. -몇 개의 인스턴스만 줄이는 것은 눈에 띄는 효과가 없으므로, -컴포넌트가 앱에서 몇 번만 렌더링되는 경우, -이것을 불필요하게 추상화하지 않아도 됩니다. -이 최적화를 고려하는 가장 좋은 시나리오는 "**큰 목록**"입니다. -100개의 아이템 목록이 있고, 각 아이템 컴포넌트에 많은 하위 컴포넌트가 포함되어 있다고 상상해 보십시오. -여기서 불필요한 컴포넌트 추상화 하나를 제거하면, -수백 개의 컴포넌트 인스턴스가 줄어들 수 있습니다. +몇 개의 인스턴스만 줄이는 것은 눈에 띄는 효과가 없으므로, 컴포넌트가 앱에서 몇 번만 렌더링되는 경우, 이것을 불필요하게 추상화하지 않아도 됩니다. 이 최적화를 고려하는 가장 좋은 시나리오는 "**큰 목록**"입니다. 100개의 아이템 목록이 있고, 각 아이템 컴포넌트에 많은 하위 컴포넌트가 포함되어 있다고 상상해 보십시오. 여기서 불필요한 컴포넌트 추상화 하나를 제거하면, 수백 개의 컴포넌트 인스턴스가 줄어들 수 있습니다. diff --git a/src/guide/components/events.md b/src/guide/components/events.md index 3046a883..487e7d2e 100644 --- a/src/guide/components/events.md +++ b/src/guide/components/events.md @@ -317,15 +317,3 @@ export default { ``` - -## 이벤트를 속성으로 {#events-props} - -또한, 대문자로 된 이벤트 이름 앞에 `on`을 접두사로 붙여 `events`를 `props`으로 선언하고 전달할 수 있습니다. - -`props.onEvent`를 사용하는 것은 `emit('event')`를 사용하는 것과 다른 동작을 가지며, 전자는 속성 기반 리스너(`@event` 또는 `:on-event`)만을 처리합니다. - -:::warning -`:onEvent`와 `@event` 둘 다 전달될 경우 `props.onEvent`는 `함수` 대신 `함수의 배열`이 될 수 있으며, 이 동작은 안정적이지 않고 미래에 변경될 수 있습니다. -::: - -이 때문에, 이벤트를 방출할 때 `props.onEvent` 대신 `emit('event')`를 사용하는 것이 권장됩니다. diff --git a/src/guide/components/props.md b/src/guide/components/props.md index 41ea1415..44f56f08 100644 --- a/src/guide/components/props.md +++ b/src/guide/components/props.md @@ -1,7 +1,10 @@ # Props {#props} -> 이 페이지에서는 [컴포넌트 기초](/guide/essentials/component-basics)를 이미 읽었다고 가정합니다. -컴포넌트를 처음 사용하는 경우, 그 문서를 먼저 읽으십시오. +> 이 페이지에서는 [컴포넌트 기초](/guide/essentials/component-basics)를 이미 읽었다고 가정합니다. 컴포넌트를 처음 사용하는 경우, 그 문서를 먼저 읽으십시오. + +
+ +
## Props 선언 {#props-declaration} @@ -31,8 +34,7 @@ export default { } ``` -`defineProps()`에 전달하는 인자는 `props` 옵션에 제공하는 값과 동일합니다. -두 선언 스타일은 동일한 props 옵션을 사용합니다. +`defineProps()`에 전달하는 인자는 `props` 옵션에 제공하는 값과 동일합니다. 두 선언 스타일은 동일한 props 옵션을 사용합니다. @@ -90,8 +92,7 @@ export default { 객체 선언 문법의 각 객체 속성의 키는 props의 이름이 되며, 객체 속성의 값은 값이 될 데이터의 타입에 해당하는 생성자 함수(`Number`, `String`같은)여야 합니다. -타입을 지정하는 것은 컴포넌트를 가독성이 좋게 문서화하는데 도움이 되며, 컴포넌트를 사용하는 다른 개발자가 잘못된 유형을 전달할 때에 브라우저 콘솔에 경고를 출력합니다. -[prop 유효성 검사](#prop-validation)에 대한 자세한 내용은 이 페이지 아래에서 더 자세히 설명하겠습니다. +타입을 지정하는 것은 컴포넌트를 가독성이 좋게 문서화하는데 도움이 되며, 컴포넌트를 사용하는 다른 개발자가 잘못된 유형을 전달할 때에 브라우저 콘솔에 경고를 출력합니다. [prop 유효성 검사](#prop-validation)에 대한 자세한 내용은 이 페이지 아래에서 더 자세히 설명하겠습니다.
@@ -120,8 +121,7 @@ defineProps<{ ### Props 이름 케이싱 {#prop-name-casing} -긴 속성명을 선언할 때 `obj['kebab-case']`와 같이 키에 따옴표를 사용하는 번거로움을 줄이기 위해, `obj.camelCase`와 같이 camelCase를 사용합니다. -이렇게 선언된 속성명은 유효한 JavaScript 식별자이므로 템플릿 표현식에서 바로 참조해서 사용 할 수 있습니다: +긴 속성명을 선언할 때 `obj['kebab-case']`와 같이 키에 따옴표를 사용하는 번거로움을 줄이기 위해, `obj.camelCase`와 같이 camelCase를 사용합니다. 이렇게 선언된 속성명은 유효한 JavaScript 식별자이므로 템플릿 표현식에서 바로 참조해서 사용 할 수 있습니다:
@@ -148,15 +148,13 @@ export default { {{ greetingMessage }} ``` -기술적으로 props를 자식 컴포넌트에 전달할 때 camelCase를 사용할 수도 있습니다([in-DOM 템플릿](/guide/essentials/component-basics.html#in-dom-template-parsing-caveats) 제외). -그러나 camelCase로 선언된 props 속성일지라도 관례적으로 HTML 속성 표기법과 동일하게 kebab-case로 표기해서 사용하도록 해야 합니다: +기술적으로 props를 자식 컴포넌트에 전달할 때 camelCase를 사용할 수도 있습니다([in-DOM 템플릿](/guide/essentials/component-basics.html#in-dom-template-parsing-caveats) 제외). 그러나 camelCase로 선언된 props 속성일지라도 관례적으로 HTML 속성 표기법과 동일하게 kebab-case로 표기해서 사용하도록 해야 합니다: ```vue-html ``` -기본 엘리먼트와 Vue 컴포넌트를 쉽게 구별하여 템플릿 가독성을 향상하기 위해 되도록 컴포넌트는 [PascalCase](/guide/components/registration.html#component-name-casing)를 사용합니다. -그러나 props를 전달할 때 camelCase를 사용하면 실질적인 이점이 많지 않으므로 각 언어의 규칙을 따르기로 했습니다. +기본 엘리먼트와 Vue 컴포넌트를 쉽게 구별하여 템플릿 가독성을 향상하기 위해 되도록 컴포넌트는 [PascalCase](/guide/components/registration.html#component-name-casing)를 사용합니다. 그러나 props를 전달할 때 camelCase를 사용하면 실질적인 이점이 많지 않으므로 각 언어의 규칙을 따르기로 했습니다. ### 정적 vs. 동적 Props {#static-vs-dynamic-props} @@ -234,8 +232,7 @@ export default { ### 객체로 여러 속성 바인딩하기 {#binding-multiple-properties-using-an-object} -객체의 모든 속성을 props로 전달하려면 [인자 없이 `v-bind`](/guide/essentials/template-syntax.html#dynamically-binding-multiple-attributes)를 사용할 수 있습니다. -예를 들어, `post` 객체가 주어지면: +객체의 모든 속성을 props로 전달하려면 [인자 없이 `v-bind`](/guide/essentials/template-syntax.html#dynamically-binding-multiple-attributes)를 사용할 수 있습니다. 예를 들어, `post` 객체가 주어지면:
@@ -278,21 +275,16 @@ const post = { ## 단방향 데이터 흐름 {#one-way-data-flow} -모든 props는 자식 속성과 부모 속성 사이에 **하향식 단방향 바인딩**을 형성합니다. -부모 속성이 업데이트되면 자식으로 흐르지만 그 반대는 안됩니다. -이렇게 하면 자식 컴포넌트가 실수로 부모의 상태를 변경하여 앱의 데이터 흐름을 이해하기 어렵게 만드는 것을 방지할 수 있습니다. +모든 props는 자식 속성과 부모 속성 사이에 **하향식 단방향 바인딩**을 형성합니다. 부모 속성이 업데이트되면 자식으로 흐르지만 그 반대는 안됩니다. 이렇게 하면 자식 컴포넌트가 실수로 부모의 상태를 변경하여 앱의 데이터 흐름을 이해하기 어렵게 만드는 것을 방지할 수 있습니다. -또한 부모 컴포넌트가 업데이트될 때마다 자식 컴포넌트의 모든 props가 최신 값으로 업데이트 됩니다. -따라서 자식 컴포넌트 내부에서 props를 변경하려 하면 **안 됩니다**. -그렇지 않을 경우, Vue는 콘솔에서 다음과 같이 경고합니다. +또한 부모 컴포넌트가 업데이트될 때마다 자식 컴포넌트의 모든 props가 최신 값으로 업데이트 됩니다. 따라서 자식 컴포넌트 내부에서 props를 변경하려 하면 **안 됩니다**. 그렇지 않을 경우, Vue는 콘솔에서 다음과 같이 경고합니다.
```js const props = defineProps(['foo']) -// ❌ warning, props are readonly! -// (경고, props는 읽기 전용입니다!) +// ❌ 경고, props는 읽기 전용입니다! props.foo = 'bar' ``` @@ -303,8 +295,7 @@ props.foo = 'bar' export default { props: ['foo'], created() { - // ❌ warning, props are readonly! - // (경고, props는 읽기 전용입니다!) + // ❌ 경고, props는 읽기 전용입니다! this.foo = 'bar' } } @@ -314,8 +305,7 @@ export default { 일반적으로 prop을 변경하고 싶은 두 가지 경우가 있습니다: -1. **prop은 초기 값을 전달하는 데 사용되며, 자식 컴포넌트는 나중에 이를 로컬 데이터 속성으로 사용하려고 합니다.** -이 경우 prop을 초기 값으로 사용하는 로컬 데이터 속성을 정의하는 것이 가장 좋습니다: +1. **prop은 초기 값을 전달하는 데 사용되며, 자식 컴포넌트는 나중에 이를 로컬 데이터 속성으로 사용하려고 합니다.** 이 경우 prop을 초기 값으로 사용하는 로컬 데이터 속성을 정의하는 것이 가장 좋습니다:
@@ -375,23 +365,15 @@ export default { ### 객체/배열 props 변경에 관하여 {#mutating-object-array-props} -객체와 배열이 props로 전달되면, 자식 컴포넌트는 바인딩된 prop을 변경할 수는 없지만, **객체 또는 배열의 중첩 속성을 변경할 수는 있습니다.** -이것은 JavaScript에서 객체와 배열이 참조로 전달되고, Vue가 이런 변경까지 방지하는 것은 너무 큰 비용이 들기 때문에 수행 하지 않습니다. +객체와 배열이 props로 전달되면, 자식 컴포넌트는 바인딩된 prop을 변경할 수는 없지만, **객체 또는 배열의 중첩 속성을 변경할 수는 있습니다.** 이것은 JavaScript에서 객체와 배열이 참조로 전달되고, Vue가 이런 변경까지 방지하는 것은 너무 큰 비용이 들기 때문에 수행 하지 않습니다. -이러한 구현의 주요 단점은 자식 컴포넌트가 명확하지 않은 방식으로 부모 컴포넌트의 상태에 영향을 미쳐 잠재적으로 향후 데이터 흐름에 대한 추론을 어렵게 만든다는 것입니다. -가장 좋은 방법은 부모와 자식이 의도적으로 밀접하게 연결되어 있지 않는 한 이러한 변경을 피하는 것이며, -필요 시 자식은 부모가 변경을 수행할 수 있도록 [emit 이벤트](/guide/components/events)를 호출하는 방식으로 구현해야 합니다. +이러한 구현의 주요 단점은 자식 컴포넌트가 명확하지 않은 방식으로 부모 컴포넌트의 상태에 영향을 미쳐 잠재적으로 향후 데이터 흐름에 대한 추론을 어렵게 만든다는 것입니다. 가장 좋은 방법은 부모와 자식이 의도적으로 밀접하게 연결되어 있지 않는 한 이러한 변경을 피하는 것이며, 필요 시 자식은 부모가 변경을 수행할 수 있도록 [emit 이벤트](/guide/components/events)를 호출하는 방식으로 구현해야 합니다. ## Prop 유효성 검사 {#prop-validation} -앞서 봤던 것처럼 컴포넌트는 props에 타입을 지정할 수 있습니다. -지정한 요구 사항이 충족되지 않으면 Vue는 브라우저의 JavaScript 콘솔에서 경고합니다. -이것은 다른 사람들이 사용하도록 컴포넌트를 개발할 때 특히 유용합니다. +앞서 봤던 것처럼 컴포넌트는 props에 타입을 지정할 수 있습니다. 지정한 요구 사항이 충족되지 않으면 Vue는 브라우저의 JavaScript 콘솔에서 경고합니다. 이것은 다른 사람들이 사용하도록 컴포넌트를 개발할 때 특히 유용합니다. -Props에 유효성 검사를 지정하려면, -`defineProps()` 매크로에`props` 옵션에 문자열로 구성된 배열 대신, -유효성 검사 요구 사항이 있는 객체를 제공하면 됩니다. -예를 들어: +Props에 유효성 검사를 지정하려면, `defineProps()` 매크로에`props` 옵션에 문자열로 구성된 배열 대신, 유효성 검사 요구 사항이 있는 객체를 제공하면 됩니다. 예를 들어:
@@ -423,8 +405,9 @@ defineProps({ } }, // 사용자 정의 유효성 검사 함수 + // 3.4+ 부터는 모든 props가 두 번째 인수로 전달 됨 propF: { - validator(value) { + validator(value, props) { // 값은 다음 문자열 중 하나와 일치해야 합니다. return ['성공', '경고', '위험'].includes(value) } @@ -442,8 +425,7 @@ defineProps({ ``` :::tip -`defineProps()` 인자 내부의 코드는 **` + + +``` + +부모는 `v-model`을 사용하여 값을 바인딩할 수 있습니다: + +```vue-html + + +``` + +`defineModel()`에 의해 반환되는 값은 ref입니다. 다른 ref처럼 접근하고 변경할 수 있지만, 부모 값과 로컬 값 사이의 양방향 바인딩으로 작동합니다: + +- `.value`는 부모 `v-model`에 의해 바인딩된 값과 동기화됩니다; +- 자식에 의해 변경되면 부모 바인딩 값도 업데이트됩니다. + +따라서 이 ref를 네이티브 입력 엘리먼트의 `v-model`에 바인딩할 수도 있어, 네이티브 입력 엘리먼트를 래핑하면서 동일한 `v-model` 사용을 제공하는 것이 간단해집니다: + +```vue + + + +``` + +[Try it in the Playground](https://play.vuejs.org/#eNqFUtFKwzAU/ZWYl06YLbK30Q10DFSYigq+5KW0t11mmoQknZPSf/cm3eqEsT0l555zuefmpKV3WsfbBuiUpjY3XDtiwTV6ziSvtTKOLNZcFKQ0qiZRnATkG6JB0BIDJen2kp5iMlfSOlLbisw8P4oeQAhFPpURxVV0zWSa9PNwEgIHtRaZA0SEpOvbeduG5q5LE0Sh2jvZ3tSqADFjFHlGSYJkmhz10zF1FseXvIo3VklcrfX9jOaq1lyAedGOoz1GpyQwnsvQ3fdTqDnTwPhQz9eQf52ob+zO1xh9NWDBbIHRgXOZqcD19PL9GXZ4H0h03whUnyHfwCrReI+97L6RBdo+0gW3j+H9uaw+7HLnQNrDUt6oV3ZBzyhmsjiz+p/dSTwJfUx2+IpD1ic+xz5enwQGXEDJJaw8Gl2I1upMzlc/hEvdOBR6SNKAjqP1J6P/o6XdL11L5h4=) + +### 내부 구조 {#under-the-hood} + +`defineModel`은 편의성을 위한 매크로입니다. 컴파일러는 다음과 같이 확장합니다: + +- 로컬 ref의 값과 동기화되는 `modelValue`라는 이름의 prop; +- 로컬 ref의 값이 변경될 때 발생하는 `update:modelValue`라는 이벤트. + +3.4 이전에 위와 같은 자식 컴포넌트를 구현하는 방법은 다음과 같습니다: + +```vue + + + +``` + +보시다시피, 이것은 훨씬 더 장황합니다. 하지만 내부에서 무슨 일이 일어나는지 이해하는 것이 도움이 됩니다. + +`defineModel`은 prop을 선언하므로, `defineModel`에 전달함으로써 기본 prop의 옵션을 선언할 수 있습니다: + +```js +// v-model을 필수로 만들기 +const model = defineModel({ required: true }) + +// 기본값 제공 +const model = defineModel({ default: 0 }) +``` + +
+ +
+ 먼저 네이티브 엘리먼트에서 v-model이 어떻게 사용되는지 다시 살펴봅시다: ```vue-html @@ -34,8 +119,6 @@ 실제로 작동하는 모습은 다음과 같습니다: -
- ```vue - - -``` - -
- 이제 `v-model`이 이 컴포넌트와 완벽하게 작동합니다: ```vue-html ``` -
- [Try it in the Playground](https://play.vuejs.org/#eNqFkctqwzAQRX9lEAEn4Np744aWrvoD3URdiHiSGvRCHpmC8b93JDfGKYGCkJjXvTrSJF69r8aIohHtcA69p6O0vfEuELzFgZx5tz4SXIIzUFT1JpfGCmmlxe/c3uFFRU0wSQtwdqxh0dLQwHSnNJep3ilS+8PSCxCQYrC3CMDgMKgrNlB8odaOXVJ2TgdvvNp6vSwHhMZrRcgRQLs1G5+M61A/S/ErKQXUR5immwXMWW1VEKX4g3j3Mo9QfXCeKU9FtvpQmp/lM0Oi6RP/qYieebHZNvyL0acLLODNmGYSxCogxVJ6yW1c2iWz/QOnEnY48kdUpMIVGSllD8t8zVZb+PkHqPG4iw==) -
-
- -[Try it in the Playground](https://play.vuejs.org/#eNp9j81qwzAQhF9lEQE7kNp344SW0kNvPfVS9WDidSrQH9LKF+N37yoOxoSQm7QzO9/sJN68r8aEohFtPAflCSJS8idplfEuEEwQcIAZhuAMFGwtVuk9RXLm0/pEN7mqN7Ocy2YAac/ORgKDMXYXhGOOLIs/1NoVe2nbekEzlD+ExuuOkH8A7ZYxvhjXoz5KcUuSAuoTTNOaPM85bU0QB3HX58GdPQ7K4ldwPpY/xZXw3Wmu/svVFvHDKMpi8j3HNneeZ/VVBucXQDPmjVx+XZdikV6vNpZ2yKTyAecAOxzRUkVduCCfkqf7Zb9m1Pbo+R9ZkqZn) - -
- 이 컴포넌트 내에서 `v-model`을 구현하는 또 다른 방법은 getter와 setter가 모두 있는 쓰기 가능한 `computed` 프로퍼티를 사용하는 것입니다. `get` 메서드는 `modelValue` 프로퍼티를 반환하고 `set` 메서드는 해당 이벤트를 발생시켜야 합니다: -
- ```vue ``` -
- -## `v-model` arguments {#v-model-arguments} +[Try it in the Playground](https://play.vuejs.org/#eNqFkl9PwjAUxb9K05dhglsMb2SQqOFBE9Soj31Zxh0Uu7bpHxxZ9t29LWOiQXzaes7p2a+9a+mt1unOA53S3JaGa0csOK/nTPJaK+NISwxUpCOVUTVJMJoM1nJ/r/BNgnS9nWYnWujFMCFMlkpaRxx3AsgsFI6S3XWtViBIYda+Dg3QFLUWkFwxmWcHFqTAhQPUCwe4IiTf3Mzbtq/qujzDddRPYfruaUzNGI1PRkmG0Twb+uiY/sI9cw0/0VdQcQnL0D5KovgfL5fa4/69jiDQOOTo+S6SOYtfrvg63VolkauNN0lLxOUCzLN2HMkYnZLoBK8QQn0+Rs0ZD+OjXm6g/Dijb20TNEZfDFgwOwQZPIdzAWQN9uLtKXIPJtL7gH3BfAWrhA+Mh9idlyvEPslF2of4J3G5freLxoG0x0MF0JDsYp5RHE6Y1F9H/8adpJO4j8mOdl/Hw/nf) -기본적으로 컴포넌트의 `v-model`은 `modelValue`를 프로퍼티로, `update:modelValue`를 이벤트로 사용합니다. `v-model`에 인자를 전달하여 이러한 이름을 수정할 수 있습니다: +prop 옵션이 필요한 경우, 모델 이름 뒤에 전달해야 합니다: -```vue-html - +```js +const title = defineModel('title', { required: true }) ``` -이 경우 자식 컴포넌트는 `title` 프로퍼티를 예상하고 부모 값을 업데이트하는 `update:title` 이벤트를 발생시켜야 합니다: - -
+
+Pre 3.4 Usage ```vue @@ -176,9 +224,12 @@ defineEmits(['update:title']) [Try it in the Playground](https://play.vuejs.org/#eNp9UE1rwzAM/SvCFNJC17BrSMvG2HGw+7xDaJTNEH/gyKEl5L9PtkPJytjJ1tPT03uaxLNzhzGgqEQ9nL1yBANScCdplHbWE0zgsYMZOm81FEwtbq2364vln0FDS/tQrrCoy2RpztYMBKSoRzhGuW0xPmjbYg+N/wo6zuOl0a7HYidNXWYn7IELQsYbQq4A6u/H0zQtUvNcl1wnfG1l0a4S6yhFeqWAkql1edMTe3Fn9o8jtNgpg+/eumH7USSl4pM9ZvxVK4p4cC0r5oWp/V8EZVyg+AOgq0sG8UJSZKgamz6sXGf0KQ0xukFeuf29cA8bHGME4msiJ4kKuzx6n3n+AX48rro=) +
+이 경우, 기본 `modelValue` prop과 `update:modelValue` 이벤트 대신, 자식 컴포넌트는 `title` prop을 기대하고 부모 값을 업데이트하기 위해 `update:title` 이벤트를 발생시켜야 합니다: + ```vue + + +``` + +[Try it in the Playground](https://play.vuejs.org/#eNqFkstuwjAQRX/F8iZUAqKKHQpIfbAoUmnVx86bKEzANLEt26FUkf+9Y4MDSAg2UWbu9fjckVv6oNRw2wAd08wUmitLDNhGTZngtZLakpZoKIkjpZY1SdCadNK3Ab3IazhowzQ2/ES0MVFIYSwpucbvxA/qJXO5FsldlKr8qDxL8EKW7kEQAQsLtapyC1gRkq3vp217mOccwf8wwLksRSlYIoMvCNkOarmEahyODAT2J4yGgtFzhx8UDf5/r6c4NEs7CNqnpxkvbO0kcVjNhCyh5AJe/SW9pBPOV3DJGvu3dsKFaiyxf8qTW9gheQwVs4Z90BDm5oF47cF/Ht4aZC75argxUmD61g9ktJC14hXoN2U5ZmJ0TILitbyq5O889KxuoB/7xRqKnwv9jdn5HqPvGnDVWwTpNJvrFSCul2efi4DeiRigqdB9RfwAI6vGM+5tj41YIvaJL9C+hOfNxerLzHYWhImhPKh3uuBnFJ/A05XoR9zRcBTOMeGo+wcs+yse) + +
+Pre 3.4 Usage + ```vue ``` -
-
+수정자에 기반하여 값을 어떻게 읽거나 쓸지 조건적으로 조정하려면, `defineModel()`에 `get`과 `set` 옵션을 전달할 수 있습니다. 이 두 옵션은 모델 ref의 get / set에서 값을 받아 변환된 값을 반환해야 합니다. 다음은 `set` 옵션을 사용하여 `capitalize` 수정자를 구현하는 방법입니다: -```vue{11} - ``` -
- -컴포넌트의 `modelModifiers` 프로퍼티에 `capitalize`가 포함되어 있고 그 값은 `v-model` 바인딩 `v-model.capitalize="myText"`에 설정되어 있기 때문에 `true`인 것을 알 수 있습니다. - - -이제 prop이 설정되었으므로 `modelModifiers` 객체 키를 확인하고 발신된 값을 변경하는 핸들러를 작성할 수 있습니다. 아래 코드에서는 `` 앨리먼트가 `input` 이벤트를 실행할 때마다 문자열을 대문자로 표시합니다. +[Try it in the Playground](https://play.vuejs.org/#eNp9UsFu2zAM/RVClzhY5mzoLUgHdEUPG9Bt2LLTtIPh0Ik6WxIkyosb5N9LybFrFG1OkvgeyccnHsWNtXkbUKzE2pdOWQKPFOwnqVVjjSM4gsMKTlA508CMqbMRuu9uDd80ajrD+XISi3WZDCB1abQnaLoNHgiuY8VsNptLvV72TbkdPwgbWxeE/ALY7JUHpW0gKAurqKjVI3rAFl1He6V30JkA3AbdKvLXUzXt+8Zssc6fM6+l6NtLAUtusF6O3cRCvFB9yY2SiYFw+8KSYcY/qfEC+FCVQuf/8rxbrJTG+4hkxyiWq2ZtUQecQ3oDqAqyMWeieyQAu0bBaUh5ebkv3A1lH+Y5md/WorstPGZzeHfGfa1KzD6yxzH11B/TCjHC4dPlX1j3P0CdjQ5S79/Z3WhpPF91lDz7Uald/uCNZj/TFFJE91SN7rslxX5JsRrmk6Koa/P/a4qRC7gY4uUey3+vxB/8Icak+OHQo2tRihGjwu2QtUb47te3pHsEWXWomX0B/Ine1CFq7Gmfg96y7Akvqf2StoKXcePvDoTaD0NFocnhxJeClyRu2FujP8u9yq+GnxGnJxSEO+M=) -
+
+Pre 3.4 Usage ```vue{11-13} + + +``` + +컴포넌트의 `modelModifiers` 프로퍼티에 `capitalize`가 포함되어 있고 그 값은 `v-model` 바인딩 `v-model.capitalize="myText"`에 설정되어 있기 때문에 `true`인 것을 알 수 있습니다. + +이제 prop이 설정되었으므로 `modelModifiers` 객체 키를 확인하고 발신된 값을 변경하는 핸들러를 작성할 수 있습니다. 아래 코드에서는 `` 앨리먼트가 `input` 이벤트를 실행할 때마다 문자열을 대문자로 표시합니다. + ```vue{13-15} +``` + +
+Pre 3.4 Usage + ```vue{5,6,10,11} ``` +
@@ -489,4 +573,5 @@ export default { } ``` +
diff --git a/src/guide/essentials/computed.md b/src/guide/essentials/computed.md index 215f1688..220ce5b3 100644 --- a/src/guide/essentials/computed.md +++ b/src/guide/essentials/computed.md @@ -1,10 +1,16 @@ # 계산된 속성 {#computed-properties} +
+ +
+ +
+ +
+ ## 기본 예제 {#basic-example} -템플릿 내 표현식은 매우 편리하지만 간단한 작업을 위한 것입니다. -템플릿에 너무 많은 논리를 넣으면 비대해져 유지 관리가 어려워질 수 있습니다. -예를 들어 객체 내 배열이 있는 경우: +템플릿 내 표현식은 매우 편리하지만 간단한 작업을 위한 것입니다. 템플릿에 너무 많은 논리를 넣으면 비대해져 유지 관리가 어려워질 수 있습니다. 예를 들어 객체 내 배열이 있는 경우:
@@ -48,12 +54,9 @@ const author = reactive({ {{ author.books.length > 0 ? 'Yes' : 'No' }} ``` -이 시점에서 템플릿이 약간 복잡해집니다. -우리가 여기서 인지해야 할 요점은 템플릿의 반응형 결과가 `author.books`에 의해 계산된다는 점보다, -템플릿 내에서 이 코드를 두 번 이상 반복하고 싶지 않다는 것입니다. +이 시점에서 템플릿이 약간 복잡해집니다. 우리가 여기서 인지해야 할 요점은 템플릿의 반응형 결과가 `author.books`에 의해 계산된다는 점보다, 템플릿 내에서 이 코드를 두 번 이상 반복하고 싶지 않다는 것입니다. -따라서 반응형 데이터를 포함하는 복잡한 논리의 경우, **계산된 속성**을 사용하는 것이 좋습니다. -다음은 개선된 동일한 예제입니다: +따라서 반응형 데이터를 포함하는 복잡한 논리의 경우, **계산된 속성**을 사용하는 것이 좋습니다. 다음은 개선된 동일한 예제입니다:
@@ -92,9 +95,7 @@ export default { `data`에 있는 `books` 배열의 값을 변경하면, 그에 따라 `publishedBooksMessage`가 어떻게 변경되는지 확인할 수 있습니다. -일반 속성처럼 템플릿의 계산된 속성에 데이터를 바인딩할 수 있습니다. -Vue는 `publishedBooksMessage`의 값이 `author.books`에 의존한다는 것을 알고 있으므로, -`author.books`가 변경되면 `publishedBooksMessage`를 바인딩해 의존하는 모든 것을 업데이트합니다. +일반 속성처럼 템플릿의 계산된 속성에 데이터를 바인딩할 수 있습니다. Vue는 `publishedBooksMessage`의 값이 `author.books`에 의존한다는 것을 알고 있으므로, `author.books`가 변경되면 `publishedBooksMessage`를 바인딩해 의존하는 모든 것을 업데이트합니다. 참고: [계산된 속성에 타입 지정하기](/guide/typescript/options-api.html#typing-computed-properties) @@ -129,14 +130,9 @@ const publishedBooksMessage = computed(() => { [온라인 연습장으로 실행하기](https://play.vuejs.org/#eNp1kM1Kw0AUhV/lkE1aqI2oq5BGWgRBqCsRxLhIk9sm2MwMmUmhhECRCoIbH0DFheC20rdq8xDO1LYr3c2d+3O+c0qrK0R7UpDlWp6M8lQoSFKF8AOWZoLnCiVyCiOVTqiFiGeiUBSjwjDnGWy9aQcsYBFnUiEsVMJzdPYbjTJgAAszcmFf8IThjJPdMp8Dzu+li1vzBuzrgnCEA3TjScgirXBepPF2dNs+1u1eKNPoj96J7l0lhP5UKsqnmgm4C1jVNHCOg9VyXj8s1i+vGm24wxXFYJzKhOKeYemTlOGINP3OZaPRRMfHxkOuQ8nZ1mF7A98eExupBD4OcQr7hqQNbfOSa3Uj7Dm/geoodaEoE+NQka4AT/j192P9NsdqMau/ZqvlB+r3p/Xzp+s5Jns9IkXI/LL8h7Kq9HkzYXT2t63qB4hTpdc=) -여기에서 계산된 속성 `publishedBooksMessage`를 선언했습니다. -`computed()` 함수에는 getter로 사용될 함수가 전달돼야 하며, 반환되는 값은 **computed ref**입니다. -일반 ref와 유사하게 계산된 결과를 `publishedBooksMessage.value`로 접근할 수 있습니다. -계산된 ref는 템플릿에서 자동으로 언래핑되므로, 템플릿 표현식에서 `.value` 없이 참조할 수 있습니다. +여기에서 계산된 속성 `publishedBooksMessage`를 선언했습니다. `computed()` 함수에는 getter로 사용될 함수가 전달돼야 하며, 반환되는 값은 **computed ref**입니다. 일반 ref와 유사하게 계산된 결과를 `publishedBooksMessage.value`로 접근할 수 있습니다. 계산된 ref는 템플릿에서 자동으로 언래핑되므로, 템플릿 표현식에서 `.value` 없이 참조할 수 있습니다. -계산된 속성은 의존된 반응형을 자동으로 추적합니다. -Vue는 `publishedBooksMessage`의 값이 `author.books`에 의존한다는 것을 알고 있으므로, -`author.books`가 변경되면 `publishedBooksMessage`를 바인딩해 의존하는 모든 것을 업데이트합니다. +계산된 속성은 의존된 반응형을 자동으로 추적합니다. Vue는 `publishedBooksMessage`의 값이 `author.books`에 의존한다는 것을 알고 있으므로, `author.books`가 변경되면 `publishedBooksMessage`를 바인딩해 의존하는 모든 것을 업데이트합니다. 참고: [computed 타입 지정하기](/guide/typescript/composition-api.html#typing-computed) @@ -174,11 +170,7 @@ function calculateBooksMessage() {
-계산된 속성 대신 메서드로 동일한 기능을 정의할 수 있습니다. -결과적으로 두 가지 접근 방식은 실제로 완전히 동일합니다. -그러나 차이점은 **계산된 속성은 의존된 반응형을 기반으로 캐시된다는 점**입니다. -계산된 속성은 의존된 반응형 중 일부가 변경된 경우에만 재평가됩니다. -즉, `author.books`가 변경되지 않은 한 여러 곳에서 `publishedBooksMessage`에 접근할 경우, getter 함수를 다시 실행하지 않고 이전에 계산된 결과를 즉시 반환합니다. +계산된 속성 대신 메서드로 동일한 기능을 정의할 수 있습니다. 결과적으로 두 가지 접근 방식은 실제로 완전히 동일합니다. 그러나 차이점은 **계산된 속성은 의존된 반응형을 기반으로 캐시된다는 점**입니다. 계산된 속성은 의존된 반응형 중 일부가 변경된 경우에만 재평가됩니다. 즉, `author.books`가 변경되지 않은 한 여러 곳에서 `publishedBooksMessage`에 접근할 경우, getter 함수를 다시 실행하지 않고 이전에 계산된 결과를 즉시 반환합니다. 아래 예제는 `Date.now()`가 반응형으로써 의존된 것이 아니기 때문에 이후 계산된 속성이 업데이트되지 않음을 의미합니다: @@ -204,17 +196,11 @@ const now = computed(() => Date.now()) 이와 반대로 메서드 호출은 **리렌더링이 발생할 때마다 항상 함수를 실행**합니다. -캐싱이 필요한 이유는 무엇일까요? -거대한 배열을 루프 하며 많은 계산을 해야 하는 값비싼 비용의 `list` 속성이 있다고 가정해봅시다. -그리고 `list`에 의존하는 또 다른 계산된 속성이 있을 수 있습니다. -캐싱이 없다면 우리는 `list`의 getter를 불필요하게 많이 실행할 것입니다! -캐싱을 원하지 않는 경우에만 메서드 호출을 사용하십시오. +캐싱이 필요한 이유는 무엇일까요? 거대한 배열을 루프 하며 많은 계산을 해야 하는 값비싼 비용의 `list` 속성이 있다고 가정해봅시다. 그리고 `list`에 의존하는 또 다른 계산된 속성이 있을 수 있습니다. 캐싱이 없다면 우리는 `list`의 getter를 불필요하게 많이 실행할 것입니다! 캐싱을 원하지 않는 경우에만 메서드 호출을 사용하십시오. ## 수정 가능한 계산된 속성 {#writable-computed} -계산된 속성은 기본적으로 getter 전용입니다. -계산된 속성에 새 값을 할당하려고 하면 런타임 에러가 발생합니다. -드물게 "수정 가능한" 계산된 속성이 필요한 경우, getter와 setter를 모두 제공하여 속성을 만들 수 있습니다. +계산된 속성은 기본적으로 getter 전용입니다. 계산된 속성에 새 값을 할당하려고 하면 런타임 에러가 발생합니다. 드물게 "수정 가능한" 계산된 속성이 필요한 경우, getter와 setter를 모두 제공하여 속성을 만들 수 있습니다.
@@ -277,20 +263,8 @@ const fullName = computed({ ### getter에서 사이드 이펙트는 금물 {#getters-should-be-side-effect-free} -계산된 속성의 getter 함수는 오로지 계산만 수행해야 함으로써, 사이드 이펙트는 없어야 함을 기억하는 것이 중요합니다. -예를 들어, **getter에서 비동기 요청을 하거나 DOM을 변경하면 안됩니다!** -계산된 속성은 다른 값을 기반으로 의도한 값을 도출하는 방법을 선언적으로 정의한 것으로 이해해야 합니다. -이것의 유일한 기능은 해당 값을 계산하고 반환하는 것이어야 합니다. -가이드의 뒷부분에서 상태 변경에 반응하는 [감시자](./watchers)를 사용하여 사이드 이펙트를 수행하는 방법에 대해 알아볼 것입니다. - -:::info 사이드 이펙트란? -프로그래밍에서 함수가 결과값 이외에 다른 상태를 변경하는 행위 -::: +계산된 getter 함수는 순수한 계산만을 수행하고 부작용이 없어야 한다는 것을 기억하는 것이 중요합니다. 예를 들어, **계산된 getter 안에서 다른 상태를 변형시키거나, 비동기 요청을 하거나, DOM을 변경하는 행위는 하지 마세요!** 계산된 속성은 다른 값들을 기반으로 값을 파생시키는 방법을 선언적으로 설명하는 것으로 생각해야 합니다 - 그것의 유일한 책임은 그 값을 계산하고 반환하는 것입니다. 가이드에서 나중에 우리는 상태 변화에 대한 반응으로 부작용을 수행하는 방법에 대해 [watchers](./watchers)를 사용하여 논의할 것입니다. ### 계산된 값을 변경하지 마십시오 {#avoid-mutating-computed-value} -계산된 속성에서 반환된 값은 파생된 상태입니다. -임시 스냅샷으로 생각하십시오. -소스 상태가 변경될 때마다 새 스냅샷이 생성됩니다. -스냅샷을 변경하는 것은 의미가 없으므로 계산된 반환 값은 읽기 전용으로 처리되어야 하며 변경되지 않아야 합니다. -대신 새 계산을 트리거하기 위해 의존하는 소스 상태를 업데이트하십시오. +계산된 속성에서 반환된 값은 파생된 상태입니다. 임시 스냅샷으로 생각하십시오. 소스 상태가 변경될 때마다 새 스냅샷이 생성됩니다. 스냅샷을 변경하는 것은 의미가 없으므로 계산된 반환 값은 읽기 전용으로 처리되어야 하며 변경되지 않아야 합니다. 대신 새 계산을 트리거하기 위해 의존하는 소스 상태를 업데이트하십시오. diff --git a/src/guide/essentials/template-refs.md b/src/guide/essentials/template-refs.md index 56c573f5..9c984eef 100644 --- a/src/guide/essentials/template-refs.md +++ b/src/guide/essentials/template-refs.md @@ -1,21 +1,18 @@ # 템플릿 참조 {#template-refs} -Vue의 선언적 렌더링 모델은 개발자가 직접 DOM에 접근해야 해야하는 대부분을 추상화하지만, 개발자가 DOM 엘리먼트에 직접 접근해야 하는 경우가 여전히 있을 수 있습니다. -이러한 필요성을 충족시키기 위해 `ref`라는 특별한 속성을 사용할 수 있습니다: +Vue의 선언적 렌더링 모델은 개발자가 직접 DOM에 접근해야 해야하는 대부분을 추상화하지만, 개발자가 DOM 엘리먼트에 직접 접근해야 하는 경우가 여전히 있을 수 있습니다. 이러한 필요성을 충족시키기 위해 `ref`라는 특별한 속성을 사용할 수 있습니다: ```vue-html ``` -`ref`는 `v-for` 장에서 언급한 `key` 속성과 유사한 특수 속성입니다. -이를 통해 마운트된 특정 DOM 엘리먼트 또는 자식 컴포넌트 인스턴스에 직접적인 참조를 얻을 수 있습니다. -예를 들어 컴포넌트 마운트의 인풋에 초점을 맞추거나 엘리먼트에서 타사 라이브러리를 초기화하려는 경우, 프로그래밍 방식으로 조작하기 편리합니다. +`ref`는 `v-for` 장에서 언급한 `key` 속성과 유사한 특수 속성입니다. 이를 통해 마운트된 특정 DOM 엘리먼트 또는 자식 컴포넌트 인스턴스에 직접적인 참조를 얻을 수 있습니다. 예를 들어 컴포넌트 마운트의 인풋에 초점을 맞추거나 엘리먼트에서 타사 라이브러리를 초기화하려는 경우, 프로그래밍 방식으로 조작하기 편리합니다. ## ref로 접근하기 {#accessing-the-refs}
-컴포지션 API로 참조를 얻으려면 동일한 이름으로 `ref`를 선언해야 합니다: +Composition API를 사용하여 참조를 얻으려면, 템플릿 ref 속성 값과 일치하는 이름으로 ref를 선언해야 합니다: ```vue + # 빠른 시작 {#quick-start} 사용 사례 및 기본 설정에 따라 빌드 과정을 포함하거나 포함하지 않고 Vue를 사용할 수 있습니다. @@ -25,9 +29,38 @@ footer: false 이 섹션에서는 로컬 컴퓨터에서 Vue [싱글 페이지 애플리케이션](/guide/extras/ways-of-using-vue.html#single-page-application-spa)을 스캐폴드하는 방법을 소개합니다. 생성된 프로젝트는 [Vite](https://vitejs.dev)를 기반으로 빌드 설정을 사용하고 Vue [싱글 파일 컴포넌트](/guide/scaling-up/sfc)(SFC)를 사용할 수 있도록 합니다. -최신 버전의 [Node.js](https://nodejs.org/)가 설치되어 있고 현재 작업 디렉터리가 프로젝트를 만들려는 디렉터리인지 확인하세요. 명령줄에서 다음 명령을 실행합니다(`>` 기호 제외): +최신 버전의 [Node.js](https://nodejs.org/)가 설치되어 있고 현재 작업 디렉터리가 프로젝트를 만들려는 디렉터리인지 확인하세요. 명령줄에서 다음 명령을 실행합니다(`$` 기호 제외): + + + + + ```sh + $ npm create vue@latest + ``` + + + + + ```sh + $ pnpm create vue@latest + ``` + + + -
> npm init vue@latest
+ ```sh + $ yarn create vue@latest + ``` + +
+ + + ```sh + $ bun create vue@latest + ``` + + +
이 명령은 공식 Vue 프로젝트 스캐폴딩 도구인 [create-vue](https://github.com/vuejs/create-vue)를 설치 및 실행합니다. TypeScript 및 테스트 지원과 같은 몇 가지 선택적 기능에 대한 프롬프트가 표시됩니다: @@ -46,23 +79,84 @@ footer: false 옵션에 대해 확신이 서지 않는다면 일단 엔터키를 눌러 `No`를 선택하면 됩니다. 프로젝트가 생성되면 지침에 따라 종속 요소를 설치하고 개발 서버를 시작합니다: -
> cd <your-project-name>
-> npm install
-> npm run dev
-
+ + -이제 첫 번째 Vue 프로젝트가 실행 중일 것입니다! 생성된 프로젝트의 예제 컴포넌트는 [Options API](/guide/introduction.html#composition-api) 대신 `