TIL 22-05-29

thisisyjin·2022년 5월 29일
0

TIL 📝

목록 보기
55/113

Vue.js

Today I Learned ... Vue.js

🙋‍♂️ Reference Book


DAY 02 - 220529

    1. 컴포넌트

컴포넌트

컴포넌트란?

View, Data, Code의 세트.
-> HTML 코드, 자바스크립트 코드, 데이터가 존재함.

가장 큰 특징은 재사용 가능하다는 점.
다른 컴포넌트에서 임포트하여 사용할 수 있음.

컴포넌트는 그 페이지 자체일수도 있고, 페이지 내 특정 기능 요소일수도 있음.

src 폴더 내에 components 폴더에는 각 기능을 하는 요소가 들어있고,
views 폴더에는 페이지 기능인 vue 컴포넌트 파일이 들어있다.

-> 둘다 내부적으로 동일한 구조를 갖게 된다.

❗️ 폴더를 구분해서 사용하는 것이 효율적임.

컴포넌트 구조

  • 기본 프로퍼티
  1. name
  2. components
  3. data
  4. computed

+) 라이프사이클 메서드 (setup / created / mounted / unmounted / methods)


기본 구조

<template>
  <div></div>
</template>

<script>
export default {
  name: '',
  components: {}, // 다른 컴포넌트 사용시 컴포넌트 임포트 후 배열로 저장함
  data() {
    return {
      sampleData: '',
    };
  },
  setup() {},
  created() {},
  mounted() {},
  unmounted() {},
  methods: {},
};
</script>

📌 프로퍼티

data : html, js 코드에서 전역 변수로 사용하기 위한 데이터. (this를 통해 접근해야 함)
데이터 바인딩을 통해 해당하는 html과 js간의 양방향 통신이 가능하게 함.

methods : 컴포넌트 내에서 사용할 메서드를 정의함. (this를 통해 접근해야 함)

📌메서드

setup : 컴포지션 API를 구현
created : 컴포넌트 생성시
mounted : html 코드가 렌더링된 직후
unmounted : 컴포넌트를 빠져나갈 시


스니펫 설정

  • snippet을 이용하면 컴포넌트 구조를 한번에 불러올 수 있어 편리함.
{
  "Generate Basic Vue Code": {
    "prefix": "vue-start",
    "body": [
      "<template>",
      "</template>",
      "",
      "<script>",
      "export default {",
      "  name: '',",
      "  components: {},",
      "  data() {",
      "    return {",
      "      sampleData: '',",
      "    };",
      "  },",
      "  setup() {},",
      "  created() {},",
      "  mounted() {},",
      "  unmounted() {},",
      "  methods: {},",
      "};",
      "</script>",
      ""
    ],
    "description": "Generate Basic Vue Code"
  }
}

  • snippet이 잘 적용됨👍

LifeCycle Hooks

  • 모든 컴포넌트는 생성시 초기화 단계를 거침.
    -> 데이터 변경감지 설정, 템플릿 컴파일, DOM에 마운트 (+업데이트) 등.

제일 먼저 보여줘야 하는 부분은 created()에 정의하여 서버로부터 미리 받아오고,
이후에 받아도 되는 부분은 mounted()에 정의하여 적절히 분배하면 좋음.
-> 로딩속도가 개선됨.

라이프사이클 Hooks 종류

  • 가장 많이 사용되는 것은 setup, created, mounted, unmounted, updated 등

✅ 주의
모든 라이프사이클 훅은 자동으로 this 컨텍스트가 인스턴스에 바인딩되어 있으므로,
data, computed 및 methods 속성에 접근할 수 있음.
즉, 화살표 함수를 사용해서 라이프사이클 메소드를 정의하면 안됨. (this 바인딩 때문)


데이터 바인딩

  • Angular과 마찬가지로 양방향 데이터 바인딩을 지원함. (Two-way data binding)
    cf> React는 단방향 데이터 바인딩만 지원.

🙋‍♂️ 양방향 데이터 바인딩 이란?

  • Model에서 데이터를 정의한 후 View와 연결시
    둘중 한쪽에 변경이 일어났을 때 자동으로 한쪽에 반영됨.
  • 실제 프로젝트 내에서 서버로부터 받아온 데이터를 바인딩 하는 경우
  1. 데이터가 html 태그의 InnerText로 바인딩되는 경우
  2. html의 속성(attribute)로 바인딩되는 경우
  3. Html form 태그의 value에 바인딩되는 경우
  4. 다중 데이터가 html의 다중 element를 생성하기 위해 바인딩되는 경우

