[Vue.js] 뷰 기초

Vue.js 기본 구조

  • Vue 컴포넌트의 경우 세개의 구역으로 나누어져 있다.
  1. template: html부분이다. 이 안에 style태그와 script태그를 사용할 수 없다.
  2. script: 이 템플릿에서 사용할 스크립트 코드이다. import로 외부라이브러리를 가져올 수 있다.
  3. style: 이 템플릿에서 사용할 css이다. scoped속성을 부여하면 딱 이 스타일에서만 사용할 수 있다. 아무것도 없으면 자식 컴포넌트도 일괄 적용된다.

  • Vue.js는 (Value, Key) 순서이다.

  • this.$set(person1, 'name', 'John') 메소드로 vue가 객채 내 변화를 알아채고 재렌더링 시킴

  • vue는 객체 자체의 변화는 감지하지만 객체 내 속성, 배열 값 변화 등 객채 내 변화는 감지하지 못한다.

  • 배열의 크기 변경을 재렌더링 할 때, splice 메소드를 사용 ex) this.arr.splice(4) : arr배열의 크기를 4로 재렌더링

  • mousedown/mousedown 이벤트(모든 이벤트는 '@'로 대체 가능하다)에서

method:function(e){
   if(e.button==0 또는 1 또는 2) // 0: 왼쪽마우스, 2: 오른쪽마우스
}

<a> : anchor 앵커 태그 (리다이렉트 시키는 태그)

<a @click.prevent href="http://www.naver.com">naver링크</a> // click이벤트에 prevent를 쓰면 리다이렉트를 막는다.

이벤트

  • self : 자신을 직접 눌렀을때만 이벤트 발생
  • stop : 자신 이벤트까지만 실행 시키고 하위 이벤트는 중지
  • prevent
  • capture : 우선순위애 따라 이벤트 발생
  • once : 이벤트 한 번만 발생

  • props : 컴포넌트의 속성, 부모 컴포넌트가 자식 컴포넌트에게 전달할 수 있다.
  • template : 외부에서 이 컴포넌트를 호출할때 렌더링될 템플릿이다.
  • data : 컴포넌트 내부에서 쓰일 변수
  • methods : 컴포넌트 내부에서 쓰일 메소드
<!DOCTYPE html>
<html lang="kr">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <simple-component v-bind:initial-counter="counter"></simple-component> <!-- HTML 영역 -->
</div>
</body>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<script> // Vue.js 영역

    // 글로벌 컴포넌트
    Vue.component('simple-component', { // Vue.component([컴포넌트명],[컴포넌트정보]) / simple-component는 해당 Vue 컴포넌트의 이름
        props: ['initialCounter'], // props만 컴포넌트끼리의 데이터 전달이 가능
        template: '<button @click="addCounter">{{counter}}</button>',
        data: function () {
            return {
                counter: this.initialCounter
            };
        },
        methods: {
            addCounter: function () {
                this.counter += 1;
            }
        }
    });

    new Vue({
        el: '#app',
        data: {
            counter: 0
        },
        components: {
            'simple-component': simpleComponent, // 로컬 컴포넌트
        }
    });
</script>
</html>

네이밍 케이스

  1. 케밥-케이스: kebab-case, spinal-case, Train-Case, Lisp-case
    -하이픈으로 단어를 연결하는 표기법
    -HTML 태그의 id, class 속성으로 흔히 사용됨.

  2. 파스칼 표기법: PascalCase, BackgroundColor, TypeName, PowerPoint
    -첫 단어를 대문자로 시작하는 표기법

  3. 스네이크 케이스(뱀 표기법):
    snake_case, background_color, type_name
    -단어를 밑줄 문자로 구분하는 표기법
    -perl, php, python, ruby, rust....

  4. 헝가리언 표기법:
    strName, bBusy, szName
    -접두어를 사용하는 표기법
    -str -> string, b -> boolean, sz -> null로 끝나는 문자열(string+zero)

  5. 카멜케이스:
    ex) userName

  • 자바스크립트 내에서 카멜케이싱으로 선언한 변수는 HTML내에서 케밥케이싱으로 자동변환된다.

  • userName(JS) -(자동변환)-> user-name(HTML)

  • '{{ }}' : 머스태치


