websocket-前端代碼

原文鏈接:https://www.sinye.xyz/front-end/729.html

我糾結(jié)了很長(zhǎng)時(shí)間要不要寫這篇文章禀梳。因?yàn)橹褂酰绻约洪_發(fā)著玩踏兜,需要學(xué)的東西太多了尺碰,如果真正開發(fā)項(xiàng)目缅帘,肯定不是一個(gè)人能完成的婶熬,所以我寫的這點(diǎn)皮毛根本不夠用为狸。
既然是我自己業(yè)余時(shí)間寫這篇文章巍棱,那我就按照我開發(fā)這個(gè)'即時(shí)通訊'項(xiàng)目的流程簡(jiǎn)單描述一下。

1黍少、頁(yè)面先行

作為前端開發(fā)寡夹,沒有頁(yè)面怎么行,功能都是在靜態(tài)頁(yè)面的基礎(chǔ)上添加的厂置。

html:
<div class="wrapper">
    <div class="container">
        <div :class="'mask '+(user_list_bool?'active':'')" @click="user_list_bool=false"></div>
        <div :class="'left '+(user_list_bool?'active':'')">
            <div class="top">
                <div class="">在線人數(shù):<span id="numbers">{{userLength}}</span> 人
                </div>
            </div>
            <ul class="people">
              <li class="person flex_c_b" data-chat="person1" v-for="(item,index) in user_list" :key="index">
                <div class="flex_c_s">
                  <img :src="item.headerimg" alt=""/>
                  <div>
                    <span class="name">{{item.username}}</span>
                    <span class="preview">在線</span>
                  </div>
                </div>
                <span class="time">{{item.login_time}}</span>
              </li>
            </ul>
        </div>
        <div class="right">
            <div class="top flex_c_b">
                <span>群名: <span class="name">又甘又刻</span></span>
                <div class="chakang" @click="user_list_bool=true"><div class="numbers">{{userLength}}</div>在線</div>
            </div>
            <!-- <van-pull-refresh v-model="isLoading" @refresh="onRefresh"> -->
            <div class="mesbox" ref="mesbox">
              <div class="chat active-chat" data-chat="person1">
                <van-loading type="spinner" size="18px" v-show="isLoading"/>
                <div v-for="(item,index) in mes" :key="index" class="mg_t20">
                  <div v-if="item.type=='sys'">
                    <div class="conversation-start">
                      <span>{{item.msg}}</span>
                    </div>
                  </div>
                  <div v-else>
                    <div class="message">
                      <img :class="item.youMe=='me'?'me-header':''" :src="item.head" alt=""/>
                      <div :class="'bubble '+item.youMe" v-html="item.msg"></div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <div class="write">
                <a href="javascript:;" class="write-link attach"></a>
                <input type="text" id="input-value" @keyup.enter="confirm()" v-model="inputValue"/>
                <a href="javascript:;" class="write-link smiley"></a>
                <a href="javascript:;" class="write-link send" @click="send()"></a>
            </div>
        </div>
    </div>
  </div>
