소스 : https://github.com/donghyeon0725/vue_tutorials

 

css
<style>
[v-cloak] {
    display: none;
}
#app {
    display: flex;
}
.block {
    display: block;
}
.f-right  {
    float: right;
}
.f-left {
    float: left;
}
.cart-wrapper {
    min-width: 130px;
    background: #e6e6e6;
    margin: 5px 5px;
    overflow: auto;
    align-justify: justify;
}

.preview-wrapper {
    min-width: 130px;
    background: #e6e6e6;
    margin: 5px 5px;
    overflow: auto;
    vertical-align: center;
}

.cart {
    margin-left: auto;
    margin-right: auto;
    margin-top: 2px;
    background-color: bisque;
    display: block;
    width: 120px;
    border: 1px solid blue;
    border-radius: 5px;
    text-align: center;
    cursor: pointer;
}
</style>

 

 

index.html
<div id="app" v-cloak>
    <!--cart-list-->
    <div class="cart-wrapper">
        <div v-if="cartList.length > 0" v-for="(json, key) in cartList" class="cart" :draggable="draggable" @dragstart='dragStart($event, json.content)'>{{ json.content }}</div>

    </div>
    <form v-on:submit.prevent="addCard">
        <div class="contentWrap">
            <!-- v-model을 통해 값을 연결한다. data에 있는 변수명이 내용물이 된다. -->
            <textarea v-model="content" name="content" id="content" cols="30" rows="10" class="block" :placeholder="defaultContents" @drop="drop($event)" @dragover="allowDrop($event)" ></textarea>
            <button type="button" class="f-left">{{ btnStr.left }}</button>
            <button type="submit" class="f-right">{{ btnStr.right }}</button>
        </div>
    </form>

    <div class="preview-wrapper">
        <div v-if="previewList.length > 0" v-for="(json, key) in previewList" class="cart" @click="deleteCard(key)">{{ json.content }}</div><!-- json, key를 반대로 사용함 -->
    </div>
    <!--preview-->
</div>

1. 이동할 요소에 draggable="true" 어트리뷰트를 추가할 것
2. 드랍될 엘리먼트의 drop 이벤트에 event.preventDefault()를 사용할 것
드랍 될 요소에 preventDefault를 사용하지 않으면 브라우저에서 드랍 이벤트를 허용하지 않을 수 있습니다.

 

다음의 스타일을 정의할 것
-- 드래그 될 때의 스타일
-- 드래그가 다른 엘리먼트 위에 있을 때 스타일
-- 다른 엘리먼트 위에 드랍될 때의 스타일

 

javascript
<script>


const CARD_STORAGE_KEY = "cardStorage";
const PREVIEW_STORAGE_KEY = "previewStorage";

// localStorage.getItem(CARD_STORAGE_KEY) || "[]" => 자바스크립트의 논리연산자 ||은 참거짓을 반환하는게 아닌, 피연산중 true인 것을 반환하는 연산자이다!!!
let cartStorage = {
    fetch: () => JSON.parse(localStorage.getItem(CARD_STORAGE_KEY) || "[]"),
    save: card => localStorage.setItem(CARD_STORAGE_KEY, JSON.stringify(card)),
    removeAll: () => localStorage.removeItem(CARD_STORAGE_KEY)
};
cartStorage.removeAll();

let previewStorage = {
    fetch: () => JSON.parse(localStorage.getItem(PREVIEW_STORAGE_KEY) || "[]"),
    save: card => localStorage.setItem(PREVIEW_STORAGE_KEY, JSON.stringify(card)),
    removeAll: () => localStorage.removeItem(PREVIEW_STORAGE_KEY)
};



/*cartStorage.removeAll();
previewStorage.removeAll();*/

