프로그래밍 언어/Vue

Vue 기초 - v-for, 컴포넌트, props, emit, slot

claire 2023. 4. 11. 18:08

v-for

<body>
    <div id="app">
      <div>{{people[0].name}} {{people[0].age}}</div>
      <div>{{people[1].name}} {{people[1].age}}</div>
      <div>{{people[2].name}} {{people[2].age}}</div>
      <div>{{people[3].name}} {{people[3].age}}</div>
      <hr />
      <!-- 위와 같은 코드를 아래와 같이 반복문으로 작성할 수 있다.  -->
      <div v-for="person in people">{{person.name}} {{person.age}}</div>
      <!-- of를 써도 된다.  -->
      <div v-for="person of people">{{person.name}} {{person.age}}</div>
      <!-- 아래와 같이 index를 쓸 수 있다.  -->
      <!-- for 문은 고유한 key를 넣어준다. key는 값의 조합으로 넣어줘도 됨. 주로 unique한 id값을 설정. 
      index값은 키로 사용하면 안된다.
      -->
      <div v-for="(person,index) in people" :key="person.name+'-'+person.age">
        {{person.name}} {{person.age}} {{index}}
      </div>
    </div>
    <script>
      new Vue({
        el: "#app",
        data: {
          people: [
            { name: "a", age: 10 },
            { name: "b", age: 21 },
            { name: "c", age: 22 },
            { name: "d", age: 27 },
          ],
        },
        methods: {},
      });
    </script>
  </body>

v-for를 사용해서 객체의 속성을 반복할 수도 있다. 

<ul id="v-for-object" class="demo">
  <li v-for="value in object">
    {{ value }}
  </li>
  <div v-for="(value, name, index) in object">
    {{ index }}. {{ name }}: {{ value }}
  </div>
</ul>
new Vue({
  el: '#v-for-object',
  data: {
    object: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
})

결과

여러개의 vue 인스턴스 사용하기

<body>
    <div id="app">
      {{name}}
      <button @click="changeText">Click</button>
    </div>
    <div id="app-1">{{name}}<button @click="changeText">Click</button></div>
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          name: "승연",
        },
        methods: {
          changeText() {
            // this.name = "승연 업데이트";
            app1.name = "헬로헬로";
          },
        },
      });

      // vue인스턴스를 변수에 담고 해당 변수를 통해 다른 인스턴스의 값에 접근한다. 
      const app1 = new Vue({
        el: "#app-1",
        data: {
          name: "승얌",
        },
        methods: {
          changeText() {
            this.name = "승얌 업데이트";
          },
        },
      });
    </script>
  </body>

 

컴포넌트 사용하기

전역 등록

Vue.component('my-component', {
  // 옵션
})

컴포넌트에서 data는 함수여야 한다. 

<body>
    <div id="app">
      <seung-button></seung-button>
    </div>

    <script>
      // 한 컴포넌트 내에서 다른 컴포넌트를 쓸 수도 있음
      Vue.component("hello-world", {
        template: "<div>hello world</div>",
      });
      Vue.component("seung-button", {
        template: `
        <div>
          <div>{{name}}</div>
          <hello-world></hello-world>
          <button @click="changeText">Click</button>
        </div>
        `,
        //data는 함수로 사용해야 한다. 만약 객체 형태면 한 곳에서 변경되면 다른 곳에도 영향을 줌
        // data: {
        //   name: "승연",
        // },
        data() {
          return {
            name: "seungYeon",
          };
        },
        methods: {
          changeText() {
            this.name = "승연 업데이트";
          },
        },
      });
      new Vue({
        el: "#app",
      });
    </script>
  </body>

지역 등록

<body>
    <div id="app">
      <seung-button></seung-button>
    </div>

    <div id="app1">
      <seung-button></seung-button>
    </div>

    <script>
      const HelloWold = {
        template: "<div>Hello World</div>",
      };

      const seungButton = {
        components: {
          "hello-world": HelloWold,
        },
        template: `<div>{{name}}
          <hello-world></hello-world>
          <button @click='changeText'>Click</button></div>`,
        data() {
          return {
            name: "seungYeon",
          };
        },
        methods: {
          changeText() {
            this.name = "seung updated";
          },
        },
      };

      const app = new Vue({
        el: "#app",
        components: {
          "seung-button": seungButton,
        },
      });

      // 지역 등록은 인스턴스마다 component 속성에 넣어줘야 한다. app1에선 seung-button 사용 불가. 
      const app1 = new Vue({
        el: "#app1",
      });
    </script>
  </body>

 

vue 프로젝트 시작하기

npm install -g @vue/cli
vue create 프로젝트명

vue/cli를 설치하지 않아도 

npx @vue/cli create 프로젝트명

으로 프로젝트 시작이 가능하다. 

 

npm run serve 로 실행. 

 

vue router

<template>
  <div id="app">
    <nav>
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </nav>
    <!-- router-view 부분이 link에 따라 바뀌는 것이다.  -->
    <router-view />
  </div>
</template>

 

싱글 파일 컴포넌트

- componets/SeungYeon.vue

<template>
  <div>
    <p>{{ name }}</p>
    <button @click="updateName">Change Name</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: "seung yeon",
    };
  },
  methods: {
    updateName() {
      this.name = "승연이올시다. ";
    },
  },
};
</script>

