jquery 2006
angular 1 2010
(library) react.js << (hybrid) vue.js << (framework) angular.js

sudo npm install -g @vue/cli @vue/cli-initvue create frontend
cd frontendnpm install -s axios vue-router@3 bootstrap bootstrap-vue lodashgit add/commitnpm run serve
npm run build<book>이라는 요소로 사용<template>
<div id="app">
<book/>
</div>
</template>
<script>
import Book from './components/Book.vue'
export default {
name: 'App',
components: {
Book,
}
}
</script>
<template>
<div id="book">
<p>title: {{ title }}</p>
<p>number: {{ number }}</p>
<p>book data: {{ bookData }}</p>
</div>
</template>
<script>
export default {
name: 'Book',
data: function () {
return {
title: 'Books',
number: 30,
bookData: [
{'author': '헤밍웨이', 'title': '노인과 바다'},
{'author': '피츠제럴드', 'title': '위대한 개츠비'},
],
status: true,
errorMessage: '',
}
},
}
</script>
module.exports = {
root: true,
parserOptions: {
parser: '@babel/eslint-parser'
},
env: {
browser: true,
node: true,
},
extends: [
'plugin:vue/essential',
'eslint:recommended'
],
// required to lint *.vue files
plugins: [
'vue'
],
// add your custom rules here
rules: {
'vue/multi-word-component-names': 'off'
}
}
<p>{{ '실행 ' + (status ? '중단' : '재개' }}</p>
-->
<p>status: {{ status }}</p>
<p>status message: {{ statusMessage }}</p>
data: function () {
return {
status: true,
}
},
computed: {
statusMessage: function () {
return '실행 ' + (this.status ? '중단' : '재개');
}
},
<p>error message: {{ errorMessage }}</p>
<b-input-group class="mt-3">
제목 입력: <b-form-input v-model="title"></b-form-input>
</b-input-group>
data: function () {
return {
errorMessage: '',
}
},
watch: {
title: function () {
this.errorMessage = '제목 변경됨';
}
},
import { BootstrapVue } from 'bootstrap-vue';
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap-vue/dist/bootstrap-vue.css';
Vue.use(BootstrapVue);
methods: {
getApiUrlPath: function () {
let pathPrefix = 'https://api.mydomain.com/myapp';
if (process.env.NODE_ENV === 'development') {
pathPrefix = 'http://127.0.0.1:5000';
}
return pathPrefix;
},
created: function () {
console.log("component created");
},
mounted: function () {
console.log("component mounted");
},

<div v-if="status">
<h1>작품 목록</h1>
<ul v-if="bookData">
</ul>
</div>
<div v-if="status">
<h1>작품 목록</h1>
<ul v-if="bookData">
<li v-for="item in bookData" :key="item.title">{{ item.title }} by {{ item.author }}</li>
</ul>
</div>
bookData: [
{'author': '헤밍웨이', 'title': '노인과 바다'},
{'author': '피츠제럴드', 'title': '위대한 개츠비'},
],
<a v-on:click="doSomething"> ... </a>
@이벤트명으로 줄여서 사용할 것
<button @click="run()">실행</button>
<div class="memo" v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
isActive: true,
hasError: false,
<dlv class="memo active">
</div>
<div class="memo" v-bind:style="{ color: activeColor }">
메모
</div>
activeColor: 'red',

v-model="title" <b-input-group class="mt-3">
제목 입력:
<b-form-input v-model="title"></b-form-input>
</b-input-group>
data: function () {
return {
title: 'Books',
}
}
<div v-bind:id="dynamicId"></div>
<my-button v-bind:class="dynamicClass"></my-button>
:variable="value"로 줄여서 사용할 것#!/usr/bin/env python
from flask import Flask, jsonify, request, render_template
from flask_cors import CORS
app = Flask(__name__, static_folder="../frontend/dist", template_folder="../frontend/dist")
CORS(app, resources={r"/*": {"origins": "*"}})
@app.route("/books")
def books():
data = {"status": False, "message": "실패", "books": []}
data["status"] = True
data["message"] = "성공"
data["books"] = [{"author": "필립 K. 딕", "title": "안드로이드는 전기양의 꿈을 꾸는가"},
{"author": "아서 클라크", "title": "유년기의 끝"},
{"author": "H.G. 웰즈", "title": "우주 전쟁"}]
print(data)
return data
if __name__ == "__main__":
app.run(host="0.0.0.0")
flask --debug runnpm install axiosimport axios from 'axios';
methods: {
getBooks() {
const url = this.getApiUrlPath() + "/books";
axios.get(url)
.then((res) => {
if (!res.data.status) {
this.errorMessage = res.data.message;
} else {
this.bookData = res.data['books'];
}
})
.catch((error) => {
this.errorMessage = error;
})
},
}
에러 핸들링이 두 군데에서 처리되고 있음에 주목할 것
.catch((error) => { }
if (res.data.status === 'failure') { }
getBooks()를 mounted()에서 호출하거나, 이벤트 리스너로 연결해볼 것
mounted: function() {
this.getBooks();
},
<button @click="bookButtonClicked">책 불러오기</button>
methods: {
bookButtonClicked() {
return this.getBooks();
},
}
@app.route("/groups/<group_name>/feeds/<feed_name>", methods=["DELETE", "POST", "GET"])
def get_feed_info(group_name, feed_name):
response_object = {"status": "failure"}
if request.method == "GET":
...
elif request.method == "DELETE":
...
axios
.get(`/groups/${groupName}/feeds/${feedName}`)
...
axios
.delete(`/groups/${groupName}/feeds/${feedName}`)
...
env FLASK_ENV=developent flask runnpm run serve
privateexport default {
name: 'MySubComponent',
props: {
variant: {
type: String,
default: 'info'
},
initialIcon: {
type: Array,
default: function () {
return [];
}
},
},
}
props에서 정의된 property로는 variant와 initialIcon이 있으며 이 property들의 타입과 초기값을 지정함
ParentComponent.vue
<my-sub-component
:initial-icon="['fas', 'search']"
variant="dark"/>
두 개의 버튼에서 발생하는 클릭 수를 합해서 보여주는 예제
<button v-on:click="incrementCounter">
{{ counter }}
</button>
export default {
name: "ButtonCounter",
data: function () {
return {
counter: 0
}
},
methods: {
incrementCounter: function () {
this.counter += 1
this.$emit('increment')
}
},
}
<div id="counter-event-example">
<p>클릭 수 합계: {{ total }}</p>
버튼1: <button-counter v-on:increment="incrementTotal"></button-counter>
버튼2: <button-counter v-on:increment="incrementTotal"></button-counter>
</div>
import ButtonCounter from './ButtonCounter.vue';
export default {
name: "TotalCounter",
components: {
ButtonCounter
},
data: function () {
return {
total: 0
}
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
}
<template>
<div id="app">
<total-counter></total-counter>
</div>
</template>
<script>
import TotalCounter from './TotalCounter.vue'
export default {
name: 'Book',
components: {
TotalCounter,
}
}
</script>
this.$emit('decrement', e.target.value)
<parent-component @decrement="decrementWithValue"/>
decrementWithValue(value) {
this.total -= value;
}
<button ref="saveFeedButton1" @click="saveFeedButtonClicked">Save</button>
methods: {
saveFeedButtonClicked: function() {
this.$refs.saveFeedButton1.innerText = "Saved";
}
}
npm install vue-router@3<router-link>로 지정<router-view>에 렌더링됨<template>
<div id="result">
결과 내용
</div>
</template>
<script>
export default {
name: "Result"
}
</script>
<template>
<div id="mgmt">
관리 요소들
</div>
</template>
<script>
export default {
name: "Management"
}
</script>
<template>
<div id="app">
<router-link to="/"><h1>홈</h1></router-link>
<router-link to="/result"><h1>결과</h1></router-link>
<router-link to="/management"><h1>관리</h1></router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
components: {
}
}
</script>
<b-navbar>
<b-navbar-nav>
<b-nav-item>
<router-link to="/">
<b-nav-text>홈</b-nav-text>
</router-link>
</b-nav-item>
<b-nav-item>
<router-link to="/result">
<b-nav-text>결과</b-nav-text>
</router-link>
</b-nav-item>
<b-nav-item>
<router-link to="/management">
<b-nav-text>관리</b-nav-text>
</router-link>
</b-nav-item>
</b-navbar-nav>
</b-navbar>
<router-view></router-view>
import Vue from 'vue';
import Router from 'vue-router';
import Book from '../components/Book.vue';
import Result from '../components/Result.vue';
import Management from '../components/Management.vue';
Vue.use(Router);
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'Home',
component: Book
},
{
path: '/result',
name: 'Result',
component: Result
},
{
path: '/management',
name: 'Management',
component: Management,
children: [
{
path: ':group/:feed',
component: Management
}
]
}
]
})
import router from './router';
new Vue({
render: h => h(App),
router,
}).$mount('#app')
function(obj) { }와 같은 callback은 쓰지 말것
(obj) => { }을 대신 사용할 것axios
.get(url)
.then(function (res) {
this.feeds = res.data;
})
.catch(function (res) {
console.error(err);
});
-->
axios
.get(url)
.then((res) => {
this.feeds = res.data;
})
.catch((err) => {
console.error(err);
});
npm install -s lodashimport _ from 'lodash';newData = _.filter(data, (o) => {
return o['sum'] > 100;
}).map(function (o) {
o['extra'] = {'name': 'John', 'age': 14};
o['job'] = 'Typist';
return o;
});
import _ from 'lodash';
getBooks() {
const url = this.getApiUrlPath() + "/books";
axios.get(url)
.then((res) => {
if (!res.data.status) {
this.errorMessage = res.data.message;
} else {
this.bookData = res.data['books'].filter((o) => {
return o['author'] !== '아서 클라크';
}).map((o) => {
o['date'] = new Date();
return o;
});
}
})
.catch((error) => {
this.errorMessage = error;
})
},