@vue/language-service 2.2.8 → 2.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/language-blocks/ko.json +6 -6
- package/data/template/ko.json +26 -26
- package/index.js +1 -0
- package/lib/nameCasing.d.ts +14 -0
- package/lib/nameCasing.js +201 -0
- package/lib/plugins/vue-compiler-dom-errors.d.ts +2 -0
- package/lib/plugins/vue-compiler-dom-errors.js +68 -0
- package/lib/plugins/vue-document-highlights.d.ts +3 -0
- package/lib/plugins/vue-document-highlights.js +46 -0
- package/lib/plugins/vue-missing-props-hints.d.ts +3 -0
- package/lib/plugins/vue-missing-props-hints.js +160 -0
- package/package.json +4 -4
- package/lib/plugins/vue-autoinsert-self-closing.d.ts +0 -2
- package/lib/plugins/vue-autoinsert-self-closing.js +0 -33
- package/lib/plugins/vue-template/autoinsert-self-closing-tags.d.ts +0 -6
- package/lib/plugins/vue-template/autoinsert-self-closing-tags.js +0 -39
- package/lib/plugins/vue-template/autoinsert-selfClosingTags.d.ts +0 -1
- package/lib/plugins/vue-template/autoinsert-selfClosingTags.js +0 -3
- package/lib/plugins/vue-template/autoinsert-selfclosing-tags.d.ts +0 -1
- package/lib/plugins/vue-template/autoinsert-selfclosing-tags.js +0 -3
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"name": "src",
|
|
9
9
|
"description": {
|
|
10
10
|
"kind": "markdown",
|
|
11
|
-
"value": "`*.vue` 컴포넌트를 여러 파일로 분할하는 것을 선호하는 경우,\n`src` 속성을 사용하여 언어 블록에서 외부 파일을 가져올 수 있습니다:\n\n```vue\n<template src=\"./template.html\"></template>\n<style src=\"./style.css\"></style>\n<script src=\"./script.js\"></script>\n```\n\n`src` 가져오기는 웹팩 모듈 요청과 동일한 경로 확인 규칙을 따릅니다.\n즉, 다음을 의미합니다:\n\n- 상대 경로는 `./`로 시작해야 함.\n- npm 의존성에서 리소스를 가져올 수 있음.\n\n```vue\n<!-- 설치된 \"todomvc-app-css\" npm 패키지에서 파일 가져오기 -->\n<style src=\"todomvc-app-css/index.css\" />\n```\n\n`src` 가져오기는 커스텀 블록에서도 작동합니다. 예를들어:\n\n```vue\n<unit-test src=\"./unit-test.js\">\n</unit-test>\n```"
|
|
11
|
+
"value": "`*.vue` 컴포넌트를 여러 파일로 분할하는 것을 선호하는 경우,\n`src` 속성을 사용하여 언어 블록에서 외부 파일을 가져올 수 있습니다:\n\n```vue\n<template src=\"./template.html\"></template>\n<style src=\"./style.css\"></style>\n<script src=\"./script.js\"></script>\n```\n\n`src` 가져오기는 웹팩 모듈 요청과 동일한 경로 확인 규칙을 따릅니다.\n즉, 다음을 의미합니다:\n\n- 상대 경로는 `./`로 시작해야 함.\n- npm 의존성에서 리소스를 가져올 수 있음.\n\n```vue\n<!-- 설치된 \"todomvc-app-css\" npm 패키지에서 파일 가져오기 -->\n<style src=\"todomvc-app-css/index.css\" />\n```\n\n`src` 가져오기는 커스텀 블록에서도 작동합니다. 예를들어:\n\n```vue\n<unit-test src=\"./unit-test.js\">\n</unit-test>\n```\n\n:::warning Note\n`src`에서 별칭(alias)을 사용할 때, `~`로 시작하지 마세요. `~` 이후의 내용은 모듈 요청으로 해석됩니다. 이를 활용하면 Node 모듈 내부의 애셋을 참조할 수 있습니다.\n```vue\n<img src=\"~some-npm-package/foo.png\">\n```\n:::"
|
|
12
12
|
},
|
|
13
13
|
"references": "api/sfc-spec.html#src-imports"
|
|
14
14
|
},
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
],
|
|
32
32
|
"description": {
|
|
33
33
|
"kind": "markdown",
|
|
34
|
-
"value": "\n- 각 `*.vue` 파일은 최상위 `<template>` 블록을 하나만 포함할 수 있습니다.\n\n- 컨텐츠는 추출되어 `@vue/compiler-dom`으로 전달되고,\n JavaScript 렌더 함수로 사전 컴파일되며,\n 내보낸 컴포넌트에 `render` 옵션으로 첨부됩니다.\n"
|
|
34
|
+
"value": "\n- 각 `*.vue` 파일은 최상위 `<template>` 블록을 한번에 하나만 포함할 수 있습니다.\n\n- 컨텐츠는 추출되어 `@vue/compiler-dom`으로 전달되고,\n JavaScript 렌더 함수로 사전 컴파일되며,\n 내보낸 컴포넌트에 `render` 옵션으로 첨부됩니다.\n"
|
|
35
35
|
},
|
|
36
36
|
"references": "api/sfc-spec.html#template"
|
|
37
37
|
},
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"name": "src",
|
|
43
43
|
"description": {
|
|
44
44
|
"kind": "markdown",
|
|
45
|
-
"value": "`*.vue` 컴포넌트를 여러 파일로 분할하는 것을 선호하는 경우,\n`src` 속성을 사용하여 언어 블록에서 외부 파일을 가져올 수 있습니다:\n\n```vue\n<template src=\"./template.html\"></template>\n<style src=\"./style.css\"></style>\n<script src=\"./script.js\"></script>\n```\n\n`src` 가져오기는 웹팩 모듈 요청과 동일한 경로 확인 규칙을 따릅니다.\n즉, 다음을 의미합니다:\n\n- 상대 경로는 `./`로 시작해야 함.\n- npm 의존성에서 리소스를 가져올 수 있음.\n\n```vue\n<!-- 설치된 \"todomvc-app-css\" npm 패키지에서 파일 가져오기 -->\n<style src=\"todomvc-app-css/index.css\" />\n```\n\n`src` 가져오기는 커스텀 블록에서도 작동합니다. 예를들어:\n\n```vue\n<unit-test src=\"./unit-test.js\">\n</unit-test>\n```"
|
|
45
|
+
"value": "`*.vue` 컴포넌트를 여러 파일로 분할하는 것을 선호하는 경우,\n`src` 속성을 사용하여 언어 블록에서 외부 파일을 가져올 수 있습니다:\n\n```vue\n<template src=\"./template.html\"></template>\n<style src=\"./style.css\"></style>\n<script src=\"./script.js\"></script>\n```\n\n`src` 가져오기는 웹팩 모듈 요청과 동일한 경로 확인 규칙을 따릅니다.\n즉, 다음을 의미합니다:\n\n- 상대 경로는 `./`로 시작해야 함.\n- npm 의존성에서 리소스를 가져올 수 있음.\n\n```vue\n<!-- 설치된 \"todomvc-app-css\" npm 패키지에서 파일 가져오기 -->\n<style src=\"todomvc-app-css/index.css\" />\n```\n\n`src` 가져오기는 커스텀 블록에서도 작동합니다. 예를들어:\n\n```vue\n<unit-test src=\"./unit-test.js\">\n</unit-test>\n```\n\n:::warning Note\n`src`에서 별칭(alias)을 사용할 때, `~`로 시작하지 마세요. `~` 이후의 내용은 모듈 요청으로 해석됩니다. 이를 활용하면 Node 모듈 내부의 애셋을 참조할 수 있습니다.\n```vue\n<img src=\"~some-npm-package/foo.png\">\n```\n:::"
|
|
46
46
|
},
|
|
47
47
|
"references": "api/sfc-spec.html#src-imports"
|
|
48
48
|
},
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"name": "src",
|
|
104
104
|
"description": {
|
|
105
105
|
"kind": "markdown",
|
|
106
|
-
"value": "`*.vue` 컴포넌트를 여러 파일로 분할하는 것을 선호하는 경우,\n`src` 속성을 사용하여 언어 블록에서 외부 파일을 가져올 수 있습니다:\n\n```vue\n<template src=\"./template.html\"></template>\n<style src=\"./style.css\"></style>\n<script src=\"./script.js\"></script>\n```\n\n`src` 가져오기는 웹팩 모듈 요청과 동일한 경로 확인 규칙을 따릅니다.\n즉, 다음을 의미합니다:\n\n- 상대 경로는 `./`로 시작해야 함.\n- npm 의존성에서 리소스를 가져올 수 있음.\n\n```vue\n<!-- 설치된 \"todomvc-app-css\" npm 패키지에서 파일 가져오기 -->\n<style src=\"todomvc-app-css/index.css\" />\n```\n\n`src` 가져오기는 커스텀 블록에서도 작동합니다. 예를들어:\n\n```vue\n<unit-test src=\"./unit-test.js\">\n</unit-test>\n```"
|
|
106
|
+
"value": "`*.vue` 컴포넌트를 여러 파일로 분할하는 것을 선호하는 경우,\n`src` 속성을 사용하여 언어 블록에서 외부 파일을 가져올 수 있습니다:\n\n```vue\n<template src=\"./template.html\"></template>\n<style src=\"./style.css\"></style>\n<script src=\"./script.js\"></script>\n```\n\n`src` 가져오기는 웹팩 모듈 요청과 동일한 경로 확인 규칙을 따릅니다.\n즉, 다음을 의미합니다:\n\n- 상대 경로는 `./`로 시작해야 함.\n- npm 의존성에서 리소스를 가져올 수 있음.\n\n```vue\n<!-- 설치된 \"todomvc-app-css\" npm 패키지에서 파일 가져오기 -->\n<style src=\"todomvc-app-css/index.css\" />\n```\n\n`src` 가져오기는 커스텀 블록에서도 작동합니다. 예를들어:\n\n```vue\n<unit-test src=\"./unit-test.js\">\n</unit-test>\n```\n\n:::warning Note\n`src`에서 별칭(alias)을 사용할 때, `~`로 시작하지 마세요. `~` 이후의 내용은 모듈 요청으로 해석됩니다. 이를 활용하면 Node 모듈 내부의 애셋을 참조할 수 있습니다.\n```vue\n<img src=\"~some-npm-package/foo.png\">\n```\n:::"
|
|
107
107
|
},
|
|
108
108
|
"references": "api/sfc-spec.html#src-imports"
|
|
109
109
|
},
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
"name": "src",
|
|
168
168
|
"description": {
|
|
169
169
|
"kind": "markdown",
|
|
170
|
-
"value": "`*.vue` 컴포넌트를 여러 파일로 분할하는 것을 선호하는 경우,\n`src` 속성을 사용하여 언어 블록에서 외부 파일을 가져올 수 있습니다:\n\n```vue\n<template src=\"./template.html\"></template>\n<style src=\"./style.css\"></style>\n<script src=\"./script.js\"></script>\n```\n\n`src` 가져오기는 웹팩 모듈 요청과 동일한 경로 확인 규칙을 따릅니다.\n즉, 다음을 의미합니다:\n\n- 상대 경로는 `./`로 시작해야 함.\n- npm 의존성에서 리소스를 가져올 수 있음.\n\n```vue\n<!-- 설치된 \"todomvc-app-css\" npm 패키지에서 파일 가져오기 -->\n<style src=\"todomvc-app-css/index.css\" />\n```\n\n`src` 가져오기는 커스텀 블록에서도 작동합니다. 예를들어:\n\n```vue\n<unit-test src=\"./unit-test.js\">\n</unit-test>\n```"
|
|
170
|
+
"value": "`*.vue` 컴포넌트를 여러 파일로 분할하는 것을 선호하는 경우,\n`src` 속성을 사용하여 언어 블록에서 외부 파일을 가져올 수 있습니다:\n\n```vue\n<template src=\"./template.html\"></template>\n<style src=\"./style.css\"></style>\n<script src=\"./script.js\"></script>\n```\n\n`src` 가져오기는 웹팩 모듈 요청과 동일한 경로 확인 규칙을 따릅니다.\n즉, 다음을 의미합니다:\n\n- 상대 경로는 `./`로 시작해야 함.\n- npm 의존성에서 리소스를 가져올 수 있음.\n\n```vue\n<!-- 설치된 \"todomvc-app-css\" npm 패키지에서 파일 가져오기 -->\n<style src=\"todomvc-app-css/index.css\" />\n```\n\n`src` 가져오기는 커스텀 블록에서도 작동합니다. 예를들어:\n\n```vue\n<unit-test src=\"./unit-test.js\">\n</unit-test>\n```\n\n:::warning Note\n`src`에서 별칭(alias)을 사용할 때, `~`로 시작하지 마세요. `~` 이후의 내용은 모듈 요청으로 해석됩니다. 이를 활용하면 Node 모듈 내부의 애셋을 참조할 수 있습니다.\n```vue\n<img src=\"~some-npm-package/foo.png\">\n```\n:::"
|
|
171
171
|
},
|
|
172
172
|
"references": "api/sfc-spec.html#src-imports"
|
|
173
173
|
}
|
|
@@ -193,7 +193,7 @@
|
|
|
193
193
|
"name": "src",
|
|
194
194
|
"description": {
|
|
195
195
|
"kind": "markdown",
|
|
196
|
-
"value": "`*.vue` 컴포넌트를 여러 파일로 분할하는 것을 선호하는 경우,\n`src` 속성을 사용하여 언어 블록에서 외부 파일을 가져올 수 있습니다:\n\n```vue\n<template src=\"./template.html\"></template>\n<style src=\"./style.css\"></style>\n<script src=\"./script.js\"></script>\n```\n\n`src` 가져오기는 웹팩 모듈 요청과 동일한 경로 확인 규칙을 따릅니다.\n즉, 다음을 의미합니다:\n\n- 상대 경로는 `./`로 시작해야 함.\n- npm 의존성에서 리소스를 가져올 수 있음.\n\n```vue\n<!-- 설치된 \"todomvc-app-css\" npm 패키지에서 파일 가져오기 -->\n<style src=\"todomvc-app-css/index.css\" />\n```\n\n`src` 가져오기는 커스텀 블록에서도 작동합니다. 예를들어:\n\n```vue\n<unit-test src=\"./unit-test.js\">\n</unit-test>\n```"
|
|
196
|
+
"value": "`*.vue` 컴포넌트를 여러 파일로 분할하는 것을 선호하는 경우,\n`src` 속성을 사용하여 언어 블록에서 외부 파일을 가져올 수 있습니다:\n\n```vue\n<template src=\"./template.html\"></template>\n<style src=\"./style.css\"></style>\n<script src=\"./script.js\"></script>\n```\n\n`src` 가져오기는 웹팩 모듈 요청과 동일한 경로 확인 규칙을 따릅니다.\n즉, 다음을 의미합니다:\n\n- 상대 경로는 `./`로 시작해야 함.\n- npm 의존성에서 리소스를 가져올 수 있음.\n\n```vue\n<!-- 설치된 \"todomvc-app-css\" npm 패키지에서 파일 가져오기 -->\n<style src=\"todomvc-app-css/index.css\" />\n```\n\n`src` 가져오기는 커스텀 블록에서도 작동합니다. 예를들어:\n\n```vue\n<unit-test src=\"./unit-test.js\">\n</unit-test>\n```\n\n:::warning Note\n`src`에서 별칭(alias)을 사용할 때, `~`로 시작하지 마세요. `~` 이후의 내용은 모듈 요청으로 해석됩니다. 이를 활용하면 Node 모듈 내부의 애셋을 참조할 수 있습니다.\n```vue\n<img src=\"~some-npm-package/foo.png\">\n```\n:::"
|
|
197
197
|
},
|
|
198
198
|
"references": "api/sfc-spec.html#src-imports"
|
|
199
199
|
}
|
package/data/template/ko.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "Transition",
|
|
6
6
|
"description": {
|
|
7
7
|
"kind": "markdown",
|
|
8
|
-
"value": "\n**싱글** 엘리먼트 또는 컴포넌트에 애니메이션 트랜지션 효과를 제공합니다.\n\n- **Props**\n\n ```ts\n interface TransitionProps {\n /**\n * 트랜지션 CSS 클래스 이름 자동 생성에 사용.\n * 예를 들어 `name: 'fade'`는 `.fade-enter`,\n * `.fade-enter-active` 등으로 자동 확장됨.\n */\n name?: string\n /**\n * CSS 트랜지션 클래스를 적용할지 여부입니다.\n * 기본 값: true\n */\n css?: boolean\n /**\n * 트랜지션 종료 타이밍을 결정하기 위해,\n * 대기할 트랜지션 이벤트의 유형을 지정.\n * 기본 동작은 지속 시간이 더 긴 유형을\n * 자동으로 감지.\n */\n type?: 'transition' | 'animation'\n /**\n * 명시적으로 트랜지션의 지속 시간을 지정.\n * 기본 동작은 루트 트랜지션 엘리먼트의 첫 번째\n * `transitionend` 또는 `animationend` 이벤트를 기다리는 것.\n */\n duration?: number | { enter: number; leave: number }\n /**\n * 진입/진출 트랜지션의 타이밍 순서를 제어.\n * 기본 동작은 동시.\n */\n mode?: 'in-out' | 'out-in' | 'default'\n /**\n * 최초 렌더링에 트랜지션을 적용할지 여부.\n * 기본 값: false\n */\n appear?: boolean\n\n /**\n * 트랜지션 클래스를 커스텀하기 위한 props.\n * 템플릿에서 kebab-case를 사용해야 함. 예: enter-from-class=\"xxx\"\n */\n enterFromClass?: string\n enterActiveClass?: string\n enterToClass?: string\n appearFromClass?: string\n appearActiveClass?: string\n appearToClass?: string\n leaveFromClass?: string\n leaveActiveClass?: string\n leaveToClass?: string\n }\n ```\n\n-
|
|
8
|
+
"value": "\n**싱글** 엘리먼트 또는 컴포넌트에 애니메이션 트랜지션 효과를 제공합니다.\n\n- **Props**\n\n ```ts\n interface TransitionProps {\n /**\n * 트랜지션 CSS 클래스 이름 자동 생성에 사용.\n * 예를 들어 `name: 'fade'`는 `.fade-enter`,\n * `.fade-enter-active` 등으로 자동 확장됨.\n */\n name?: string\n /**\n * CSS 트랜지션 클래스를 적용할지 여부입니다.\n * 기본 값: true\n */\n css?: boolean\n /**\n * 트랜지션 종료 타이밍을 결정하기 위해,\n * 대기할 트랜지션 이벤트의 유형을 지정.\n * 기본 동작은 지속 시간이 더 긴 유형을\n * 자동으로 감지.\n */\n type?: 'transition' | 'animation'\n /**\n * 명시적으로 트랜지션의 지속 시간을 지정.\n * 기본 동작은 루트 트랜지션 엘리먼트의 첫 번째\n * `transitionend` 또는 `animationend` 이벤트를 기다리는 것.\n */\n duration?: number | { enter: number; leave: number }\n /**\n * 진입/진출 트랜지션의 타이밍 순서를 제어.\n * 기본 동작은 동시.\n */\n mode?: 'in-out' | 'out-in' | 'default'\n /**\n * 최초 렌더링에 트랜지션을 적용할지 여부.\n * 기본 값: false\n */\n appear?: boolean\n\n /**\n * 트랜지션 클래스를 커스텀하기 위한 props.\n * 템플릿에서 kebab-case를 사용해야 함. 예: enter-from-class=\"xxx\"\n */\n enterFromClass?: string\n enterActiveClass?: string\n enterToClass?: string\n appearFromClass?: string\n appearActiveClass?: string\n appearToClass?: string\n leaveFromClass?: string\n leaveActiveClass?: string\n leaveToClass?: string\n }\n ```\n\n- **이벤트**\n\n - `@before-enter`\n - `@before-leave`\n - `@enter`\n - `@leave`\n - `@appear`\n - `@after-enter`\n - `@after-leave`\n - `@after-appear`\n - `@enter-cancelled`\n - `@leave-cancelled` (`v-show`에서만)\n - `@appear-cancelled`\n\n- **예제**\n\n 간단한 엘리먼트:\n\n ```html\n <Transition>\n <div v-if=\"ok\">토글된 컨텐츠</div>\n </Transition>\n ```\n\n `key` 속성을 변경하여 강제로 트랜지션(전환):\n\n ```html\n <Transition>\n <div :key=\"text\">{{ text }}</div>\n </Transition>\n ```\n \n 트랜지션 모드 + 등장 애니메이션을 가진 동적 컴포넌트:\n\n ```html\n <Transition name=\"fade\" mode=\"out-in\" appear>\n <component :is=\"view\"></component>\n </Transition>\n ```\n\n 트랜지션 이벤트 수신:\n\n ```html\n <Transition @after-enter=\"onTransitionComplete\">\n <div v-show=\"ok\">토글된 컨텐츠</div>\n </Transition>\n ```\n\n- **참고** [가이드 - Transition](https://ko.vuejs.org/guide/built-ins/transition.html)\n"
|
|
9
9
|
},
|
|
10
10
|
"attributes": [],
|
|
11
11
|
"references": "api/built-in-components.html#transition"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"name": "TransitionGroup",
|
|
15
15
|
"description": {
|
|
16
16
|
"kind": "markdown",
|
|
17
|
-
"value": "\n리스트의 **여러** 엘리먼트 또는 컴포넌트에 트랜지션 효과를 제공합니다.\n\n- **Props**\n\n `<TransitionGroup>`은 `<Transition>`과 동일한 props에서 `mode`를 제외하고 두 개의 추가 props를 허용합니다:\n\n ```ts\n interface TransitionGroupProps extends Omit<TransitionProps, 'mode'> {\n /**\n * 정의하지 않으면, 렌더는 프래그먼트처럼 취급함.\n */\n tag?: string\n /**\n * 이동 전환 중에 적용되는 CSS 클래스를 사용자 정의합니다.\n * 템플릿에서 kebab-case를 사용해야 함. 예: move-class=\"xxx\"\n */\n moveClass?: string\n }\n ```\n\n-
|
|
17
|
+
"value": "\n리스트의 **여러** 엘리먼트 또는 컴포넌트에 트랜지션 효과를 제공합니다.\n\n- **Props**\n\n `<TransitionGroup>`은 `<Transition>`과 동일한 props에서 `mode`를 제외하고 두 개의 추가 props를 허용합니다:\n\n ```ts\n interface TransitionGroupProps extends Omit<TransitionProps, 'mode'> {\n /**\n * 정의하지 않으면, 렌더는 프래그먼트처럼 취급함.\n */\n tag?: string\n /**\n * 이동 전환 중에 적용되는 CSS 클래스를 사용자 정의합니다.\n * 템플릿에서 kebab-case를 사용해야 함. 예: move-class=\"xxx\"\n */\n moveClass?: string\n }\n ```\n\n- **이벤트**\n\n `<TransitionGroup>`은 `<Transition>`과 동일한 이벤트를 발생시킵니다.\n\n- **세부 사항**\n\n 기본적으로 `<TransitionGroup>`은 래퍼 DOM 엘리먼트를 렌더링하지 않지만 `tag` prop을 통해 정의할 수 있습니다.\n\n 애니메이션이 제대로 작동하려면 `<transition-group>`의 모든 자식이 [**고유 키**](https://ko.vuejs.org/guide/essentials/list.html#maintaining-state-with-key)를 가져야 합니다.\n\n `<TransitionGroup>`은 CSS `transform`으로 이동 트랜지션을 지원합니다. 업데이트 후 화면에서 자식의 위치가 변경되면, 움직이는 CSS 클래스가 적용됩니다(`name` 속성에서 자동 생성되거나 `move-class` prop으로 구성됨). 이동 클래스가 적용될 때 CSS의 `transform` 속성이 \"트랜지션 가능\"이면, [FLIP 기술](https://aerotwist.com/blog/flip-your-animations/)을 사용하여 엘리먼트가 목적지까지 부드럽게 애니메이션됩니다.\n\n- **예제**\n\n ```html\n <TransitionGroup tag=\"ul\" name=\"slide\">\n <li v-for=\"item in items\" :key=\"item.id\">\n {{ item.text }}\n </li>\n </TransitionGroup>\n ```\n\n- **참고** [가이드 - TransitionGroup](https://ko.vuejs.org/guide/built-ins/transition-group.html)\n"
|
|
18
18
|
},
|
|
19
19
|
"attributes": [],
|
|
20
20
|
"references": "api/built-in-components.html#transitiongroup"
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"name": "KeepAlive",
|
|
24
24
|
"description": {
|
|
25
25
|
"kind": "markdown",
|
|
26
|
-
"value": "\n내부에 래핑된 동적으로 토글되는 컴포넌트를 캐시합니다.\n\n- **Props**\n\n ```ts\n interface KeepAliveProps {\n /**\n * `include`와 이름이 일치하는\n * 컴포넌트만 캐시됨.\n */\n include?: MatchPattern\n /**\n * `exclude`와 이름이 일치하는\n * 컴포넌트는 캐시되지 않음.\n */\n exclude?: MatchPattern\n /**\n * 캐시할 컴포넌트 인스턴스의 최대 수.\n */\n max?: number | string\n }\n\n type MatchPattern = string | RegExp | (string | RegExp)[]\n ```\n\n- **세부
|
|
26
|
+
"value": "\n내부에 래핑된 동적으로 토글되는 컴포넌트를 캐시합니다.\n\n- **Props**\n\n ```ts\n interface KeepAliveProps {\n /**\n * `include`와 이름이 일치하는\n * 컴포넌트만 캐시됨.\n */\n include?: MatchPattern\n /**\n * `exclude`와 이름이 일치하는\n * 컴포넌트는 캐시되지 않음.\n */\n exclude?: MatchPattern\n /**\n * 캐시할 컴포넌트 인스턴스의 최대 수.\n */\n max?: number | string\n }\n\n type MatchPattern = string | RegExp | (string | RegExp)[]\n ```\n\n- **세부 사항**\n\n `<KeepAlive>`로 래핑된 동적 컴포넌트는 비활성화 되면, 컴포넌트 인스턴스가 파괴되지 않고 캐시됩니다.\n\n `<KeepAlive>`에는 언제나 활성화된 직계 자식의 컴포넌트 인스턴스가 하나만 있을 수 있습니다.\n\n 컴포넌트가 `<KeepAlive>` 내에서 토글되면, `mounted` 및 `unmounted` 대신 `activated` 및 `deactivated` 생명 주기 훅이 호출됩니다. 이는 `<KeepAlive>`의 직계 자식과 모든 하위 항목에 적용됩니다.\n\n- **예제**\n\n 기본 사용법:\n\n ```html\n <KeepAlive>\n <component :is=\"view\"></component>\n </KeepAlive>\n ```\n\n `v-if` / `v-else`를 사용할 때, 한 번에 하나의 컴포넌트만 렌더링되어야 합니다:\n\n ```html\n <KeepAlive>\n <comp-a v-if=\"a > 1\"></comp-a>\n <comp-b v-else></comp-b>\n </KeepAlive>\n ```\n\n `<Transition>`과 함께 사용:\n\n ```html\n <Transition>\n <KeepAlive>\n <component :is=\"view\"></component>\n </KeepAlive>\n </Transition>\n ```\n\n `include` / `exclude` 사용:\n\n ```html\n <!-- 쉼표로 구분된 문자열 -->\n <KeepAlive include=\"a,b\">\n <component :is=\"view\"></component>\n </KeepAlive>\n\n <!-- 정규식 사용(`v-bind` 포함) -->\n <KeepAlive :include=\"/a|b/\">\n <component :is=\"view\"></component>\n </KeepAlive>\n\n <!-- 배열 사용(`v-bind` 포함) -->\n <KeepAlive :include=\"['a', 'b']\">\n <component :is=\"view\"></component>\n </KeepAlive>\n ```\n\n `max`를 활용한 사용:\n\n ```html\n <KeepAlive :max=\"10\">\n <component :is=\"view\"></component>\n </KeepAlive>\n ```\n\n- **참고** [가이드 - KeepAlive](https://ko.vuejs.org/guide/built-ins/keep-alive.html)\n"
|
|
27
27
|
},
|
|
28
28
|
"attributes": [],
|
|
29
29
|
"references": "api/built-in-components.html#keepalive"
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"name": "Teleport",
|
|
33
33
|
"description": {
|
|
34
34
|
"kind": "markdown",
|
|
35
|
-
"value": "\n슬롯 컨텐츠를 DOM 내 다른 위치에서 렌더링합니다.\n\n- **Props**\n\n ```ts\n interface TeleportProps {\n /**\n * 필수. 대상이 될 컨테이너를 지정.\n * 셀렉터 또는 실제 엘리먼트일 수 있음.\n */\n to: string | HTMLElement\n /**\n * `true`이면 컨텐츠가 대상이 될 컨테이너로\n * 이동하지 않고 원래 위치에 남아 있음.\n * 동적으로 변경할 수 있음.\n */\n disabled?: boolean\n }\n ```\n\n- **예제**\n\n 대상이 될 컨테이너 지정:\n\n ```html\n <Teleport to=\"#some-id\" />\n <Teleport to=\".some-class\" />\n <Teleport to=\"[data-teleport]\" />\n ```\n\n 조건부 비활성화:\n\n ```html\n <Teleport to=\"#popup\" :disabled=\"displayVideoInline\">\n <video src=\"./my-movie.mp4\">\n </Teleport>\n ```\n\n- **참고** [가이드 - Teleport](https://ko.vuejs.org/guide/built-ins/teleport.html)\n"
|
|
35
|
+
"value": "\n슬롯 컨텐츠를 DOM 내 다른 위치에서 렌더링합니다.\n\n- **Props**\n\n ```ts\n interface TeleportProps {\n /**\n * 필수. 대상이 될 컨테이너를 지정.\n * 셀렉터 또는 실제 엘리먼트일 수 있음.\n */\n to: string | HTMLElement\n /**\n * `true`이면 컨텐츠가 대상이 될 컨테이너로\n * 이동하지 않고 원래 위치에 남아 있음.\n * 동적으로 변경할 수 있음.\n */\n disabled?: boolean\n /**\n * `true`이면 텔레포트는 대상을 확인하기 전에 애플리케이션의 다른 부분이 마운트될 때까지 지연됩니다. (3.5+)\n */\n defer?: boolean\n }\n ```\n\n- **예제**\n\n 대상이 될 컨테이너 지정:\n\n ```html\n <Teleport to=\"#some-id\" />\n <Teleport to=\".some-class\" />\n <Teleport to=\"[data-teleport]\" />\n ```\n\n 조건부 비활성화:\n\n ```html\n <Teleport to=\"#popup\" :disabled=\"displayVideoInline\">\n <video src=\"./my-movie.mp4\">\n </Teleport>\n ```\n\n 텔레포트 대상 지연(Defered) 확인 <sup class=\"vt-badge\" data-text=\"3.5+\" />:\n\n ```html\n <Teleport defer to=\"#late-div\">...</Teleport>\n\n <!-- 이 안의 내용이 나중에 채워짐 -->\n <div id=\"late-div\"></div>\n ```\n\n- **참고** [가이드 - Teleport](https://ko.vuejs.org/guide/built-ins/teleport.html)\n"
|
|
36
36
|
},
|
|
37
37
|
"attributes": [],
|
|
38
38
|
"references": "api/built-in-components.html#teleport"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"name": "Suspense",
|
|
42
42
|
"description": {
|
|
43
43
|
"kind": "markdown",
|
|
44
|
-
"value": "\n컴포넌트 트리에서 중첩된 비동기 의존성을 조정하는 데 사용됩니다.\n\n- **Props**\n\n ```ts\n interface SuspenseProps {\n timeout?: string | number\n suspensible?: boolean\n }\n ```\n\n-
|
|
44
|
+
"value": "\n컴포넌트 트리에서 중첩된 비동기 의존성을 조정하는 데 사용됩니다.\n\n- **Props**\n\n ```ts\n interface SuspenseProps {\n timeout?: string | number\n suspensible?: boolean\n }\n ```\n\n- **이벤트**\n\n - `@resolve`\n - `@pending`\n - `@fallback`\n\n- **세부 사항**\n\n `<Suspense>`는 `#default` 슬롯과 `#fallback` 슬롯이라는 두 개의 슬롯을 사용합니다. 메모리에서 기본 슬롯을 렌더링하는 동안, 폴백 슬롯의 대체 컨텐츠를 노출합니다.\n\n 기본 슬롯을 렌더링하는 동안 비동기 의존성([비동기 컴포넌트](https://ko.vuejs.org/guide/components/async.html) 및 [`async setup()`](https://ko.vuejs.org/guide/built-ins/suspense.html#async-setup)이 있는 컴포넌트)을 만나면, 기본 슬롯을 표시하기 전에 모든 것이 해결될 때까지 대기합니다.\n\n Suspense를 `suspensible`로 설정하면 모든 비동기 종속성 처리가 부모 Suspense에 의해 처리됩니다. [구현 세부 사항](https://github.com/vuejs/core/pull/6736)을 참조하세요.\n\n- **참고** [가이드 - Suspense](https://ko.vuejs.org/guide/built-ins/suspense.html)\n"
|
|
45
45
|
},
|
|
46
46
|
"attributes": [],
|
|
47
47
|
"references": "api/built-in-components.html#suspense"
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"name": "component",
|
|
51
51
|
"description": {
|
|
52
52
|
"kind": "markdown",
|
|
53
|
-
"value": "\n동적 컴포넌트 또는 엘리먼트를 렌더링하기 위한 \"메타 컴포넌트\"입니다.\n\n- **Props**\n\n ```ts\n interface DynamicComponentProps {\n is: string | Component\n }\n ```\n\n- **세부
|
|
53
|
+
"value": "\n동적 컴포넌트 또는 엘리먼트를 렌더링하기 위한 \"메타 컴포넌트\"입니다.\n\n- **Props**\n\n ```ts\n interface DynamicComponentProps {\n is: string | Component\n }\n ```\n\n- **세부 사항**\n\n `is`라는 prop의 값으로 렌더링할 실제 컴포넌트가 결정됩니다:\n\n - 문자열인 경우, HTML 태그 이름 또는 컴포넌트로 등록된 이름일 수 있음.\n\n - 컴포넌트의 정의에 직접 바인딩될 수도 있음.\n\n- **예제**\n\n 등록된 이름으로 컴포넌트 렌더링(옵션 API):\n\n ```vue\n <script>\n import Foo from './Foo.vue'\n import Bar from './Bar.vue'\n\n export default {\n components: { Foo, Bar },\n data() {\n return {\n view: 'Foo'\n }\n }\n }\n </script>\n\n <template>\n <component :is=\"view\" />\n </template>\n ```\n\n 정의에 따른 컴포넌트 렌더링(`<script setup>`이 있는 컴포지션 API):\n\n ```vue\n <script setup>\n import Foo from './Foo.vue'\n import Bar from './Bar.vue'\n </script>\n\n <template>\n <component :is=\"Math.random() > 0.5 ? Foo : Bar\" />\n </template>\n ```\n\n HTML 엘리먼트 렌더링:\n\n ```html\n <component :is=\"href ? 'a' : 'span'\"></component>\n ```\n\n [빌트인 컴포넌트](./built-in-components)는 모두 `is`에 전달할 수 있지만,\n 이름으로 전달하려면 등록해야 합니다.\n 예를 들어:\n\n ```vue\n <script>\n import { Transition, TransitionGroup } from 'vue'\n\n export default {\n components: {\n Transition,\n TransitionGroup\n }\n }\n </script>\n\n <template>\n <component :is=\"isGroup ? 'TransitionGroup' : 'Transition'\">\n ...\n </component>\n </template>\n ```\n\n 이름이 아닌 컴포넌트 자체를 `is`에 전달하는 경우,\n 등록이 필요하지 않습니다(예를 들어 `<script setup>`에서).\n\n `v-model`이 `<component>` 태그에 사용되면, 템플릿 컴파일러는 다른 컴포넌트와 마찬가지로 이를 `modelValue` prop 및 `update:modelValue` 이벤트 리스너로 확장합니다.\n 그러나 이것은 `<input>` 또는 `<select>`와 같은 기본 HTML 엘리먼트와 호환되지 않습니다.\n 결과적으로 동적으로 생성된 기본 엘리먼트와 함께 `v-model`을 사용하면 작동하지 않습니다:\n\n ```vue\n <script setup>\n import { ref } from 'vue'\n \n const tag = ref('input')\n const username = ref('')\n </script>\n\n <template>\n <!-- 'input'이 기본 HTML 엘리먼트이므로 작동하지 않음 -->\n <component :is=\"tag\" v-model=\"username\" />\n </template>\n ```\n\n 실제로는 기본 양식(form) 필드가 일반적으로 실제 앱의 컴포넌트에 래핑되기 때문에 이러한 예외적인 경우는 일반적이지 않습니다.\n 네이티브 엘리먼트를 직접 사용해야 하는 경우, `v-model`을 속성과 이벤트로 수동으로 분할할 수 있습니다.\n\n- **참고** [가이드 - 컴포넌트 기초: 동적 컴포넌트](https://ko.vuejs.org/guide/essentials/component-basics.html#dynamic-components)\n"
|
|
54
54
|
},
|
|
55
55
|
"attributes": [],
|
|
56
56
|
"references": "api/built-in-special-elements.html#component"
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"name": "slot",
|
|
60
60
|
"description": {
|
|
61
61
|
"kind": "markdown",
|
|
62
|
-
"value": "\n템플릿의 슬롯 컨텐츠를 내보낼 지점을 나타냅니다.\n\n- **Props**\n\n ```ts\n interface SlotProps {\n /**\n * 범위가 지정된 슬롯의 인자로 전달하기 위해\n * <slot>에 전달된 모든 props\n */\n [key: string]: any\n /**\n * 슬롯 이름을 지정.\n */\n name?: string\n }\n ```\n\n- **세부
|
|
62
|
+
"value": "\n템플릿의 슬롯 컨텐츠를 내보낼 지점을 나타냅니다.\n\n- **Props**\n\n ```ts\n interface SlotProps {\n /**\n * 범위가 지정된 슬롯의 인자로 전달하기 위해\n * <slot>에 전달된 모든 props\n */\n [key: string]: any\n /**\n * 슬롯 이름을 지정.\n */\n name?: string\n }\n ```\n\n- **세부 사항**\n\n `<slot>` 엘리먼트는 `name` 속성을 사용하여 슬롯 이름을 지정할 수 있습니다.\n `name`을 지정하지 않으면 기본 슬롯으로 렌더링됩니다.\n 슬롯 엘리먼트에 전달된 추가 속성은 부모 내부에서 범위가 정의된 슬롯에 슬롯 props로 전달됩니다.\n\n 엘리먼트는 일치하는 슬롯의 컨텐츠로 대체됩니다.\n\n Vue 템플릿의 `<slot>` 엘리먼트는 JavaScript로 컴파일되므로 [네이티브 `<slot>` 엘리먼트](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot)와 혼동하면 안됩니다.\n\n- **참고** [가이드 - 슬롯](https://ko.vuejs.org/guide/components/slots.html)\n"
|
|
63
63
|
},
|
|
64
64
|
"attributes": [],
|
|
65
65
|
"references": "api/built-in-special-elements.html#slot"
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"name": "template",
|
|
69
69
|
"description": {
|
|
70
70
|
"kind": "markdown",
|
|
71
|
-
"value": "\n`<template>` 태그는 DOM에 렌더링없이 사용할 앨리먼트들에 대한 위치기술을 위해(placeholder)로 사용할수 있습니다. \nThe `<template>` tag is used as a placeholder when we want to use a built-in directive without rendering an element in the DOM.\n\n- **세부 사항:**\n `<template>` 의 이런 특수한 취급은 다음 디렉티브들과 함께 사용될때만 적용됩니다. \n \n - `v-if`, `v-else-if`, or `v-else`\n - `v-for`\n - `v-slot`\n \n 만약 이런 디렉티브가 없다면, [네이티브 `<template>` 앨리먼트](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template)로 렌더링됩니다. \n \n `v-for`가 있는 `<template>`은 [`key` 속성](https://ko.vuejs.org/api/built-in-special-attributes.html#key)도 가질 수 있습니다. 다른 모든 속성과 디렉티브는 해당 엘리먼트가가 없으면 의미가 없으므로 버려집니다.\n\n \n 싱글 파일 컴포넌트는 [최상위 `<template>` 태그](https://ko.vuejs.org/api/sfc-spec.html#language-blocks)를 사용하여 전체 템플릿을 래핑합니다. 그 사용법은 위에서 설명한 `<template>`의 사용과는 별개입니다. 해당 최상위 태그는 템플릿 자체의 일부가 아니며
|
|
71
|
+
"value": "\n`<template>` 태그는 DOM에 렌더링없이 사용할 앨리먼트들에 대한 위치기술을 위해(placeholder)로 사용할수 있습니다. \nThe `<template>` tag is used as a placeholder when we want to use a built-in directive without rendering an element in the DOM.\n\n- **세부 사항:**\n `<template>` 의 이런 특수한 취급은 다음 디렉티브들과 함께 사용될때만 적용됩니다. \n \n - `v-if`, `v-else-if`, or `v-else`\n - `v-for`\n - `v-slot`\n \n 만약 이런 디렉티브가 없다면, [네이티브 `<template>` 앨리먼트](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template)로 렌더링됩니다. \n \n `v-for`가 있는 `<template>`은 [`key` 속성](https://ko.vuejs.org/api/built-in-special-attributes.html#key)도 가질 수 있습니다. 다른 모든 속성과 디렉티브는 해당 엘리먼트가가 없으면 의미가 없으므로 버려집니다.\n\n \n 싱글 파일 컴포넌트는 [최상위 `<template>` 태그](https://ko.vuejs.org/api/sfc-spec.html#language-blocks)를 사용하여 전체 템플릿을 래핑합니다. 그 사용법은 위에서 설명한 `<template>`의 사용과는 별개입니다. 해당 최상위 태그는 템플릿 자체의 일부가 아니며 디렉티브과 같은 템플릿 문법을 지원하지 않습니다.\n\n- **참고**\n - [가이드 - `v-if` on `<template>`](https://ko.vuejs.org/guide/essentials/conditional.html#v-if-on-template) \n - [가이드 - `v-for` on `<template>`](https://ko.vuejs.org/guide/essentials/list.html#v-for-on-template) \n - [가이드 - Named slots](https://ko.vuejs.org/guide/components/slots.html#named-slots) \n"
|
|
72
72
|
},
|
|
73
73
|
"attributes": [],
|
|
74
74
|
"references": "api/built-in-special-elements.html#template"
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"name": "v-text",
|
|
80
80
|
"description": {
|
|
81
81
|
"kind": "markdown",
|
|
82
|
-
"value": "엘리먼트의 텍스트 컨텐츠를 업데이트합니다.\n\n- **요구되는
|
|
82
|
+
"value": "엘리먼트의 텍스트 컨텐츠를 업데이트합니다.\n\n- **요구되는 값** `string`\n\n- **세부 사항**\n\n `v-text`는 엘리먼트의 [텍스트 컨텐츠](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) 속성을 설정하므로, 엘리먼트 내부의 기존 컨텐츠를 덮어씁니다. `텍스트 컨텐츠`의 일부를 업데이트해야 하는 경우, [이중 중괄호](https://ko.vuejs.org/guide/essentials/template-syntax.html#text-interpolation)를 사용해야 합니다.\n\n- **예제**\n\n ```html\n <span v-text=\"msg\"></span>\n <!-- 아래와 같음 -->\n <span>{{msg}}</span>\n ```\n\n- **참고** [템플릿 문법 - 텍스트 보간법](https://ko.vuejs.org/guide/essentials/template-syntax.html#text-interpolation)"
|
|
83
83
|
},
|
|
84
84
|
"references": "api/built-in-directives.html#v-text"
|
|
85
85
|
},
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"name": "v-html",
|
|
88
88
|
"description": {
|
|
89
89
|
"kind": "markdown",
|
|
90
|
-
"value": "엘리먼트의 [innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)을 업데이트합니다.\n\n- **요구되는
|
|
90
|
+
"value": "엘리먼트의 [innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)을 업데이트합니다.\n\n- **요구되는 값** `string`\n\n- **세부 사항**\n\n `v-html`의 내용은 Vue 템플릿 문법을 처리하지 않고 일반 HTML로 삽입됩니다. `v-html`을 사용하여 템플릿을 작성하려고 한다면, 이 방법 대신 컴포넌트를 사용하여 해결하는 방법을 고민해봐야 합니다.\n\n ::: warning 보안 참고 사항\n 웹사이트에서 임의의 HTML을 동적으로 렌더링하는 것은 [XSS 공격](https://en.wikipedia.org/wiki/Cross-site_scripting)으로 쉽게 이어질 수 있기 때문에 매우 위험할 수 있습니다. 신뢰할 수 있는 컨텐츠에만 `v-html`을 사용하고, 사용자가 제공하는 컨텐츠에는 **절대** 사용하면 안됩니다.\n :::\n\n [싱글 파일 컴포넌트(SFC)](https://ko.vuejs.org/guide/scaling-up/sfc.html)에서 `scoped`(범위를 지정한) Style은 `v-html` 내부 컨텐츠에 적용되지 않습니다. 왜냐하면 해당 HTML은 Vue의 템플릿 컴파일러에서 처리되지 않기 때문입니다. 범위를 지정한 CSS로 `v-html` 컨텐츠를 대상으로 지정하려는 경우, [CSS 모듈](./sfc-css-features#css-modules) 또는 BEM과 같은 수동 범위 지정 방법과 함께 전역 `<style>` 엘리먼트를 사용할 수 있습니다.\n\n- **예제**\n\n ```html\n <div v-html=\"html\"></div>\n ```\n\n- **참고** [템플릿 문법 - HTML 출력](https://ko.vuejs.org/guide/essentials/template-syntax.html#raw-html)"
|
|
91
91
|
},
|
|
92
92
|
"references": "api/built-in-directives.html#v-html"
|
|
93
93
|
},
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"name": "v-show",
|
|
96
96
|
"description": {
|
|
97
97
|
"kind": "markdown",
|
|
98
|
-
"value": "표현식의 [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) 값을 기반으로 엘리먼트의 가시성을 전환합니다.\n\n- **요구되는
|
|
98
|
+
"value": "표현식의 [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) 값을 기반으로 엘리먼트의 가시성을 전환합니다.\n\n- **요구되는 값** `any`\n\n- **세부 사항**\n\n `v-show`는 인라인 스타일을 통해 `display` CSS 속성을 설정하며, 엘리먼트가 표시될 때 초기 `display` 값을 설정하려고 시도합니다. 또한 조건이 변경될 때 전환을 트리거합니다.\n\n- **참고** [조건부 렌더링 - v-show](https://ko.vuejs.org/guide/essentials/conditional.html#v-show)"
|
|
99
99
|
},
|
|
100
100
|
"references": "api/built-in-directives.html#v-show"
|
|
101
101
|
},
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"name": "v-if",
|
|
104
104
|
"description": {
|
|
105
105
|
"kind": "markdown",
|
|
106
|
-
"value": "표현식의 truthy 값을 기반으로 엘리먼트 또는 템플릿 일부를 조건부로 렌더링합니다.\n\n- **요구되는
|
|
106
|
+
"value": "표현식의 truthy 값을 기반으로 엘리먼트 또는 템플릿 일부를 조건부로 렌더링합니다.\n\n- **요구되는 값** `any`\n\n- **세부 사항**\n\n `v-if` 엘리먼트가 토글되면, 엘리먼트와 여기에 포함된 디렉티브/컴포넌트가 파괴되고 재구성됩니다. 초기 조건 값이 falsy이면, 내부 컨텐츠가 전혀 렌더링되지 않습니다.\n\n 텍스트 또는 여러 엘리먼트를 포함하는 조건부 블록을 나타내기 위해 `<template>`에 사용할 수도 있습니다.\n\n 이 디렉티브는 조건이 변경될 때, [트랜지션](https://ko.vuejs.org/guide/built-ins/transition.html)을 트리거합니다.\n\n `v-for`와 함께 사용하는 경우, `v-if`의 우선 순위가 높습니다. 하나의 엘리먼트에 이 두 디렉티브을 함께 사용하는 것은 권장되지 않습니다. 자세한 내용은 [리스트 렌더링](https://ko.vuejs.org/guide/essentials/list.html#v-for-with-v-if)을 참고하세요.\n\n- **참고** [조건부 렌더링 - v-if](https://ko.vuejs.org/guide/essentials/conditional.html#v-if)"
|
|
107
107
|
},
|
|
108
108
|
"references": "api/built-in-directives.html#v-if"
|
|
109
109
|
},
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"valueSet": "v",
|
|
113
113
|
"description": {
|
|
114
114
|
"kind": "markdown",
|
|
115
|
-
"value": "`v-if` 또는 `v-else-if` 체인에 대한 `else`입니다.\n\n- **표현식을 허용하지 않습니다**.\n\n- **세부
|
|
115
|
+
"value": "`v-if` 또는 `v-else-if` 체인에 대한 `else`입니다.\n\n- **표현식을 허용하지 않습니다**.\n\n- **세부 사항**\n\n - 제한사항: 이전 형제 엘리먼트에 `v-if` 또는 `v-else-if`가 있어야 합니다.\n\n - `<template>`에서 텍스트 또는 여러 엘리먼트를 포함하는 조건부 블록을 나타내는 데 사용할 수 있습니다.\n\n- **예제**\n\n ```html\n <div v-if=\"Math.random() > 0.5\">\n 이제 나를 볼 수 있어요!\n </div>\n <div v-else>\n 아직이에요!\n </div>\n ```\n\n- **참고** [조건부 렌더링 - v-else](https://ko.vuejs.org/guide/essentials/conditional.html#v-else)"
|
|
116
116
|
},
|
|
117
117
|
"references": "api/built-in-directives.html#v-else"
|
|
118
118
|
},
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"name": "v-else-if",
|
|
121
121
|
"description": {
|
|
122
122
|
"kind": "markdown",
|
|
123
|
-
"value": "`v-if`에 대한 `else if` 블록을 나타냅니다. `v-else-if`는 계속 이어서 사용할 수 있습니다.\n\n- **요구되는
|
|
123
|
+
"value": "`v-if`에 대한 `else if` 블록을 나타냅니다. `v-else-if`는 계속 이어서 사용할 수 있습니다.\n\n- **요구되는 값** `any`\n\n- **세부 사항**\n\n - 제한사항: 이전 형제 엘리먼트에 `v-if` 또는 `v-else-if`가 있어야 합니다.\n\n - `<template>`에서 텍스트 또는 여러 엘리먼트를 포함하는 조건부 블록을 나타내는 데 사용할 수 있습니다.\n\n- **예제**\n\n ```html\n <div v-if=\"type === 'A'\">\n A\n </div>\n <div v-else-if=\"type === 'B'\">\n B\n </div>\n <div v-else-if=\"type === 'C'\">\n C\n </div>\n <div v-else>\n A/B/C 가 아니야!\n </div>\n ```\n\n- **참고** [조건부 렌더링 - v-else-if](https://ko.vuejs.org/guide/essentials/conditional.html#v-else-if)"
|
|
124
124
|
},
|
|
125
125
|
"references": "api/built-in-directives.html#v-else-if"
|
|
126
126
|
},
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"name": "v-for",
|
|
129
129
|
"description": {
|
|
130
130
|
"kind": "markdown",
|
|
131
|
-
"value": "소스 데이터를 기반으로 엘리먼트 또는 템플릿 블록을 여러 번 렌더링합니다.\n\n- **요구되는
|
|
131
|
+
"value": "소스 데이터를 기반으로 엘리먼트 또는 템플릿 블록을 여러 번 렌더링합니다.\n\n- **요구되는 값** `Array | Object | number | string | Iterable`\n\n- **세부 사항**\n\n 디렉티브는 반복되는 과정의 현재 값에 별칭을 제공하기 위해, 특수 문법인 `alias in expression`(표현식 내 별칭)을 사용해야 합니다:\n\n ```html\n <div v-for=\"item in items\">\n {{ item.text }}\n </div>\n ```\n\n 또한 인덱스(객체에서 사용되는 경우 키)의 별칭을 지정할 수도 있습니다:\n\n ```html\n <div v-for=\"(item, index) in items\"></div>\n <div v-for=\"(value, key) in object\"></div>\n <div v-for=\"(value, name, index) in object\"></div>\n ```\n\n `v-for`의 기본 동작은 엘리먼트를 이동하지 않고 제자리에 패치(patch)하려고 합니다. 강제로 엘리먼트를 재정렬하려면, 특수 속성 `key`를 사용하여 순서 지정을 위한 힌트를 제공해야 합니다:\n\n ```html\n <div v-for=\"item in items\" :key=\"item.id\">\n {{ item.text }}\n </div>\n ```\n\n `v-for`는 네이티브 `Map`,`Set`과 더불어 [Iterable Protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterable_protocol)을 구현한 값에서도 작동합니다.\n\n- **참고**\n - [가이드 - 리스트 렌더링](https://ko.vuejs.org/guide/essentials/list.html)"
|
|
132
132
|
},
|
|
133
133
|
"references": "api/built-in-directives.html#v-for"
|
|
134
134
|
},
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
"name": "v-on",
|
|
137
137
|
"description": {
|
|
138
138
|
"kind": "markdown",
|
|
139
|
-
"value": "엘리먼트에 이벤트 리스너를 연결합니다.\n\n- **단축 문법:** `@`\n\n- **요구되는
|
|
139
|
+
"value": "엘리먼트에 이벤트 리스너를 연결합니다.\n\n- **단축 문법:** `@`\n\n- **요구되는 값** `Function | Inline Statement | Object (without argument)`\n\n- **인자:** `event` (선택사항: 객체 문법을 사용하는 경우)\n\n- **수식어:**\n\n - `.stop` - `event.stopPropagation()` 호출.\n - `.prevent` - `event.preventDefault()` 호출.\n - `.capture` - 캡처 모드로 이벤트 등록.\n - `.self` - 이벤트가 이 엘리먼트에서 전달된 경우에만 트리거 됨.\n - `.{keyAlias}` - 이벤트가 특정 키에 대해서만 트리거 됨.\n - `.once` - 이벤트가 한 번만 트리거 됨(일회용처럼).\n - `.left` - 마우스 왼쪽 버튼으로만 이벤트가 트리거 됨.\n - `.right` - 마우스 오른쪽 버튼으로만 이벤트가 트리거 됨.\n - `.middle` - 마우스 중앙(힐 클릭) 버튼으로만 이벤트가 트리거 됨.\n - `.passive` - `{ passive: true }` 옵션으로 DOM 이벤트를 등록.\n\n- **세부 사항**\n\n 이벤트 타입은 인자로 표시됩니다. 표현식은 메서드 이름 또는 인라인 명령문이거나, 수식어가 있는 경우 생략될 수 있습니다.\n\n 일반 엘리먼트에 사용되면 [**네이티브 DOM 이벤트**](https://developer.mozilla.org/en-US/docs/Web/Events)만 수신합니다. 커스텀 엘리먼트 컴포넌트에서 사용되는 경우, 해당 자식 컴포넌트에서 발송(emit)하는 **커스텀 이벤트**를 수신합니다.\n\n 네이티브 DOM 이벤트를 수신할 때, 메서드의 인자는 네이티브 이벤트 뿐 입니다. 인라인 명령문을 사용하는 경우, 명령문은 특수 속성인 `$event`로 `v-on:click=\"handle('ok', $event)\"`와 같이 이벤트 객체에 접근할 수 있습니다.\n\n `v-on`은 인자 없이 `이벤트(키): 리스너(값)` 형식의 객체 바인딩도 지원합니다. 수식어는 객체 문법을 사용할 때는 지원하지 않습니다.\n\n- **예제**\n\n ```html\n <!-- 메서드 핸들러 -->\n <button v-on:click=\"doThis\"></button>\n\n <!-- 동적 이벤트 -->\n <button v-on:[event]=\"doThis\"></button>\n\n <!-- 인라인 표현식 -->\n <button v-on:click=\"doThat('hello', $event)\"></button>\n\n <!-- 단축 문법 -->\n <button @click=\"doThis\"></button>\n\n <!-- 단축 문법 동적 이벤트 -->\n <button @[event]=\"doThis\"></button>\n\n <!-- 전파 중지 -->\n <button @click.stop=\"doThis\"></button>\n\n <!-- event.preventDefault() 작동 -->\n <button @click.prevent=\"doThis\"></button>\n\n <!-- 표현식 없이 event.preventDefault()만 사용 -->\n <form @submit.prevent></form>\n\n <!-- 수식어 이어서 사용 -->\n <button @click.stop.prevent=\"doThis\"></button>\n\n <!-- 키 별칭을 수식어로 사용 -->\n <input @keyup.enter=\"onEnter\" />\n\n <!-- 클릭 이벤트 단 한 번만 트리거 -->\n <button v-on:click.once=\"doThis\"></button>\n\n <!-- 객체 문법 -->\n <button v-on=\"{ mousedown: doThis, mouseup: doThat }\"></button>\n ```\n\n 자식 컴포넌트의 커스텀 이벤트 수신 대기(자식에서 \"my-event\"가 발생하면 핸들러가 호출됨):\n\n ```html\n <MyComponent @my-event=\"handleThis\" />\n\n <!-- 인라인 표현식 -->\n <MyComponent @my-event=\"handleThis(123, $event)\" />\n ```\n\n- **참고**\n - [이벤트 핸들링](https://ko.vuejs.org/guide/essentials/event-handling.html)\n - [컴포넌트 - 이벤트 청취하기](https://ko.vuejs.org/guide/essentials/component-basics.html#listening-to-events)"
|
|
140
140
|
},
|
|
141
141
|
"references": "api/built-in-directives.html#v-on"
|
|
142
142
|
},
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
"name": "v-bind",
|
|
145
145
|
"description": {
|
|
146
146
|
"kind": "markdown",
|
|
147
|
-
"value": "하나 이상의 속성 또는 컴포넌트 prop을 표현식에 동적으로 바인딩합니다.\n\n- **단축 문법:**\n - `:` 또는 `.`(`.prop` 수식어를 사용할 때)\n - 속성(attribute)과 바인딩된 값이 같은 이름을 가질 경우 값을 생략할 수 있음 <sup class=\"vt-badge\">3.4+</sup>\n\n- **요구되는
|
|
147
|
+
"value": "하나 이상의 속성 또는 컴포넌트 prop을 표현식에 동적으로 바인딩합니다.\n\n- **단축 문법:**\n - `:` 또는 `.`(`.prop` 수식어를 사용할 때)\n - 속성(attribute)과 바인딩된 값이 같은 이름을 가질 경우 값을 생략할 수 있음 <sup class=\"vt-badge\">3.4+</sup>\n\n- **요구되는 값** `any (인자 있이) | Object (인자 없이)`\n\n- **인자:** `attrOrProp (optional)`\n\n- **수식어:**\n\n - `.camel` - kebab-case 속성 이름을 camelCase로 변환.\n - `.prop` - 바인딩을 [DOM 속성(property: 이하 프로퍼티)](https://developer.mozilla.org/en-US/docs/Web/API/Element#properties)으로 강제 설정. (3.2+).\n - `.attr` - 바인딩을 [DOM 속성(attribute)](https://developer.mozilla.org/en-US/docs/Glossary/Attribute)으로 강제 설정. (3.2+).\n- **사용법**\n\n `class` 또는 `style` 속성을 바인딩하는 데 사용되는 경우, `v-bind`는 배열 또는 객체와 같이 값을 추가할 수 있는 타입을 지원합니다. 자세한 내용은 아래 링크된 가이드 섹션을 참고합시다.\n\n 엘리먼트에 바인딩을 설정할 때, Vue는 기본적으로 연산자 검사를 위한 `in`을 사용하여, 엘리먼트에 프로퍼티로 정의된 키가 있는지 확인합니다. 프로퍼티가 정의되면, Vue는 속성 대신 DOM 프로퍼티로 값을 설정합니다. 이것은 대부분의 경우 작동하지만, `.prop` 또는 `.attr` 수식어를 명시적으로 사용하여 이 동작을 재정의할 수 있습니다. 이것은 특히 [커스텀 엘리먼트로 작업](https://ko.vuejs.org/guide/extras/web-components.html#passing-dom-properties)할 때 필요합니다.\n\n 컴포넌트 prop 바인딩에 사용될 때 prop은 자식 컴포넌트에서 적절하게 선언되어야 합니다.\n\n 인자 없이 사용하는 경우, 속성을 이름-값 쌍으로 포함하는 객체를 바인딩하는 데 사용할 수 있습니다. 이 모드에서 `class`와 `style`은 배열 또는 객체를 지원하지 않습니다.\n\n- **예제**\n\n ```html\n <!-- 속성 바인딩 -->\n <img v-bind:src=\"imageSrc\" />\n\n <!-- 동적인 속성명 -->\n <button v-bind:[key]=\"value\"></button>\n\n <!-- 단축 문법 -->\n <img :src=\"imageSrc\" />\n\n <!-- 같은 이름 생략 가능 (3.4+), 오른쪽과 같음 :src=\"src\" -->\n <img :src />\n \n <!-- 단축 문법과 동적 속성명 -->\n <button :[key]=\"value\"></button>\n\n <!-- 인라인으로 문자열 결합 -->\n <img :src=\"'/path/to/images/' + fileName\" />\n\n <!-- class 바인딩 -->\n <div :class=\"{ red: isRed }\"></div>\n <div :class=\"[classA, classB]\"></div>\n <div :class=\"[classA, { classB: isB, classC: isC }]\"></div>\n\n <!-- style 바인딩 -->\n <div :style=\"{ fontSize: size + 'px' }\"></div>\n <div :style=\"[styleObjectA, styleObjectB]\"></div>\n\n <!-- 속성을 객체로 바인딩 -->\n <div v-bind=\"{ id: someProp, 'other-attr': otherProp }\"></div>\n\n <!-- prop 바인딩. \"prop\"은 자식 컴포넌트에서 선언되어 있어야 함 -->\n <MyComponent :prop=\"someThing\" />\n\n <!-- 자식 컴포넌트와 공유될 부모 props를 전달 -->\n <MyComponent v-bind=\"$props\" />\n \n <!-- XLink -->\n <svg><a :xlink:special=\"foo\"></a></svg>\n ```\n\n `.prop` 수식어에는 전용 단축 문법 `.`가 있습니다:\n\n ```html\n <div :someProperty.prop=\"someObject\"></div>\n\n <!-- 위 코드는 아래와 같이 단축할 수 있음 -->\n <div .someProperty=\"someObject\"></div>\n ```\n\n `.camel` 수식어는 DOM 내 템플릿을 사용할 때, `v-bind`의 속성명을 카멜라이징(camelizing)할 수 있습니다. 예를 들면, SVG `viewBox` 속성:\n\n ```html\n <svg :view-box.camel=\"viewBox\"></svg>\n ```\n\n 문자열 템플릿을 사용하거나 템플릿을 빌드 과정으로 미리 컴파일하는 경우에는 `.camel`이 필요하지 않습니다.\n\n- **참고**\n - [가이드 - 클래스와 스타일 바인딩](https://ko.vuejs.org/guide/essentials/class-and-style.html)\n - [가이드 - Props: Props 전달에 관한 심화](https://ko.vuejs.org/guide/components/props.html#prop-passing-details)"
|
|
148
148
|
},
|
|
149
149
|
"references": "api/built-in-directives.html#v-bind"
|
|
150
150
|
},
|
|
@@ -152,7 +152,7 @@
|
|
|
152
152
|
"name": "v-model",
|
|
153
153
|
"description": {
|
|
154
154
|
"kind": "markdown",
|
|
155
|
-
"value": "사용자 입력을 받는 폼(form) 엘리먼트 또는 컴포넌트에 양방향 바인딩을 만듭니다.\n\n- **요구되는
|
|
155
|
+
"value": "사용자 입력을 받는 폼(form) 엘리먼트 또는 컴포넌트에 양방향 바인딩을 만듭니다.\n\n- **요구되는 값** 사용자 입력을 받는 폼 엘리먼트 또는 컴포넌트의 출력 값에 따라 다름.\n\n- **다음으로 제한됨**:\n\n - `<input>`\n - `<select>`\n - `<textarea>`\n - 컴포넌트\n\n- **수식어:**\n\n - [`.lazy`](https://ko.vuejs.org/guide/essentials/forms.html#lazy) - `input` 대신 `change` 이벤트를 수신함.\n - [`.number`](https://ko.vuejs.org/guide/essentials/forms.html#number) - 유효한 입력 문자열을 숫자로 변환하여 전달.\n - [`.trim`](https://ko.vuejs.org/guide/essentials/forms.html#trim) - 사용자 입력의 공백을 트리밍.\n\n- **참고**\n\n - [가이드 - Form 입력 바인딩](https://ko.vuejs.org/guide/essentials/forms.html)\n - [가이드 - 이벤트: `v-model`과 함께 사용하기](https://ko.vuejs.org/guide/components/v-model.html)"
|
|
156
156
|
},
|
|
157
157
|
"references": "api/built-in-directives.html#v-model"
|
|
158
158
|
},
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
"name": "v-slot",
|
|
161
161
|
"description": {
|
|
162
162
|
"kind": "markdown",
|
|
163
|
-
"value": "이름이 있는 슬롯 또는 props를 받을 것으로 예상되는 범위형 (Scoped) 슬롯을 나타냅니다.\n\n- **단축 문법:** `#`\n\n- **요구되는
|
|
163
|
+
"value": "이름이 있는 슬롯 또는 props를 받을 것으로 예상되는 범위형 (Scoped) 슬롯을 나타냅니다.\n\n- **단축 문법:** `#`\n\n- **요구되는 값** JavaScript expression that is valid in a function argument position, including support for destructuring. Optional - only needed if expecting props to be passed to the slot.\n\n- **인자:** 슬롯 이름 (선택적, 기본값은 `default`)\n\n- **다음으로 제한됨**:\n\n - `<template>`\n - [컴포넌트](https://ko.vuejs.org/guide/components/slots.html#scoped-slots) (props를 수신할 기본 슬롯만 있는 경우)\n\n- **예제**\n\n ```html\n <!-- 이름이 있는 슬롯 -->\n <BaseLayout>\n <template v-slot:header>\n 해더 컨텐츠\n </template>\n\n <template v-slot:default>\n 기본 슬롯 컨텐츠\n </template>\n\n <template v-slot:footer>\n 푸터 컨텐츠\n </template>\n </BaseLayout>\n\n <!-- props를 수신할 기본 슬롯 -->\n <InfiniteScroll>\n <template v-slot:item=\"slotProps\">\n <div class=\"item\">\n {{ slotProps.item.text }}\n </div>\n </template>\n </InfiniteScroll>\n\n <!-- props를 수신할 기본 슬롯, 분해할당을 사용 -->\n <Mouse v-slot=\"{ x, y }\">\n 마우스 위치: {{ x }}, {{ y }}\n </Mouse>\n ```\n\n- **참고**\n - [가이드 - 슬롯](https://ko.vuejs.org/guide/components/slots.html)"
|
|
164
164
|
},
|
|
165
165
|
"references": "api/built-in-directives.html#v-slot"
|
|
166
166
|
},
|
|
@@ -169,7 +169,7 @@
|
|
|
169
169
|
"valueSet": "v",
|
|
170
170
|
"description": {
|
|
171
171
|
"kind": "markdown",
|
|
172
|
-
"value": "이 엘리먼트와 모든 자식 엘리먼트의 컴파일을 생략합니다.\n\n- **표현식을 허용하지 않습니다**.\n\n- **세부
|
|
172
|
+
"value": "이 엘리먼트와 모든 자식 엘리먼트의 컴파일을 생략합니다.\n\n- **표현식을 허용하지 않습니다**.\n\n- **세부 사항**\n\n `v-pre`가 있는 엘리먼트 내에서 모든 Vue 템플릿 구문은 그대로 유지되고 렌더링됩니다. 가장 일반적인 사용 사례는 이중 중괄호 태그를 표시하는 것입니다.\n\n- **예제**\n\n ```html\n <span v-pre>{{ 이곳은 컴파일되지 않습니다. }}</span>\n ```"
|
|
173
173
|
},
|
|
174
174
|
"references": "api/built-in-directives.html#v-pre"
|
|
175
175
|
},
|
|
@@ -178,7 +178,7 @@
|
|
|
178
178
|
"valueSet": "v",
|
|
179
179
|
"description": {
|
|
180
180
|
"kind": "markdown",
|
|
181
|
-
"value": "엘리먼트와 컴포넌트를 한 번만 렌더링하고, 향후 업데이트를 생략합니다.\n\n- **표현식을 허용하지 않습니다**.\n\n- **세부
|
|
181
|
+
"value": "엘리먼트와 컴포넌트를 한 번만 렌더링하고, 향후 업데이트를 생략합니다.\n\n- **표현식을 허용하지 않습니다**.\n\n- **세부 사항**\n\n 이후 다시 렌더링할 때 엘리먼트/컴포넌트 및 모든 자식들은 정적 컨텐츠로 처리되어 생략됩니다. 이것은 업데이트 성능을 최적화하는 데 사용할 수 있습니다.\n\n ```html\n <!-- 단일 엘리먼트 -->\n <span v-once>절대 바뀌지 않음: {{msg}}</span>\n <!-- 자식이 있는 엘리먼트 -->\n <div v-once>\n <h1>댓글</h1>\n <p>{{msg}}</p>\n </div>\n <!-- 컴포넌트 -->\n <MyComponent v-once :comment=\"msg\"></MyComponent>\n <!-- `v-for` 디렉티브 -->\n <ul>\n <li v-for=\"i in list\" v-once>{{i}}</li>\n </ul>\n ```\n\n 3.2부터는 [`v-memo`](#v-memo)를 사용하여 무효화 조건으로 템플릿의 일부를 메모화할 수도 있습니다.\n\n- **참고**\n - [가이드 - 템플릿 문법: 텍스트 보간법](https://ko.vuejs.org/guide/essentials/template-syntax.html#text-interpolation)\n - [v-memo](#v-memo)"
|
|
182
182
|
},
|
|
183
183
|
"references": "api/built-in-directives.html#v-once"
|
|
184
184
|
},
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
"name": "v-memo",
|
|
187
187
|
"description": {
|
|
188
188
|
"kind": "markdown",
|
|
189
|
-
"value": "- **요구되는
|
|
189
|
+
"value": "- 3.2+ 버전 이상에서만 지원합니다.\n\n- **요구되는 값** `any[]`\n\n- **세부 사항**\n\n 템플릿의 하위 트리를 메모합니다. 엘리먼트와 컴포넌트 모두에 사용할 수 있습니다. 디렉티브는 메모이제이션을 위해 비교할 의존성 값의 고정된 길이의 배열을 요구합니다. 배열의 모든 값이 마지막 렌더링과 같으면 전체 하위 트리에 대한 업데이트를 생략합니다. 예를 들어:\n\n ```html\n <div v-memo=\"[valueA, valueB]\">\n ...\n </div>\n ```\n\n 컴포넌트가 다시 렌더링될 때 `valueA`와 `valueB`가 모두 동일하게 유지되면, 이 `<div>`와 하위 항목에 대한 모든 업데이트를 생략합니다. 사실, 하위 트리의 메모된 복사본을 재사용할 수 있기 때문에 가상 DOM VNode 생성도 생략합니다.\n\n 메모이제이션 배열을 올바르게 지정하는 것이 중요합니다. 그렇지 않으면 실제로 적용되어야 하는 업데이트를 건너뛸 수 있습니다. 빈 의존성 배열(`v-memo=\"[]\"`)이 있는 `v-memo`는 기능적으로 `v-once`와 동일합니다.\n\n **`v-for`과 함께 사용하기**\n\n `v-memo`는 성능이 중요한 시나리오에서 마이크로 최적화를 위해 제공되는 것으로, 일반적으로 거의 필요하지 않습니다. 이것이 도움이 될 수 있는 가장 일반적인 경우는 큰 리스트(`length > 1000`)를 `v-for`로 렌더링할 때입니다:\n\n ```html\n <div v-for=\"item in list\" :key=\"item.id\" v-memo=\"[item.id === selected]\">\n <p>ID: {{ item.id }} - 선택됨: {{ item.id === selected }}</p>\n <p>...더 많은 자식 노드</p>\n </div>\n ```\n\n 컴포넌트의 `selected` 상태가 변경되면, 대부분의 아이템이 정확히 동일하게 유지되더라도 많은 양의 VNode가 생성됩니다. 여기서 `v-memo` 사용법은 본질적으로 \"아이템의 선택여부가 바뀐 경우에만, 이 아이템을 업데이트하십시오\"입니다. 이렇게 하면 영향을 받지 않는 모든 아이템이 이전 VNode를 재사용하고, 차이점 비교를 생략할 수 있습니다. Vue는 아이템의 `:key`로 자동 추론하므로, 메모 의존성 배열에 `item.id`를 포함할 필요가 없습니다.\n\n :::warning\n `v-for`와 함께 `v-memo`를 사용할 때, 동일한 엘리먼트에 사용되는지 확인이 필요합니다. **`v-memo`는 `v-for` 내에서 작동하지 않습니다**.\n :::\n\n `v-memo`는 자식 컴포넌트 업데이트 확인이 최적화되지 않은 특정 엣지 케이스에서 원치 않는 업데이트를 수동으로 방지하기 위해 컴포넌트에 사용할 수도 있습니다. 그러나 필요한 업데이트를 건너뛰지 않도록 올바른 의존성 배열을 지정하는 것은 개발자의 책임입니다.\n\n- **참고**\n - [v-once](#v-once)"
|
|
190
190
|
},
|
|
191
191
|
"references": "api/built-in-directives.html#v-memo"
|
|
192
192
|
},
|
|
@@ -195,7 +195,7 @@
|
|
|
195
195
|
"valueSet": "v",
|
|
196
196
|
"description": {
|
|
197
197
|
"kind": "markdown",
|
|
198
|
-
"value": "준비될 때까지 컴파일되지 않은 템플릿을 숨기는 데 사용됩니다.\n\n- **표현식을 허용하지 않습니다**.\n\n- **세부
|
|
198
|
+
"value": "준비될 때까지 컴파일되지 않은 템플릿을 숨기는 데 사용됩니다.\n\n- **표현식을 허용하지 않습니다**.\n\n- **세부 사항**\n\n **이 디렉티브는 빌드 과정이 없는 설정에서만 필요합니다**.\n\n DOM 내 템플릿을 사용할 때, \"컴파일되지 않은 템플릿이 순간 보이는 현상\"이 있을 수 있습니다. 이러면 사용자는 컴포넌트가 렌더링된 컨텐츠로 대체할 때까지 이중 중괄호 태그를 볼 수 있습니다.\n\n `v-cloak`은 연결된 컴포넌트 인스턴스가 마운트될 때까지 엘리먼트에 남아 있습니다. `[v-cloak] { display: none }`과 같은 CSS 규칙과 결합하여, 컴포넌트가 준비될 때까지 템플릿을 숨기는 데 사용할 수 있습니다.\n\n- **예제**\n\n ```css\n [v-cloak] {\n display: none;\n }\n ```\n\n ```html\n <div v-cloak>\n {{ message }}\n </div>\n ```\n\n `<div>`는 컴파일이 완료될 때까지 표시되지 않습니다."
|
|
199
199
|
},
|
|
200
200
|
"references": "api/built-in-directives.html#v-cloak"
|
|
201
201
|
},
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
"name": "key",
|
|
204
204
|
"description": {
|
|
205
205
|
"kind": "markdown",
|
|
206
|
-
"value": "특수 속성 `key`는 Vue의 가상 DOM 알고리즘이 이전 목록과 새 노드 목록을 비교할 때 vnode를 식별하는 힌트로 주로 사용됩니다.\n\n- **요구되는
|
|
206
|
+
"value": "특수 속성 `key`는 Vue의 가상 DOM 알고리즘이 이전 목록과 새 노드 목록을 비교할 때 vnode를 식별하는 힌트로 주로 사용됩니다.\n\n- **요구되는 값** `number | string | symbol`\n\n- **세부 사항**\n\n 키가 없으면 Vue는 엘리먼트 이동을 최소화하고 동일한 유형의 엘리먼트를 가능한 한 제자리에서 패치/재사용하는 알고리즘을 사용합니다.\n 키를 사용하면 키의 순서 변경에 따라 엘리먼트를 재정렬하고 더 이상 존재하지 않는 키가 있는 엘리먼트는 항상 제거/파기됩니다.\n\n 동일한 공통 부모의 자식들은 **고유 키**가 있어야 합니다.\n 키가 중복되면 렌더링 에러가 발생합니다.\n\n `v-for`에서 가장 일반적으로 사용 됩니다:\n\n ```html\n <ul>\n <li v-for=\"item in items\" :key=\"item.id\">...</li>\n </ul>\n ```\n\n 또는 엘리먼트/컴포넌트를 재사용하는 대신 강제로 교체하는 데 사용할 수도 있습니다.\n 다음과 같은 경우에 유용할 수 있습니다:\n\n - 컴포넌트의 생명 주기 훅을 올바르게 트리거함.\n - 트랜지션 트리거\n\n 예제:\n\n ```html\n <transition>\n <span :key=\"text\">{{ text }}</span>\n </transition>\n ```\n\n `text`가 변경되면 `<span>`이 패치 대신 항상 교체되므로 트랜지션이 트리거됩니다.\n\n- **참고** [가이드 - 리스트 렌더링: `key`를 통한 상태유지](https://ko.vuejs.org/guide/essentials/list.html#maintaining-state-with-key)"
|
|
207
207
|
},
|
|
208
208
|
"references": "api/built-in-special-attributes.html#key"
|
|
209
209
|
},
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
"name": "ref",
|
|
212
212
|
"description": {
|
|
213
213
|
"kind": "markdown",
|
|
214
|
-
"value": "[템플릿 참조](https://ko.vuejs.org/guide/essentials/template-refs.html)를 의미합니다.\n\n- **요구되는
|
|
214
|
+
"value": "[템플릿 참조](https://ko.vuejs.org/guide/essentials/template-refs.html)를 의미합니다.\n\n- **요구되는 값** `string | Function`\n\n- **세부 사항**\n\n `ref` is used to register a reference to an element or a child component.\n\n In Options API, the reference will be registered under the component's `this.$refs` object:\n\n `ref`는 엘리먼트 또는 자식 컴포넌트를 참조하기 위해 사용됩니다.\n\n 옵션 API에서 참조는 컴포넌트의 `this.$refs` 객체 내에 등록됩니다.\n\n ```html\n <!-- 저장됨: this.$refs.p -->\n <p ref=\"p\">안녕!</p>\n ```\n\n 컴포지션 API에서 참조는 이름이 일치하는 `ref`에 저장됩니다.\n\n ```vue\n <script setup>\n import { useTemplateRef } from 'vue'\n\n const pRef = useTemplateRef('p')\n </script>\n\n <template>\n <p ref=\"p\">hello</p>\n </template>\n ```\n\n 일반 DOM 엘리먼트에서 사용되는 경우, 참조는 해당 엘리먼트가 됩니다.\n 자식 컴포넌트에 사용되는 경우, 참조는 자식 컴포넌트 인스턴스가 됩니다.\n\n `ref`는 함수를 사용하여 참조 저장을 완전히 제어할 수 있습니다:\n\n ```html\n <ChildComponent :ref=\"(el) => child = el\" />\n ```\n\n 참조 등록 타이밍에 대한 중요한 참고 사항:\n 참조는 렌더 함수의 결과로 생성되므로,\n 접근하기 전에 컴포넌트가 마운트될 때까지 기다려야 합니다.\n\n `this.$refs`도 반응형이 아니므로 데이터 바인딩을 위한 템플릿에서 사용하면 안됩니다.\n\n- **참고** \n - [가이드 - 템플릿 refs](https://ko.vuejs.org/guide/essentials/template-refs.html)\n - [Guide - 템플릿 Refs에 타입 적용하기Typing Template Refs](https://ko.vuejs.org/guide/typescript/composition-api.html#typing-template-refs) <sup class=\"vt-badge ts\" />\n - [Guide - 컴포넌트 템플릿 Refs에 타입 적용하기](https://ko.vuejs.org/guide/typescript/composition-api.html#typing-component-template-refs) <sup class=\"vt-badge ts\" />"
|
|
215
215
|
},
|
|
216
216
|
"references": "api/built-in-special-attributes.html#ref"
|
|
217
217
|
},
|
|
@@ -219,7 +219,7 @@
|
|
|
219
219
|
"name": "is",
|
|
220
220
|
"description": {
|
|
221
221
|
"kind": "markdown",
|
|
222
|
-
"value": "[동적 컴포넌트](https://ko.vuejs.org/guide/essentials/component-basics.html#dynamic-components) 바인딩에 사용합니다.\n\n- **요구되는
|
|
222
|
+
"value": "[동적 컴포넌트](https://ko.vuejs.org/guide/essentials/component-basics.html#dynamic-components) 바인딩에 사용합니다.\n\n- **요구되는 값** `string | Component`\n\n- **네이티브 엘리먼트에 사용**\n - 3.1+ 버전에서 지원. \n\n `is` 속성이 네이티브 HTML 엘리먼트에 사용되면,\n 네이티브 웹 플랫폼 함수인 [커스터마이즈 빌트인 엘리먼트](https://html.spec.whatwg.org/multipage/custom-elements#custom-elements-customized-builtin-example)로 해석됩니다.\n\n 그러나 [in-DOM 템플릿 파싱 주의 사항](https://ko.vuejs.org/guide/essentials/component-basics.html#in-dom-template-parsing-caveats)에 설명된 대로,\n 기본 엘리먼트를 Vue 컴포넌트로 교체하기 위해 Vue가 필요할 수 있는 사용 사례가 있습니다.\n Vue가 엘리먼트를 Vue 컴포넌트로 렌더링하도록 `is` 속성 값에 `vue:` 접두사를 붙일 수 있습니다:\n\n ```html\n <table>\n <tr is=\"vue:my-row-component\"></tr>\n </table>\n ```\n\n- **참고**\n\n - [API - 특수 엘리먼트: `<component>`](https://ko.vuejs.org/api/built-in-special-elements.html#component)\n - [가이드 - 컴포넌트 기초: 동적 컴포넌트](https://ko.vuejs.org/guide/essentials/component-basics.html#dynamic-components)"
|
|
223
223
|
},
|
|
224
224
|
"references": "api/built-in-special-attributes.html#is"
|
|
225
225
|
}
|
package/index.js
CHANGED
|
@@ -74,6 +74,7 @@ function getFullLanguageServicePlugins(ts, { disableAutoImportCache } = {}) {
|
|
|
74
74
|
languageService.getCompletionsAtPosition = proxy.getCompletionsAtPosition;
|
|
75
75
|
languageService.getCompletionEntryDetails = proxy.getCompletionEntryDetails;
|
|
76
76
|
languageService.getCodeFixesAtPosition = proxy.getCodeFixesAtPosition;
|
|
77
|
+
languageService.getDefinitionAndBoundSpan = proxy.getDefinitionAndBoundSpan;
|
|
77
78
|
languageService.getQuickInfoAtPosition = proxy.getQuickInfoAtPosition;
|
|
78
79
|
}
|
|
79
80
|
return created;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LanguageServiceContext } from '@volar/language-service';
|
|
2
|
+
import type * as vscode from 'vscode-languageserver-protocol';
|
|
3
|
+
import type { URI } from 'vscode-uri';
|
|
4
|
+
import { AttrNameCasing, TagNameCasing } from './types';
|
|
5
|
+
export declare function convertTagName(context: LanguageServiceContext, uri: URI, casing: TagNameCasing, tsPluginClient: import('@vue/typescript-plugin/lib/requests').Requests | undefined): Promise<vscode.TextEdit[] | undefined>;
|
|
6
|
+
export declare function convertAttrName(context: LanguageServiceContext, uri: URI, casing: AttrNameCasing, tsPluginClient?: import('@vue/typescript-plugin/lib/requests').Requests): Promise<vscode.TextEdit[] | undefined>;
|
|
7
|
+
export declare function getNameCasing(context: LanguageServiceContext, uri: URI): Promise<{
|
|
8
|
+
tag: TagNameCasing;
|
|
9
|
+
attr: AttrNameCasing;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function detect(context: LanguageServiceContext, uri: URI): Promise<{
|
|
12
|
+
tag: TagNameCasing[];
|
|
13
|
+
attr: AttrNameCasing[];
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertTagName = convertTagName;
|
|
4
|
+
exports.convertAttrName = convertAttrName;
|
|
5
|
+
exports.getNameCasing = getNameCasing;
|
|
6
|
+
exports.detect = detect;
|
|
7
|
+
const language_core_1 = require("@vue/language-core");
|
|
8
|
+
const alien_signals_1 = require("alien-signals");
|
|
9
|
+
const types_1 = require("./types");
|
|
10
|
+
async function convertTagName(context, uri, casing, tsPluginClient) {
|
|
11
|
+
const sourceFile = context.language.scripts.get(uri);
|
|
12
|
+
if (!sourceFile) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const root = sourceFile?.generated?.root;
|
|
16
|
+
if (!(root instanceof language_core_1.VueVirtualCode)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const { template } = root.sfc;
|
|
20
|
+
if (!template) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const document = context.documents.get(sourceFile.id, sourceFile.languageId, sourceFile.snapshot);
|
|
24
|
+
const edits = [];
|
|
25
|
+
const components = await tsPluginClient?.getComponentNames(root.fileName) ?? [];
|
|
26
|
+
const tags = getTemplateTagsAndAttrs(root);
|
|
27
|
+
for (const [tagName, { offsets }] of tags) {
|
|
28
|
+
const componentName = components.find(component => component === tagName || (0, language_core_1.hyphenateTag)(component) === tagName);
|
|
29
|
+
if (componentName) {
|
|
30
|
+
for (const offset of offsets) {
|
|
31
|
+
const start = document.positionAt(template.startTagEnd + offset);
|
|
32
|
+
const end = document.positionAt(template.startTagEnd + offset + tagName.length);
|
|
33
|
+
const range = { start, end };
|
|
34
|
+
if (casing === types_1.TagNameCasing.Kebab && tagName !== (0, language_core_1.hyphenateTag)(componentName)) {
|
|
35
|
+
edits.push({ range, newText: (0, language_core_1.hyphenateTag)(componentName) });
|
|
36
|
+
}
|
|
37
|
+
if (casing === types_1.TagNameCasing.Pascal && tagName !== componentName) {
|
|
38
|
+
edits.push({ range, newText: componentName });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return edits;
|
|
44
|
+
}
|
|
45
|
+
async function convertAttrName(context, uri, casing, tsPluginClient) {
|
|
46
|
+
const sourceFile = context.language.scripts.get(uri);
|
|
47
|
+
if (!sourceFile) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const root = sourceFile?.generated?.root;
|
|
51
|
+
if (!(root instanceof language_core_1.VueVirtualCode)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const { template } = root.sfc;
|
|
55
|
+
if (!template) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const document = context.documents.get(uri, sourceFile.languageId, sourceFile.snapshot);
|
|
59
|
+
const edits = [];
|
|
60
|
+
const components = await tsPluginClient?.getComponentNames(root.fileName) ?? [];
|
|
61
|
+
const tags = getTemplateTagsAndAttrs(root);
|
|
62
|
+
for (const [tagName, { attrs }] of tags) {
|
|
63
|
+
const componentName = components.find(component => component === tagName || (0, language_core_1.hyphenateTag)(component) === tagName);
|
|
64
|
+
if (componentName) {
|
|
65
|
+
const props = (await tsPluginClient?.getComponentProps(root.fileName, componentName) ?? []).map(prop => prop.name);
|
|
66
|
+
for (const [attrName, { offsets }] of attrs) {
|
|
67
|
+
const propName = props.find(prop => prop === attrName || (0, language_core_1.hyphenateAttr)(prop) === attrName);
|
|
68
|
+
if (propName) {
|
|
69
|
+
for (const offset of offsets) {
|
|
70
|
+
const start = document.positionAt(template.startTagEnd + offset);
|
|
71
|
+
const end = document.positionAt(template.startTagEnd + offset + attrName.length);
|
|
72
|
+
const range = { start, end };
|
|
73
|
+
if (casing === types_1.AttrNameCasing.Kebab && attrName !== (0, language_core_1.hyphenateAttr)(propName)) {
|
|
74
|
+
edits.push({ range, newText: (0, language_core_1.hyphenateAttr)(propName) });
|
|
75
|
+
}
|
|
76
|
+
if (casing === types_1.AttrNameCasing.Camel && attrName !== propName) {
|
|
77
|
+
edits.push({ range, newText: propName });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return edits;
|
|
85
|
+
}
|
|
86
|
+
async function getNameCasing(context, uri) {
|
|
87
|
+
const detected = await detect(context, uri);
|
|
88
|
+
const [attr, tag] = await Promise.all([
|
|
89
|
+
context.env.getConfiguration?.('vue.complete.casing.props', uri.toString()),
|
|
90
|
+
context.env.getConfiguration?.('vue.complete.casing.tags', uri.toString()),
|
|
91
|
+
]);
|
|
92
|
+
const tagNameCasing = detected.tag.length === 1 && (tag === 'autoPascal' || tag === 'autoKebab') ? detected.tag[0] : (tag === 'autoKebab' || tag === 'kebab') ? types_1.TagNameCasing.Kebab : types_1.TagNameCasing.Pascal;
|
|
93
|
+
const attrNameCasing = detected.attr.length === 1 && (attr === 'autoCamel' || attr === 'autoKebab') ? detected.attr[0] : (attr === 'autoCamel' || attr === 'camel') ? types_1.AttrNameCasing.Camel : types_1.AttrNameCasing.Kebab;
|
|
94
|
+
return {
|
|
95
|
+
tag: tagNameCasing,
|
|
96
|
+
attr: attrNameCasing,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
async function detect(context, uri) {
|
|
100
|
+
const rootFile = context.language.scripts.get(uri)?.generated?.root;
|
|
101
|
+
if (!(rootFile instanceof language_core_1.VueVirtualCode)) {
|
|
102
|
+
return {
|
|
103
|
+
tag: [],
|
|
104
|
+
attr: [],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
tag: await getTagNameCase(rootFile),
|
|
109
|
+
attr: getAttrNameCase(rootFile),
|
|
110
|
+
};
|
|
111
|
+
function getAttrNameCase(file) {
|
|
112
|
+
const tags = getTemplateTagsAndAttrs(file);
|
|
113
|
+
const result = [];
|
|
114
|
+
for (const [_, { attrs }] of tags) {
|
|
115
|
+
for (const [tagName] of attrs) {
|
|
116
|
+
// attrName
|
|
117
|
+
if (tagName !== (0, language_core_1.hyphenateTag)(tagName)) {
|
|
118
|
+
result.push(types_1.AttrNameCasing.Camel);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (const [tagName] of attrs) {
|
|
123
|
+
// attr-name
|
|
124
|
+
if (tagName.includes('-')) {
|
|
125
|
+
result.push(types_1.AttrNameCasing.Kebab);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
function getTagNameCase(file) {
|
|
133
|
+
const result = new Set();
|
|
134
|
+
if (file.sfc.template?.ast) {
|
|
135
|
+
for (const element of (0, language_core_1.forEachElementNode)(file.sfc.template.ast)) {
|
|
136
|
+
if (element.tagType === 1) {
|
|
137
|
+
if (element.tag !== (0, language_core_1.hyphenateTag)(element.tag)) {
|
|
138
|
+
// TagName
|
|
139
|
+
result.add(types_1.TagNameCasing.Pascal);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// Tagname -> tagname
|
|
143
|
+
// TagName -> tag-name
|
|
144
|
+
result.add(types_1.TagNameCasing.Kebab);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return [...result];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const map = new WeakMap();
|
|
153
|
+
function getTemplateTagsAndAttrs(sourceFile) {
|
|
154
|
+
if (!map.has(sourceFile)) {
|
|
155
|
+
const getter = (0, alien_signals_1.computed)(() => {
|
|
156
|
+
if (!(sourceFile instanceof language_core_1.VueVirtualCode)) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const ast = sourceFile.sfc.template?.ast;
|
|
160
|
+
const tags = new Map();
|
|
161
|
+
if (ast) {
|
|
162
|
+
for (const node of (0, language_core_1.forEachElementNode)(ast)) {
|
|
163
|
+
if (!tags.has(node.tag)) {
|
|
164
|
+
tags.set(node.tag, { offsets: [], attrs: new Map() });
|
|
165
|
+
}
|
|
166
|
+
const tag = tags.get(node.tag);
|
|
167
|
+
const startTagHtmlOffset = node.loc.start.offset + node.loc.source.indexOf(node.tag);
|
|
168
|
+
const endTagHtmlOffset = node.loc.start.offset + node.loc.source.lastIndexOf(node.tag);
|
|
169
|
+
tag.offsets.push(startTagHtmlOffset);
|
|
170
|
+
if (!node.isSelfClosing) {
|
|
171
|
+
tag.offsets.push(endTagHtmlOffset);
|
|
172
|
+
}
|
|
173
|
+
for (const prop of node.props) {
|
|
174
|
+
let name;
|
|
175
|
+
let offset;
|
|
176
|
+
if (prop.type === 7
|
|
177
|
+
&& prop.arg?.type === 4
|
|
178
|
+
&& prop.arg.isStatic) {
|
|
179
|
+
name = prop.arg.content;
|
|
180
|
+
offset = prop.arg.loc.start.offset;
|
|
181
|
+
}
|
|
182
|
+
else if (prop.type === 6) {
|
|
183
|
+
name = prop.name;
|
|
184
|
+
offset = prop.loc.start.offset;
|
|
185
|
+
}
|
|
186
|
+
if (name !== undefined && offset !== undefined) {
|
|
187
|
+
if (!tag.attrs.has(name)) {
|
|
188
|
+
tag.attrs.set(name, { offsets: [] });
|
|
189
|
+
}
|
|
190
|
+
tag.attrs.get(name).offsets.push(offset);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return tags;
|
|
196
|
+
});
|
|
197
|
+
map.set(sourceFile, getter);
|
|
198
|
+
}
|
|
199
|
+
return map.get(sourceFile)() ?? new Map();
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=nameCasing.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.create = create;
|
|
4
|
+
const language_core_1 = require("@vue/language-core");
|
|
5
|
+
const vscode_uri_1 = require("vscode-uri");
|
|
6
|
+
function create() {
|
|
7
|
+
return {
|
|
8
|
+
name: 'vue-compiler-dom-errors',
|
|
9
|
+
capabilities: {
|
|
10
|
+
diagnosticProvider: {
|
|
11
|
+
interFileDependencies: false,
|
|
12
|
+
workspaceDiagnostics: false,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
create(context) {
|
|
16
|
+
return {
|
|
17
|
+
provideDiagnostics(document) {
|
|
18
|
+
if (!isSupportedDocument(document)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const uri = vscode_uri_1.URI.parse(document.uri);
|
|
22
|
+
const decoded = context.decodeEmbeddedDocumentUri(uri);
|
|
23
|
+
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
|
|
24
|
+
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
|
|
25
|
+
if (!virtualCode) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const root = sourceScript?.generated?.root;
|
|
29
|
+
if (!(root instanceof language_core_1.VueVirtualCode)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const templateErrors = [];
|
|
33
|
+
const { template } = root.sfc;
|
|
34
|
+
if (template) {
|
|
35
|
+
for (const error of template.errors) {
|
|
36
|
+
onCompilerError(error, 1);
|
|
37
|
+
}
|
|
38
|
+
for (const warning of template.warnings) {
|
|
39
|
+
onCompilerError(warning, 2);
|
|
40
|
+
}
|
|
41
|
+
function onCompilerError(error, severity) {
|
|
42
|
+
const templateHtmlRange = {
|
|
43
|
+
start: error.loc?.start.offset ?? 0,
|
|
44
|
+
end: error.loc?.end.offset ?? 0,
|
|
45
|
+
};
|
|
46
|
+
let errorMessage = error.message;
|
|
47
|
+
templateErrors.push({
|
|
48
|
+
range: {
|
|
49
|
+
start: document.positionAt(templateHtmlRange.start),
|
|
50
|
+
end: document.positionAt(templateHtmlRange.end),
|
|
51
|
+
},
|
|
52
|
+
severity,
|
|
53
|
+
code: error.code,
|
|
54
|
+
source: 'vue',
|
|
55
|
+
message: errorMessage,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return templateErrors;
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
function isSupportedDocument(document) {
|
|
65
|
+
return document.languageId === 'jade' || document.languageId === 'html';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=vue-compiler-dom-errors.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.create = create;
|
|
4
|
+
const language_core_1 = require("@vue/language-core");
|
|
5
|
+
const vscode_uri_1 = require("vscode-uri");
|
|
6
|
+
function create(getDocumentHighlights) {
|
|
7
|
+
return {
|
|
8
|
+
name: 'vue-document-highlights',
|
|
9
|
+
capabilities: {
|
|
10
|
+
documentHighlightProvider: true,
|
|
11
|
+
},
|
|
12
|
+
create(context) {
|
|
13
|
+
return {
|
|
14
|
+
async provideDocumentHighlights(document, position) {
|
|
15
|
+
const uri = vscode_uri_1.URI.parse(document.uri);
|
|
16
|
+
const decoded = context.decodeEmbeddedDocumentUri(uri);
|
|
17
|
+
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
|
|
18
|
+
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
|
|
19
|
+
if (!sourceScript?.generated || virtualCode?.id !== 'main') {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const root = sourceScript.generated.root;
|
|
23
|
+
if (!(root instanceof language_core_1.VueVirtualCode)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const result = await getDocumentHighlights(root.fileName, document.offsetAt(position));
|
|
27
|
+
return result
|
|
28
|
+
?.filter(({ fileName }) => fileName === root.fileName)
|
|
29
|
+
.flatMap(({ highlightSpans }) => highlightSpans)
|
|
30
|
+
.map(({ textSpan, kind }) => ({
|
|
31
|
+
range: {
|
|
32
|
+
start: document.positionAt(textSpan.start),
|
|
33
|
+
end: document.positionAt(textSpan.start + textSpan.length),
|
|
34
|
+
},
|
|
35
|
+
kind: kind === 'reference'
|
|
36
|
+
? 2
|
|
37
|
+
: kind === 'writtenReference'
|
|
38
|
+
? 3
|
|
39
|
+
: 1,
|
|
40
|
+
}));
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=vue-document-highlights.js.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { LanguageServiceContext } from '@volar/language-service';
|
|
2
|
+
import { LanguageServicePlugin } from '../types';
|
|
3
|
+
export declare function create(getTsPluginClient?: (context: LanguageServiceContext) => import('@vue/typescript-plugin/lib/requests').Requests | undefined): LanguageServicePlugin;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.create = create;
|
|
4
|
+
const language_core_1 = require("@vue/language-core");
|
|
5
|
+
const html = require("vscode-html-languageservice");
|
|
6
|
+
const vscode_uri_1 = require("vscode-uri");
|
|
7
|
+
const nameCasing_1 = require("../nameCasing");
|
|
8
|
+
const types_1 = require("../types");
|
|
9
|
+
function create(getTsPluginClient) {
|
|
10
|
+
return {
|
|
11
|
+
name: 'vue-missing-props-hints',
|
|
12
|
+
capabilities: {
|
|
13
|
+
inlayHintProvider: {},
|
|
14
|
+
},
|
|
15
|
+
create(context) {
|
|
16
|
+
const tsPluginClient = getTsPluginClient?.(context);
|
|
17
|
+
let intrinsicElementNames;
|
|
18
|
+
return {
|
|
19
|
+
async provideInlayHints(document, range, cancellationToken) {
|
|
20
|
+
if (!isSupportedDocument(document)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (!context.project.vue) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const vueCompilerOptions = context.project.vue.compilerOptions;
|
|
27
|
+
const enabled = await context.env.getConfiguration?.('vue.inlayHints.missingProps') ?? false;
|
|
28
|
+
if (!enabled) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const uri = vscode_uri_1.URI.parse(document.uri);
|
|
32
|
+
const decoded = context.decodeEmbeddedDocumentUri(uri);
|
|
33
|
+
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
|
|
34
|
+
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
|
|
35
|
+
if (!virtualCode) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const root = sourceScript?.generated?.root;
|
|
39
|
+
if (!(root instanceof language_core_1.VueVirtualCode)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const scanner = getScanner(context, document);
|
|
43
|
+
if (!scanner) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const result = [];
|
|
47
|
+
const casing = await (0, nameCasing_1.getNameCasing)(context, decoded[0]);
|
|
48
|
+
const components = await tsPluginClient?.getComponentNames(root.fileName) ?? [];
|
|
49
|
+
const componentProps = {};
|
|
50
|
+
intrinsicElementNames ??= new Set(await tsPluginClient?.getElementNames(root.fileName) ?? []);
|
|
51
|
+
let token;
|
|
52
|
+
let current;
|
|
53
|
+
while ((token = scanner.scan()) !== html.TokenType.EOS) {
|
|
54
|
+
if (token === html.TokenType.StartTag) {
|
|
55
|
+
const tagName = scanner.getTokenText();
|
|
56
|
+
const tagOffset = scanner.getTokenOffset();
|
|
57
|
+
const checkTag = tagName.includes('.')
|
|
58
|
+
? tagName
|
|
59
|
+
: components.find(component => component === tagName || (0, language_core_1.hyphenateTag)(component) === tagName);
|
|
60
|
+
if (intrinsicElementNames.has(tagName) || !checkTag) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (tagOffset < document.offsetAt(range.start)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (tagOffset > document.offsetAt(range.end)) {
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
if (!componentProps[checkTag]) {
|
|
70
|
+
if (cancellationToken.isCancellationRequested) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
componentProps[checkTag] = (await tsPluginClient?.getComponentProps(root.fileName, checkTag) ?? [])
|
|
74
|
+
.filter(prop => prop.required)
|
|
75
|
+
.map(prop => prop.name);
|
|
76
|
+
}
|
|
77
|
+
current = {
|
|
78
|
+
unburnedRequiredProps: [...componentProps[checkTag]],
|
|
79
|
+
labelOffset: scanner.getTokenOffset() + scanner.getTokenLength(),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
else if (token === html.TokenType.AttributeName) {
|
|
83
|
+
if (current) {
|
|
84
|
+
let attrText = scanner.getTokenText();
|
|
85
|
+
if (attrText === 'v-bind') {
|
|
86
|
+
current.unburnedRequiredProps = [];
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// remove modifiers
|
|
90
|
+
if (attrText.includes('.')) {
|
|
91
|
+
attrText = attrText.split('.')[0];
|
|
92
|
+
}
|
|
93
|
+
// normalize
|
|
94
|
+
if (attrText.startsWith('v-bind:')) {
|
|
95
|
+
attrText = attrText.slice('v-bind:'.length);
|
|
96
|
+
}
|
|
97
|
+
else if (attrText.startsWith(':')) {
|
|
98
|
+
attrText = attrText.slice(':'.length);
|
|
99
|
+
}
|
|
100
|
+
else if (attrText.startsWith('v-model:')) {
|
|
101
|
+
attrText = attrText.slice('v-model:'.length);
|
|
102
|
+
}
|
|
103
|
+
else if (attrText === 'v-model') {
|
|
104
|
+
attrText = vueCompilerOptions.target >= 3 ? 'modelValue' : 'value'; // TODO: support for experimentalModelPropName?
|
|
105
|
+
}
|
|
106
|
+
else if (attrText.startsWith('v-on:')) {
|
|
107
|
+
attrText = 'on-' + (0, language_core_1.hyphenateAttr)(attrText.slice('v-on:'.length));
|
|
108
|
+
}
|
|
109
|
+
else if (attrText.startsWith('@')) {
|
|
110
|
+
attrText = 'on-' + (0, language_core_1.hyphenateAttr)(attrText.slice('@'.length));
|
|
111
|
+
}
|
|
112
|
+
current.unburnedRequiredProps = current.unburnedRequiredProps.filter(propName => {
|
|
113
|
+
return attrText !== propName
|
|
114
|
+
&& attrText !== (0, language_core_1.hyphenateAttr)(propName);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else if (token === html.TokenType.StartTagSelfClose || token === html.TokenType.StartTagClose) {
|
|
120
|
+
if (current) {
|
|
121
|
+
for (const requiredProp of current.unburnedRequiredProps) {
|
|
122
|
+
result.push({
|
|
123
|
+
label: `${requiredProp}!`,
|
|
124
|
+
paddingLeft: true,
|
|
125
|
+
position: document.positionAt(current.labelOffset),
|
|
126
|
+
kind: 2,
|
|
127
|
+
textEdits: [{
|
|
128
|
+
range: {
|
|
129
|
+
start: document.positionAt(current.labelOffset),
|
|
130
|
+
end: document.positionAt(current.labelOffset),
|
|
131
|
+
},
|
|
132
|
+
newText: ` :${casing.attr === types_1.AttrNameCasing.Kebab ? (0, language_core_1.hyphenateAttr)(requiredProp) : requiredProp}=`,
|
|
133
|
+
}],
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
current = undefined;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
function getScanner(context, document) {
|
|
146
|
+
if (document.languageId === 'html') {
|
|
147
|
+
return context.inject('html/languageService').createScanner(document.getText());
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const pugDocument = context.inject('pug/pugDocument', document);
|
|
151
|
+
if (pugDocument) {
|
|
152
|
+
return context.inject('pug/languageService').createScanner(pugDocument);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function isSupportedDocument(document) {
|
|
157
|
+
return document.languageId === 'jade' || document.languageId === 'html';
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=vue-missing-props-hints.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vue/language-service",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.10",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"files": [
|
|
6
6
|
"data",
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"@volar/language-service": "~2.4.11",
|
|
22
22
|
"@volar/typescript": "~2.4.11",
|
|
23
23
|
"@vue/compiler-dom": "^3.5.0",
|
|
24
|
-
"@vue/language-core": "2.2.
|
|
24
|
+
"@vue/language-core": "2.2.10",
|
|
25
25
|
"@vue/shared": "^3.5.0",
|
|
26
|
-
"@vue/typescript-plugin": "2.2.
|
|
26
|
+
"@vue/typescript-plugin": "2.2.10",
|
|
27
27
|
"alien-signals": "^1.0.3",
|
|
28
28
|
"path-browserify": "^1.0.1",
|
|
29
29
|
"volar-service-css": "0.0.62",
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"@volar/kit": "~2.4.11",
|
|
46
46
|
"vscode-languageserver-protocol": "^3.17.5"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "0422c03ffa4958431c9cd3cd19ae51f726c30b07"
|
|
49
49
|
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.create = create;
|
|
4
|
-
function create() {
|
|
5
|
-
return {
|
|
6
|
-
name: 'vue-autoinsert-selfClosing',
|
|
7
|
-
capabilities: {
|
|
8
|
-
autoInsertionProvider: {
|
|
9
|
-
triggerCharacters: ['/'],
|
|
10
|
-
configurationSections: ['vue.autoInsert.selfClosing'],
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
create(context) {
|
|
14
|
-
return {
|
|
15
|
-
async provideAutoInsertSnippet(document, selection, change) {
|
|
16
|
-
if (document.languageId !== 'html') {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
const enabled = await context.env.getConfiguration?.('vue.autoInsert.selfClosing') ?? true;
|
|
20
|
-
if (!enabled) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
if (change.text === '{}'
|
|
24
|
-
&& document.getText().slice(change.rangeOffset - 1, change.rangeOffset + 3) === '{{}}'
|
|
25
|
-
&& document.offsetAt(selection) === change.rangeOffset + 1) {
|
|
26
|
-
return ` $0 `;
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
//# sourceMappingURL=vue-autoinsert-self-closing.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { LanguageServiceContext, LanguageServicePluginInstance } from '@volar/language-service';
|
|
2
|
-
import * as html from 'vscode-html-languageservice';
|
|
3
|
-
import type { TextDocument } from 'vscode-languageserver-textdocument';
|
|
4
|
-
export declare function provideAutoInsertSelfClosingTags(context: LanguageServiceContext, service: LanguageServicePluginInstance, document: TextDocument, position: html.Position, change: {
|
|
5
|
-
text: string;
|
|
6
|
-
}): Promise<">" | undefined>;
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.provideAutoInsertSelfClosingTags = provideAutoInsertSelfClosingTags;
|
|
4
|
-
const html = require("vscode-html-languageservice");
|
|
5
|
-
const vue_template_1 = require("../vue-template");
|
|
6
|
-
async function provideAutoInsertSelfClosingTags(context, service, document, position, change) {
|
|
7
|
-
if (document.languageId !== 'html') {
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
const enabled = await context.env.getConfiguration?.('vue.autoInsert.selfClosing') ?? true;
|
|
11
|
-
if (!enabled) {
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
if (change.text !== '/') {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
const scanner = (0, vue_template_1.getScanner)(service, document, 'html');
|
|
18
|
-
if (!scanner) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
const offset = document.offsetAt(position);
|
|
22
|
-
let token;
|
|
23
|
-
let inStartTag = false;
|
|
24
|
-
while ((token = scanner.scan()) !== html.TokenType.EOS) {
|
|
25
|
-
if (token === html.TokenType.StartTagOpen) {
|
|
26
|
-
inStartTag = true;
|
|
27
|
-
}
|
|
28
|
-
else if (token === html.TokenType.StartTagClose || token === html.TokenType.StartTagSelfClose) {
|
|
29
|
-
inStartTag = false;
|
|
30
|
-
}
|
|
31
|
-
else if (scanner.getTokenOffset() + 1 === offset) {
|
|
32
|
-
if (token === html.TokenType.Unknown) {
|
|
33
|
-
return '>';
|
|
34
|
-
}
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
//# sourceMappingURL=autoinsert-self-closing-tags.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|