css:
*, *:before, *:after {box-sizing: border-box;}html{height: 100%;}:root {--white: #fff;--black: #000;--bg: #f8f8f8;--grey: #999;--dark: #1a1a1a;--light: #e6e6e6;--wrapper: 100vw;--blue: #00b0ff;}body {background-color: var(--bg);-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-rendering: optimizeLegibility;font-family: 'Source Sans Pro', sans-serif;font-weight: 400;font-size: .16rem;background-image: url("../img/image.jpg");background-size: cover;background-repeat: none;height: 100%;}.wrapper {position: relative;width: var(--wrapper);height: 100%;}.container {position: relative;width: 100%;height: 100%;background-color: var(--white);}.container .left {position: absolute;left: -100%;width: 60%;max-width: 250px;height: 100%;border: 1px solid var(--light);background-color: var(--white);z-index: 3;-webkit-transition: ease .4s;-moz-transition: ease .4s;-o-transition: ease .4s;transition: ease .4s;}.container .left.active{left: 0%;}.mask{position: absolute;width: 100vw;height: 100%;left: 0;top: 0;z-index: 2;background-color: rgba(0, 0, 0, 0.3);display: none;}.mask.active{display: block;}.container .left .top {position: relative;width: 100%;height: 47px;padding: .29rem;font-size: 14px;}.container .left .top #numbers{font-size: 16px;}.container .left .top:after {position: absolute;bottom: 0;left: 50%;display: block;width: 90%;height: 1px;content: '';background-color: var(--light);-webkit-transform: translate(-50%, 0);transform: translate(-50%, 0);}.container .left input {float: left;width: 1.88rem;height: .42rem;padding: 0 .15rem;border: 1px solid var(--light);background-color: #eceff1;border-radius: .21rem;font-family: 'Source Sans Pro', sans-serif;font-weight: 400;}.container .left input:focus {outline: none;}.container .left a.search {display: block;float: left;width: .42rem;height: .42rem;margin-left: .1rem;border: 1px solid var(--light);background-color: var(--blue);background-image: url("../img//name-type.png");background-repeat: no-repeat;background-position: top .12rem left .14rem;border-radius: 50%;}.container .left .people {margin-left: -1px;border-right: 1px solid var(--light);border-left: 1px solid var(--light);width: calc(100% + .02rem);}.container .left .people .person {position: relative;width: 100%;padding: 10px 14px;cursor: pointer;background-color: var(--white);}.container .left .people .person:after {position: absolute;bottom: 0;left: 50%;display: block;width: 86%;height: 1px;content: '';background-color: var(--light);-webkit-transform: translate(-50%, 0);transform: translate(-50%, 0);}.container .left .people .person img {width: 40px;height: 40px;border-radius: 50%;border: 1px solid #eee;margin-right: 5px;}.container .left .people .person .name {font-size: 14px;line-height: .22rem;color: var(--dark);font-family: 'Source Sans Pro', sans-serif;font-weight: 600;}.container .left .people .person .time {font-size: 12px;color: var(--grey);background-color: var(--white);}.container .left .people .person .preview {font-size: 12px;color: green;display: block;margin-top: 3px;text-align: start;}.container .left .people .person.active, .container .left .people .person:hover {margin-top: -1px;margin-left: -1px;padding-top: .13rem;border: 0;background-color: var(--blue);width: calc(100% + .02rem);padding-left: calc(10% + 1px);}.container .left .people .person.active span, .container .left .people .person:hover span {color: var(--white);background: transparent;}.container .left .people .person.active:after, .container .left .people .person:hover:after {display: none;}.container .right {position: relative;float: left;width: 100%;height: 100%;}.container .right .top {width: 100%;height: 47px;padding: .15rem .29rem;background-color: #eceff1;font-size: 14px;}.container .right .top .chakang{display: flex;align-items: center;color: var(--blue);cursor: pointer;}.container .right .top span {color: var(--grey);}.container .right .top span .name {color: var(--dark);font-family: 'Source Sans Pro', sans-serif;font-weight: 600;}.container .right .mesbox{padding: 0 15px;height: calc(100% - 89px);overflow: auto;}.container .right .chat {position: relative;display: none;flex-direction: column;}.container .right .chat.active-chat {display: flex;text-align: left;}.container .right .chat .mg_t20:last-child{margin-bottom: 15px;}.container .right .write {position: absolute;bottom: 0;height: 42px;padding-left: 8px;border: 1px solid var(--light);background-color: #e8ebee;width: 100%;display: flex;align-items: center;padding: 0 10px;}.container .right .write input {font-size: 16px;width: 100%;height: 30px;margin: 5px 10px 5px 5px;padding: 0 10px;color: var(--dark);border: 0;outline: none;background-color: #f6f7f8;font-family: 'Source Sans Pro', sans-serif;font-weight: 400;border-radius: 4px;}.container .right .write .write-link.attach:before {display: inline-block;width: 20px;height: 42px;content: '';background-image: url("../img/attachment.png");background-repeat: no-repeat;background-position: center;}.container .right .write .write-link.smiley:before {display: flex;width: 20px;height: 42px;content: '';background-image: url("../img/smiley.png");background-repeat: no-repeat;background-position: center;}.container .right .write .write-link.send:before {display: flex;float: left;width: 20px;height: 42px;margin-left: 11px;content: '';background-image: url("../img/send.png");background-repeat: no-repeat;background-position: center;}.container .right .bubble {font-size: 16px;position: relative;display: inline-block;clear: both;padding: 10px 14px;vertical-align: top;border-radius: 5px;word-break: break-all }.container .right .bubble:before {position: absolute;top: 14px;display: block;width: 12px;height: 12px;content: '\00a0';-webkit-transform: rotate(29deg) skew(-35deg);transform: rotate(29deg) skew(-35deg);}.container .right .bubble.you {float: left;color: var(--dark);background-color: #eceff1;align-self: flex-start;-webkit-animation-name: slideFromLeft;animation-name: slideFromLeft;}.container .right .bubble.you:before {left: -4px;background-color: #eceff1;}.container .right .bubble.me {float: right;color: var(--white);background-color: var(--blue);align-self: flex-end;-webkit-animation-name: slideFromRight;animation-name: slideFromRight;}.container .right .bubble.me:before {right: -4px;background-color: var(--blue);}.container .right .conversation-start {position: relative;width: 100%;text-align: center;}.container .right .conversation-start span {font-size: 12px;display: inline-block;color: var(--grey);}.container .right .conversation-start span:before, .container .right .conversation-start span:after {position: absolute;top: 7px;display: inline-block;width: 20%;height: 1px;content: '';background-color: var(--light);}.container .right .conversation-start span:before {left: 0;}.container .right .conversation-start span:after {right: 0;}@keyframes slideFromLeft {0% {margin-left: -2rem;opacity: 0;}100% {margin-left: 0;opacity: 1;}}@-webkit-keyframes slideFromLeft {0% {margin-left: -2rem;opacity: 0;}100% {margin-left: 0;opacity: 1;}}@keyframes slideFromRight {0% {margin-right: -2rem;opacity: 0;}100% {margin-right: 0;opacity: 1;}}@-webkit-keyframes slideFromRight {0% {margin-right: -2rem;opacity: 0;}100% {margin-right: 0;opacity: 1;}}.message img {float: left;width: 40px;height: 40px;margin-right: 12px;border-radius: 50%;border: 1px solid #eee;}.you {margin-left: 60px;margin-top: -39px;}.me-header {float: right !important;margin-right: 0 !important;}.me {margin-right: 60px;margin-top: -39px;}.active-chat::-webkit-scrollbar, .left::-webkit-scrollbar {width: 2px;}

<span style="color: red">css里加載了一些圖片菩掏,自己找?guī)讖垐D片替換了</span>