//previewStorage.save({name:'sds',content:'내용'});
let app = new Vue({
    el: '#app',
    data: {
        btnStr: {
            left : '비동기로 데이터 요청',
            right : '카드 삽입하기'
        },
        content: '',
        cartList: cartStorage.fetch(),
        previewList: previewStorage.fetch()
    },
    methods: {

        addCard(event) {
            if (!this.content) {
                return;
            }
            this.previewList.push({content: this.content});
            this.content = "";
        },
        deleteCard(key) {
            console.log(key);
            this.previewList.splice(key, 1);
        },
        dragStart(evt, content) {
            evt.dataTransfer.dropEffect = 'move'
            evt.dataTransfer.effectAllowed = 'move'
            evt.dataTransfer.setData('itemID', content)
        },
        allowDrop(event) {
            event.preventDefault();
        },
        drop:function(evt) {
            evt.preventDefault();
            const itemID = evt.dataTransfer.getData('itemID')
            this.content = itemID;
        }
    },
    computed: {
        defaultContents() {
            return '내용을 입력해주세요.';
        },
        draggable() {
            return true;
        }
    },
    beforeCreate() {
        console.log('beforeCreate', this.btnStr);
    },
    created() {
        console.log('beforeCreate', this.btnStr);
        this.cartList.push({content: '내용1'});
        this.cartList.push({content: '내용2'});
    },
    beforeMount() {
        alert('beforeMount => 랜더링 이전(내부 노드 추가 안된 상태)');
    },
    mounted() {
        alert('mounted => 랜더링 이후');
    },
    beforeUpdate() {
        alert('beforeUpdate')
    },
    updated() {
        alert('updated')
    },
    beforeDestroy() {

    },
    destroyed() {

    },
    // 변화를 보고 있다가 변화발생 시 작동할 옵션
    watch: {
        previewList: {
            handler: function(aa) {
                previewStorage.save(aa);
            },
            deep: true
        },
        cartList:{
            handler: function(aa) {
                cartStorage.save(aa);
            },
            deep: true
        }
    }
});




</script>

 

  • localStorage 라는 키워드를 통해 로컬에 값을 저장함. 저장된 값은 리스트에 json 데이터가 저장된 형태이다.
  • 한편 자바스크립트의 논리연산자 "||"은 자바스크립트의 논리연산자 ||은 참거짓을 반환하는게 아닌, 피연산중 true인 것을 반환하는 연산자이다
  • watch 라는 프로퍼티로 와쳐를 관리하고, data 내부의 데이터를 보고 있다가 변화가 생기면 같은 이름의 프로퍼티를 호출한다.
  • created() (created: function() 의 설탕문법)와 같이 생애주기에 해당하는 메소드를 프로퍼티로 추가함으로써 생애주기를 관리할 수 있다. 주요한 생에주기는 아래와 같다.

 

  • beforeCreate : 인스턴스의 내부 이벤트와 라이프 사이클 상태가 초기화 된 후에 호출
  • created : 인스턴스(vm)의 주입과 반응형 시스템이 초기화 된 후 호출. 이때부터 data 객체에 접근 가능
  • beforeMount : 인스턴스는 생성이 되었고 vue.js가 템플릿들의 컴파일을 마친 시점. 생성된 DOM을 랜더링 할 준비가 된 후 호출된다. 이때 개발자 도구를 열면 내부에 아직 DOM 요소가 부착되어 있지 않다.
  • mounted : DOM이 랜더링 된 이후. 사용자가 DOM 요소들을 확인할 수 있다.
  • beforeUpdate : 데이터가 변경 되기 전에 호출 됨. 옵저버 패턴으로 데이터를 보고 있어서 가능함.
  • activated: keep-alive 컴포넌트가 활성화 될 때 => 추상 엘리먼트로(태그) 주로 엘리먼트를 보존하거나 재 랜더링 하는데 사용
  • deactivated: keep-alive 컴포넌트가 비활성화 될 때
  • updated : 데이터가 변경 된 후 호출
  • beforeDestroy : 생성 된 인스턴스가 파괴 되기 전에 호출 됨. 이 때 인스턴스는 아직 제 기능을 할 수 있음.
  • destroyed: 인스턴스가 파괴된 후 호출. 인스턴스의 모든 지시자 바인딩이 해제 됨. 이벤트 리스너가 제거되고 모든 하위 인스턴스가 파괴됨.
  • errorCaptured: 자손컴포넌트에서 에러가 검출 될 때마다 호출

 

실행결과


라이프사이클