데이터 바인딩 예시

  1. 컴포넌트 생성

src/views/DataBinding.vue 생성

<template>
  <h1>Hello, {{ title }}</h1>
</template>

<script>
export default {
  data() {
    return {
      title: 'Yjin',
    };
  },
};
</script>

data 프로퍼티에 정의된 title이 template의 {{title}} 에 바인딩 되는 구조
-> data에 정의되는 데이터는 이중 중괄호 {{ }} 로 html에 데이터 바인딩 할 수 있음.

  1. 라우터에 등록

router/index.js 수정

import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '../views/HomeView.vue';
import DataBinding from '../views/DataBinding.vue';

const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView,
  },
  {
    path: '/about',
    name: 'about',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () =>
      import(/* webpackChunkName: "about" */ '../views/AboutView.vue'),
  },
  {
    path: '/databinding',
    name: 'DataBinding',
    component: DataBinding,
  },
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

export default router;
  1. App.vue 수정
<template>
  <nav>
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link> |
    <router-link to="/databinding">Data Binding</router-link>
  </nav>
  <router-view />
</template>

RESULT

data 프로퍼티의 title에 설정한 'Yjin'이 잘 반영되어 있음을 알 수 있다.


데이터 바인딩 형식

✅ 디렉티브(directive) 란?

  • HTML 태그 안에 들어가는 하나의 속성으로, 엘리먼트에게 동작을 지시하는 지시문임.

문자열 데이터 바인딩

  • 이중 중괄호 {{ }}
<h1>hello, {{title}}</h1>

raw HTML 데이터 바인딩

  • ❗️이중 중괄호로 바인딩시 string으로 인식됨.
  • v-html 디렉티브 사용
<template>
  <div>{{ htmlString }}</div>
  <div v-html="htmlString"></div>
</template>

<script>
export default {
  data() {
    return {
      htmlString: '<p style="color:red">Here is HTML string</p>',
    };
  },
};
</script>

-> 주의: <div v-html="htmlString"> 에서 " " 안에 넣으면 된다. { } 아님.


form 입력 데이터 바인딩

  • Form Element에서 v-model 디렉티브를 사용하여 데이터바인딩.
  • v-model은 서로 다른 속성을 사용하고, 서로 다른 입력 요소에 대해 서로 다른 이벤트를 전송.

input type=text

  • input의 value 속성에 텍스트가 저장됨.
  • v-model은 자동으로 value 속성을 사용하게 됨.
<template>
  <div>
    <input type="text" v-model="valueModel" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      valueModel: 'Lee Yjin',
    };
  },
};
</script>

-> 사용자가 텍스트를 입력하면 data.valueModel에 자동으로 입력한 텍스트가 저장이 된다.
-> 양방향 데이터 바인딩 때문.

리액트에서는 onChange 이벤트에 setState를 해서 직접 바꿔주어야 했음. (단방향 데이터바인딩)


input type=number

문자가 아닌 숫자를 바로 처리할 수 있도록 v-model.number 디렉티브 사용 가능.

<template>
  <div>
    <input type="number" v-model.number="numberModel" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      numberModel: 123,
    };
  },
};
</script>


Textarea

textarea 태그도 <textarea>{{ message }}</textarea> 가 아닌
v-model 디렉티브를 사용해야 한다!

<template>
  <div>
    <textarea v-model="message"></textarea>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'text를 입력할 수 있는 태그입니다.',
    };
  },
};
</script>


select

v-model은 내부적으로 select의 value 속성을 사용하여 양방향 데이터바인딩.

<template>
  <div>
    <select v-model="city">
      <option value="01">서울</option>
      <option value="02">부산</option>
      <option value="03">제주</option>
    </select>
  </div>
</template>

<script>
export default {
  data() {
    return {
      city: '03',
    };
  },
};
</script>


input type=checkbox

v-model은 내부적으로 체크박스의 checked 속성을 사용함 (true/false)

<template>
  <div>
    <label><input type="checkbox" v-model="checked" />{{ checked }}</label>
  </div>
</template>

<script>
export default {
  data() {
    return {
      checked: true,
    };
  },
};
</script>

  • 자동으로 Data(checked)가 바뀜

+) true-value, false-value를 설정해서 체크/체크해제시 기본값 설정 가능.

++) 여러개의 체크박스가 있다면 배열을 이용하여 데이터 바인딩 가능.
-> 한번에 처리 가능!

