Vue.js
프론트 엔드
- HTML, CSS , JavaScript
- Vue.js, React(facebook), Angular(google)
Vue.js
- 사용자 인터페이스를 만들기 위한 프로그레시브 프레임워크
- 현대적인 tool과 다양한 라비러리를 통해 SPA(Single page Aplication)를 완벽하게 지원
Evan You (2014)
- Angular 개발자 출신
- 학사 미술, 미술사 전공/ 석사 디자인 & 테크놀로지 전공
- 구글 Angular 보다 더 가볍고, 간편하게 사용할 수 있는 프레임워크를 만들기 위해 개발
SPA
단일 페이지 애플리케이션
현재 페이지를 동적으로 작성, 사용자와 소통하는 웹 애플리케이션
단일 페이지 구성, 처음 페이지만 받아오고 (HTML) 동적으로 DOM을 구성
- 새로고침 X
- 사용자 경험(UX)을 향상
- 트래픽 감소, 속도, 사용성, 반응성
동작원리 일부가 CSR을 따름
등장 배경
- 모바일 최적화에 대한 필요성
CSR (Client side Rendering)
- 최초 요청 시 서버에서 빈 문서를 응답 후 클라이언트에서 데이터를 요청해서 데이터를 받아 DOM을 렌더링하는 방식
- SSR보다 초기 전송되는 페이지 속도는 빠르지만, 서비스에서 필요한 데이터 클라이언트(브라우저)에서 추가로 요청하여 재구성해야기때문에 페이지 완료시점은 SSR보다 느림
- SPA가 사용하는 렌더링 방식
CSR단점
Server > HTML > JS
Vue가 JS를 다 실행해야하고 이 과정이 다끝나야 페이지를 띄움
// 빈페이지를 받아온다. JS에서 전부 다운받아 실행시켜서 완료해야지 사용자가 사용가능하다.
장점
서버와 클라이언트 간의 트래픽 감소
- 웹 애플리케이션에 필요한 모든 정적 리소스를 최초에 한번 다운로드
사용자 경험 향상
- 전체 페이지를 다시 렌더링하지 않고 변경되는 부분만을 갱신
단점
SEO(검색엔진 최적화) 문제가 발생할 수 있음
- 구글 엔진 , naver 등 검색결과 최상단에 올리기가 힘들다. (단, 강제하는 방법이있음)
SSR(반대되는 개념 Server side)
- 서버에서 사용자에게 보여줄 page를 미리 모두 구성해서 사용자에게 페이지를 보여줌
Server > 모두완료시킴 HTML > 보여줌 ( 매번 HTML을 받아야해서 느림)
장점
- 초기 로딩 속도가 빠르기 때문에 사용자가 컨텐츠를 빨리 볼 수 있음
- SEO(검색엔진 최적화)가 가능
단점
모든 요청에 새로고침이 되기 때문에 사용자 경험이 떨어짐
- 상대적으로 요청 횟수가 많아져 서버 부담이 커짐
SEO
search engine Optimization(검색 엔진 최적화)
웹 페이지 검색엔진이 자료를 수집하고 순위를 메기는 방식에 맞게
웹페이지를 구성해서 검색 결과의 상위에 노출될 수 있도록 하는 작업
인터넷 마케팅 방법중 하나
구글 등장이후 검색 엔진들이 컨텐츠의 신뢰도를 파악하는 기초 지표로 사용됨
- 다른 웹사이트에서 얼마나 이용됐나
- 얼마나 제목이 ?
SEO 문제 대응
Vue.js 또는 React 등의 SPA 프레임워크는 SSR을 지원하는 SEO 대응 기술이 존제
Vue.js 는 왜 사용해야 할까
- 페이지 규모 계속 커져, 데이터 늘어나고, 사용자랑 상 호작용 많이 이뤄짐
- Vanilla JS만으로 관리하기가 어렵다.
Django & Vue.js 코드 작성 순서
Django
- url > view > temlplate
Vue.js
- Data 로직 작성 > DOM작성
Vue.js
(https://kr.vuejs.org/v2/guide/index.html)
여기에 다있습니다.
# 개발버전
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue Quick Start</title>
</head>
<body>
<!-- 2. 선언적 렌더링 -->
<h2>선언적 렌더링</h2>
<div id="app">
<p>{{ message }}</p>
<p>{{ message }}</p>
<p>{{ message }}</p>
</div>
<!-- 3. 엘리멘트 속성 바인딩 , 데이터 바인딩 -->
<h2>Element 속성 바인딩</h2>
<div id="app-2">
<span v-bind:title="message">
내 위에 잠시 마우스를 올리면 동적으로 바인딩 된 title을 볼 수 있습니다!
</span>
</div>
<!-- 4. 조건 v-if="들어갈조건"-->
<h2>조건</h2>
<div id="app-3">
<p v-if="isVisible">이제 나를 볼 수 있어요</p>
</div>
<!-- 5. 반복 -->
<h2>반복</h2>
<div id="app-4">
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
<!-- 6. 사용자 입력 핸들링 -->
<h2>사용자 입력 핸들링</h2>
<div id="app-5">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">메시지 뒤집기</button>
</div>
<!-- 1. Vue CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// const pList = document.querySelectorAll('p')
// pList.forEach(p => {
// p.innerText = '새로운 메세지'
// })
// 2. 선언적 렌더링
var app = new Vue({
el: '#app',
data: {
message: '안녕하세요 Vue!'
}
})
// 3. 엘리먼트 속성 바인딩 , 데이터 바인딩
var app2 = new Vue({
el: '#app-2',
data: {
message: '너모재밌다'
}
})
// 4. 조건
var app3 = new Vue({
el: '#app-3',
data: {
isVisible: true
}
})
// 5. 반복
var app4 = new Vue({
el: '#app-4',
data: {
todos: [
{ text: 'JavaScript 배우기' },
{ text: 'Vue 배우기' },
{ text: '무언가 멋진 것을 만들기' }
]
}
})
// 6. 사용자 입력 핸들링
var app5 = new Vue({
el: '#app-5',
data: {
message: '안녕하세요! Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
</script>
</body>
</html>
Vue instance
하나의 Vue 인스탠스는 Vue 컴포넌트이다.
- Vue Instance === Vue Component
Options/DOM – ‘el’
- Vue 인스턴스의 데이터 객체
- Vue 앱의 데이터를 정의하는 곳
- v-bind, v-on과 같은 디렉티브에서 사용가능
Options/Data – ‘data’
- Vue 인스턴스에 추가할 메서드
- Vue template에서 interpolation을 통해 접근 가능
- v-on과 같은 디렉티브에서도 사용 가능 •
- Vue 객체 내 다른 함수에서 this 키워드를 통해 접근 가
this keyword in vue.js(중요)
- vue 인스턴스 자체를 가리킴(자기자신 객체를 가르침)
Template Syntax
렌더링 된 DOM을 기본 Vue 인스턴스의 데이터에 선언적으로 바인딩 할 수 있는 HTML 기반 템플릿 구문을 사용
- Interpolation
- Directiv
v-text
- 엘리먼트의 textContent를 업데이트
- 내부적으로 interpolation 문법이 v-text로 컴파일 됨
<div id = "app">
<p v-text="message"></p>
<p>{{ message }}</P>
<div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message:'Hello',
}
})
</script>
v-html
- 안녕!
- v-html 사용 절대 금지 Xss 공격을 받을수있다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<p v-html="coolMessage"></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data:{
coolMessage: '<h1>안녕!</h1>'
},
})
</script>
</body>
</html>
v-show
isLoggedIn 이 참이면 보이고 거짓이면 안보이고~
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<p v-show="isLoggedIn">보이나요</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data:{
isLoggedIn: true,
},
})
</script>
</body>
</html>
v-if, v-else-if, v-else
조건문으로 나타내깅~
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<p v-if="myType==='AB'">
AB입니다.
</p>
<p v-else-if="myType==='O'"> O입니다.</p>
<p v-else>사람이 아닙니다.</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
seen: true,
myType: 'O',
},
})
</script>
</body>
</html>
v-for
key 속성
v-if와 v-for 와 동시에 사용하지 말 것!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h2>String</h2>
<p v-for="(char, idx) in message" v-bind:key="idx">
{{ idx }} {{ char }}
</p>
<h2>Array</h2>
<p v-for="(todo, idx) in todos" v-bind:key="idx">
{{ todo.title }}
</p>
<h2>Object</h2>
<p v-for="(value, idx) in myObj" v-bind:key="idx">
{{ value }}
</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message:' Hello, Vue!',
todos: [
{ title:'점심 먹기'},
{ title: 'JS복습하기'},
],
myObj: {
name: 'Lee',
age: 100,
},
},
})
</script>
</body>
</html>
v-on(중요 많이 씌임)
v-on:clikc => @click 너무많이쓰여서
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 메서드 핸들러 -->
<button v-on:click="onClick">Show Message</button>
<button @click="onClick">Show Message</button>
<!-- 기본 동작 방지 (이벤트가 발동했을때)-->
<form @submit.prevent>
<button>submit</button>
</form>
<!-- 키 별칭을 이용한 키 입력 수식어 (때질때 발동)-->
<input type="text" @keyup.enter="onInput">
<input type="text" @keyup.enter="onInput2($event, '값')">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
message: 'Hello, Vue!',
},
methods: {
onClick: function() {
alert(this.message)
},
onInput: function(e) {
console.log(e.target.value)
},
onInput2: function(e, value) {
console.log(e, value)
},
},
})
</script>
</body>
</html>
v-bind
https://kr.vuejs.org/v2/guide/index.html
class와 속성의 bind
동적으로 클래스 바인딩을 주고싶다
HTML요소의 속성에 Vue의 상태 데이터를 값으로 할당
Object 형태로 사용하면 value가 true인 key가 class 바인딩 값으로 할당
약어
- : (콜론)
- v-bind:href => :href
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.active {
color: red;
}
.my-background-color {
background-color: yellow;
}
</style>
</head>
<body>
<div id="app">
<!-- 속성 바인딩 -->
<img v-bind:src="imgSrc" alt="">
<img :src="imgSrc" alt="">
<hr>
<!-- 클래스 바인딩 -->
<div :class="{ active:isRed }">클래스 바인딩</div>
<button @Click="toggleIsRed">toggle</button>
<div :class="[activeRed, myBackground]">클래스 바인딩2</div>
<hr>
<!-- 스타일 바인딩 -->
<ul>
<li
:style="{ fontSize: fontSize + 'px' }"
:class="{ active: todo.isActive }"
>
{{ todo.title }}
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data : {
imgSrc: 'data:image/jpeg;base64,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',
isRed: false,
activeRed: 'active',
myBackground: 'my-background-color',
todoList: [
{ id:1, title: '여진님 퀴즈에 답하기', isActive: false },
{ id:1, title: '보충 수업하기', isActive: true },
],
fontSize: '48px',
},
methods: {
toggleIsRed: function() {
this.isRed = !this.isRed
},
},
})
</script>
</body>
</html>
v-model
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 1. input = > data -->
<h1> input => data</h1>
<p>{{ message }}</p>
<input @input="onInput" type="text">
<!-- 2. input <=> data -->
<h1> input <=> data</h1>
<p>{{ message2 }}</p>
<input v-model="message2" type="text">
<!-- 3. CheckBox -->
<!-- input id와 label의 for 는 같아야한다. -->
<h1>CheckBox</h1>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message:'',
message2:'',
checked:false,
},
methods: {
onInput: function(event){
//console.log(event)
this.message = event.target.value
},
},
})
</script>
</body>
</html>
computed 와 methods 의 차이
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<p>{{ message }}</p>
<p>{{ reversedMessageComputed }}</p>
<p>{{ reversedMessageMethods() }}</p>
<input type="text" v-model="message">
<p>{{ count }}</p>
<button @click="onClick">증가!</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
count: 0,
message: '안녕하세요!'
},
computed: {
reversedMessageComputed: function() {
// 1. 캐싱됨
// 2. 반드시 리턴 필요
// 3. 종속 대상이 바뀔때만 호출
return this.message.split('').reverse().join('')
},
},
methods: {
reversedMessageMethods: function() {
this.count += 1
// ...
console.log('methods실행됨.')
return this.message.split('').reverse().join('')
},
onClick: function() {
this.count += 1
},
},
})
</script>
</body>
</html>
watch
filters
말그대로 필터
Lifecycle Hooks
태어났을떄 자동으로 호출되게하기
반드시 함수호출 ( 메소드랑 같은 라인에 적어주기)
'SSAFY > Django' 카테고리의 다른 글
[Django] Dog api(댕댕이 랜덤 추출기) html 만들기! (0) | 2021.05.03 |
---|---|
[Django] 게시판, 좋아요, 댓글, 계정 기능 종합 (0) | 2021.05.02 |
Dom 실무? (0) | 2021.04.29 |
[Django] Dom (0) | 2021.04.28 |
[Django] swagger (0) | 2021.04.27 |