라우팅


동적변경

  • v-model을 이용하여 동적변경을 간단하게 작성할 수 있음
<body>
<div id="app">
    <input v-model="name">
    <br/>
    {{name}}
</div>
</body>
<script>

    new Vue({
        el: '#app',
        data: {
            name: 'kimdongmin',
        }
    });

</script>

Vue Import

    <template>
        <div id="app">
            <img alt="Vue logo" src="./assets/logo.png">
            <HelloWorld msg="Welcome to Your Vue.js App"/>
        </div>
    </template>

    <script>
    import HelloWorld from './components/HelloWorld.vue'

    export default {
        name: 'app',
        components: {
            HelloWorld
        },
    }
    </script>
  1. Vue파일을 import시킨다.
    • import구문의 결과로 vue컴포넌트가 반환된다.
    • 단, 불러 오는 시점에서 js객체로 바뀌게 된다. (컴파일 되는거임)
  2. components에 포함시켜준다.
    • 그래야 template 구문에 사용할 수 있다.

Vue.js 라이프사이클

  1. beforeCreate: 돔에 접근 불가능

  2. created: this.data 또는 this.fetchData() 를 이용하여 data, methods에 정의된 값에 접근 가능. 단, template 속성에 정의된 돔 요소로 접근 불가

  3. beforeMount: render() 함수가 호출되기 직전의 로직을 추가하기 좋음

  4. mounted: 화면 요소(돔)에 접근할 수 있어 화면 요소를 제어하는 로직을 수행하기 좋은 단계

  5. beforeUpdate: 데이터 값을 갱신하는 로직은 가급적 beforeUpdate에 추가하는 것이 좋음

  6. updated: 변경 데이터의 화면 요소(돔)와 관련된 로직을 추가하는 것이 좋음

  7. beforeDestroy: 뷰 인스턴스가 파괴되기 직전에 호출. 뷰 인스턴스의 데이터를 삭제하기 좋은 단계

  8. destroyed

    new Vue({
            el: '#app',
            data: {
                name: 'kimdongmin',
            },
            beforeCreate: function(){
                console.log("beforeCreate")
            },
            created: function(){
                console.log("created")
            },
            mounted: function(){
                console.log("mounted")
                this.name = 'dongminkim' // beforeUpdate & updated 라이프사이클로 넘어가기 위해 name을 update 함
            },
            beforeUpdate: function(){
                console.log("beforeUpdate")
            },
            updated: function(){
                console.log("updated")
            }                
        });
    • 이러한 라이프사이클 메소드 내에 각 라이프사이클 시점에 맞는 로직을 작성할 수 있다.

Vue 컴포넌트

  1. 전역 컴포넌트
Vue.component('컴포넌트 이름', {
    // 컴포넌트 내용
});

예시

<body>
<div id="app">
    <button>컴포넌트 등록</button>
    <test-component></test-component> <!-- JS의 카멜케이스는 HTML에서 케밥케이스로 바뀜 -->
</div>
</body>
<script>

    // 글로벌 컴포넌트
    Vue.component('testComponent', { // JS의 카멜케이스는 HTML에서 케밥케이스로 바뀜 (testComponent -> test-component)
        template: '<div>전역 컴포넌트가 등록되었습니다.</div>',
    });

    new Vue({
        el: '#app',
        data: {
            name: 'kimdongmin',
        },
        
    });

</script>
  1. 지역 컴포넌트
<script>
    var cmp = {
        template: '<div>지역 컴포넌트가 등록되었습니다!</div>'
    };

    new Vue({
        el: '#app',
        components: {
            'my-local-component': cmp
        }
    });
</script>
  1. 지역 컴포넌트 & 전역 컴포넌트
<body>
<div id="app">
    <b>컴포넌트 등록</b>
    <test-local-component></test-local-component>  <!-- 지역 컴포넌트 표시 -->
    <test-global-component></test-global-component> <!-- 전역 컴포넌트 표시 -->
</div>
</body>
<script>

    // 전역 컴포넌트 등록
    Vue.component('test-global-component', {
        template: "<div>전역 컴포넌트입니다.</div>" // 전역 컴포넌트 내용
    });

    // 지역 컴포넌트 내용
    var localcmp = {
        template: "<div>지역 컴포넌트입니다.</div>"
    }

    new Vue({
        el: '#app',
        data: {
        },
        components:{ // 지역 컴포넌트 등록 
            'test-local-component' : localcmp,
        }
    });

