Vue 채팅 - 프런트엔드

39262 단어
안녕하세요 DEV 여러분,

이전 게시물에서 채팅 백엔드의 80%를 구현했습니다.
놓친 경우를 대비하여 게시물에 대한 링크입니다.

오늘 우리는 사용자를 기억하기 위해 기본 프런트엔드를 구현하고 백엔드를 향상시키는 작업을 계속할 것입니다. 갑시다!

전제 조건 - NPM/yarn과 함께 Node.js 10+ 설치

다음을 사용하여 vue-cli를 설치합니다.

 npm i -g @vue/cli # you may need sudo under linux 


또는

yarn global add @vue/cli 


그런 다음 계속해서 다음을 사용하여 새 vue 프로젝트를 만듭니다.

vue create {project_name} 


종속성을 수동/자동으로 선택하라는 메시지가 표시됩니다. 어떤 의존성을 가질지는 당신에게 달려 있습니다. 나는 선택했다:
  • 바벨
  • 뷰 라우터
  • Vuex

  • 다음과 같이 프로젝트 구조로 표시되어야 합니다.


    프로젝트 폴더에서 VSCode를 열고 터미널에 다음을 입력합니다.
    npm run serve
    이렇게 하면 포트 8080에서 프런트엔드 애플리케이션이 시작됩니다. (Spring의 기본 포트도 8080이므로 vue 앱이 포트 8081에서 시작할 수 있습니다.)

    종속성을 추가해 보겠습니다.

    npm i axios bootstrap bootstrap-vue 
    npm i sockjs-client webstomp-client
    


    라우터와 채팅 페이지와 같은 몇 가지 구성 요소를 정의해야 합니다. 본질적으로 더 복잡하기 때문에 채팅 페이지부터 시작하겠습니다.
    무엇보다도 이것은 main.js 파일입니다. 복잡하지 않고 vue+bootstrap 설정만 있으면 됩니다.

    
    import Vue from 'vue'
    import App from './App.vue'
    import './registerServiceWorker'
    import router from './router'
    
    import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
    
    // Import Bootstrap an BootstrapVue CSS files (order is important)
    import 'bootstrap/dist/css/bootstrap.css'
    import 'bootstrap-vue/dist/bootstrap-vue.css'
    
    // Make BootstrapVue available throughout your project
    Vue.use(BootstrapVue)
    // Optionally install the BootstrapVue icon components plugin
    Vue.use(IconsPlugin)
    
    Vue.config.productionTip = false
    
    new Vue({
      router,
      render: h => h(App)
    }).$mount('#app')
    
    
    


    다음과 같이 채팅 페이지에서 메서드를 정의하여 시작하겠습니다.

    
    methods: {
                goBack(){
                    this.$router.push({name: 'Home'});
                },
    
                addMessage(message) {
    
                    if (this.messages.length > 5) {
                        this.messages.shift();
                    }
                    this.messages.push(message);
                    this.scrollOnNewMessage();
                },
    
                async sendInitialConnectionRequest() {
                    const ax = axios.default;
                    const ipifyUrl = 'https://api.ipify.org?format=json';
                    const ipData = (await ax.get(ipifyUrl)).data;
                    const {
                        ip
                    } = ipData;
                    const checkUrl = this.beUrl + '/check/' + ip;
                    // data should contain IP
                    const rsp = await ax.get(checkUrl);
    
                    const {
                        data
                    } = rsp;
    
                    if (data.ipAddressHash && data.username) {
                        this.username = data.username;
                    } else {
                        this.username = prompt("Enter a username");
                        if (this.username) {
                            const registerUrl = this.beUrl + "/create";
                            const requestData = {
                                ipAddress: ip,
                                username: this.username
                            };
                            await ax.post(registerUrl, requestData);
                        } else {
                            await this.sendInitialConnectionRequest();
                        }
                    }
    
                },
    
                subscribeToChat() {
                    if (this.username && this.username !== '') {
                        this.stompClient.subscribe("/topic/messages/" + this.username, (data) => {
                            const {
                                senderUsername,
                                recipientUsername,
                                content
                            } = JSON.parse(data.body);
    
                            const message = {
                                senderUsername,
                                recipientUsername,
                                content,
                            };
    
                            this.addMessage(message);
                        });
                    }
                },
                connectToChatServer() {
                    this.connectedToServer = false;
                    this.socket = new SockJs(this.beUrl + "/chat-app");
                    this.stompClient = StompClient.over(this.socket, {
                        debug: false
                    });
    
                    this.stompClient.connect({}, (frame) => {
                        this.connectedToServer = true;
                        this.subscribeToChat();
                    });
                },
    
                sendMessage(e) {
                    e.preventDefault(); // prevent default form submission
                    const message = {
                        senderUsername: this.username,
                        recipientUsername: this.recipientUsername,
                        content: this.messageContent,
                    };
                    if (message.content.length > 0) {
                        this.stompClient.send("/topic/messages/" + message.recipientUsername, JSON.stringify(message), {});
                        this.addMessage(message); // add message to currently shown messages
                        this.messageContent = "";
                    }
                },
                scrollOnNewMessage(){
                    const msgContainerHtmlNode = document.getElementById("message-container"); 
                    msgContainerHtmlNode.scrollTop = msgContainerHtmlNode.scrollHeight;
                }
            }
    
    


    다음은 여러분이 알아차렸을 가능성이 있는 누락된 변수입니다.

    
    data() {
                return {
                    connectedToServer: false,
                    messages: [],
                    showInfo: Boolean,
                    username: String,
                                    messageContent: "",
                                    recipientUsername: "",
                    beUrl: 'http://localhost:8080' //'https://java-chat-backend.herokuapp.com'
                }
            },
    
    


    다음은 채팅 페이지 마크업입니다.

    
    <template>
        <div class="home bg-dark">
    
            <show-info-button></show-info-button>
            <go-home-button></go-home-button>
    
    
            <p style="color: white; font-size: 22px; color: #b491c8;">Your are: {{username}}</p>
    
            <b-form @submit="sendMessage"
                style="display: flex; flex-direction: column; justify-content: space-between; margin: auto; width: 75%;">
                <b-form-input id="recipient" v-model="recipientUsername" type="text" placeholder="recipient..."
                    class="bg-dark" style="margin-top: 5px; margin-bottom: 5px; color: #b491c8;" />
    
                <div id="message-container" class="chatmessagecontainer" v-show="messages.length > 0">
    
                    <span v-for="message in messages" v-bind:key="message.content + Math.random().toString()">
                        <message :content="message.content" :senderUsername="message.senderUsername"></message>
                    </span>
    
                </div>
    
                <div class="chat-controller">
    
                    <b-form-input id="messageContent" v-model="messageContent" type="text" placeholder="Your message.."
                        class="bg-dark"  style="margin-top: 5px; margin-bottom: 10px; color: #b491c8;" />
    
                    <span
                        style="display: flex; flex-direction: row; justify-content: space-between; margin-top: 5px; margin: auto;">
                        <b-button type="submit" variant="outline-success" @click="sendMessage">
                            <b-icon icon="chat-fill"></b-icon> Send
                        </b-button>
                        <b-button variant="outline-danger" @click="()=>{messages=[]}">
                            <b-icon icon="trash"></b-icon> Clear
                        </b-button>
                    </span>
    
                </div>
    
    
            </b-form>
    
    
        </div>
    </template>
    
    


    그리고 범위 지정 스타일:

    
        .chatmessagecontainer {
            display: flex;
            flex-direction: column;
            justify-content: space-evenly;
            max-height: 320px;
            overflow-x: none;
            overflow-y: scroll;
            border: 1px solid black;
            border-radius: 8px;
            margin: auto;
            margin-top: 10px;
            width: 100%;
            padding: 4px 4px 4px 4px;
        }
    
        .chat-controller {
            margin: auto;
            width: 75%;
            position: absolute;
            left: 0;
            right: 0;
            bottom: 5px;
    
        }
    
        button {
            font-size: 15px;
        }
    
        input {
            font-size: 16px;
            color: #b491c8;
        }
    
    


    그리고 라우터 파일:

    
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import  Chat from '../views/Chat.vue'
    Vue.use(VueRouter)
    
    const routes = [
      {
        path: '/chat',
        name: 'Chat',
        component: Chat
      },
    ]
    
    const router = new VueRouter({
      mode: 'history',
      base: process.env.BASE_URL,
      routes
    })
    
    export default router
    
    


    그리고 마지막으로 세 가지 누락된 구성 요소는 다음과 같습니다.

    메시지:

    
    <template>
      <div class="chatmessage">
                        <p class="chat-message-username"><b>{{senderUsername}}</b></p>
                        <p class="chat-message-content">{{content}}</p>
      </div>
    </template>
    
    <script>
    export default {
        props:{
            content: String,
            senderUsername: String,
            createdAt: Date
        }
    }
    </script>
    
    <style >
    .chatmessage{
      background-color: #212121;
      color: white;
      border-radius: 9px;
      max-width: 250px;
      margin: auto;
      font-size: 15px;
      max-height: 60px;
    }
    
    .chat-message-content{
      font-size: 14px;
      margin-top: 0;
      padding: 0;
    
    }
    
    .chat-message-username{
      font-size: 15px;
      font-family: monospace;
    }
    
    </style>
    
    
    
    


    GoHome버튼

    
    <template>
        <b-icon icon="arrow-left" style="position: absolute; top: 5px; left: 5px; font-size: 33px;" class="h4 text-danger"
                @click="goBack()"></b-icon>
    </template>
    
    <script>
    export default {
        methods:{
            goBack(){
                this.$router.push("/");
            }
        }
    }
    </script>
    
    <style>
    
    </style>
    
    


    정보 표시 버튼

    
    <template>
        <div>
            <b-icon icon="question" style="position: absolute; top: 5px; right: 5px; font-size: 33px;"
                class="h4 text-success" v-b-modal.modal-1></b-icon>
    
    
            <b-modal id="modal-1" title="Safemessage" body-bg-variant="dark" header-bg-variant="dark"
                footer-bg-variant="dark" body-text-variant="light" header-text-variant="light" :cancel-disabled="true">
                <p>A secure chat aimed at maximum privacy.</p>
                <p>No cookies.</p>
                <p>No trackers.</p>
                <p>No database for messages.</p>
                <router-link to="/donate">Support the author :)</router-link>
            </b-modal>
    
        </div>
    
    </template>
    
    
    


    이 게시물은 꽤 길어졌기 때문에 여기까지입니다. 다음 세 번째 부분에서는 백엔드를 개선하여 프런트엔드에서 보낸 initialConnectionRequest를 수락할 수 있도록 합니다.

    들러주셔서 감사합니다, 좋은 하루 보내세요 :)

    좋은 웹페이지 즐겨찾기