<style scoped></style>

- views/Home.vue

<template>
  <div>
    <h1>This is home page</h1>
    <SeungYeon />
  </div>
</template>

<script>
import SeungYeon from "@/components/SeungYeon.vue";

export default {
  components: {
    SeungYeon,
  },
  data() {
    return {
      name: "승연",
    };
  },
};
</script>

<!-- scoped를 넣어줘야 이 파일에만 적용됨-->
<style scoped>
h1 {
  color: red;
}
</style>

props 

자식 컴포넌트에 데이터 보내기

- componets/SeungYeon.vue

<template>
  <div>
    <h1>{{ title }}</h1>
    <h2>{{ names }}</h2>
    <p>{{ name }}</p>
    <button @click="updateName">Change Name</button>
  </div>
</template>

<script>
export default {
  // props 작성 시 type과 required를 설정해준다.
  //   props는 methods에서 바꾸면 안된다.자식에서는 직접적으로 props를 바꾸지 않고 부모에서만 변경한다.
  props: {
    title: {
      type: String,
      required: false,
      default: "기본 타이틀",
    },
    names: {
      type: String,
      default: "기본 타이틀",
    },
  },
  data() {
    return {
      name: "seung yeon",
    };
  },
  methods: {
    updateName() {
      this.name = "승연이올시다. ";
    },
  },
};
</script>

<style scoped></style>

- views/Home.vue

<template>
  <div>
    <h1>This is home page</h1>
    <SeungYeon title="헬로우!" names="이건 이름이야~" />
  </div>
</template>

<script>
import SeungYeon from "@/components/SeungYeon.vue";

export default {
  components: {
    SeungYeon,
  },
  data() {
    return {
      name: "승연",
    };
  },
};
</script>

<!-- scoped를 넣어줘야 이 파일에만 적용됨-->
<style scoped>
h1 {
  color: red;
}
</style>

 

 

emit

- components/inputField.vue

<template>
  <div>
    <label for="">Name</label>
    <input
      type="text"
      :value="name"
      style="padding: 30px; border: 2px solid green"
      @input="updateName"
    />
  </div>
</template>

<script>
export default {
  props: {
    name: {
      type: String,
      required: true,
    },
  },
  methods: {
    updateName(e) {
      console.log(e.target.value);
      this.$emit("update-name", e.target.value);
    },
  },
};
</script>

- views/HomeView.vue

<template>
  <div>
    <h1>This is home page</h1>
    <form action="">
      <InputField :name="name" @update-name="updateName" />
      <br />
      <button>Submitt</button>
    </form>
    {{ name }}
  </div>
</template>

<script>
import InputField from "@/components/InputField.vue";
export default {
  components: {
    InputField,
  },
  data() {
    return {
      name: "",
    };
  },
  methods: {
    updateName(name) {
      this.name = name;
    },
  },
};
</script>

<!-- scoped를 넣어줘야 이 파일에만 적용됨-->
<style scoped>
h1 {
  color: red;
}
</style>

v-model로 다시

- inputField.vue

<template>
  <div>
    <label for="">Name</label>
    <input
      type="text"
      :value="value"
      style="padding: 30px; border: 2px solid green"
      @input="$emit('input', $event.target.value)"
    />
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: String,
      required: true,
    },
  },
  methods: {
    updateName(e) {
      console.log(e.target.value);
      this.$emit("update-name", e.target.value);
    },
  },
};
</script>

- Home.vue

<template>
  <div>
    <h1>This is home page</h1>
    <form action="">
      <InputField v-model="name" />
      <br />
      <button>Submitt</button>
    </form>
    {{ name }}
  </div>
</template>

<script>
import InputField from "@/components/InputField.vue";
export default {
  components: {
    InputField,
  },
  data() {
    return {
      name: "",
    };
  },
  methods: {
    updateName(name) {
      this.name = name;
    },
  },
};
</script>

<!-- scoped를 넣어줘야 이 파일에만 적용됨-->
<style scoped>
h1 {
  color: red;
}
</style>

Slot 

- about.vue

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <!-- slot이 있는 있는 컴포넌트의 태그 안에 작성 -->
    <SeungYeonVue :title="title">
      <!-- name이 header인 slot에 들어간다.  -->
      <!-- v-slot은 #으로 줄여쓸 수 있다.  -->
      <template v-slot:header="props">
        <p>header11{{ props.seung }}</p>
      </template>
      <!-- v-slot:default는 name에 아무것도 쓰지 않은 slot에 들어간다.  -->
      <template #default="{ seung }"> hello22{{ seung }} </template>
    </SeungYeonVue>
  </div>
</template>

<script>
import SeungYeonVue from "../components/SeungYeon.vue";

export default {
  components: {
    SeungYeonVue,
  },
  data() {
    return {
      title: "하이염",
    };
  },
};
</script>

- SeungYeon.vue

<template>
  <div>
    <p>header</p>
    <!-- 원하는 데이터를 변수에 넣어주면 된다. -->
    <slot name="header" :seung="seung"></slot>
    <p>body</p>
    <slot :seung="seung"></slot>
    <p>footer</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      seung: "coder",
    };
  },
};
</script>