</script>
  • 전역 컴포넌트는 app, app1 ... 등 재사용이 가능하지만 지역 컴포넌트는 그렇지 못하다.
<body>
<div id="app">
    <simple-component></simple-component> <!-- 전역 컴포넌트 재사용 O -->
    <local-component></local-component> <!-- 로컬 컴포넌트 -->
</div>

<div id="app1">
    <simple-component></simple-component> <!-- 전역 컴포넌트 재사용 O -->
    <local-component></local-component> <!-- Error 로컬 컴포넌트 재사용 X -->
</div>

</body>
<script>

    Vue.component('simpleComponent', {
        template: '<b>Hello, world!</b>',
    });

    var cmp = {
        template: '<h1>Hello world local compoenents</h1>'
    }

    new Vue({
        el: "#app",
        data: {
        },
        components: {
            'local-component': cmp,
        },
    });

    new Vue({ // 생성자 추가
        el: "#app1",
        data: {
        },
    });
    
</script>

상하위 컴포넌트 관계

  • props 를 통해 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달한다.
  • 하위 컴포넌트에서 상위 컴포넌트로는 이벤트만 전달할 수 있음. (이벤트 버스를 이용하여 데이터 전달 가능?)
Vue.component('child-component', {
    props:['props 속성 이름'],
});

예시

<body>
<div id="app">
    <child-component :propstest="message"></child-component>
</div>
</body>
<script>
    Vue.component('child-component', {
        props:['propstest'],
        template: "<p>{{propstest}}</p>" 
    });
    new Vue({
        el: '#app',
        data: {
            message: "hello, world",
        },
    });

</script>

상하위 컴포넌트 간 이벤트

  • 이벤트 발생
this.$emit('이벤트명')
  • 이벤트 수신
<child-component v-on:이벤트명="상위 컴포넌트의 메서드명"></child-component>

예시

<body>
<div id="app">
    <child-component v-on:show-log="printText"></child-component> 
    <!-- show-log: 하위 컴포넌트의 이벤트 명, printText: 상위 컴포넌트의 메서드 명 --> 
</div>
</body>
<script>

    Vue.component('child-component', {
        template: "<button v-on:click='showLog'>show</button>" // 버튼 요소 추가
        ,methods:{
            showLog:function(){
                this.$emit('show-log'); // 이벤트 발생 로직 (케밥 케이싱)
            }
        }
    });
    new Vue({ // 최상위 컴포넌트  
        el: '#app',
        data: {
            message: "hello, world",
        },
        methods:{
            printText:function(){
                console.log(this.message);
            }
        }
    });

</script>

이벤트 버스

  • 같은 레벨의 컴포넌트의 경우, 데이터 전달은 상위 컴포넌트(공통)로 이벤트를 전달 후 props를 이용하여 하위 컴포넌트들(이벤트를 전달한 자기자신 포함, 대상 같은레벨 컴포넌트)에게 데이터를 전달함
  • 이와 같은 데이터 전달 구조는 비효율적이므로 '이벤트 버스'를 활용
  • 이벤트 버스는 상하위 관계를 유지하고 있지 않아도 컴포넌트 간 데이터 전달 가능. (Vuex 주로 사용)
<body>
<div id="app">
    <child-component></child-component>
</div>
</body>
<script>

    var eventBus = new Vue(); // 이벤트 버스 인스턴스 생성

    // 하위 컴포넌트
    Vue.component('child-component',{
        template: "<button v-on:click='showLog'>show</button>",
        methods:{
            showLog:function(){
                eventBus.$emit('triggerEventBus', 100, 150, "hello"); // 이벤트 발신, $emit
            }
        }
    });

    // 최상위 컴포넌트
    new Vue({
        el: '#app',
        data: {
            message: "hello, world! Vue.js"
        },
        methods:{
        },
        created:function(){
            eventBus.$on('triggerEventBus', function(value1, value2, value3){ // 이벤트 수신, $on
                console.log(value1, value2, value3);
            });
        }
    });

