SFC 구조.
아래와 같은 구조가 기본 구조이다.
<template> 태그 안에 HTML 을 사용하되, 데이터는 <script> 태그 안에 data() 함수안에 객체로 리턴받아 사용하는 구조이다. 태그 안에 사용할때 {{ 변수명 }} 를 사용해야만 한다. data() 함수 형태는 예약어처럼 사용되기때문에 반드시 이 문법이 사용되야 함.
<style> 도 역시 한 파일안에 넣는다.
Props를 통해 Vue template 내에서 사용하는 모든 정보를 저장가능하다.
그러나, 태그의 속성값에 동적으로 넣으려면 directive를 사용해야 한다.
--------------------------------------------------------------------------------------------------------------------------------------------
<template>
<div>{{color}}</div>
</template>
<script>
export default {
data() {
return {
color: 'red'
}
}
}
</script>
<style>
</style>
--------------------------------------------------------------------------------------------------------------------------------------------
간단한 샘플 예제)
<template>
<h1>{{title}}</h1>
<hr/>
<p>{{description}}</p>
<ul>
<li>{{productions[0]}}</li>
<li>{{productions[2]}}</li>
<li>{{productions[3]}}</li>
</ul>
</template>
<script>
export default {
data() {
return {
title: 'GC몰에 오신것을 환경합니다.',
description: '자주 이용햇 주세요.^^;,
products : ['스포티지', '투싼', '그랜져']
}
}
}
</script>
--------------------------------------------------------------------------------------------------------------------------------------------
CSS 를 사용한 Vue 스타일링. scopped는 현 컴포넌트에만 style이 적용된다는 의미.
<style lang="scss" scopped>
h1,
h2 {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
text-align: center;
}
.title {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
color: #2c3e50
margin-top: 60px;
}
.subtitle {
color : #4fc08d;
font-style: italic;
}
</style>
--------------------------------------------------------------------------------------------------------------------------------------------
태그 attribute 를 사용하는 간단한 샘플 예제). vue-directive를 사용해야 함. 변수명이 ""기호안에 들어간다.
<template>
<h1>{{title}}</h1>
<hr/>
<p>{{description}}</p>
<img :src="imgSrc" v-if="isShowImage">
<ul>
<li v-for="product in products" :key="product">{{product}}</li>
</ul>
</template>
<script>
export default {
data() {
return {
title: 'GC몰에 오신것을 환경합니다.',
imgSrc: 'https://static.gcmall.com/hello/youreidiot/aaa.jpeg',
description: '자주 이용햇 주세요.^^;,
products : ['스포티지', '투싼', '그랜져']
}
}
}
</script>
--------------------------------------------------------------------------------------------------------------------------------------------
컴포넌트의 분리.
vue extensions 를 사용해서, 각 컴포넌트 파일을 만든후, <template> 부분만 구현해도 컴포넌트는 분리됨.
App.vue 에서 갖다 사용하면 됨. 사용할때는 script 부분에서 import 하는 코드가 있어야 하고, <template> 부분에서 사용하면 됨. 그런데 실무에서는 App.vue 에서 가져오지 않음.
--------------------------------------------------------------------------------------------------------------------------------------------
Props 의 사용
자식컴포넌트로 정보를 전달하기 위해서 사용.
자식 컴포넌트는 props 라는 식별자를 선언하고, 받을 변수들을 리스트 배열로 받음. 그리고, template에서 변수명을 사용하면 됨.
--------------------------------------------------------------------------------------------------------------------------------------------
Slot
커스텀 태그(컴포넌트) 사이에 들어가는 콘텐츠인데...하나면 들어간다면 Slot을 사용하면 됨
그러나 여러개를 사용하면..Named Slots 를 사용해야 함.
--------------------------------------------------------------------------------------------------------------------------------------------
Named Slot
보내는 쪽에서는 태그를 사용하되, <slot name="title /> 같은 slot 태그를 추가로 넣어서, 이름을 지정해주면 됨.
받는쪽에서는 <template> 태그나 커스텀 태그 안에 v-slot:title 처럼 사용할 태그를 지정하여 사용함.
<template v-slot:title>
<h3>My Article Tile<h3>
</template>
- 자세한 예제는 교재 참조.
--------------------------------------------------------------------------------------------------------------------------------------------
Filters - vue3에서 제거되서 신경 안써도 됨.
데이터 출력시 필터를 통해 데이터 가공을 할수 있음.
사용법은 <template> 태그에서 변수를 사용할 때, 변수옆에 '|' 를 넣고 사용할 필터 함수명을 지정해주면 됨
{{message | truncate }}
구현은 <script> 태그 안에 filters: 안에 필터 함수를 구현해 주면 됨.
- 자세한 예제는 교재 참조.
--------------------------------------------------------------------------------------------------------------------------------------------
refs : DOM 직접 참조하기
보통 this.$refs.변수명이나 proxy.$refs.변수명등을 통해 접근한다. 변수명 사용하는쪽에서는 ref 를 쓰고, script쪽에선 refs 로 접근함.
예)
<template>
<div id="app">
<input ref="theInput" />
</div>
</template>
<script>
export default {
methods: {
focus() {
this.$refs.theInput.focus()
}
}
}
</script>
--------------------------------------------------------------------------------------------------------------------------------------------
$emit : 자식이 부모로 데이터 전달하기
이 방식은 react, vue, angular 모두 동일한데, vue가 사용하기가 제일 쉬움.
커스텀 이벤트를 임으로 발생시킨다. this.$emit 을 사용하거나, $emit 을 사용함.
예)
data() {
return {
message: null
}
},
methods: {
send() {
this.$emit('send', this.message);
}
}
부모 컴포넌트에서 사용법 예제)
<MessageEditor @send="message = $event" />
해석 : send라는 이벤트가 발생하면 자식이 보낸데이터를 $event를 통해 받아서 message 변수에 넣어라.
형태 :
this.$emit('eventName', /* payload */)
"Payload"는 문맥에 따라 다소 의미가 달라질 수 있지만, 일반적으로 데이터나 전송되는 정보를 지칭하는 데 사용됨.
--------------------------------------------------------------------------------------------------------------------------------------------
Mixin, 믹스인
여러 Vue 컴포넌트에서 기능을 재사용하는 방법. 일종에 전역함수 같은 개념임.
<template>과 <style> 태그는 필요없고, <script> 태그만 있으면됨. 파일 확장자는 vue를 사용함.
.js 파일로 생성
독립적으로는 사용할 수 없고, 다른 컴포넌트에 mixin 되서 사용됨.
사용하는 쪽에서는 import 하고 함수명을 {{함수명()}} 이런식으로 <template> 태그에서 사용하면 됨.
예)
mixin 파일
export default {
methods: {
greet(name) {
return `$(this.greeting) $(name)`
}
},
data() {
return {
greeing: 'Hello'
}
}
}
사용하는 파일
<template>
<div> {{ ('World') }} </div>
<h2> {{hello()}} </h2> <-----------greeter라는 파일에 있는 함수를 그냥 {{}} 표기법으로 사용하면 됨
</template>
<script>
import greeter from './mixins/greeter.js' <------ mixin 임포트하기
export default {
mixins: [greeter] <----- mixin 등록하기. 파일명을 그대로 쓰면 됨.
}
</script>
--------------------------------------------------------------------------------------------------------------------------------------------
Global component
공통으로 사용하는 컴포넌트.
보통 별도의 디렉토리를 하나 만들고, component라는 메서드를 사용해서 등록을 해야 함.
main.js 에서 다음과 같은 형태로 사용함. @는 절대경로로 root로부터 시작되는 경로임.
app이 실행하면서 바로 메모리에 올라감.
// 페이지 매김 구성요소
import Pagination from '@/components/Pagination'
const app = createApp(App)
// 전역 구성요소 장착
app.component('DictTag', DictTag)
app.component('Pagination', Pagination)
app.component('TreeSelect', TreeSelect)
app.component('FileUpload', FileUpload)
app.component('ImageUpload', ImageUpload)
app.component('ImagePreview', ImagePreview)
app.component('RightToolbar', RightToolbar)
app.component('Editor', Editor)
//사용법은 따로 import를 하지 않아도 되고(main.js에서 했으므로), 그냥 넣어서 사용하면 됨.
예)
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
예2)
--> 사용(호출)하는 곳의 코드
<global-button @click="buttonCokeced">눌러주세요</global-button>
methods : {
buttonCliked() {
console.log('button cliked...")
}
}
--> global component 를 만드는 부분
<template>
<button @click="$emit('click', $event)">
<slot />
</button>
</template>
<script>
export default {
}
</script>
<style scoped>
button {
color: brown;
}
</style>
--------------------------------------------------------------------------------------------------------------------------------------------
라우팅 (클라이언트 사이드 라우팅)
main.js에서 router 라이브러리를 사용해야 함. 해당 코드는 다음과 같다.
app.use(router) <---------------use는 plugin 을 사용하겠다는 개념임.
src/router/index.js 에다가 라우팅 경로를 잡아준다. 여기에 설정된 경로들은 서버측으로 전달되는게 아니라, 클라이언트측 라우팅을 하도록 만든다.
<script setup>
import { useRoute, useRouter } from 'vue-router'
const route = useRoute();
const router = useRouter();
const { params, query } = route
const { path } = params
router.replace({ path: '/' + path, query })
</script>
src/router/index.js 에서 -> src/view/redirect/index.vue 로 전달하는 구조?
src/router/index.js 내용
------------------------------------------------------------------------------------------
import { createWebHistory, createRouter } from 'vue-router'
/* Layout */
import Layout from '@/layout'
/**
* Note: 라우팅 구성 항목
*
* hidden: true // true로 설정하면 경로가 401, 로그인 등과 같은 페이지 또는 /edit/1과 같은 일부 편집 페이지의 사이드바에 더 이상 표시되지 않습니다
* alwaysShow: true // 경로 아래에 자식이 선언한 경로가 1개 이상 있으면 자동으로 구성 요소 페이지와 같은 중첩 패턴이 됩니다
* // 한 가지만 있는 경우 장군 서브루트가 존재 사이드바에 루트 경로로 표시됩니다--예를 들어 부트스트랩 페이지
* // 아래 라우팅을 무시하고 싶다면 children 선언 수는 루트 경로를 보여줍니다.
* // 당신은 설정할 수 있습니다 alwaysShow: true, 이렇게 하면 이전에 정의된 규칙을 무시하고 항상 루트 경로를 표시합니다.
* redirect: noRedirect // 설정시 noRedirect 탐색경로 내비게이션에서 해당 경로를 클릭할 수 없는 경우
* name:'router-name' // 경로 이름을 설정하고, 그렇지 않은 경우에는 반드시 입력하십시오.<keep-alive>다양한 문제가 발생할 수 있습니다
* query: '{"id": 1, "name": "ry"}' // 액세스 경로에 대한 기본 전달 매개변수
* roles: ['admin', 'common'] // 라우팅에 액세스할 수 있는 권한
* permissions: ['a:a:a', 'b:b:b'] // 접근 경로 메뉴권한
* meta : {
noCache: true // true로 설정하면 그렇지 않습니다. <keep-alive> 캐시(기본값은 false)
title: 'title' // 이 경로의 사이드바 및 탐색경로에 표시되는 이름을 설정합니다.
icon: 'svg-name' // 경로의 아이콘과 해당 경로를 설정합니다. src/assets/icons/svg
breadcrumb: false // false로 설정하면, 탐색경로에 존재breadcrumb 표시 않을 것이다
activeMenu: '/system/user' // 경로가 이 속성을 설정하면 해당 사이드바가 강조 표시됩니다.
}
*/
// 공개 경로
export const constantRoutes = [
{
path: '/redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue')
<------ lazy-loading. 최초 바이트 도달시간(서버 응답 시간...TTFB, Time to First Byte)
<------ 의미는 이 페이지는 나중에 실제 로딩될때, 서버에서 가져와라라는 의미.
}
]
},
{
path: '/login',
component: () => import('@/views/login'),
hidden: true
},
{
path: '/register',
component: () => import('@/views/register'),
hidden: true
},
{
path: "/:pathMatch(.*)*",
component: () => import('@/views/error/404'),
hidden: true
},
{
path: '/401',
component: () => import('@/views/error/401'),
hidden: true
},
{
path: '',
component: Layout,
redirect: '/index',
children: [
{
path: '/index',
component: () => import('@/views/dashboard/index'),
name: 'Index',
meta: { title: 'home', icon: 'dashboard', affix: true }
}
]
},
{
path: '/user',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'profile',
component: () => import('@/views/system/user/profile/index'),
name: 'Profile',
meta: { title: '프로필', icon: 'user' }
}
]
}
]
// 동태 경로, 사용자 권한에 따라 동태 to load
export const dynamicRoutes = [
{
path: '/system/user-auth',
component: Layout,
hidden: true,
permissions: ['system:user:edit'],
children: [
{
path: 'role/:userId(\\d+)',
component: () => import('@/views/system/user/authRole'),
name: 'AuthRole',
meta: { title: '역할 할당', activeMenu: '/system/user' }
}
]
},
{
path: '/system/role-auth',
component: Layout,
hidden: true,
permissions: ['system:role:edit'],
children: [
{
path: 'user/:roleId(\\d+)',
component: () => import('@/views/system/role/authUser'),
name: 'AuthUser',
meta: { title: '사용자 할당', activeMenu: '/system/role' }
}
]
},
{
path: '/system/dict-data',
component: Layout,
hidden: true,
permissions: ['system:dict:list'],
children: [
{
path: 'index/:dictId(\\d+)',
component: () => import('@/views/system/dict/data'),
name: 'Data',
meta: { title: '사전 데이터', activeMenu: '/system/dict' }
}
]
},
{
path: '/monitor/job-log',
component: Layout,
hidden: true,
permissions: ['monitor:job:list'],
children: [
{
path: 'index/:jobId(\\d+)',
component: () => import('@/views/monitor/job/log'),
name: 'JobLog',
meta: { title: '로그 스케줄링', activeMenu: '/monitor/job' }
}
]
},
{
path: '/tool/gen-edit',
component: Layout,
hidden: true,
permissions: ['tool:gen:edit'],
children: [
{
path: 'index/:tableId(\\d+)',
component: () => import('@/views/tool/gen/editTable'),
name: 'GenEdit',
meta: { title: '빌드 구성 수정', activeMenu: '/tool/gen' }
}
]
}
]
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
},
});
export default router;
src/view/redirect/index.vue 내용
------------------------------------------------------------------------------------------
<template>
<div></div>
</template>
<script setup>
import { useRoute, useRouter } from 'vue-router'
const route = useRoute();
const router = useRouter();
const { params, query } = route
const { path } = params
router.replace({ path: '/' + path, query })
</script>
--------------------------------------------------------------------------------------------------------------------------------------------
router-link : 페이지 이동을 위한 링크를 생성
router-view : 현재 URL에 맞는 컴포넌트를 렌더링.
사용자 요청 URL에 따라 해당 최신 뷰를 로딩해주는 Functional 컴포넌트.
런타임 시 다이나믹하게 컨텐츠 렌더링.
하위 컴포넌트 렌더링, 라우팅 경로에 따라 컴포넌트 마운트/언마운트
App.vue에서....
<template>
<router-view />
</template>
src/layout/components/Navbar.vue 소스에서.....
<template #dropdown>
<el-dropdown-menu>
<router-link to="/user/profile">
<el-dropdown-item>프로필</el-dropdown-item>
</router-link>
<el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
<span>레이아웃 설정</span>
</el-dropdown-item>
<el-dropdown-item divided command="logout">
<span>로그아웃</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
--------------------------------------------------------------------------------------------------------------------------------------------
아래 2가지 경우의 차이점.
<router-link to="/user/profile">
<el-dropdown-item>프로필</el-dropdown-item>
</router-link>
<router-link class="link-type" :to="'/login'">기존 계정을 사용하여 로그인</router-link>
1. to 앞에 콜론이 없는 경우 (to="/user/profile")
- to의 값이 문자열(String)로 고정됩니다.
- 여기서는 "/user/profile"이 그대로 전달되며, 정적인 값을 의미합니다.
- 즉, 이 경우에는 /user/profile로 항상 동일한 경로로 이동합니다.
예시:
<router-link to="/user/profile">
<el-dropdown-item>프로필</el-dropdown-item>
</router-link>
- 이 코드는 단순히 고정된 경로(/user/profile)로 이동합니다.
2. to 앞에 콜론이 붙은 경우 (:to="'/login'")
- 바인딩된 값을 사용하며, 동적인 데이터를 표현할 수 있습니다.
- JavaScript 표현식이나 Vue의 데이터 속성을 통해 값이 할당됩니다.
- 콜론은 v-bind 디렉티브의 축약형입니다.
예시:
<router-link :to="'/login'">
기존 계정을 사용하여 로그인
</router-link>
- 이 경우, :to의 값은 JavaScript 표현식으로 평가됩니다.
- 예를 들어, '${baseUrl}/login'과 같은 동적 값을 전달하거나, 컴포넌트의 데이터 속성(this.somePath)을 바인딩할 수 있습니다.
정리
to="/user/profile" | 정적 값, 항상 문자열로 해석됨 ('/user/profile'로 고정). |
:to="'/login'" | 동적 값, Vue의 데이터나 JavaScript 표현식을 바인딩하여 값이 변동될 수 있음. |
즉, :(콜론)은 Vue.js에서 속성을 정적으로 사용하느냐, 동적으로 바인딩하느냐를 결정짓는 중요한 요소임.
--------------------------------------------------------------------------------------------------------------------------------------------
메뉴 링크 설정, Navigation links
프로그래머틱하게 접근법
function back() {
if (proxy.$route.query.noGoBack) {
proxy.$router.push({ path: "/" });
} else {
proxy.$router.go(-1); <--- 한칸 뒤로 가기
}
}
잘못된 경로 요청시...아래 코드를 쓰면, 서버로 요청을 보내지 않고 404 Not found 페이지를 보여준다.
{
path: "/:pathMatch(.*)*",
component: () => import('@/views/error/404'),
hidden: true
},
--------------------------------------------------------------------------------------------------------------------------------------------
Navigation Guards
Navigatin 경로를 들어갈때, 권한이 있는지 체크.
--------------------------------------------------------------------------------------------------------------------------------------------
상태관리 (state management)
로컬 state management : 단순함. 많이지면 무거워짐.
글로벌에 데이터를 저장(global store)
- vuex 사용
app.use(store) <------------------------vuex 를 쓰겠다는 의미임.
src/store 라는 디렉토리가 생기고, index.js 파일에 다음 내용이 있음.
const store = createPinia()
export default store
'IT > VueJS' 카테고리의 다른 글
VueJS 배포 과정 (0) | 2025.03.24 |
---|---|
main.js에 import RightToolbar from '@/components/RightToolbar 문장이 있어. 여기서 @의 의미 (0) | 2025.03.11 |
vite 설치법 (0) | 2025.01.28 |
자식 컴포넌트로 데이터 보내기 (0) | 2022.08.25 |
Component 로 만들어 사용하기 (0) | 2022.08.25 |
댓글