2、請(qǐng)慢用js脫發(fā)劑

我先簡(jiǎn)單的說一下我的邏輯:由于沒有登入這一步操作昵济,就不能確認(rèn)是誰(shuí)發(fā)的消息智绸,所以我就用ip當(dāng)作用戶名(雖然ip會(huì)變,當(dāng)短時(shí)間不變砸紊,對(duì)短時(shí)間測(cè)試沒有影響)传于。然后前端需要操作的數(shù)據(jù)就是區(qū)分信息是在左側(cè)還是右側(cè)顯示以及歷史信息回顯,就是用v-if判斷ip顯示不同html就行了醉顽。

websocket關(guān)鍵代碼
connect() {
    // 創(chuàng)建一個(gè) websocket 連接  ws://ip:端口號(hào)
    this.ws = new WebSocket("ws://websockets.sinye.xyz/websocket");

    // 連接狀態(tài) 1已建立連接
    // console.log(this.ws.readyState)

    //  連接建立時(shí)觸發(fā)
    this.ws.onopen = ()=>{
        this.onopen();
    };

    // 客戶端接收服務(wù)端數(shù)據(jù)時(shí)觸發(fā)
    this.ws.onmessage = (event)=>{
        this.onmessage(event)
    };

    // 連接關(guān)閉時(shí)觸發(fā)
    this.ws.onclose = ()=>{
        this.onclose();
    };

    //  通信發(fā)生錯(cuò)誤時(shí)觸發(fā)
    this.ws.onerror = ()=>{
        this.onerror();
    };
},
// 通信建立成功
onopen(){
    var data = "系統(tǒng)消息:建立連接成功";
    console.log(data);
},
// 接收客戶端的數(shù)據(jù),發(fā)送數(shù)據(jù)
onmessage(e){
    var data = JSON.parse(e.data);
    // console.log(data)

    switch (data.type) {
        case 'handShake':
            //首次登錄沼溜,發(fā)送登陸數(shù)據(jù)
            var user_info = {'type': 'login', 'msg': this.uname, 'headerimg': this.headerimg};
            this.sendMsg(user_info);
            break;
        case 'login':
            this.userList(data.user_list);
            this.systemMessage('系統(tǒng)消息: ' + data.msg + ' 已上線');
            break;
        case 'logout':
            this.userList(data.user_list);
            if (data.msg.length > 0) {
                this.systemMessage('系統(tǒng)消息: ' + data.msg + ' 已下線');
            }
            break;
        case 'user':
            this.messageList1(data);
            break;
        case 'system':
            this.systemMessage();
            break;
    }
},
// 關(guān)閉連接時(shí)觸發(fā)
onclose(){
    console.log("連接關(guān)閉,定時(shí)重連");
    this.connect();
},
// websocket 錯(cuò)誤事件
onerror(){
    var data = "系統(tǒng)消息 : 出錯(cuò)了,請(qǐng)退出重試.";
    console.log(data);
},
// 輸入框輸入后按回車游添,向服務(wù)器發(fā)送信息
confirm() {
    this.send();
},
// 發(fā)送的數(shù)據(jù)先保存到數(shù)據(jù)庫(kù)
send() {
  let msg = this.inputValue.replace(/^[\s+]$/g, ' ')
  var data = {type: "user", msg: msg, id: this.userid}
  if(msg!=''){
    this.$axios.post(
      '/api/InfoSave/save', data
    ).then((response)=>{
      if(response.data !== ''){
        // 保存成功后再通過websocket群發(fā)
        this.sendMsg(data);
        this.inputValue = "";
      }
    })
  }
},
// 群發(fā)數(shù)據(jù)
sendMsg(msg) {
    var data = JSON.stringify(msg);
    this.ws.send(data);
},

