diff --git a/README.md b/README.md index f906d37f9..72b9fa6b5 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # vue-router-next [![release candidate](https://img.shields.io/npm/v/vue-router/next.svg)](https://www.npmjs.com/package/vue-router/v/next) [![CircleCI](https://badgen.net/circleci/github/vuejs/vue-router-next/master)](https://circleci.com/gh/vuejs/vue-router-next) -> This is the repository for Vue Router 4 (for Vue 3) +> 여기는 Vue Router 4에 대한 저장소입니다. (Vue 3를 위한) -

Supporting Vue Router

+

Vue Router 지원

-Vue Router is part of the Vue Ecosystem and is an MIT-licensed open source project with its ongoing development made possible entirely by the support of Sponsors. If you would like to become a sponsor, please consider: +Vue 라우터는 Vue 생태계의 일부이며 MIT가 허가한 오픈 소스 프로젝트로, 전적으로 스폰서의 지원을 통해 개발되고 있습니다. 후원자가 되고 싶다면 다음을 고려하십시오. -- [Become a Sponsor on GitHub](https://github.com/sponsors/posva) -- [One-time donation via PayPal](https://paypal.me/posva) +- [GitHub의 후원자가 되기](https://github.com/sponsors/posva) +- [PayPal을 통한 일회성 기부](https://paypal.me/posva) -

Gold Sponsors

+

Gold 스폰서

@@ -24,7 +24,7 @@ Vue Router is part of the Vue Ecosystem and is an MIT-licensed open source proje

-

Silver Sponsors

+

Silver 스폰서

@@ -44,7 +44,7 @@ Vue Router is part of the Vue Ecosystem and is an MIT-licensed open source proje

-

Bronze Sponsors

+

Bronze 스폰서

@@ -58,29 +58,29 @@ Vue Router is part of the Vue Ecosystem and is an MIT-licensed open source proje --- -Get started with the [documentation](https://next.router.vuejs.org). +이 [문서](https://next.router.vuejs.org)로 시작하세요. -## Quickstart +## 빠른시작 -- Via CDN: `` -- In-browser playground on [CodeSandbox](https://codesandbox.io/s/vue-router-4-reproduction-hb9lh) -- Add it to an existing Vue Project: +- CDN을 통해: `` +- [CodeSandbox](https://codesandbox.io/s/vue-router-4-reproduction-hb9lh)의 브라우저 내 playground +- 기존 Vue 프로젝트에 추가: ```bash npm install vue-router@4 ``` -## Changes from Vue Router 3 +## Vue Router 3에서의 변경사항 -Please consult the [Migration Guide](https://next.router.vuejs.org/guide/migration/). +[Migration Guide](https://next.router.vuejs.org/guide/migration/)를 참조하세요. -## Contributing +## 기여하기 -See [Contributing Guide](https://github.com/vuejs/vue-router-next/blob/master/.github/contributing.md). +[Contributing Guid](https://github.com/vuejs/vue-router-next/blob/master/.github/contributing.md)를 참조하세요. -## Special Thanks +## 특별한 감사 BrowserStack Logo -Special thanks to [BrowserStack](https://www.browserstack.com) for letting the maintainers use their service to debug browser specific issues. +메인테이너가가 서비스를 사용하여 브라우저별 문제를 디버깅 할 수 있도록 해 준 [BrowserStack](https://www.browserstack.com)에 특별히 감사드립니다. diff --git a/docs/.vitepress/components/HomeSponsors.vue b/docs/.vitepress/components/HomeSponsors.vue index 92bf73467..827ea1763 100644 --- a/docs/.vitepress/components/HomeSponsors.vue +++ b/docs/.vitepress/components/HomeSponsors.vue @@ -23,6 +23,7 @@ import sponsors from './sponsors.json' const translations = { 'en-US': 'Become a Sponsor!', 'zh-CN': '成为赞助者!', + 'ko-KR': '스폰서가 되어 주세요!', } diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 02c920e1a..2ab6dcb39 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -15,7 +15,7 @@ if (process.env.NODE_ENV === 'production') { /** @type {UserConfig} */ const config = { - lang: 'en-US', + lang: 'en', title: 'Vue Router', description: 'The official router for Vue.js.', head, @@ -309,6 +309,138 @@ const config = { }, ], }, + '/ko/': { + lang: 'ko-KR', + label: '한국어', + selectText: '언어', + title: 'Vue Router', + description: 'Vue.js 공식 라우터', + nav: [ + { + text: '가이드', + link: '/ko/guide/', + }, + { + text: 'API 레퍼런스', + link: '/ko/api/', + }, + { + text: 'v4.x', + items: [{ text: 'v3.x', link: 'https://router.vuejs.org/ko' }], + }, + { + text: '변경내역', + link: 'https://github.com/vuejs/vue-router-next/blob/master/CHANGELOG.md', + }, + ], + + sidebar: [ + { + text: '소개', + link: '/ko/introduction.html', + }, + { + text: '설치', + link: '/ko/installation.html', + }, + { + text: '핵심', + collapsable: false, + children: [ + { + text: '시작하기', + link: '/ko/guide/', + }, + { + text: '동적 라우트 매칭', + link: '/ko/guide/essentials/dynamic-matching.html', + }, + { + text: '라우트 매칭 문법', + link: '/ko/guide/essentials/route-matching-syntax.html', + }, + { + text: '중첩된 라우트', + link: '/ko/guide/essentials/nested-routes.html', + }, + { + text: '프로그래밍 방식 네비게이션', + link: '/ko/guide/essentials/navigation.html', + }, + { + text: '이름을 가지는 라우트', + link: '/ko/guide/essentials/named-routes.html', + }, + { + text: '이름을 가지는 뷰', + link: '/ko/guide/essentials/named-views.html', + }, + { + text: '리다이렉트와 별칭', + link: '/ko/guide/essentials/redirect-and-alias.html', + }, + { + text: '라우트 컴포넌트에 속성 전달', + link: '/ko/guide/essentials/passing-props.html', + }, + { + text: 'HTML5 히스토리 모드', + link: '/ko/guide/essentials/history-mode.html', + }, + ], + }, + { + text: '고급', + collapsable: false, + children: [ + { + text: '네비게이션 가드 ', + link: '/ko/guide/advanced/navigation-guards.html', + }, + { + text: '라우트 메타 필드', + link: '/ko/guide/advanced/meta.html', + }, + { + text: '데이터 가져오기', + link: '/ko/guide/advanced/data-fetching.html', + }, + { + text: '컴포지션 API', + link: '/ko/guide/advanced/composition-api.html', + }, + { + text: '트랜지션', + link: '/ko/guide/advanced/transitions.html', + }, + { + text: '스크롤 동작', + link: '/ko/guide/advanced/scroll-behavior.html', + }, + { + text: '지연된 로딩', + link: '/ko/guide/advanced/lazy-loading.html', + }, + { + text: 'RouterLink 확장하기', + link: '/ko/guide/advanced/extending-router-link.html', + }, + { + text: '네비게이션 실패 처리하기', + link: '/ko/guide/advanced/navigation-failures.html', + }, + { + text: '동적 라우팅', + link: '/ko/guide/advanced/dynamic-routing.html', + }, + ], + }, + { + text: 'Vue2에서 이전하기', + link: '/ko/guide/migration/index.html', + }, + ], + }, }, }, } diff --git a/docs/guide/essentials/history-mode.md b/docs/guide/essentials/history-mode.md index 174764732..5ed0bd558 100644 --- a/docs/guide/essentials/history-mode.md +++ b/docs/guide/essentials/history-mode.md @@ -1,9 +1,5 @@ # Different History modes - The `history` option when creating the router instance allows us to choose among different history modes. diff --git a/docs/guide/essentials/named-routes.md b/docs/guide/essentials/named-routes.md index 3580ad6ad..b02a93a82 100644 --- a/docs/guide/essentials/named-routes.md +++ b/docs/guide/essentials/named-routes.md @@ -1,10 +1,5 @@ # Named Routes - - Alongside the `path`, you can provide a `name` to any route. This has the following advantages: - No hardcoded URLs diff --git a/docs/guide/essentials/nested-routes.md b/docs/guide/essentials/nested-routes.md index 7e7b6eb30..eddf2aab0 100644 --- a/docs/guide/essentials/nested-routes.md +++ b/docs/guide/essentials/nested-routes.md @@ -1,9 +1,5 @@ # Nested Routes - Some application's UIs are composed of components that are nested multiple levels deep. In this case, it is very common that the segments of a URL corresponds to a certain structure of nested components, for example: diff --git a/docs/index.md b/docs/index.md index f4f785604..d26622b2a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -25,5 +25,5 @@ footer: MIT Licensed | Copyright © 2014-present Evan You, Eduardo San Martin Mo diff --git a/docs/ko/api/index.md b/docs/ko/api/index.md new file mode 100644 index 000000000..2ced3c4ad --- /dev/null +++ b/docs/ko/api/index.md @@ -0,0 +1,1085 @@ +--- +sidebar: auto +--- + +# API Reference + +## `` Props + +### to + +- **Type**: [`RouteLocationRaw`](#routelocationraw) +- **Details**: + + Denotes the target route of the link. When clicked, the value of the `to` prop will be passed to `router.push()` internally, so it can either be a `string` or a [route location object](#routelocationraw). + +```html + +Home + +Home + + +Home + + +Home + + +User + + + + Register + +``` + +### replace + +- **Type**: `boolean` +- **Default**: `false` +- **Details**: + + Setting `replace` prop will call `router.replace()` instead of `router.push()` when clicked, so the navigation will not leave a history record. + +```html + +``` + +### active-class + +- **Type**: `string` +- **Default**: `"router-link-active"` (or global [`linkActiveClass`](#linkactiveclass)) +- **Details**: + + Class to apply on the rendered `` when the link is active. + +### aria-current-value + +- **Type**: `'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false'` (`string`) +- **Default**: `"page"` +- **Details**: + + Value passed to the attribute `aria-current` when the link is exactly active. + +### custom + +- **Type**: `boolean` +- **Default**: `false` +- **Details**: + + Whether `` should not wrap its content in an `` element. Useful when using [`v-slot`](#router-link-s-v-slot) to create a custom RouterLink. By default, `` will render its content wrapped in an `` element, even when using `v-slot`. Passing the `custom` prop, removes that behavior. + +- **Examples**: + + ```html + + {{ route.fullPath }} + + ``` + + Renders `/home`. + + ```html + + {{ route.fullPath }} + + ``` + + Renders `/home`. + +### exact-active-class + +- **Type**: `string` +- **Default**: `"router-link-exact-active"` (or global [`linkExactActiveClass`](#linkexactactiveclass)) +- **Details**: + + Class to apply on the rendered `` when the link is exact active. + +## ``'s `v-slot` + +`` exposes a low level customization through a [scoped slot](https://v3.vuejs.org/guide/component-slots.html#scoped-slots). This is a more advanced API that primarily targets library authors but can come in handy for developers as well, to build a custom component like a _NavLink_ or other. + +:::tip +Remember to pass the `custom` option to `` to prevent it from wrapping its content inside of an `` element. +::: + +```html + + + {{ route.fullPath }} + + +``` + +- `href`: resolved url. This would be the `href` attribute of an `` element. It contains the `base` if any was provided. +- `route`: resolved normalized location. +- `navigate`: function to trigger the navigation. **It will automatically prevent events when necessary**, the same way `router-link` does, e.g. `ctrl` or `cmd` + click will still be ignored by `navigate`. +- `isActive`: `true` if the [active class](#active-class) should be applied. Allows to apply an arbitrary class. +- `isExactActive`: `true` if the [exact active class](#exact-active-class) should be applied. Allows to apply an arbitrary class. + +### Example: Applying Active Class to Outer Element + +Sometimes we may want the active class to be applied to an outer element rather than the `` element itself, in that case, you can wrap that element inside a `router-link` and use the `v-slot` properties to create your link: + +```html + +

  • + {{ route.fullPath }} +
  • + +``` + +:::tip +If you add a `target="_blank"` to your `a` element, you must omit the `@click="navigate"` handler. +::: + +## `` Props + +### name + +- **Type**: `string` +- **Default**: `"default"` +- **Details**: + + When a `` has a `name`, it will render the component with the corresponding name in the matched route record's `components` option. + +- **See Also**: [Named Views](/guide/essentials/named-views.md) + +### route + +- **Type**: [`RouteLocationNormalized`](#routelocationnormalized) +- **Details**: + + A route location that has all of its component resolved (if any was lazy loaded) so it can be displayed. + +## ``'s `v-slot` + +`` exposes a `v-slot` API mainly to wrap your route components with `` and `` components. + +```html + + + +``` + +- `Component`: VNodes to be passed to a ``'s `is` prop. +- `route`: resolved normalized [route location](#routelocationnormalized). + +## createRouter + +Creates a Router instance that can be used by a Vue app. Check the [`RouterOptions`](#routeroptions) for a list of all the properties that can be passed. + +**Signature:** + +```typescript +export declare function createRouter(options: RouterOptions): Router +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ------------------------------- | -------------------------------- | +| options | [RouterOptions](#routeroptions) | Options to initialize the router | + +## createWebHistory + +Creates an HTML5 history. Most common history for single page applications. The application must be served through the http protocol. + +**Signature:** + +```typescript +export declare function createWebHistory(base?: string): RouterHistory +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | -------- | --------------------------------------------------------------------------------------------------------------------- | +| base | `string` | optional base to provide. Useful when the application is hosted inside of a folder like `https://example.com/folder/` | + +### Examples + +```js +createWebHistory() // No base, the app is hosted at the root of the domain `https://example.com` +createWebHistory('/folder/') // gives a url of `https://example.com/folder/` +``` + +## createWebHashHistory + +Creates a hash history. Useful for web applications with no host (e.g. `file://`) or when configuring a server to handle any URL isn't an option. **Note you should use [`createWebHistory`](#createwebhistory) if SEO matters to you**. + +**Signature:** + +```typescript +export declare function createWebHashHistory(base?: string): RouterHistory +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| base | `string` | optional base to provide. Defaults to `location.pathname` or `/` if at root. If there is a `base` tag in the `head`, its value will be **ignored**. | + +### Examples + +```js +// at https://example.com/folder +createWebHashHistory() // gives a url of `https://example.com/folder#` +createWebHashHistory('/folder/') // gives a url of `https://example.com/folder/#` +// if the `#` is provided in the base, it won't be added by `createWebHashHistory` +createWebHashHistory('/folder/#/app/') // gives a url of `https://example.com/folder/#/app/` +// you should avoid doing this because it changes the original url and breaks copying urls +createWebHashHistory('/other-folder/') // gives a url of `https://example.com/other-folder/#` + +// at file:///usr/etc/folder/index.html +// for locations with no `host`, the base is ignored +createWebHashHistory('/iAmIgnored') // gives a url of `file:///usr/etc/folder/index.html#` +``` + +## createMemoryHistory + +Creates a in-memory based history. The main purpose of this history is to handle SSR. It starts in a special location that is nowhere. If the user is not on a browser context, it's up to them to replace that location with the starter location by either calling `router.push()` or `router.replace()`. + +**Signature:** + +```typescript +export declare function createMemoryHistory(base?: string): RouterHistory +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | -------- | ----------------------------------------- | +| base | `string` | Base applied to all urls, defaults to '/' | + +### Returns + +A history object that can be passed to the router constructor + +## NavigationFailureType + +Enumeration with all possible types for navigation failures. Can be passed to [isNavigationFailure](#isnavigationfailure) to check for specific failures. **Never use any of the numerical values**, always use the variables like `NavigationFailureType.aborted`. + +**Signature:** + +```typescript +export declare enum NavigationFailureType +``` + +### Members + +| Member | Value | Description | +| ---------- | ----- | -------------------------------------------------------------------------------------------------------------------------------- | +| aborted | 4 | An aborted navigation is a navigation that failed because a navigation guard returned `false` or called `next(false)` | +| cancelled | 8 | A cancelled navigation is a navigation that failed because a more recent navigation finished started (not necessarily finished). | +| duplicated | 16 | A duplicated navigation is a navigation that failed because it was initiated while already being at the exact same location. | + +## START_LOCATION + +- **Type**: [`RouteLocationNormalized`](#routelocationnormalized) +- **Details**: + + Initial route location where the router is. Can be used in navigation guards to differentiate the initial navigation. + + ```js + import { START_LOCATION } from 'vue-router' + + router.beforeEach((to, from) => { + if (from === START_LOCATION) { + // initial navigation + } + }) + ``` + +## Composition API + +### onBeforeRouteLeave + +Add a navigation guard that triggers whenever the component for the current location is about to be left. Similar to `beforeRouteLeave` but can be used in any component. The guard is removed when the component is unmounted. + +**Signature:** + +```typescript +export declare function onBeforeRouteLeave(leaveGuard: NavigationGuard): void +``` + +#### Parameters + +| Parameter | Type | Description | +| ---------- | ------------------------------------- | ----------------------- | +| leaveGuard | [`NavigationGuard`](#navigationguard) | Navigation guard to add | + +### onBeforeRouteUpdate + +Add a navigation guard that triggers whenever the current location is about to be updated. Similar to `beforeRouteUpdate` but can be used in any component. The guard is removed when the component is unmounted. + +**Signature:** + +```typescript +export declare function onBeforeRouteUpdate(updateGuard: NavigationGuard): void +``` + +#### Parameters + +| Parameter | Type | Description | +| ----------- | ------------------------------------- | ----------------------- | +| updateGuard | [`NavigationGuard`](#navigationguard) | Navigation guard to add | + +### useLink + +Returns everything exposed by the [`v-slot` API](#router-link-s-v-slot). + +**Signature:** + +```typescript +export declare function useLink(props: RouterLinkOptions): { + route: ComputedRef, + href: ComputedRef, + isActive: ComputedRef, + isExactActive: ComputedRef, + navigate: (event?: MouseEvent) => Promise(NavigationFailure | void), +} +``` + +#### Parameters + +| Parameter | Type | Description | +| --------- | ----------------- | ------------------------------------------------------------------------------------- | +| props | RouterLinkOptions | props object that can be passed to ``. Accepts `Ref`s and `ComputedRef`s | + +### useRoute + +Returns the current route location. Equivalent to using `$route` inside templates. Must be called inside of `setup()`. + +**Signature:** + +```typescript +export declare function useRoute(): RouteLocationNormalized +``` + +### useRouter + +Returns the [router](#Router) instance. Equivalent to using `$router` inside templates. Must be called inside of `setup()`. + +**Signature:** + +```typescript +export declare function useRouter(): Router +``` + +## TypeScript + +Here are some of the interfaces and types used by Vue Router. The documentation references them to give you an idea of the existing properties in objects. + +## Router Properties + +### currentRoute + +- **Type**: [`Ref`](#routelocationnormalized) +- **Details**: + + Current route location. Readonly. + +### options + +- **Type**: [`RouterOptions`](#routeroptions) +- **Details**: + + Original options object passed to create the Router. Readonly. + +## Router Methods + +### addRoute + +Add a new [Route Record](#routerecordraw) as the child of an existing route. If the route has a `name` and there is already an existing one with the same one, it removes it first. + +**Signature:** + +```typescript +addRoute(parentName: string | symbol, route: RouteRecordRaw): () => void +``` + +_Parameters_ + +| Parameter | Type | Description | +| ---------- | ----------------------------------- | ------------------- | +| parentName | `string | symbol` | Parent Route Record where `route` should be appended at | +| route | [`RouteRecordRaw`](#routerecordraw) | Route Record to add | + +### addRoute + +Add a new [route record](#routerecordraw) to the router. If the route has a `name` and there is already an existing one with the same one, it removes it first. + +**Signature:** + +```typescript +addRoute(route: RouteRecordRaw): () => void +``` + +_Parameters_ + +| Parameter | Type | Description | +| --------- | ----------------------------------- | ------------------- | +| route | [`RouteRecordRaw`](#routerecordraw) | Route Record to add | + +:::tip +Note adding routes does not trigger a new navigation, meaning that the added route will not be displayed unless a new navigation is triggered. +::: + +### afterEach + +Add a navigation hook that is executed after every navigation. Returns a function that removes the registered hook. + +**Signature:** + +```typescript +afterEach(guard: NavigationHookAfter): () => void +``` + +_Parameters_ + +| Parameter | Type | Description | +| --------- | ------------------- | ---------------------- | +| guard | NavigationHookAfter | navigation hook to add | + +#### Examples + +```js +router.afterEach((to, from, failure) => { + if (isNavigationFailure(failure)) { + console.log('failed navigation', failure) + } +}) +``` + +### back + +Go back in history if possible by calling `history.back()`. Equivalent to `router.go(-1)`. + +**Signature:** + +```typescript +back(): void +``` + +### beforeEach + +Add a navigation guard that executes before any navigation. Returns a function that removes the registered guard. + +**Signature:** + +```typescript +beforeEach(guard: NavigationGuard): () => void +``` + +_Parameters_ + +| Parameter | Type | Description | +| --------- | ------------------------------------- | ----------------------- | +| guard | [`NavigationGuard`](#navigationguard) | navigation guard to add | + +### beforeResolve + +Add a navigation guard that executes before navigation is about to be resolved. At this state all component have been fetched and other navigation guards have been successful. Returns a function that removes the registered guard. + +**Signature:** + +```typescript +beforeResolve(guard: NavigationGuard): () => void +``` + +_Parameters_ + +| Parameter | Type | Description | +| --------- | ------------------------------------- | ----------------------- | +| guard | [`NavigationGuard`](#navigationguard) | navigation guard to add | + +#### Examples + +```js +router.beforeEach(to => { + if (to.meta.requiresAuth && !isAuthenticated) return false +}) +``` + +### forward + +Go forward in history if possible by calling `history.forward()`. Equivalent to `router.go(1)`. + +**Signature:** + +```typescript +forward(): void +``` + +### getRoutes + +Get a full list of all the [route records](#routerecord). + +**Signature:** + +```typescript +getRoutes(): RouteRecord[] +``` + +### go + +Allows you to move forward or backward through the history. + +**Signature:** + +```typescript +go(delta: number): void +``` + +_Parameters_ + +| Parameter | Type | Description | +| --------- | -------- | ----------------------------------------------------------------------------------- | +| delta | `number` | The position in the history to which you want to move, relative to the current page | + +### hasRoute + +Checks if a route with a given name exists + +**Signature:** + +```typescript +hasRoute(name: string | symbol): boolean +``` + +_Parameters_ + +| Parameter | Type | Description | +| --------- | ------- | ----------- | +| name | `string | symbol` | Name of the route to check | + +### isReady + +Returns a Promise that resolves when the router has completed the initial navigation, which means it has resolved all async enter hooks and async components that are associated with the initial route. If the initial navigation already happened, the promise resolves immediately.This is useful in server-side rendering to ensure consistent output on both the server and the client. Note that on server side, you need to manually push the initial location while on client side, the router automatically picks it up from the URL. + +**Signature:** + +```typescript +isReady(): Promise +``` + +### onError + +Adds an error handler that is called every time a non caught error happens during navigation. This includes errors thrown synchronously and asynchronously, errors returned or passed to `next` in any navigation guard, and errors occurred when trying to resolve an async component that is required to render a route. + +**Signature:** + +```typescript +onError(handler: (error: any) => any): () => void +``` + +_Parameters_ + +| Parameter | Type | Description | +| --------- | --------------------- | ------------------------- | +| handler | `(error: any) => any` | error handler to register | + +### push + +Programmatically navigate to a new URL by pushing an entry in the history stack. + +**Signature:** + +```typescript +push(to: RouteLocationRaw): Promise +``` + +_Parameters_ + +| Parameter | Type | Description | +| --------- | --------------------------------------- | ----------------------------- | +| to | [`RouteLocationRaw`](#routelocationraw) | Route location to navigate to | + +### removeRoute + +Remove an existing route by its name. + +**Signature:** + +```typescript +removeRoute(name: string | symbol): void +``` + +_Parameters_ + +| Parameter | Type | Description | +| --------- | ------- | ----------- | +| name | `string | symbol` | Name of the route to remove | + +### replace + +Programmatically navigate to a new URL by replacing the current entry in the history stack. + +**Signature:** + +```typescript +replace(to: RouteLocationRaw): Promise +``` + +_Parameters_ + +| Parameter | Type | Description | +| --------- | --------------------------------------- | ----------------------------- | +| to | [`RouteLocationRaw`](#routelocationraw) | Route location to navigate to | + +### resolve + +Returns the [normalized version](#routelocation) of a [route location](#routelocationraw). Also includes an `href` property that includes any existing `base`. + +**Signature:** + +```typescript +resolve(to: RouteLocationRaw): RouteLocation & { + href: string +} +``` + +_Parameters_ + +| Parameter | Type | Description | +| --------- | --------------------------------------- | ----------------------------- | +| to | [`RouteLocationRaw`](#routelocationraw) | Raw route location to resolve | + +## RouterOptions + +### history + +History implementation used by the router. Most web applications should use `createWebHistory` but it requires the server to be properly configured. You can also use a _hash_ based history with `createWebHashHistory` that does not require any configuration on the server but isn't handled at all by search engines and does poorly on SEO. + +**Signature:** + +```typescript +history: RouterHistory +``` + +#### Examples + +```js +createRouter({ + history: createWebHistory(), + // other options... +}) +``` + +### linkActiveClass + +Default class applied to active [RouterLink](#router-link-props). If none is provided, `router-link-active` will be applied. + +**Signature:** + +```typescript +linkActiveClass?: string +``` + +### linkExactActiveClass + +Default class applied to exact active [RouterLink](#router-link-props). If none is provided, `router-link-exact-active` will be applied. + +**Signature:** + +```typescript +linkExactActiveClass?: string +``` + +### parseQuery + +Custom implementation to parse a query. Must decode query keys and values. See its counterpart, [stringifyQuery](#stringifyquery). + +**Signature:** + +```typescript +parseQuery?: (searchQuery: string) => Record +``` + +#### Examples + +Let's say you want to use the package [qs](https://github.com/ljharb/qs) to parse queries, you can provide both `parseQuery` and `stringifyQuery`: + +```js +import qs from 'qs' + +createRouter({ + // other options... + parseQuery: qs.parse, + stringifyQuery: qs.stringify, +}) +``` + +### routes + +Initial list of routes that should be added to the router. + +**Signature:** + +```typescript +routes: RouteRecordRaw[] +``` + +### scrollBehavior + +Function to control scrolling when navigating between pages. Can return a Promise to delay scrolling. Check . + +**Signature:** + +```typescript +scrollBehavior?: ScrollBehavior +``` + +#### Examples + +```js +function scrollBehavior(to, from, savedPosition) { + // `to` and `from` are both route locations + // `savedPosition` can be null if there isn't one +} +``` + +### stringifyQuery + +Custom implementation to stringify a query object. Should not prepend a leading `?`. Should properly encode query keys and values. [parseQuery](#parsequery) counterpart to handle query parsing. + +**Signature:** + +```typescript +stringifyQuery?: ( + query: Record< + string | number, + string | number | null | undefined | (string | number | null | undefined)[] + > +) => string +``` + +## RouteRecordRaw + +Route record that can be provided by the user when adding routes via the [`routes` option](#routeroptions) or via [`router.addRoutes()`](#addroutes). There are three different kind of route records: + +- Single views records: have a `component` option +- Multiple views records ([named views](/guide/essentials/named-views.md)): have a `components` option +- Redirect records: cannot have `component` or `components` option because a redirect record is never reached. + +### path + +- **Type**: `string` +- **Details**: + + Path of the record. Should start with `/` unless the record is the child of another record. + Can define parameters: `/users/:id` matches `/users/1` as well as `/users/posva`. + +- **See Also**: [Dynamic Route Matching](/guide/essentials/dynamic-matching.md) + +### redirect + +- **Type**: `RouteLocationRaw | (to: RouteLocationNormalized) => RouteLocationRaw` (Optional) +- **Details**: + + Where to redirect if the route is directly matched. The redirection happens + before any navigation guard and triggers a new navigation with the new target + location. Can also be a function that receives the target route location and + returns the location we should redirect to. + +### children + +- **Type**: Array of [`RouteRecordRaw`](#routerecordraw) (Optional) +- **Details**: + + Nested routes of the current record. + +- **See Also**: [Nested Routes](/guide/essentials/nested-routes.md) + +### alias + +- **Type**: `string | string[]` (Optional) +- **Details**: + + Aliases for the route. Allows defining extra paths that will behave like a + copy of the record. This enables paths shorthands like `/users/:id` and + `/u/:id`. **All `alias` and `path` values must share the same params**. + +### name + +- **Type**: `string | symbol` (Optional) +- **Details**: + + Unique name for the route record. + +### beforeEnter + +- **Type**: [`NavigationGuard | NavigationGuard[]`](#navigationguard) (Optional) +- **Details**: + + Before enter guard specific to this record. Note `beforeEnter` has no effect if the record has a `redirect` property. + +### props + +- **Type**: `boolean | Record | (to: RouteLocationNormalized) => Record` (Optional) +- **Details**: + + Allows passing down params as props to the component rendered by `router-view`. When passed to a _multiple views record_, it should be an object with the same keys as `components` or a `boolean` to be applied to each component. + target location. + +- **See Also**: [Passing props to Route Components](/guide/essentials/passing-props.md) + +### meta + +- **Type**: [`RouteMeta`](#routemeta) (Optional) +- **Details**: + + Custom data attached to the record. + +- **See Also**: [Meta fields](/guide/advanced/meta.md) + +## RouteRecordNormalized + +Normalized version of a [Route Record](#routerecordraw) + +### aliasOf + +- **Type**: `RouteRecordNormalized | undefined` +- **Details**: + + Defines if this record is the alias of another one. This property is `undefined` if the record is the original one. + +### beforeEnter + +- **Type**: [`NavigationGuard`](#navigationguard) +- **Details**: + + Navigation guard applied when entering this record from somewhere else. + +- **See Also**: [Navigation guards](/guide/advanced/navigation-guards.md) + +### children + +- **Type**: Array of normalized [route records](#routerecordnormalized) +- **Details**: + + Children route records of the current route. Empty array if none. + +### components + +- **Type**: `Record` +- **Details**: + + Dictionary of named views, if none, contains an object with the key `default`. + +### meta + +- **Type**: `RouteMeta` +- **Details**: + + Arbitrary data attached to the record. + +- **See also**: [Meta fields](/guide/advanced/meta.md) + +### name + +- **Type**: `string | symbol | undefined` +- **Details**: + + Name for the route record. `undefined` if none was provided. + +### path + +- **Type**: `string` +- **Details**: + + Normalized path of the record. Includes any parent's `path`. + +### props + +- **Type**: `Record>` +- **Details**: + + Dictionary of the [`props` option](#props) for each named view. If none, it will contain only one property named `default`. + +### redirect + +- **Type**: [`RouteLocationRaw`](#routelocationraw) +- **Details**: + + Where to redirect if the route is directly matched. The redirection happens before any navigation guard and triggers a new navigation with the new target location. + +## RouteLocationRaw + +User-level route location that can be passed to `router.push()`, `redirect`, and returned in [Navigation Guards](/guide/advanced/navigation-guards.md). + +A raw location can either be a `string` like `/users/posva#bio` or an object: + +```js +// these three forms are equivalent +router.push('/users/posva#bio') +router.push({ path: '/users/posva', hash: '#bio' }) +router.push({ name: 'users', params: { username: 'posva' }, hash: '#bio' }) +// only change the hash +router.push({ hash: '#bio' }) +// only change query +router.push({ query: { page: '2' } }) +// change one param +router.push({ params: { username: 'jolyne' } }) +``` + +Note `path` must be provided encoded (e.g. `phantom blood` becomes `phantom%20blood`) while `params`, `query` and `hash` must not, they are encoded by the router. + +Raw route locations also support an extra option `replace` to call `router.replace()` instead of `router.push()` in navigation guards. Note this also internally calls `router.replace()` even when calling `router.push()`: + +```js +router.push({ hash: '#bio', replace: true }) +// equivalent to +router.replace({ hash: '#bio' }) +``` + +## RouteLocation + +Resolved [RouteLocationRaw](#routelocationraw) that can contain [redirect records](#routerecordraw). Apart from that it has the same properties as [RouteLocationNormalized](#routelocationnormalized). + +## RouteLocationNormalized + +Normalized route location. Does not have any [redirect records](#routerecordraw). In navigation guards, `to` and `from` are always of this type. + +### fullPath + +- **Type**: `string` +- **Details**: + + Encoded URL associated to the route location. Contains `path`, `query` and `hash`. + +### hash + +- **Type**: `string` +- **Details**: + + Decoded `hash` section of the URL. Always starts with a `#`. Empty string if there is no `hash` in the URL. + +### query + +- **Type**: `Record` +- **Details**: + + Dictionary of decoded query params extracted from the `search` section of the URL. + +### matched + +- **Type**: [`RouteRecordNormalized[]`](#routerecordnormalized) +- **Details**: + + Array of [normalized route records](#routerecord) that were matched with the given route location. + +### meta + +- **Type**: `RouteMeta` +- **Details**: + + Arbitrary data attached to all matched records merged (non recursively) from parent to child. + +- **See also**: [Meta fields](/guide/advanced/meta.md) + +### name + +- **Type**: `string | symbol | undefined | null` +- **Details**: + + Name for the route record. `undefined` if none was provided. + +### params + +- **Type**: `Record` +- **Details**: + + Dictionary of decoded params extracted from `path`. + +### path + +- **Type**: `string` +- **Details**: + + Encoded `pathname` section of the URL associated to the route location. + +### redirectedFrom + +- **Type**: [`RouteLocation`](#routelocation) +- **Details**: + + Route location we were initially trying to access before ending up on the current location when a `redirect` option was found or a navigation guard called `next()` with a route location. `undefined` if there was no redirection. + +## NavigationFailure + +### from + +- **Type**: [`RouteLocationNormalized`](#routelocationnormalized) +- **Details**: + + Route location we were navigating from + +### to + +- **Type**: [`RouteLocationNormalized`](#routelocationnormalized) +- **Details**: + + Route location we were navigating to + +### type + +- **Type**: [`NavigationFailureType`](#navigationfailuretype) +- **Details**: + + Type of the navigation failure. + +- **See Also**: [Navigation Failures](/guide/advanced/navigation-failures.md) + +## NavigationGuard + +- **Arguments**: + + - [`RouteLocationNormalized`](#routelocationnormalized) to - Route location we are navigating to + - [`RouteLocationNormalized`](#routelocationnormalized) from - Route location we are navigating from + - `Function` next (Optional) - Callback to validate the navigation + +- **Details**: + + Function that can be passed to control a router navigation. The `next` callback can be omitted if you return a value (or a Promise) instead, which is encouraged. Possible return values (and parameters for `next`) are: + + - `undefined | void | true`: validates the navigation + - `false`: cancels the navigation + - [`RouteLocationRaw`](#routelocationraw): redirects to a different location + - `(vm: ComponentPublicInstance) => any` **only for `beforeRouteEnter`**: A callback to be executed once the navigation completes. Receives the route component instance as the parameter. + +- **See Also**: [Navigation Guards](/guide/advanced/navigation-guards.md) + +## Component Injections + +### Component Injected Properties + +These properties are injected into every child component by calling `app.use(router)`. + +- **this.\$router** + + The router instance. + +- **this.\$route** + + The current active [route location](#routelocationnormalized). This property is read-only and its properties are immutable, but it can be watched. + +### Component Enabled Options + +- **beforeRouteEnter** +- **beforeRouteUpdate** +- **beforeRouteLeave** + +See [In Component Guards](/guide/advanced/navigation-guards.md#in-component-guards). diff --git a/docs/ko/guide/advanced/composition-api.md b/docs/ko/guide/advanced/composition-api.md new file mode 100644 index 000000000..55841319f --- /dev/null +++ b/docs/ko/guide/advanced/composition-api.md @@ -0,0 +1,114 @@ +# Vue Router 와 컴포지션 API + +Vue에서 `setup` 및 [Composition API](https://v3.vuejs.org/guide/composition-api-introduction.html) 이 도입되어서 새로운 가능성이 열렸습니다. vue 라우터에서 이를 최대한 활용하기 위해서 `this` 를 대신하여 컴포넌트간의 네비게이션을 수행하는 새로운 함수를 사용해야 합니다. + +## `setup` 안에서 라우터와 라우트(경로)에 접근하기 + +컴포지션 API 기반의 `setup` 함수 안에서는 `this`를 사용하면 않됩니다. 따라서 `this.$router` 나 `this.$route`를 사용할수 없습니다. 그대신 `useRouter` 함수를 사용해야 합니다. + +```js +import { useRouter, useRoute } from 'vue-router' + +export default { + setup() { + const router = useRouter() + const route = useRoute() + + function pushWithQuery(query) { + router.push({ + name: 'search', + query: { + ...route.query, + }, + }) + } + }, +} +``` + +`route` 객체는 반응형 객체입니다. 라우트 객체의 속성을 watch하는 것은 좋지만, `route` 자체를 watch 하는 것은 피하는게 좋습니다. 대부분의 시나리오에서는 변경될 파라메터를 직접 watch할수 있습니다. + +```js +import { useRoute } from 'vue-router' +import { ref } from 'vue' + +export default { + setup() { + const route = useRoute() + const userData = ref() + + // fetch the user information when params change + watch( + () => route.params.id, + async newId => { + userData.value = await fetchUser(newId) + } + ) + }, +} +``` + +Note we still have access to `$router` and `$route` in templates, so there is no need to return `router` or `route` inside of `setup`. + +## 네비게이션 가드 + +예전 방식대로 컴포넌트 내 네비게이션 가드 선언 방식을 사용할수도 있습니다. 하지만 `setup` 내에서 컴포지션 API 함수를 이용해 네비게이션 가드를 이용할수 있습니다. + +```js +import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router' +import { ref } from 'vue' + +export default { + setup() { + // `this` 업이도 beforeRouteLeave 옵션을 사용할수 있습니다. + onBeforeRouteLeave((to, from) => { + const answer = window.confirm( + 'Do you really want to leave? you have unsaved changes!' + ) + // 네비게이션을 취소하고 현재 페이지에 머무를수 있습니다. + if (!answer) return false + }) + + const userData = ref() + + // `this` 없이도 onBeforeRouteUpdate 옵션을 지정할수 있습니다. + onBeforeRouteUpdate(async (to, from) => { + //URL상의 쿼리나 해시가 변경되어 사용자 ID가 변경되었을때만 가져오게 한다. + if (to.params.id !== from.params.id) { + userData.value = await fetchUser(to.params.id) + } + }) + }, +} +``` + +Composition API guards can also be used in any component rendered by ``, they don't have to be used directly on the route component like in-component guards. + +## `useLink` + +Vue Router는 RouterLink의 내부 동작을 Composition API 함수로 노출합니다. [`v-slot` API](../../api/#router-link-s-v-slot) 와 동일한 속성에 접근 할수 있습니다. + +```js +import { RouterLink, useLink } from 'vue-router' +import { computed } from 'vue' + +export default { + name: 'AppLink', + + props: { + // add @ts-ignore if using TypeScript + ...RouterLink.props, + inactiveClass: String, + }, + + setup(props) { + const { route, href, isActive, isExactActive, navigate } = useLink(props) + + const isExternalLink = computed( + () => typeof props.to === 'string' && props.to.startsWith('http') + ) + + return { isExternalLink, href, navigate, isActive } + }, +} +``` diff --git a/docs/ko/guide/advanced/data-fetching.md b/docs/ko/guide/advanced/data-fetching.md new file mode 100644 index 000000000..591362ae0 --- /dev/null +++ b/docs/ko/guide/advanced/data-fetching.md @@ -0,0 +1,105 @@ +# 데이터 가져오기 + +때로는 경로가 활성화 될 때 서버에서 데이터를 가져와야합니다. 예를 들어 사용자 프로필을 렌더링하기 전에 서버에서 사용자 데이터를 가져와야합니다. 두 가지 방법으로 이를 달성 할 수 있습니다. + +- **내비게이션 후에 데이터 가져 오기** : 먼저 내비게이션을 수행하고 들어오는 컴포넌트의 수명주기 후크에서 데이터를 가져옵니다. 데이터를 가져 오는 동안 로딩 상태를 표시합니다. + +- **내비게이션 전에 데이터 가져 오기** : 경로에서 내비게이션 전에 데이터를 가져와 가드에 진입하고 데이터를 가져온 후 내비게이션을 수행합니다. + +기술적으로는 둘 다 유효한 선택이며 궁극적으로 목표로하는 사용자 경험에 따라 다릅니다. + + +## 내비게이션 후에 데이터 가져 오기 + +이 접근 방식을 사용하면 들어오는 컴포넌트를 즉시 탐색 및 렌더링하고 컴포넌트의 `created` 후크에서 데이터를 가져옵니다. 네트워크를 통해 데이터를 가져 오는 동안 로딩 상태를 표시 할 수있는 기회를 제공하며, 각 뷰에 대해 로딩을 다르게 처리 할 수도 있습니다. + +`Post` 컴포넌트가 `$route.params.id` 기반으로 한 게시물에 데이터를 가져와야 한다고 가정 해 보겠습니다. + +```html + +``` + +```js +export default { + data() { + return { + loading: false, + post: null, + error: null, + } + }, + created() { + // 라우트 파라메터의 변경을 watch하여 변경시 다시 가져온다 + this.$watch( + () => this.$route.params, + () => { + this.fetchData() + }, + // 뷰가 만들어질때 즉시 데이터를 가져오고 파라메터 변경을 추적한다. + { immediate: true } + ) + }, + methods: { + fetchData() { + this.error = this.post = null + this.loading = true + // replace `getPost` with your data fetching util / API wrapper + getPost(this.$route.params.id, (err, post) => { + this.loading = false + if (err) { + this.error = err.toString() + } else { + this.post = post + } + }) + }, + }, +} +``` + +## 내비게이션 전에 데이터 가져 오기 + +이 접근 방식을 사용하면 실제로 새 경로로 이동하기 전에 데이터를 가져옵니다. 들어오는 컴포넌트 `beforeRouteEnter` 가드에서 데이터 가져 오기를 수행 할 수 있으며, 가져 오기가 완료 되면 `next`를 호출하면 됩니다. + +```js +export default { + data() { + return { + post: null, + error: null, + } + }, + beforeRouteEnter(to, from, next) { + getPost(to.params.id, (err, post) => { + next(vm => vm.setData(err, post)) + }) + }, + // when route changes and this component is already rendered, + // the logic will be slightly different. + async beforeRouteUpdate(to, from) { + this.post = null + try { + this.post = await getPost(to.params.id) + } catch (error) { + this.error = error.toString() + } + }, +} +``` + +새로 보여질 뷰에서 데이터를 가져 오는 동안 이전 뷰가 유지됩니다. 따라서 데이터를 가져 오는 동안 진행상태나 적절한 표시기를 보여주는게 좋습니다. 데이터 가져 오기에 실패하면 적절한 전역 경고 메시지도 표시해야합니다. + + + + diff --git a/docs/ko/guide/advanced/dynamic-routing.md b/docs/ko/guide/advanced/dynamic-routing.md new file mode 100644 index 000000000..440cc845c --- /dev/null +++ b/docs/ko/guide/advanced/dynamic-routing.md @@ -0,0 +1,103 @@ +# Dynamic Routing + +Adding routes to your router is usually done via the [`routes` option](../../api/#routes) but in some situations, you might want to add or remove routes while the application is already running. Application with extensible interfaces like [Vue CLI UI](https://cli.vuejs.org/dev-guide/ui-api.html) can use this to make the application grow. + +## Adding Routes + +Dynamic routing is achieved mainly via two functions: `router.addRoute()` and `router.removeRoute()`. They **only** register a new route, meaning that if the newly added route matches the current location, it would require you to **manually navigate** with `router.push()` or `router.replace()` to display that new route. Let's take a look at an example: + +Imagine having the following router with one single route: + +```js +const router = createRouter({ + history: createWebHistory(), + routes: [{ path: '/:articleName', component: Article }], +}) +``` + +Going to any page, `/about`, `/store`, or `/3-tricks-to-improve-your-routing-code` ends up rendering the `Article` component. If we are on `/about` and we add a new route: + +```js +router.addRoute({ path: '/about', component: About }) +``` + +The page will still show the `Article` component, we need to manually call `router.replace()` to change the current location and overwrite where we were (instead of pushing a new entry, ending up in the same location twice in our history): + +```js +router.addRoute({ path: '/about', component: About }) +// we could also use this.$route or route = useRoute() (inside a setup) +router.replace(router.currentRoute.value.fullPath) +``` + +Remember you can `await router.replace()` if you need to wait for the new route to be displayed. + +## Adding Routes inside navigation guards + +If you decide to add or remove routes inside of a navigation guard, you should not call `router.replace()` but trigger a redirection by returning the new location: + +```js +router.beforeEach(to => { + if (!hasNecessaryRoute(to)) { + router.addRoute(generateRoute(to)) + // trigger a redirection + return to.fullPath + } +}) +``` + +The example above assumes two things: first, the newly added route record will match the `to` location, effectively resulting in a different location from the one we were trying to access. Second, `hasNecessaryRoute()` returns `false` after adding the new route to avoid an infinite redirection. + +Because we are redirecting, we are replacing the ongoing navigation, effectively behaving like the example shown before. In real world scenarios, adding is more likely to happen outside of navigation guards, e.g. when a view component mounts, it register new routes. + +## Removing routes + +There are few different ways to remove existing routes: + +- By adding a route with a conflicting name. If you add a route that has the same name as an existing route, it will remove the route first and then add the route: + ```js + router.addRoute({ path: '/about', name: 'about', component: About }) + // this will remove the previously added route because they have the same name and names are unique + router.addRoute({ path: '/other', name: 'about', component: Other }) + ``` +- By calling the callback returned by `router.addRoute()`: + ```js + const removeRoute = router.addRoute(routeRecord) + removeRoute() // removes the route if it exists + ``` + This is useful when the routes do not have a name +- By using `router.removeRoute()` to remove a route by its name: + ```js + router.addRoute({ path: '/about', name: 'about', component: About }) + // remove the route + router.removeRoute('about') + ``` + Note you can use `Symbol`s for names in routes if you wish to use this function but want to avoid conflicts in names. + +Whenever a route is removed, **all of its aliases and children** are removed with it. + +## Adding nested routes + +To add nested routes to an existing route, you can pass the _name_ of the route as its first parameter to `router.addRoute()`, this will effectively add the route as if it was added through `children`: + +```js +router.addRoute({ name: 'admin', path: '/admin', component: Admin }) +router.addRoute('admin', { path: 'settings', component: AdminSettings }) +``` + +This is equivalent to: + +```js +router.addRoute({ + name: 'admin', + path: '/admin', + component: Admin, + children: [{ path: 'settings', component: AdminSettings }], +}) +``` + +## Looking at existing routes + +Vue Router gives you two functions to look at existing routes: + +- [`router.hasRoute()`](../../api/#hasroute): check if a route exists +- [`router.getRoutes()`](../../api/#getroutes): get an array with all the route records. diff --git a/docs/ko/guide/advanced/extending-router-link.md b/docs/ko/guide/advanced/extending-router-link.md new file mode 100644 index 000000000..592e079d5 --- /dev/null +++ b/docs/ko/guide/advanced/extending-router-link.md @@ -0,0 +1,88 @@ +# Extending RouterLink + +The RouterLink component exposes enough `props` to suffice most basic applications but it doesn't try to cover every possible use case and you will likely find yourself using `v-slot` for some advanced cases. In most medium to large sized applications, it's worth creating one if not multiple custom RouterLink components to reuse them across your application. Some examples are Links in a Navigation Menu, handling external links, adding an `inactive-class`, etc. + +Let's extend RouterLink to handle external links as well and adding a custom `inactive-class` in an `AppLink.vue` file: + +```vue + + + +``` + +If you prefer using a render function or create `computed` properties, you can use the `useLink` from the [Composition API](./composition-api.md): + +```js +import { RouterLink, useLink } from 'vue-router' + +export default { + name: 'AppLink', + + props: { + // add @ts-ignore if using TypeScript + ...RouterLink.props, + inactiveClass: String, + }, + + setup(props) { + // `props` contains `to` and any other prop that can be passed to + const { navigate, href, route, isActive, isExactActive } = useLink(props) + + // profit! + + return { isExternalLink } + }, +} +``` + +In practice, you might want to use your `AppLink` component for different parts of your application. e.g. using [Tailwind CSS](https://tailwindcss.com), you could create a `NavLink.vue` component with all the classes: + +```vue + +``` diff --git a/docs/ko/guide/advanced/lazy-loading.md b/docs/ko/guide/advanced/lazy-loading.md new file mode 100644 index 000000000..a43ef24d5 --- /dev/null +++ b/docs/ko/guide/advanced/lazy-loading.md @@ -0,0 +1,54 @@ +# 경로 지연 로딩 + + + +번들러를 사용하여 앱을 빌드하면 JavaScript 번들이 상당히 커져 페이지 로드 시간에 영향을 미칠 수 있습니다. 각 경로의 컴포넌트를 별도의 청크로 분할하고 경로를 방문 할 때 만 로드 할 수 있다면 더 효율적일 것입니다. + +Vue Router는 기본적으로 [동적 임포트(Dynamic import)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports) 지원하므로 정적 임포트(Static Import)를 동적 임포트(Dynamic import)로 바꿀 수 있습니다. + +```js +// replace +// import UserDetails from './views/UserDetails' +// with +const UserDetails = () => import('./views/UserDetails') + +const router = createRouter({ + // ... + routes: [{ path: '/users/:id', component: UserDetails }], +}) +``` + +`component` (및 `components` ) 옵션은 컴포넌트의 약속을 반환하는 함수를 허용하며 Vue Router **는 처음 페이지에 들어갈 때만 가져온** 다음 캐시 된 버전을 사용합니다. 즉, Promise를 반환하는 한 더 복잡한 함수를 가질 수도 있습니다. + +```js +const UserDetails = () => + Promise.resolve({ + /* component definition */ + }) +``` + +일반적으로 모든 경로에 대해 **항상 동적 임포트(Dynamic import)** 를 사용하는 것이 좋습니다. + +::: tip Router 설정에서는 [비동기 컴포넌트](https://v3.vuejs.org/guide/component-dynamic-async.html#async-components)를 **사용하지** 마십시오. 경로 컴포넌트 내에서는 여전히 비동기 컴포넌트를 사용해도 되지만, 경로 컴포넌트 자체는 정적 임포트를 해야 합니다 ::: + +웹팩과 같은 번 들러를 사용하면 [코드 분할의 이점이 자동으로 제공됩니다.](https://webpack.js.org/guides/code-splitting/) + +Babel을 사용할 때 Babel이 구문을 올바르게 구문 분석 할 수 있도록 [syntax-dynamic-import 플러그인을 추가해야합니다.](https://babeljs.io/docs/plugins/syntax-dynamic-import/) + +## 동일한 청크에서 컴포넌트 그룹화 + +때로는 동일한 경로에 중첩 된 모든 컴포넌트를 동일한 비동기 청크로 그룹화 할 수 있습니다. 이를 위해서는 특별한 주석 구문을 사용하여 청크 이름을 제공하여 [명명 된 청크를 사용해야합니다 (webpack> 2.4 필요).](https://webpack.js.org/guides/code-splitting/#dynamic-imports) + +```js +const UserDetails = () => + import(/* webpackChunkName: "group-user" */ './UserDetails.vue') +const UserDashboard = () => + import(/* webpackChunkName: "group-user" */ './UserDashboard.vue') +const UserProfileEdit = () => + import(/* webpackChunkName: "group-user" */ './UserProfileEdit.vue') +``` + +webpack은 동일한 청크 이름을 가진 모든 비동기 모듈을 동일한 비동기 청크로 그룹화합니다. diff --git a/docs/ko/guide/advanced/meta.md b/docs/ko/guide/advanced/meta.md new file mode 100644 index 000000000..bb4a57186 --- /dev/null +++ b/docs/ko/guide/advanced/meta.md @@ -0,0 +1,69 @@ +# 경로(Route) 메타 필드 + +때로는 트랜지션 이름, 경로에 접근 허용된 사용자등과 같은 임의의 정보를 경로(Route)에 첨부 할 수 있습니다. 경로 설정 객체에 `meta` 속성을 추가하면, 경로 위치나 네비게이션 가드에서 사용할수 있습니다. 다음 예제 처럼 `meta` 속성을 정의 할 수 있습니다. + +```js +const routes = [ + { + path: '/posts', + component: PostsLayout, + children: [ + { + path: 'new', + component: PostsNew, + // 권한이 있는 사용자만 Post를 만들수 있음 + meta: { requiresAuth: true } + }, + { + path: ':id', + component: PostsDetail + // 아무나 읽을수 있음 + meta: { requiresAuth: false } + } + ] + } +] +``` + +그렇다면 이 `meta` 필드에 어떻게 접근 할까요? + + + +일단 알아두어야 할것은, `routes` 구성의 각 경로 개체를 **경로 레코드(Route record)** 라고합니다. 경로 레코드는 중첩 될 수 있습니다. 따라서 경로가 일치하면 잠재적으로 하나 이상의 경로 레코드와 일치 할 수 있습니다. + +예를 들어 위의 경로 구성에서 URL `/posts/new` 는 상위 경로 레코드 ( `path: '/posts'` ) 및 하위 경로 레코드 ( `path: 'new'` )와 모두 일치합니다. + +경로와 일치하는 모든 경로 레코드는 `$route`의 `$route.matched` 배열 개체로 에 노출됩니다. (내비게이션 가드에 주어지는 경로(Route) 개체에도 동일하게 노출됩니다). `meta` 필드를 확인하기 위해 해당 배열을 순회 할 수 있지만 Vue Router는 부모와 자식 경로의  모든 meta 필드를 `$route.meta` 로 병합해서 제공합니다.
    다음 예제 처럼 간단히 쓸 수 있다는 의미입니다 + +```js +router.beforeEach((to, from) => { + // 경로가 접근 권한을 필요로 하는지 모든 매칭된 경로를 뒤지지 않고 + if (to.meta.requiresAuth && !auth.isLoggedIn()) { + // 이 경로는 접근 권한을 필요로 하기 때문에 로그인 여부를 체크함 + // 권한이 없다면 로그인 페이지로 리다이렉트 + return { + path: '/login', + // 어느 페이지에서 왔는지 저장해둠 + query: { redirect: to.fullPath }, + } + } +}) +``` + +## TypeScript + +`RouteMeta` 인터페이스를 확장하여 메타 필드를 입력 할 수 있습니다. + +```ts +// typings.d.ts or router.ts +import 'vue-router' + +declare module 'vue-router' { + interface RouteMeta { + // is optional + isAdmin?: boolean + // must be declared by every route + requiresAuth: boolean + } +} +``` diff --git a/docs/ko/guide/advanced/navigation-failures.md b/docs/ko/guide/advanced/navigation-failures.md new file mode 100644 index 000000000..a6657882d --- /dev/null +++ b/docs/ko/guide/advanced/navigation-failures.md @@ -0,0 +1,92 @@ +# 네비게이션 결과를 기다리기 + +`router-link` 사용할 때 Vue Router는 `router.push` 를 호출하여 탐색을 트리거합니다. 대부분의 링크에서 예상되는 동작은 사용자를 새 페이지로 이동하는 것이지만 사용자가 동일한 페이지에 남아있어야 하는 몇 가지 상황이 있습니다. + +- 사용자가 탐색하려는 페이지에 이미 있습니다. +- [네비게이션 가드](./navigation-guards.md)함수가 `false`를 반환하여 탐색을 중단합니다. +- 이전 가드가 완료되지 않은 동안 새로운 내비게이션 가드가 발생합니다. +- [내비게이션 가드](./navigation-guards.md)가 새로운 위치를 반환하여 다른 곳으로 리디렉션합니다 (예 : `return '/login'` ). +- [내비게이션 가드](./navigation-guards.md)가 `Error` 를 던집니다. + +네비게이션이 끝난후에 추가적으로 무언가를 하고 싶다면 `router.push` 를 호출 한 후 기다릴 방법이 필요합니다. 다른 페이지로 이동할 수있는 모바일 메뉴가 있고 새 페이지로 이동 한 후에 만 메뉴를 숨기고 싶다고 가정 해 보겠습니다. 다음과 같이 할 수 있습니다. + +```js +router.push('/my-profile') +this.isMenuOpen = false +``` + +그러나 이것은 **네비게이션은 비동기적으로 수행되기 때문에 ** 때문에 즉시 메뉴를 닫을 것입니다. 기다리기 위해서는 `router.push` 에서 반환 된 promise를 `await` 해야 합니다. + +```js +await router.push('/my-profile') +this.isMenuOpen = false +``` + +이제 네비게이션이 완료되면 메뉴가 닫힙니다. 하지만 이 코드는 네비게이션이 취소 된 경우에도 메뉴를 닫게 됩니다. 현재 페이지가 페이지를 실제로 변경되었는지 감지하는 방법이 필요합니다. + +## 네비게이션 실패 감지하기 + +네비게이션이 취소되어 사용자가 동일한 페이지에 머무르게 된다면, `router.push`에서 반환한 `Promise` *의 resolve 된 값은 Navigation Failure*가 됩니다. 그렇지 않다면 *falsy* 값 (일반적으로 `undefined` )이됩니다. 이를 통해 네비게이션이 성공하는 경우를 구별 할 수 있습니다. + +```js +const navigationResult = await router.push('/my-profile') + +if (navigationResult) { + // 네비게이션이 방지됨 +} else { + // 네비게이션이 성공함(리다이렉트 포함) + this.isMenuOpen = false +} +``` + +*네비게이션 실패(Navigation Failure)* 는 `Error`의 인스턴스로, 여러 추가 정보를 제공합니다. 네비게이션 취소 여부와 그 이유를 담고 있습니다. 이렇게 반환된 네비게이션 결과를 확인 하기 위해서 `isNavigationFailure` 함수를 사용할수 있습니다. + +```js +import { NavigationFailureType, isNavigationFailure } from 'vue-router' + +// 페이지 수정중에 저장없이 떠나도 되는지 시도해봄 +const failure = await router.push('/articles/2') + +if (isNavigationFailure(failure, NavigationFailureType.aborted)) { + // 사용자에게 알림을 보여줌 + showToast('You have unsaved changes, discard and leave anyway?') +} +``` + +::: tip `isNavigationFailure(failure)` 함수를 호출할떄 두 번째 매개 변수 `failure`를 생략하면 *탐색 실패* 인지 여부 만 확인합니다. ::: + +## 네비게이션 실패 구분하기 + +처음에 언급했듯이 탐색을 중단하는 여러 상황이 있으며 모두 다른 *탐색 실패를* 초래합니다. `isNavigationFailure` 및 `NavigationFailureType` 사용하여 구분할 수 있습니다. 세 가지 유형이 있습니다. + +- `aborted` : 내비게이션이 발생했을때 내비게이션 가드 내에서 `false`를 반환함 +- `cancelled` : 현재 네비게이션이 완료되기 전에 새 네비게이션이 수행되었음. 예를 들어, 내비게이션 가드안에서 `router.push`가 호출됨(eg: 권한이 없어서 로그인 페이지로 이동시키는등) +- `duplicated` : 이미 대상 위치에 있기 때문에 네비게이션이 차단되었습니다. + +## *네비게이션 실패(Natvigation Failure)* 속성 + +모든 탐색 실패는 `to` 와 `from`속성을 제공하며, 이를 통해 현재 위치와 가고자 했던 위치를 알수 있습니다. + +```js +// trying to access the admin page +router.push('/admin').then(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + failure.to.path // '/admin' + failure.from.path // '/' + } +}) +``` + +모든 경우에, `to` 와 `from` 는 정규화 된 경로 위치입니다. + +## 리디렉션 감지 + +내비게이션 가드 내부에 새 위치를 반환 할 때 진행중인 위치를 무시하는 새 내비게이션을 트리거합니다. 다른 반환 값과 달리 리디렉션은 탐색을 방해하지 않고 새 값 **을 만듭니다** . 이를 알기 위해 Route Location에서 `redirectedFrom` 속성을 읽을수 있습니다. + +```js +await router.push('/my-profile') +if (router.currentRoute.value.redirectedFrom) { + // redirectedFrom is resolved route location like to and from in navigation + // guards +} +``` diff --git a/docs/ko/guide/advanced/navigation-guards.md b/docs/ko/guide/advanced/navigation-guards.md new file mode 100644 index 000000000..8c0932373 --- /dev/null +++ b/docs/ko/guide/advanced/navigation-guards.md @@ -0,0 +1,248 @@ +# 네비게이션 가드 + +이름에서 알 수 있듯이 Vue 라우터에서 제공하는 내비게이션 가드는 주로 리디렉션하거나 취소하여 내비게이션을 보호하는 데 사용됩니다. 경로를 네비게이션 하는 과정을 후킹하는 것은 몇가지 방법이 있습니다. 전역, 경로 별, 컴포넌트 내부 구성 + +## 전역 선행(Before) 가드 + +`router.beforeEach` 사용하여 전역 선행(Before)가드를 등록할수 있습니다. + +```js +const router = createRouter({ ... }) + +router.beforeEach((to, from) => { + // ... + // explicitly return false to cancel the navigation + return false +}) +``` + +전역 선행(Before)가드는 네비게이션이 요청되었을때 만들어진 순서대로 호출됩니다. 가드는 비동기적으로 해소(resolve) 될수 있기 때문에 모든 훅이 해소되기 전에는 네비게이션은 **대기(Pending)** 상태입니다. + +모든 가드 함수는 두 개의 인수를받습니다. + +- **`to`** : 탐색중인 [정규화 된 형식](../../api/#routelocationnormalized) 의 목적지 경로 위치. +- **`from`** : 네비게이션이 출발하는 [되는 정규화 된 형식](../../api/#routelocationnormalized) 의 현재 경로 위치. + +선택적으로 다음 값 중 하나를 반환 할 수 있습니다. + +- `false` : 현재 탐색을 취소합니다. 브라우저 URL이 변경된 경우 (사용자가 수동으로 또는 뒤로 버튼을 통해) `from` 경로의 URL로 재설정됩니다. +- [경로 위치](../../api/#routelocationraw): [`router.push()`](../../api/#push) 호출하는 것처럼, 새로운 경로 위치를 전달하여 다른 위치로 리디렉션합니다. `replace: true` 또는 `name: 'home'` 과 같은 옵션을 전달할 수 있습니다. 현재 실행중인 네비게이션을 버리고 `from` 을 새로 가지는 새로운 네비게이션이 만들어집니다. + +예기치 않은 상황이 발생 `Error` 발생시킬 수도 있습니다. [`router.onError()`](../../api/#onerror) 를 통해 등록 된 콜백을 호출합니다. + +`undefined` 또는 `true` 가 반환되지 않으면 ** 네비게이션에 대한 검증이 완료되고 ** 되고 다음 탐색 가드가 호출됩니다. + +모든 동작은 ** `async` 함수나 ** 프로마이즈(Promises)에서도 동일하게 동작합니다. + +```js +router.beforeEach(async (to, from) => { + // canUserAccess() returns `true` or `false` + const canAccess = await canUserAccess(to) + if (!canAccess) return '/login' +}) +``` + +### 선택적 세 번째 인자:`next` + +이전 버전의 Vue Router에서는 next 세 번째 인자 를 사용할 수도있었습니다. 이것은 일반적인 실수의 원인이었으며 이를 제거하기 위해 [RFC를 거쳤습니다.](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0037-router-return-guards.md#motivation) 그러나 여전히 지원되므로 모든 내비게이션 가드에 세 번째 인수를 전달할 수 있습니다. 이 경우 내비게이션 가드를 통해 주어진 패스에서 **정확히 한 번 `next` 를 호출해야합니다.** 두 번 이상 나타날 수 있지만 논리적 경로가 겹치지 않는 경우에만 겹치지 않으면 후크가 해결되지 않거나 오류가 발생하지 않습니다. 다음은 사용자가 인증되지 않은 경우 /login 으로 리디렉션하는 잘못된 예입니다. + +```js +// BAD +router.beforeEach((to, from, next) => { + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) + // if the user is not authenticated, `next` is called twice + next() +}) +``` + +올바른 버전은 다음과 같습니다. + +```js +// GOOD +router.beforeEach((to, from, next) => { + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) + else next() +}) +``` + +## 전역 리졸빙 가드(Global Resolve Guards) + +`router.beforeResolve` 로 글로벌 가드를 등록 할 수 있습니다. 이는 모든 탐색 에서 트리거되기 때문에 router.beforeEach 와 비슷해 보이지만 네비게이션 이동이 확정되기 직전에 **모든 컴포넌트 가드 및 비동기 경로 컴포넌트가 의존성 해소 된 후에** 의존성 해소 가드(resolve guard)가 호출됩니다. [ 다음은 사용자 지정 메타](./meta.md) 속성 `requiresCamera` 정의한 경로에 대해 사용자가 카메라에 대한 액세스 권한을 부여했는지 확인하는 예입니다. + +```js +router.beforeResolve(async to => { + if (to.meta.requiresCamera) { + try { + await askForCameraPermission() + } catch (error) { + if (error instanceof NotAllowedError) { + // ... handle the error and then cancel the navigation + return false + } else { + // unexpected error, cancel the navigation and pass the error to the global handler + throw error + } + } + } +}) +``` + +`router.beforeResolve` 는 데이터를 가져 오거나 사용자가 페이지에 들어갈 수없는 경우 수행하지 않으려는 다른 작업을 수행하기에 이상적인 지점입니다. + + + +## 전역 후행 훅(Global After Hooks) + +전역 후행 훅(Global after hook)은 가드와 달리 `next` 함수를 넘겨 받지 않기 때문에 탐색 결정에 영향을 주지 못합니다. + +```js +router.afterEach((to, from) => { + sendToAnalytics(to.fullPath) +}) +``` + + + +분석, 페이지 제목 변경, 페이지 소개 같은 접근성 기능 및 기타 여러 가지에 유용합니다. + +[네비게이션 실패](./navigation-failures.md) 를 세 번째 인자로 받을수 있습니다. + +```js +router.afterEach((to, from, failure) => { + if (!failure) sendToAnalytics(to.fullPath) +}) +``` + +[가이드](./navigation-failures.md) 에서 탐색 실패에 대해 자세히 알아보세요. + +## 경로별 가드(Per-Route Guard) + +경로 설정 객체에 바로 `beforeEnter` 가드를 정의 할 수 있습니다. + +```js +const routes = [ + { + path: '/users/:id', + component: UserDetails, + beforeEnter: (to, from) => { + // reject the navigation + return false + }, + }, +] +``` + +`beforeEnter` 가드 **는 경로에 진입할때만 실행되며 ** `params` , `query` 또는 `hash`가 변경될때는 실행 되지 않습니다.(예 : `/users/2` 에서 `/users/3` 하거나 `/users/2#info` 에서 `/users/2#projects` . ** 다른** 경로에서 이 경로로 네비게이션 해올때만 실행됩니다. + +`beforeEnter` 에 함수 배열을 전달할 수도 있습니다. 이는 다른 경로에 대해 가드를 재사용 할 때 유용합니다. + +```js +function removeQueryParams(to) { + if (Object.keys(to.query).length) + return { path: to.path, query: {}, hash: to.hash } +} + +function removeHash(to) { + if (to.hash) return { path: to.path, query: to.query, hash: '' } +} + +const routes = [ + { + path: '/users/:id', + component: UserDetails, + beforeEnter: [removeQueryParams, removeHash], + }, + { + path: '/about', + component: UserDetails, + beforeEnter: [removeQueryParams], + }, +] +``` + +[경로 메타 필드](./meta.md) 와 [전역 내비게이션 가드](#global-before-guards) 를 사용하여 유사한 동작을 달성 할 수 있습니다. + +## 컴포넌트 내부 가드(In-Component Guards) + +마지막으로 경로 컴포넌트(라우터 설정에 전달 된 컴포넌트) 내에서 경로 네비게이션 가드를 직접 정의 할 수 있습니다. + +### 옵션 API 사용 + +다음 옵션을 추가하여 구성 요소를 라우팅 할 수 있습니다. + +- `beforeRouteEnter` +- `beforeRouteUpdate` +- `beforeRouteLeave` + +```js +const UserDetails = { + template: `...`, + beforeRouteEnter(to, from) { + // called before the route that renders this component is confirmed. + // does NOT have access to `this` component instance, + // because it has not been created yet when this guard is called! + }, + beforeRouteUpdate(to, from) { + // called when the route that renders this component has changed, + // but this component is reused in the new route. + // For example, given a route with params `/users/:id`, when we + // navigate between `/users/1` and `/users/2`, the same `UserDetails` component instance + // will be reused, and this hook will be called when that happens. + // Because the component is mounted while this happens, the navigation guard has access to `this` component instance. + }, + beforeRouteLeave(to, from) { + // called when the route that renders this component is about to + // be navigated away from. + // As with `beforeRouteUpdate`, it has access to `this` component instance. + }, +} +``` + +The `beforeRouteEnter` guard does **NOT** have access to `this`, because the guard is called before the navigation is confirmed, thus the new entering component has not even been created yet. + +However, you can access the instance by passing a callback to `next`. The callback will be called when the navigation is confirmed, and the component instance will be passed to the callback as the argument: + +```js +beforeRouteEnter (to, from, next) { + next(vm => { + // access to component public instance via `vm` + }) +} +``` + +Note that `beforeRouteEnter` is the only guard that supports passing a callback to `next`. For `beforeRouteUpdate` and `beforeRouteLeave`, `this` is already available, so passing a callback is unnecessary and therefore *not supported*: + +```js +beforeRouteUpdate (to, from) { + // just use `this` + this.name = to.params.name +} +``` + +The **leave guard** is usually used to prevent the user from accidentally leaving the route with unsaved edits. The navigation can be canceled by returning `false`. + +```js +beforeRouteLeave (to, from) { + const answer = window.confirm('Do you really want to leave? you have unsaved changes!') + if (!answer) return false +} +``` + +### Using the composition API + +If you are writing your component using the [composition API and a `setup` function](https://v3.vuejs.org/guide/composition-api-setup.html#setup), you can add update and leave guards through `onBeforeRouteUpdate` and `onBeforeRouteLeave` respectively. Please refer to the [Composition API section](./composition-api.md#navigation-guards) for more details. + +## The Full Navigation Resolution Flow + +1. Navigation triggered. +2. Call `beforeRouteLeave` guards in deactivated components. +3. Call global `beforeEach` guards. +4. Call `beforeRouteUpdate` guards in reused components. +5. Call `beforeEnter` in route configs. +6. Resolve async route components. +7. Call `beforeRouteEnter` in activated components. +8. Call global `beforeResolve` guards. +9. Navigation is confirmed. +10. Call global `afterEach` hooks. +11. DOM updates triggered. +12. Call callbacks passed to `next` in `beforeRouteEnter` guards with instantiated instances. diff --git a/docs/ko/guide/advanced/scroll-behavior.md b/docs/ko/guide/advanced/scroll-behavior.md new file mode 100644 index 000000000..1d72a5c0d --- /dev/null +++ b/docs/ko/guide/advanced/scroll-behavior.md @@ -0,0 +1,109 @@ +# 스크롤 동작 + +클라이언트 측 라우팅을 사용할 때 새 경로로 네비게이션 할때 맨 위로 스크롤 하거나 기존 페이지 리로딩처럼 스크롤 위치를 히스트리 이력으로 저장해두었다가 스크롤 위치를 복구 할수 있습니다. Vue Router를 사용하면 이러한 작업을 더 잘 수행 할 수 합니다. 또한 경로 네비게이션에서 스크롤 동작을 완전히 커스터마이징 할 수 있습니다. + +**참고 :이 기능은 브라우저가 `history.pushState`를 지원하는 경우에만 작동합니다.** + +라우터 인스턴스를 만들 때 `scrollBehavior` 함수를 설정 할 수 있습니다. + +```js +const router = createRouter({ + history: createWebHashHistory(), + routes: [...], + scrollBehavior (to, from, savedPosition) { + // return desired position + } +}) +``` + +`scrollBehavior` 함수는 다른 [네비게이션 가드](./navigation-guards.md)처럼 함수 인자로 `to` 과 `from`를 넘겨 받습니다. 세 번째 함수 인자로 `savedPosition`를 넘겨 받을수 있는데 이 인자는 브라우자 앞으로가기/뒤로가기 버튼을 눌러 히스토리 이력에서 `popstate`를 받아 왔을때만 넘겨 받습니다. + +이 함수는 [`ScrollToOptions`](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions) 위치 객체를 반환 할 수 있습니다. + +```js +const router = createRouter({ + scrollBehavior(to, from, savedPosition) { + // 언제나 페이지 제일 위로 이동하기 + return { top: 0 } + }, +}) +``` + +`el` 통해 CSS 셀렉터나 DOM 앨리먼트를 전달할 수도 있습니다. 그럴 경우 `top` 과 `left` 은 해당 요소에 대한 상대적 오프셋으로 처리됩니다. + +```js +const router = createRouter({ + scrollBehavior(to, from, savedPosition) { + // 언제나 #main으로 부터 10px 만큼 위로 스크롤 시킨다 + return { + // could also be + // el: document.getElementById('main'), + el: '#main', + top: -10, + } + }, +}) +``` + +잘못된 값이나 빈 개체가 반환되면 스크롤이 발생하지 않습니다. + +`savedPosition` 을 반환하면 뒤로가기 / 앞으로가기 버튼으로 이동할떄 네이티브와 유사하게 동작합니다. + +```js +const router = createRouter({ + scrollBehavior(to, from, savedPosition) { + if (savedPosition) { + return savedPosition + } else { + return { top: 0 } + } + }, +}) +``` + +"링크로 스크롤하기" 동작을 흉내낼때 + +```js +const router = createRouter({ + scrollBehavior(to, from, savedPosition) { + if (to.hash) { + return { + el: to.hash, + } + } + }, +}) +``` + +브라우저가 [스크롤 동작](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions/behavior)을 지원하는 경우 부드럽게 만들 수 있습니다. + +```js +const router = createRouter({ + scrollBehavior(to, from, savedPosition) { + if (to.hash) { + return { + el: to.hash, + behavior: 'smooth', + } + } + } +}) +``` + +## 스크롤 지연 + +때로는 페이지를 스크롤하기 전에 잠시 기다려야합니다. 예를 들어 트랜지션을 처리 할 때 스크롤하기 전에 트랜지션이 완료 될 때까지 기다립니다. 이를 위해 원하는 위치 설명자를 반환하는 Promise를 반환 할 수 있습니다. 다음은 스크롤하기 전에 500ms를 기다리는 예입니다. + +```js +const router = createRouter({ + scrollBehavior(to, from, savedPosition) { + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve({ left: 0, top: 0 }) + }, 500) + }) + }, +}) +``` + +페이지 레벨을 구현하는 컴포넌트의 이벤트를 받아서 각 페이지 컴포넌트의 전환와 스크롤 동작을 잘 동작하게 할수 있습니다. 하지만 다양한 사용예와 복잡성 때문에 단순 구현만 제공하고, 그 외에는직접 구현을 해서 사용할수 있게 하였습니다. diff --git a/docs/ko/guide/advanced/transitions.md b/docs/ko/guide/advanced/transitions.md new file mode 100644 index 000000000..d59bf331d --- /dev/null +++ b/docs/ko/guide/advanced/transitions.md @@ -0,0 +1,73 @@ +# 트랜지션(Transitions) + + + +route 컴포넌트에서 트랜지션을 사용하고 탐색을 애니메이션 처리 하려면 [v-slot API](../../api/#router-view-s-v-slot) 를 사용합니다. + +```html + + + + + +``` + +[Vue의 모든 transition APIs](https://v3.vuejs.org/guide/transitions-enterleave.html) 와 동일하게 동작 + +## 경로별 트랜지션(Per-Route Transition) + +위의 사용법은 모든 경로에 동일한 트랜지션을 적용합니다. 각 경로의 컴포넌트가 서로 다른 트랜지션을 갖도록 하려면, <transition> 에서 메타 필드 와 동적 `name` 을 결합 할 수 있습니다. + +```js +const routes = [ + { + path: '/custom-transition', + component: PanelLeft, + meta: { transition: 'slide-left' }, + }, + { + path: '/other-transition', + component: PanelRight, + meta: { transition: 'slide-right' }, + }, +] +``` + +```html + + + + + + +``` + +## 경로 기반 동적 트랜지션(Route-Based Dynamic Transition) + +대상 경로와 현재 경로 사이의 관계에 따라 동적으로 사용할 트랜지션을 결정할 수도 있습니다.
    직전과 매우 유사한 예시 사용 : + +```html + + + + + + +``` + +경로의 깊이에 따라 `meta` 필드에 정보를 동적으로 추가하기 위해 [after navigation hook](./navigation-guards.md#global-after-hooks) 을 추가 할 수 있습니다. + +```js +router.afterEach((to, from) => { + const toDepth = to.path.split('/').length + const fromDepth = from.path.split('/').length + to.meta.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left' +}) +``` + + + + diff --git a/docs/ko/guide/essentials/dynamic-matching.md b/docs/ko/guide/essentials/dynamic-matching.md new file mode 100644 index 000000000..0b68a3071 --- /dev/null +++ b/docs/ko/guide/essentials/dynamic-matching.md @@ -0,0 +1,127 @@ +# 매개변수(Params)를 활용한 동적 라우트 매칭(Dynamic Route Matching) + + + + +매우 자주 주어진 패턴의 경로를 동일한 컴포넌트에 매핑해야합니다. 예를 들어 모든 사용자에 대해 렌더링되어야하지만 사용자 ID가 다른 `User` 컴포넌트가 있을 수 있습니다. Vue Router 에서는 이를 달성하기 위해 경로의 동적 세그먼트를 사용할 수 있으며 이를 *param* 이라고합니다. + +```js +const User = { + template: '
    User
    ', +} + +// 이것은 `createRouter` 로 전달됩니다. +const routes = [ + // 동적 세그먼트는 콜론(:)으로 시작합니다. + { path: '/users/:id', component: User }, +] +``` + +이제 `/users/johnny` 및 `/users/jolyne` 과 같은 URL 은 모두 동일한 경로에 매핑됩니다. + +*param* 은(는) 콜론 `:` (으)로 표시됩니다. 경로가 일치하면 해당 *params* 의 값이 모든 컴포넌트에서 `this.$route.params` 로 노출됩니다. 따라서 `User` 의 템플릿을 다음과 같이 업데이트하여 현재 사용자 ID를 렌더링 할 수 있습니다. + +```js +const User = { + template: '
    User {{ $route.params.id }}
    ', +} +``` + +동일한 경로에 여러 *params* 가 있을 수 있으며 `$route.params` 의 해당 필드에 매핑됩니다.
    예 : + +pattern | matched path | $route.params +--- | --- | --- +/users/:username | /users/eduardo | `{ username: 'eduardo' }` +/users/:username/posts/:postId | /users/eduardo/posts/123 | `{ username: 'eduardo', postId: '123' }` + +`$route.params` 외에도 `$route` 객체는 `$route.query` 와 같은 기타 유용한 정보도 노출합니다.(URL 에 query 가 있는 경우) `$route.hash` 기타 등 [API Reference](../../api/#routelocationnormalized) 에서 자세한 내용을 확인할 수 있습니다. + +이 예제의 작동 데모는 [여기](https://codesandbox.io/s/route-params-vue-router-examples-mlb14?from-embed&initialpath=%2Fusers%2Feduardo%2Fposts%2F1) 에서 찾을 수 있습니다. + + + +## 매개변수(Params) 변경에 대한 반응 + + + +매개 변수(params)와 함께 경로를 사용할 때 주의해야 할 점은 사용자가 `/users/johnny` 에서 `/users/jolyne ` 으로 이동 할 때 ** 동일한 컴포넌트 인스턴스가 재사용 된다는 것입니다. **두 경로 모두 동일한 컴포넌트를 렌더링하므로 이전 인스턴스를 삭제 한 다음 새 인스턴스를 만드는 것보다 더 효율적입니다. **그러나 이는 컴포넌트 수명주기 훅(hook)이 호출되지 않음을 의미하기도 합니다.** + +동일한 컴포넌트의 매개 변수(params) 변경에 대응하려면 `$route` 객체를 감시합니다. 이 시나리오에서는 `$route.params` 의 모든 항목을 관찰하면됩니다. + +```js +const User = { + template: '...', + created() { + this.$watch( + () => this.$route.params, + (toParams, previousParams) => { + // 라우트 변경에 대응... + } + ) + }, +} +``` + +또는 `beforeRouteUpdate`, [navigation guard](../advanced/navigation-guards.md) 를 사용하여 탐색을 취소 할 수도 있습니다. + +```js +const User = { + template: '...', + async beforeRouteUpdate(to, from) { + // 라우트 변경에 대응... + this.userData = await fetchUser(to.params.id) + }, +} +``` + +## 모두 캐치(Catch all) / 404 Not fount Route + + + + +일반 매개 변수(params)는 `/` 로 구분된 URL 조각 사이의 문자만 일치합니다. **무엇이든** 일치 시키려면 *param* 바로 뒤에 괄호 안에 정규식을 추가하여 맞춤 *param* 정규식을 사용할 수 있습니다. + +```js +const routes = [ + // 모든 것을 일치시키고`$ route.params.pathMatch` 아래에 넣습니다. + { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound }, + // `/ user-`로 시작하는 모든 항목과 일치하고`$ route.params.afterUser` 아래에 배치합니다. + { path: '/user-:afterUser(.*)', component: UserGeneric }, +] +``` + +이 특정 시나리오에서는 괄호 사이에 [커스텀 정규식(custom regexp)](./route-matching-syntax.md#custom-regexp-in-params) 을 사용하고 `pathMatch` 매개 변수를 [선택적으로 반복 가능(optionally repeatable)](./route-matching-syntax.md#optional-parameters) 으로 표시합니다. 이를 통해 필요한 경우 `path` 를 배열로 분할하여 경로로 직접 이동할 수 있습니다. + +```js +this.$router.push({ + name: 'NotFound', + // 현재 경로를 유지하고 `//` 로 시작하는 대상 URL 을 피하기 위해 첫 번째 문자를 제거 + params: { pathMatch: this.$route.path.substring(1).split('/') }, + // 기존 쿼리 및 해시(있는 경우) 유지 + query: this.$route.query, + hash: this.$route.hash, +}) +``` + +[반복 매개변수(repeated params)](./route-matching-syntax.md#repeatable-params) 부분에서 자세히 알아보세요. + +[History mode](./history-mode.md) 를 사용하는 경우 지침에 따라 서버도 올바르게 구성해야합니다. + +## 고급 매칭 패턴 + +Vue Router 는 `express` 에서 사용하는 것에서 영감을 얻은 자체 경로 일치 구문을 사용하므로 선택적 매개 변수, 0 개 이상 또는 하나 이상의 요구 사항, 심지어 사용자 정의 정규식 패턴과 같은 많은 고급 매칭 패턴을 지원합니다. [고급 매칭(Advanced Matching)](./route-matching-syntax.md) 문서를 확인하여 살펴보세요. diff --git a/docs/ko/guide/essentials/history-mode.md b/docs/ko/guide/essentials/history-mode.md new file mode 100644 index 000000000..fbc56862f --- /dev/null +++ b/docs/ko/guide/essentials/history-mode.md @@ -0,0 +1,182 @@ +# 다양한 히스토리 모드 + + +`history` 옵션을 사용하면 다른 히스토리 모드 중에서 선택할 수 있습니다. + +## 해시 모드 + +해시 히스토리 모드는 `createWebHashHistory()` 를 이용해 만듭니다. + +```js +import { createRouter, createWebHashHistory } from 'vue-router' + +const router = createRouter({ + history: createWebHashHistory(), + routes: [ + //... + ], +}) +``` + +내부적으로 전달되는 실제 URL 앞에 해시 문자 ( `#`) 이 URL 섹션은 서버로 전송되지 않기 때문에 서버 수준에서 특별한 처리가 필요하지 않습니다. **그러나 이렇게 하면 SEO에 나쁜 영향을 미칩니다** . 검색엔진최적화가 걱정된다면 HTML5 히스토리 모드를 사용하세요. + +## HTML5 Mode + +HTML5 모드는 `createWebHistory()`를 이용해 만듭니다. 이 모드가 추천되는 모드입니다. + +```js +import { createRouter, createWebHistory } from 'vue-router' + +const router = createRouter({ + history: createWebHistory(), + routes: [ + //... + ], +}) +``` + +히스토리 모드를 사용할 때 URL은 `https://example.com/user/id` 와 같이 "정상"으로 보입니다. 멋지죠! + +하지만 여기에 문제가 있습니다. 우리 앱은 적절한 서버 구성이없는 단일 페이지 클라이언트 측 앱이므로 사용자가 브라우저에서 `https://example.com/user/id` 을 입력하여 접속하면 404페이지를 보게 됩니다. + +걱정하지 마세요. 문제를 해결하려면 서버에 간단한 포괄 대체 경로를 추가하기 만하면됩니다. URL이 정적 리소스와 일치하지 않는 경우 `index.html` 페이지를 제공하면 됩니다. 멋지조! + +## 서버 구성 예제 + +**참고** : 다음 예제에서는 루트 폴더에서 앱을 제공한다고 가정합니다. 하위 폴더에 배포하는 경우 [Vue CLI의 `publicPath`](https://cli.vuejs.org/config/#publicpath) 옵션과 라우터의 관련 [`base` 속성을](../../api/#createwebhistory) 사용해야합니다. 또한 루트 폴더 대신 하위 폴더를 사용하려면 아래 예제를 조정해야합니다 (예 : `RewriteBase /` 를 `RewriteBase /name-of-your-subfolder/` 대체). + +### Apache + +```apacheconf + + RewriteEngine On + RewriteBase / + RewriteRule ^index\.html$ - [L] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule . /index.html [L] + +``` + +`mod_rewrite` 대신 [`FallbackResource`](https://httpd.apache.org/docs/2.2/mod/mod_dir.html#fallbackresource) 를 사용할 수도 있습니다. + +### nginx + +```nginx +location / { + try_files $uri $uri/ /index.html; +} +``` + +### Native Node.js + +```js +const http = require('http') +const fs = require('fs') +const httpPort = 80 + +http + .createServer((req, res) => { + fs.readFile('index.html', 'utf-8', (err, content) => { + if (err) { + console.log('We cannot open "index.html" file.') + } + + res.writeHead(200, { + 'Content-Type': 'text/html; charset=utf-8', + }) + + res.end(content) + }) + }) + .listen(httpPort, () => { + console.log('Server listening on: http://localhost:%s', httpPort) + }) +``` + +### Express with Node.js + +Node.js / Express의 경우 [connect-history-api-fallback 미들웨어](https://github.com/bripkens/connect-history-api-fallback) 사용을 고려하십시오. + +### Internet Information Services (IIS) + +1. [IIS UrlRewrite](https://www.iis.net/downloads/microsoft/url-rewrite) 설치 +2. 다음을 사용하여 사이트의 루트 디렉터리에 `web.config` + +```xml + + + + + + + + + + + + + + + + + +``` + +### Caddy v2 + +``` +try_files {path} / +``` + +### Caddy v1 + +``` +rewrite { + regexp .* + to {path} / +} +``` + +### Firebase hosting + +`firebase.json`에 추가하십시오. + +```json +{ + "hosting": { + "public": "dist", + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } +} +``` + +### Netlify + +배포 된 파일에 포함 된 `_redirects` 파일을 만듭니다. + +``` +/* /index.html 200 +``` + +vue-cli, nuxt 및 vite 프로젝트에서이 파일은 일반적으로 `static` 또는 `public` 이라는 폴더 아래에 있습니다. + +You can more about the syntax on [Netlify documentation](https://docs.netlify.com/routing/redirects/rewrites-proxies/#history-pushstate-and-single-page-apps). You can also [create a `netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/) to combine *redirections* with other Netlify features. + +## 경고 + +이에 대한주의 사항이 있습니다. 찾을 수없는 모든 경로가 이제 `index.html` 파일을 제공하므로 서버에서 더 이상 404 오류를보고하지 않습니다. 이 문제를 해결하려면 404 페이지를 표시하도록 Vue 앱 내에서 포괄 경로를 구현해야합니다. + +```js +const router = createRouter({ + history: createWebHistory(), + routes: [{ path: '/:pathMatch(.*)', component: NotFoundComponent }], +}) +``` + +또는 Node.js 서버를 사용하는 경우 서버 측의 라우터를 사용하여 수신 URL을 일치시키고 일치하는 경로가 없으면 404로 응답하여 대체를 구현할 수 있습니다. 자세한 내용은 [Vue 서버 측 렌더링 문서](https://v3.vuejs.org/guide/ssr/introduction.html#what-is-server-side-rendering-ssr) 를 확인하세요. diff --git a/docs/ko/guide/essentials/named-routes.md b/docs/ko/guide/essentials/named-routes.md new file mode 100644 index 000000000..5084d39f3 --- /dev/null +++ b/docs/ko/guide/essentials/named-routes.md @@ -0,0 +1,37 @@ +# 명명 된 경로 + + +`path` 와 함께 모든 `name` 을 제공 할 수 있습니다. 이것은 다음과 같은 장점이 있습니다. + +- 하드 코딩 된 URL 없음 +- `params` 자동 인코딩 / 디코딩 +- URL에 오타가 발생하지 않도록 방지 +- 경로 순위 우회 (ex a 를 표시) + +```js +const routes = [ + { + path: '/user/:username', + name: 'user', + component: User + } +] +``` + +명명된 경로에 링크하기 위해 `router-link` 컴포넌트에 `to` prop에 객체를 줄수 있습니다. + +```html + + User + +``` + +`router.push()` 와 함께 프로그래밍 방식으로 사용되는 것과 똑같은 객체입니다. + +```js +router.push({ name: 'user', params: { username: 'erina' } }) +``` + +두 경우 모두 라우터는 `/user/erina` 경로로 이동합니다. + +[여기에](https://github.com/vuejs/vue-router/blob/dev/examples/named-routes/app.js) 전체 예가 있습니다. diff --git a/docs/ko/guide/essentials/named-views.md b/docs/ko/guide/essentials/named-views.md new file mode 100644 index 000000000..03de0e27d --- /dev/null +++ b/docs/ko/guide/essentials/named-views.md @@ -0,0 +1,87 @@ +# 명명 된 뷰 + +때로는 여러 뷰를 중첩하지 않고 동시에 표시해야합니다 (예 : `sidebar` 뷰및 `main` 뷰 가 있는 레이아웃 만들기). 이것은 명명 된 뷰가 유용한 곳입니다. 뷰에 하나의 콘센트를 사용하는 대신 여러 개를 갖고 각각에 이름을 지정할 수 있습니다. 이름을 주지 않은 `router-view` 는 `default` 이 기본값으로 지정됩니다. + +```html + + + +``` + +뷰는 컴포넌트를 사용하여 렌더링되므로 여러 뷰에는 동일한 경로에 대해 여러 컴포넌트가 필요합니다. `components` ( **s 사용** ) 옵션을 사용해야합니다. + +```js +const router = createRouter({ + history: createWebHashHistory(), + routes: [ + { + path: '/', + components: { + default: Home, + // short for LeftSidebar: LeftSidebar + LeftSidebar, + // they match the `name` attribute on `` + RightSidebar, + }, + }, + ], +}) +``` + +이 예제의 작동 데모는 [여기](https://codesandbox.io/s/named-views-vue-router-4-examples-rd20l) 에서 찾을 수 있습니다. + +## 중첩 된 명명 된 뷰 + +중첩 된 뷰가있는 명명 된 뷰를 사용하여 복잡한 레이아웃을 만들 수 있습니다. 그렇게 할 때 중첩 된 `router-view` 에 이름을 지정해야합니다. 예를 들면 설정 패널이 있습니다. + +``` +/settings/emails /settings/profile ++-----------------------------------+ +------------------------------+ +| UserSettings | | UserSettings | +| +-----+-------------------------+ | | +-----+--------------------+ | +| | Nav | UserEmailsSubscriptions | | +------------> | | Nav | UserProfile | | +| | +-------------------------+ | | | +--------------------+ | +| | | | | | | | UserProfilePreview | | +| +-----+-------------------------+ | | +-----+--------------------+ | ++-----------------------------------+ +------------------------------+ +``` + +- `Nav` 는 일반적인 컴포넌트입니다. +- `UserSettings` 는 부모 뷰 컴포넌트입니다. +- `UserEmailsSubscriptions` , `UserProfile` , `UserProfilePreview` 는 중첩 된 뷰 컴포넌트입니다. + +**참고** : *HTML / CSS가 이러한 레이아웃을 표현하고 사용 된 컴포넌트에 초점을 맞추기 위해 어떻게 생겼는지 잊어 버리겠습니다.* + +`UserSettings`의 `