<template>
  <div>
    <label><input type="checkbox" v-model="checked" value="사과" />사과</label>
    <label><input type="checkbox" v-model="checked" value="" /></label>
    <label><input type="checkbox" v-model="checked" value="포도" />포도</label>
    <label><input type="checkbox" v-model="checked" value="바나나" />바나나</label>
    <br />
    <span>좋아하는 과일: {{ checked }} </span>
  </div>
</template>

<script>
export default {
  data() {
    return {
      checked: [],
    };
  },
};
</script>


input type=radio

마찬가지로 v-model은 checked 속성과 바인딩이 이루어짐.
-> value 속성에 데이터 바인딩을 하려면 v-bind:value를 사용해야 함.

<template>
  <div>
    <label><input type="radio" v-model="picked" v-bind:value="radioValue1" />사과</label>
    <label><input type="radio" v-model="picked" v-bind:value="radioValue2" /></label>
    <label><input type="radio" v-model="picked" v-bind:value="radioValue3" />포도</label>
    <label><input type="radio" v-model="picked" v-bind:value="radioValue4" />바나나</label>
    <br />
    <span>좋아하는 과일: {{ picked }} </span>
  </div>
</template>

<script>
export default {
  data() {
    return {
      picked: '',
      radioValue1: '사과',
      radioValue2: '배',
      radioValue3: '포도',
      radioValue4: '바나나',
    };
  },
};
</script>


속성

= Attribute.

  • value를 제외한 속성에 데이터를 바인딩하기 위해서는 v-bind 디렉티브를 사용함.
  • v-bind 생략하여 :로도 사용 가능.

img src속성

  • img 태그의 src에 이미지 주소를 바인딩 해야하는 경우
<template>
  <div>
    <img v-bind:src="imgSrc" alt="image" />
   
  </div>
</template>

<script>
export default {
  data() {
    return {
      imgSrc:
        'https://velog.velcdn.com/images/thisisyjin/profile/194fbad3-2c57-4ca5-89ad-7008fd5a455a/image.jpg',
    };
  },
};
</script>

v-bind를 생략하고 아래와 같이 콜론(:)만 써줘도 됨.

<img :src="imgSrc" alt="image" />

button disabled

  • 버튼 객체의 disabled 속성 (true/false)
  • disabled 가 true면 버튼은 비활성화 되고, 클릭을 해도 이벤트 발생 X.
  • v-bind:disabled 에 조건식을 적어주면 된다.
<template>
  <div>
    <input type="text" v-model="textValue" />
    <button type="button" v-bind:disabled="textValue === ''">Click</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      textValue: '',
    };
  },
};
</script>


클래스 바인딩

  • 반드시 적용해야 하는 클래스는 html에서 클래스명을 입력.
  • 조건에 따라 클래스 바인딩을 할때는 v-bind:class로 입력해줌.
  • 객체 형태로 -> key = 클래스명 / value = data값( true면 클래스 부여)

ex>

<span v-bind:class="{
	'active': isActive, 'error': isError
}">

<template>
  <div>
    <div
      class="container"
      v-bind:class="{
        active: isActive,
        'text-red': hasError,
      }"
    >
      Class Binding
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isActive: true,
      hasError: false,
    };
  },
};
</script>

<style scoped>
.container {
  width: 100%;
  height: 200px;
}
.active {
  background-color: yellow;
  font-weight: bold;
}
.text-red {
  color: red;
}
</style>

+) 응용

<template>
  <div>
    <div
      class="container"
      v-bind:class="{
        active: isActive,
        'text-red': hasError,
      }"
    >
      Class Binding
    </div>
    <label> <input type="checkbox" v-model="hasError" /> 에러 생성</label>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isActive: true,
      hasError: false,
    };
  },
};
</script>

<style scoped>
.container {
  width: 100%;
  height: 200px;
}
.active {
  background-color: yellow;
  font-weight: bold;
}

.text-red {
  color: red;
}
</style>

-> checkbox의 v-model(=checked)에 hasError을 바인딩하고,
체크하면 hasError가 true가 되게 함.

✅ 참고 - style scoped
scoped 속성은 이 컴포넌트 안에서만 유효하는 style 속성


(배열) 클래스 바인딩

  • 배열을 사용해서 클래스를 바인딩 할 수도 있음.
<template>
  <div>
    <div class="container" v-bind:class="[activeClass, errorClass]">
      Class Binding
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeClass: 'active',
      errorClass: 'text-red',
    };
  },
};
</script>
  • 배열 사용시 조건부로 클래스 바인딩은 할 수 없음.