</script>
  • 하지만, 컴포넌트가 많아지면 데이터를 어디서 어디로 보냈는지 관리가 어려워져 Vuex(뷰엑스)라는 상태 관리 도구가 필요하다.

라우팅

  • 웹 페이지 간의 이동 방법으로 싱글 페이지 애플리케이션에서 주로 사용

  • 싱글 페이지 애플리케이션: 페이지를 이동할 때마다 서버에 웹 페이지를 요청하여 새로 갱신하는 것이 아니라 미리 해당 페이지들을 받아 놓고 페이지 이동 시에 클라이언트의 라우팅을 이용하여 화면을 갱신하는 패턴

  • CDN: 콘텐츠 전송 네트워크(Content delivery network 또는 content distribution network)

 <body> 
    <div id="app">
      <h1>뷰 라우터 예제</h1>
      <p>
        <router-link to="/main">Main 컴포넌트로 이동</router-link> <!-- to=""에 정의된 텍스트 값이 브라우저 URL 끝에 추가됨 -->
        <router-link to="/login">Login 컴포넌트로 이동</router-link>
        <router-link to="/sub">Sub 컴포넌트로 이동</router-link>
      </p>
      <router-view></router-view> <!-- URL 값에 따라 갱신되는 화면 영역 -->
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/vue-router.js"></script> <!-- 라우터 CDN -->
    <script>
      // 3. Main. Login 컴포넌트 내용 정의
      var Main = { template: '<div>main</div>' }; // Main 컴포넌트 정의
      var Login = { template: '<div>login</div>' };
      var Sub = { template: '<div>sub</div>'}

      // 4. 각 url에 해당하는 컴포넌트 등록
      var routes = [
        { path: '/main', component: Main }, // routes 변수에는 URL 값이 /main 일 때 Main 컴포넌트를 표시하도록 정의
        { path: '/login', component: Login },
        { path: '/sub', component: Sub },
      ];

      // 5. 뷰 라우터 정의
      var router = new VueRouter({ // 뷰 라우터를 하나 생성하고, routes를 삽입해 URL에 따라 화면이 전환될 수 있게 정의
        routes
      });

      // 6. 뷰 라우터를 인스턴스에 등록
      var app = new Vue({
        router // 라우터 추가
      }).$mount('#app'); // $mount: 인스턴스를 화면에 붙여줌 (el 속성과 동일)

        // 인스턴스를 생성할 때 el 속성을 넣지 않았더라도 생성하고 나서 $mount()를 이용하면 강제로 인스턴스를 화면에 붙일 수 있음

    </script>
  </body>