發(fā)送和接收就已經(jīng)完成了系草,接下來就是回顯歷史信息

歷史信息回顯
// 追加數(shù)據(jù) 上下線的系統(tǒng)消息
systemMessage(msg) {
    this.mes.push({type:'sys',msg:msg})
    this.active_chat.scrollTop = this.active_chat.scrollHeight;
},
// 追加從服務(wù)端返回的數(shù)據(jù) 左側(cè)在線人數(shù)列表
userList(user) {
  this.user_list = []
  for (var i = 0; i < user.length; i++) {
    this.user_list.push({
      headerimg: user[i].headerimg,
      username: user[i].username,
      login_time: user[i].login_time,
    })
  }
  this.userLength = user.length
},
// 右側(cè)聊天記錄列表
messageList1(data) {
  // var html
    // 判讀是不是自己發(fā)送的消息,對(duì)應(yīng)的樣式不同
    if (data.ip == this.uname) {   
      // 如果當(dāng)前用戶名和feom的用戶名相同唆涝,就說明時(shí)自己發(fā)送的消息
      this.mes.push({type:'info', youMe:'me', msg:data.msg, head: data.headerimg})  
    } else {
      // 別人發(fā)送的信息列表
      this.mes.push({type:'info', youMe:'you', msg:data.msg, head: data.headerimg})
    }
    this.$nextTick(() =>{
      this.active_chat.scrollTop = this.active_chat.scrollHeight;
    })
},
// 右側(cè)聊天記錄列表
messageList2(data,bool) {
  // var html
    // 判讀是不是自己發(fā)送的消息找都,對(duì)應(yīng)的樣式不同
    if (data.ip == this.uname) {   
      this.mes.splice(0,0,{type:'info', youMe:'me', msg:data.msg, head: data.headerimg}) 
    } else {
      this.mes.splice(0,0,{type:'info', youMe:'you', msg:data.msg, head: data.headerimg})
    }
    if(bool){
      this.$nextTick(() =>{
        this.active_chat.scrollTop = this.active_chat.scrollHeight;
      })
    }
    else{
      this.$nextTick(() =>{
        this.active_chat.scrollTop = this.active_chat.scrollHeight - this.oldH
      })
      
    }
},
// 獲取歷史信息ajax
onRefresh(bool) {
  this.isLoading = true;
  this.page++;
  this.$axios.post(
    '/api/InfoSave/get', {page: this.page}
  ).then((response)=>{
    if(response.data){
      for (let i = 0; i < response.data.length; i++) {
        const data = response.data[i];
        this.messageList2(data,bool)
      }
      this.isLoading = false;
    }else{
      this.page--
      this.isLoading = false;
    }
  })
},
// 監(jiān)聽滾動(dòng)
handleScroll () {
  if (this.active_chat.scrollTop <= 0) {
    this.oldH = JSON.parse(JSON.stringify(this.active_chat.scrollHeight))
    this.onRefresh(false)
  }
},
// 獲取ip并連接服務(wù)器
login(){
    this.$axios.get(
    '/api/login'
    ).then((response)=>{
    var data = response.data
    this.uname = data.ip
    this.headerimg = data.headerimg
    this.userid = data.id
    if(this.uname!=''){
        this.connect()
        this.onRefresh(true)
    }else{
        console.log('連接失敗廊酣!')
    }
    })
},
初始化還是必須的
mounted(){
    this.login()
    this.$nextTick(() =>{
        this.active_chat = document.querySelector('.mesbox')
        this.$refs.mesbox.addEventListener('scroll', this.handleScroll)
    })
},
data() {
    return {
      userid: '',//用戶id
      ws: {},
      uname: '',//用戶名
      headerimg: '',//用戶頭像
      mes:[],//消息
      inputValue: '',//輸入的信息
      active_chat: '',//信息dome
      user_list: [],//在線用戶列表
      user_list_bool: false,//在線用戶列表彈窗是否顯示
      userLength: '',//在線人數(shù)
      page: 0,
      isLoading: false,
      oldH: '',//原來信息高度
    }
},