인라인 스타일 바인딩

  • 스타일을 나타내는 데이터를 객체로 선언하여 v-bind:style에 바인딩.
<template>
  <div>
    <div :style="styleObj">Class Binding</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      styleObj: {
        color: 'blue',
        fontSize: '32px',
        textDecoration: 'underline',
      },
    };
  },
};
</script>

+) 배열로 스타일링 바인딩 가능

여러 스타일 객체를 배열에 담아 바인딩 할수도 있음.

<template>
  <div>
    <div :style="[basicStyle, addStyle]">Class Binding</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      basicStyle: 'color:blue;width:100%;height:200px;',
      addStyle: 'font-weight:bold;background-color:lightyellow',
    };
  },
};
</script>

✅ 배열로 여러개의 스타일 객체를 적용할 때는 반드시 css 코드로 string으로 작성해야함!
cf> 하나 적용시 - Js 코드로 (camelCase + 객체)로 작성.


리스트 랜더링

  • option, tr, li 태그 등 반복되는 객체 처리시 - 다중 데이터 처리 필요.
  • v-for 디렉티브를 이용하여 바인딩 가능
    -> 배열에 있는 아이템 수만큼 Html 태그를 반복적으로 렌더링. (map과 유사)
<template>
  <div>
    <table>
      <thead>
        <tr>
          <th>이름</th>
          <th>나이</th>
          <th>성별</th>
        </tr>
      </thead>
      <tbody>
        <tr :key="i" v-for="(person, i) in personList">
          <td>{{ person.person_name }}</td>
          <td>{{ person.age }}</td>
          <td>{{ person.gender }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      personList: [
        { person_name: 'yjin', age: 23, gender: 'female' },
        { person_name: 'yeon', age: 42, gender: 'male' },
        { person_name: 'kimj', age: 31, gender: 'male' },
        { person_name: 'thisis', age: 21, gender: 'female' },
      ],
    };
  },
};
</script>
  • js의 forEach 처럼 작동함.
  • 반드시 각 tr에는 key를 지정해줘야 함! (react에서 map을 할때처럼)
  • v-for="(item, index) in 배열명" 과 같이 작성해줌.

+) 참고 - 테이블 스타일링

<style scoped>
table {
  margin: 0 auto;
  border-collapse: collapse;
}

td,
th {
  border: 1px solid black;
}
td {
  padding: 8px 16px;
}
</style>

조건 렌더링

-> v-if / v-show

  • 조건에 따라 렌더링 하는 방법
  1. v-if 디렉티브 (+ v-else)
  2. v-show 디렉티브

v-if

<h1 v-if="h1Render">안녕하세요</h1>

-> h1Render 라는 값이 true면 렌더링 된다.
만약 false라면 else, else-if 를 이용해서 렌더링.

v-else / v-else-if

<template>
  <div>
    <h1 v-if="type === 'A'">A</h1>
    <h1 v-else-if="type === 'B'">B</h1>
    <h1 v-else>C</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      type: 'A',
    };
  },
};
</script>

+) 응용 ver.

<template>
  <div>
    <label><input type="radio" v-bind:value="'A'" v-model="type" /> A</label>
    <label><input type="radio" v-bind:value="'B'" v-model="type" /> B</label>
    <label><input type="radio" v-bind:value="'C'" v-model="type" /> C</label>
    <h1 v-if="type === 'A'">A</h1>
    <h1 v-else-if="type === 'B'">B</h1>
    <h1 v-else-if="type === 'C'">C</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      type: '',
    };
  },
};
</script>

v-show

<h1 v-show="h1Show">안녕하세요</h1>
  • 마찬가지로 h1Show라는 값이 true면 렌더링 된다.
  • 형식은 v-if와 같지만, 내부적으로 렌더링 되는 방식이 다르다.
v-ifv-show
조건 만족시 html 블록 생성 / 불만족시 삭제조건 만족 여부와 관계없이 html 블록이 생성됨.

= 조건 만족시 display(css)를 이용하여 호면에 보이고, 조건 불만족시 숨기게 처리함.