라우터 URL의 해시 값(#)을 없애려면

  • 히스토리 모드(history mode) 활용
var router = new VueRouter({
    mode: 'history',
    routes
});

네스티드 라우터

  • 라우터로 페이지를 이동할 때, 최소 2개 이상의 컴포넌트를 화면에 나타낼 수 있게 함
  • 네스티드 라우터를 이용하면 URL에 따라서 컴포넌트의 하위 컴포넌트가 다르게 표시됨
 <body>
    <div id="app">
      <router-view></router-view> <!-- User (상위) 컴포넌트가 뿌려질 영역 -->
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/vue-router.js"></script>
    <script>
      var User = { // 상위 컴포넌트 템플릿 정의 (가시적으로 이해를 돕기 위해 button으로 정의함)
        template: `               
          <button>
            User Component
            <router-view></router-view>
          </button>
        `
        // <router-view></router-view> : 하위 컴포넌트가 네스티드 될 영역
        // 위와 같이 template을 들여쓰기하여 표기하려면 ' 가 아닌 ` 로 묶어 표기해아 함 
      };
      var UserProfile = { template: '<button>User Profile Component</button>' }; // 하위 컴포넌트 템플릿 정의 (Profile)
      var UserPost = { template: '<button>User Post Component</button>' }; // 하위 컴포넌트 (Post)

      var routes = [ // 네스티드 라우팅 정의
        {
          path: '/user',
          component: User, // /user url에 User컴포넌트 매핑
          children: [ // 하위 컴포넌트 라우팅 정의
            {
              path: 'posts', // /posts url에 UserPost컴포넌트 매핑
              component: UserPost
            },
            {
              path: 'profile',
              component: UserProfile
            },
          ]
        }
      ];

      var router = new VueRouter({
        routes
      });

      var app = new Vue({ // 뷰 인스턴스에 라우터 추가
        router
      }).$mount('#app');
    </script>
  </body>
  • domain 뒤에 붙이기

    1. /user : 최상위 컴포넌트 위에 user(상위 컴포넌트)가 붙음
    2. /user/profile : 최상위 컴포넌트 위에 user(상위 컴포넌트) 위에 profile(하위 컴포넌트)가 붙음
    3. /user/posts
  • 네스티드 라우터는 화면을 구성하는 컴포넌트의 수가 적을 때는 유용하지만 많은 컴포넌트를 표시할 때는 네임드 뷰가 좋음


네임드 뷰

  • 네임드 뷰는 특정 페이지로 이동했을 때, 여러 개의 컴포넌트를 동시에 표시하는 라우팅 방식
  • 네임드 뷰는 위 그림 처럼 같은 레벨에서 여러 개의 컴포넌트를 한 번에 표시하는 방식
  <body>
    <div id="app">
      <router-view name="header"></router-view> <!-- name="header" 라는 name을 명시 -->
      <router-view></router-view> <!-- name이 없는 경우 'default' -->
      <router-view name="footer"></router-view>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/vue-router.js"></script>
    <script>

      // 컴포넌트의 템플릿 정의
      var Body = { template: '<div>This is Body</div>' };
      var Header = { template: '<div>This is Header</div>' };
      var Footer = { template: '<div>This is Footer</div>' };

      var router = new VueRouter({
        routes: [
          {
            path: '/', // 루트 url에 모두 표시; URL 값 '/'에 의해 네임드 뷰가 바로 실행 됨
            components: { // <router-view>의 name 속성과 컴포넌트를 연결
              default: Body, // default에 Body 컴포넌트 연결 (default는 <router-view> 태그에서 name을 정의하지 않은 곳에 매핑됨)
              header: Header, // header에 Header 컴포넌트 연결. <router-view name="header"> 에 매핑
              footer: Footer
            }
          }
        ]
      })

      var app = new Vue({
        router
      }).$mount('#app');
    </script>
  </body>

뷰 HTTP 통신

  • 웹 앱 HTTP 통신의 대표적인 사례로는 jQuery의 ajax가 있음 (JavaScript)

  • ajax는 서버에서 받아온 데이터를 표시할 때, 화면 전체를 갱신하지 않고 화면 일부분만 변경할 수 있게 함

  • Vue에서는 뷰 리소스 또는 Axios를 사용하여 HTTP 통신을 함. Axios를 통해 Vue와 Spring Controller 간 데이터 전송 가능

  1. 뷰 리소스

  2. Axios 액시오스

  • 액시오스 또한 NPM을 이용하여 설치하거나 CDN(콘텐츠 전송 네트워크)을 이용하여 설치할 수 있음

  • Axios CDN

    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    • 위 코드를 HTML에 추가하면 라이브러리를 로딩하여 사용할 수 있는 상태가 됨

1.

// HTTP GET 요청
axios.get('URL 주소').then().catch();
  • .get() : HTTP GET 요청을 보냄
  • .then() : 서버에서 보낸 데이터를 정상적으로 받아오면 then() 안에 정의한 로직 실행
  • .catch() : 데이터를 받아올 때 오류 발생 시 catch() 안에 정의한 로직 수행

2.

// HTTP POST 요청
axios.post('URL 주소').then().catch();
  • .post() : HTTP POST 요청을 보냄
  • .then() : 서버에서 보낸 데이터를 정상적으로 받아오면 then() 안에 정의한 로직 실행
  • .catch() : 데이터를 받아올 때 오류 발생 시 catch() 안에 정의한 로직 수행

3.

// HTTP 요청에 대한 옵션 속성 정의
axios({
    method: 'get',
    url: 'URL 주소',
    ...
});
  • HTTP 요청에 대한 자세한 속성들을 직접 정의가능

  • 예시

	<body>
		<div id="app">
			<button v-on:click="getData">프레임워크 목록 가져오기</button>
		</div>

		<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
		<script src="https://unpkg.com/axios/dist/axios.min.js"></script> <!-- axios CDN -->
		<script>
			new Vue({
				el: '#app',
				methods: {
					getData: function() {
						axios.get('https://raw.githubusercontent.com/joshua1988/doit-vuejs/master/data/demo.json')
							.then(function(response) {
								console.log(response);
							}).catch(function(){
                                console.log('Error!')
                            });
					}
				}
			});
		</script>
	</body>
  • '프레임워크 목록 가져오기' 버튼 클릭 후 '개발자 도구'로 객체 확인

뷰 템플릿

  • 자바스크립트 표현식 사용 시 주의할 점
<!-- 1. -->
{{ var a = 10; }} <!-- X, 선언문은 사용 불가능 -->
{{ if (true) {return 100} }} <!-- X, 분기 구문은 사용 불가능 -->
{{ true ? 100 : 0 }} <!-- O, 삼항 연산자로 표현 가능 -->

<!-- 2. -->
{{ message.split('').reverse().join('') }} <!-- X, 복잡한 연산은 인스턴스 안에서 수행 -->
{{ reversedMessage }} <!-- O, 스크립트에서 computed 속성으로 계산 후 최종 값만 표현 -->     
  • 캐싱: 데이터나 값을 임시 장소에 미리 복사해 놓는 동작
  • 뷰 디렉티브: HTML 태그 안에 v- 접두사를 가지는 모든 속성 ex) v-bind, v-if ...