大致完成

成品鏈接:http://socket.sinye.xyz

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末能耻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子亡驰,更是在濱河造成了極大的恐慌晓猛,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凡辱,死亡現(xiàn)場(chǎng)離奇詭異戒职,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)透乾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門洪燥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來磕秤,“玉大人,你說我怎么就攤上這事捧韵∈信兀” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵再来,是天一觀的道長(zhǎng)床绪。 經(jīng)常有香客問我,道長(zhǎng)其弊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任膀斋,我火速辦了婚禮梭伐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘仰担。我一直安慰自己糊识,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布摔蓝。 她就那樣靜靜地躺著赂苗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪贮尉。 梳的紋絲不亂的頭發(fā)上拌滋,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音猜谚,去河邊找鬼败砂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛魏铅,可吹牛的內(nèi)容都是我干的昌犹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼览芳,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼斜姥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起沧竟,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤铸敏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后屯仗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搞坝,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年魁袜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了桩撮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敦第。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖店量,靈堂內(nèi)的尸體忽然破棺而出芜果,到底是詐尸還是另有隱情,我是刑警寧澤融师,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布右钾,位于F島的核電站,受9級(jí)特大地震影響旱爆,放射性物質(zhì)發(fā)生泄漏舀射。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一怀伦、第九天 我趴在偏房一處隱蔽的房頂上張望脆烟。 院中可真熱鬧,春花似錦房待、人聲如沸邢羔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拜鹤。三九已至,卻和暖如春流椒,著一層夾襖步出監(jiān)牢的瞬間敏簿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工宣虾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留极谊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓安岂,卻偏偏與公主長(zhǎng)得像轻猖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子域那,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容