🙋‍♂️ TIP - 언제 어떤 것을 사용할지?
v-show 는 자주 토글이 일어날때 사용한다.
(html 을 삭제/생성 하는것보다 css로 사라졌다 보였다 하는것이 훨씬 효율적이기 때문.


이벤트 처리

-> v-on 디렉티브
-> 심볼 @로도 사용 가능.

클릭 이벤트

  • v-on:click 또는 @click 을 사용함.
    -> 참고로, @ 심볼 사용시 콜론(:)도 생략하면 됨
<template>
  <div>
    <button @click="increaseCounter">Add +1</button>
    <p>The Counter is {{ counter }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      counter: 0,
    };
  },
  methods: {
    increaseCounter() {
      this.counter = this.counter + 1;
    },
  },
};
</script>
  • 상테 (정적변수)는 data() 의 return 부분에 넣어주고,
  • 메서드는 methods 객체에 넣어준다.
    -> 이때, 메서드에서는 data 사용 가능. (this 바인딩 때문에 화살표 함수는 ❌)

+) 메서드로 인자 전달도 가능

<template>
  <div>
    <button @click="setCount(7)">set 7</button>
    <p>The Counter is {{ counter }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      counter: 0,
    };
  },
  methods: {
    setCount(number) {
      this.counter = number;
    },
  },
};
</script>

++) 여러개의 함수 호출하려면?
-> 콤마(,)로 구분하여 호출함.

<template>
  <div>
    <button @click="one(), two()">Click!</button>
  </div>
</template>

<script>
export default {
  methods: {
    one() {
      alert('one');
    },
    two() {
      alert('two');
    },
  },
};
</script>

change 이벤트

  • select 태그에서 가장 많이 사용함.
  • 옵션을 바꿀 때 마다 change 이벤트 발생.

참고 - react에서는 input에서 onChange 이벤트를 등록해주었지만,
양방향 데이터 바인딩을 지원하는 Vue에서는 그럴 필요가 없어짐.


key 이벤트

  • 사용자가 키보드 입력시 발생하는 이벤트.
  • 엔터키 클릭시 폼이 제출되는 것 처럼 (예> 검색엔진 검색창)
<input type="text" @keyup.enter="submit">

-> 엔터키 눌렀을 때.

여러 키를 동싱 입력하거나 클릭+키입력을 동시에 했을때를 이벤트 등록할수도 있음.

<!-- Alt + Enter -->
<input type="text" @keyup.alt.enter="submit">

<!-- Ctrl + 클릭 -->
<input type="text" @click.ctrl="submit">

computed, watch

  • Vue 인스턴스 내의 정의된 데이터 값이 변화하는지 감시하고,
    변경될 때 마다 정의된 함수가 실행됨.

computed

  • vue 인스턴스 내에 정의된 데이터와 연관된 또하나의 데이터를 정의하여 사용할 수 있게 해줌.
<template>
  <div>
    <h1>{{ fullName }}</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstName: 'Yjin',
      lastName: 'Lee',
    };
  },
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName;
    },
  },
};
</script>

  • 데이터 값에 변경이 일어나는지 감시함.
  • 위에서 fullName(=computed) 에서 사용된 this.firstName,
    this.lastName 중에 하나라도 변경되면 fullName 함수가 실행되고, 값이 갱신됨.

-> 리액트의 useCallback(useMemo)와 비슷함.

❗️ computed는 함수이자 동시에 Vue 인스턴스의 데이터임.
-> 따라서 여러번 사용하더라도 함수 연산은 한번밖에 일어나지 않음.
+) 함수는 this.firstName, this.lastName이 변화했는지 감지할 수 없음.


watch

<template>
  <div>
    <h1>이름 : {{ fullName }}</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstName: 'Yjin',
      lastName: 'Lee',
      fullName: '',
    };
  },
  watch: {
    firstName() {
      this.fullName = this.firstName + ' ' + this.lastName;
    },
    lastName() {
      this.fullName = this.firstName + ' ' + this.lastName;
    },
  },
};
</script>


-> 초기 할당된 값이 변경이 일어나야만 watch가 실행됨.


+) 수정

<template>
  <div>
    <h1>이름 : {{ fullName }}</h1>
    <label>이름<input type="text" v-model="lastName" /></label>
    <label><input type="text" v-model="firstName" /></label>
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstName: 'Yjin',
      lastName: 'Lee',
      fullName: '',
    };
  },
  watch: {
    firstName() {
      this.fullName = this.firstName + ' ' + this.lastName;
    },
    lastName() {
      this.fullName = this.firstName + ' ' + this.lastName;
    },
  },
};
</script>

-> input을 추가하여 둘중 하나라도 값을 바꿔야지만 watch가 실행됨.


computedwatch
기존 데이터 기반으로 새로운 데이터를 활용하기 위해 사용데이터 값 하나만을 감시하기 위해 사용
데이터 변경이 없어도 처음에 실행됨실제 데이터 변경이 일어나기 전까지는 실행되지 않음.
profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글