뷰 디렉티브

  • v-if 지정한 뷰 데이터 값의 참, 거짓 여부에 따라 HTML 태그를 화면에 표시하거나 미표시
  • v-for 지정한 뷰 데이터의 개수만큼 HTML 태그를 반복 출력
  • v-show v-if와 유사, v-if는 거짓시 태그를 완전 삭제하지만 v-show는 css효과만 display:none으로 주어 실제 태그는 남고 화면 상으로만 보이지 않음
  • v-bind HTML 태그의 기본 속성과 뷰 데이터 속성을 연결
  • v-on 화면 요소의 이벤트를 감지 ex) v-on:click은 해당 태그의 클릭 이벤트를 감지하여 특정 메서드를 실행할 수 있음
  • v-model form(폼)에서 주로 사용되는 속성. 폼에 입력한 값을 뷰 인스턴스의 데이터와 즉시 동기화

computed 속성과 methods 속성의 차이점 (+ watch 속성)

  • methods 속성은 호출할 때만 해당 로직이 수행되고, computed 속성은 대상 데이터의 값이 변경되면 자동적으로 수행됨

computed 장점

  • computed 속성의 첫 번째 장점은 data 속성 값의 변화에 따라 자동으로 다시 연산
  • computed 속성의 두 번째 장점은 캐싱 - 동일한 연산을 반복해서 하지 않기 위해 연산의 결과 값을 미리 저장하고 있다가 필요할 때 호출
  • 만약 화면 여러 곳에 같은 값을 표시해야 한다면 캐싱을 제공하는 computed를 활용하면 좋음
new Vue({
        el: '#app',
        data: {
          message: 'Hello Vue.js!'
        },
        computed: {     // computed
          reverseMsg: function() {
            return this.message.split('').reverse().join('');
          }
        }
      });

watch 속성

  • watch 속성은 데이터 변화를 실시간으로 감지하여 자동으로 특정 로직을 수행
  • 데이터 호출과 같이 시간이 상대적으로 더 많이 소모되는 비동기 처리에 적합
  <body>
    <div id="app">
      <input v-model="message"> <!-- v-model 활용 -->
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script>
      new Vue({
        el: '#app',
        data: {
          message: 'Hello Vue.js!'
        },
        watch: {    // watch
          message: function(data) {
            console.log("message의 값이 바뀝니다 : ", data);
          }
        }
      });
    </script>
  </body>

.vue 파일 구조 / 싱글 파일 컴포넌트 체계

  • .vue 파일은 아래와 같은 기본구조를 가진다.
<template>
    <!-- HTML 태그 내용 -->
</template>

<script>
export default {
    // 자바스크립트 내용
}
</script>

<style>
    /* CSS 스타일 내용 */
</style>

좋은 웹페이지 즐겨찾기