vue和socket.io开发简单web聊天室

效果预览 https://www.wangchunjian.top/chat.html

default

default

default

需要用到的库

  1. https://socket.io/
  2. https://muse-ui.org/#/zh-CN
  3. https://cli.vuejs.org/

server端

socket建立长连接,接收并向所有建立连接的client推送消息

笼统的讲就是on接收客户端消息,emit向客户端发送消息,详细请移步https://socket.io/

var app = require('express')();
var fs = require('fs');
var privatekey = fs.readFileSync('/www/wdlinux/nginx/conf/cert/www.wangchunjian.top.key', 'utf8');
var certificate = fs.readFileSync('/www/wdlinux/nginx/conf/cert/www.wangchunjian.top.pem', 'utf8');
var options={key:privatekey, cert:certificate};
var https = require('https').Server(options,app);
var io = require('socket.io')(https);
var _ = require('lodash');
let users=[];

app.get('/', function(req, res){
  res.send('<h1>Hello world</h1>');
});

io.on('connection',function(socket){

  socket.on('chat loading',function(msg){
    var findIndex=_.findIndex(users,function(o){return o.chatUserId==msg.chatUserId;});
    socket.chatUserId=msg.chatUserId;
    console.log(findIndex);
    if(findIndex<0){
      users.push({chatUserId:msg.chatUserId,nickname:msg.nickname});
      socket.broadcast.emit('tooltip',msg.nickname+' join..');
    }
    io.emit('onlines',users);
  });


  socket.on('chat message', function(msg){
    var findIndex=_.findIndex(users,function(o){return o.chatUserId==msg.chatUserId;});
    if(findIndex<0){
      users.push({chatUserId:msg.chatUserId,nickname:msg.nickname});
      socket.broadcast.emit('tooltip',msg.nickname+' join..');
      io.emit('onlines',users);
    }
		io.emit('chat message',msg); 
	});

  socket.on('nickname', function(msg){
    var findIndex=_.findIndex(users,function(o){return o.chatUserId==msg.chatUserId;});
    if(findIndex<0){
      users.push(msg);
      socket.broadcast.emit('tooltip',msg.nickname+' join..');
    }else{
      users[findIndex]=msg;
    }
    io.emit('onlines',users);
  }); 

  socket.on('disconnect', function(){
      var findIndex=_.findIndex(users,function(o){return o.chatUserId==socket.chatUserId;});
      var leaveUser=users[findIndex];
      if(findIndex>-1){
        socket.broadcast.emit('tooltip',leaveUser.nickname+' leave..');
        users.splice(findIndex,1);
        socket.broadcast.emit('onlines',users);
      } 
	});
});

https.listen(3000, function(){
  console.log('listening on *:3000');
});

client端

/views/Chat.vue引入的一些库

import 'muse-ui-message/dist/muse-ui-message.css';
import Vue from 'vue';
import Message from 'muse-ui-message';
import Toast from 'muse-ui-toast';

var myMarked = require('marked');
import 'highlight.js/styles/github.css';
// Set options
// `highlight` example uses `highlight.js`
myMarked.setOptions({
  renderer: new myMarked.Renderer(),
  highlight: function(code) {
    return require('highlight.js').highlightAuto(code).value;
  },
  pedantic: false,
  gfm: true,
  tables: true,
  breaks: false,
  sanitize: false,
  smartLists: true,
  smartypants: false,
  xhtml: false
});

Vue.use(Message);
Vue.use(Toast,{position:'top'});

import docCookies from './../cookies'
import io from 'socket.io-client';

const uuidv1 = require('uuid/v1')
const socket = io('https://www.wangchunjian.top:3000');
let chatUserId='';
let nickname='匿名';

if(docCookies.hasItem('chatUserId')){
    chatUserId=docCookies.getItem('chatUserId');
}else{
    chatUserId=uuidv1();
    docCookies.setItem('chatUserId',chatUserId,Infinity);
}

if(docCookies.hasItem('nickname')){
    nickname=docCookies.getItem('nickname');
}

/views/Chat.vue里处理Vue事件和属性的代码

<script>
export default {
  data () {
    return {
      chatUserId:chatUserId,
      nickname:nickname,
      messages:messages,
      openFullscreen: false,
      onlines:[],
      form: {
        input: '',
      }
    }
  },
  mounted(){
    if(this.nickname=='匿名'){
      this.EditProfile();
    }
    var that=this;
    socket.on('chat message', function(msg){
        msg.msg=myMarked(msg.msg);
        that.messages.push(msg);
    });

    socket.on('onlines', function(msg){
        that.onlines=msg;
    });

    socket.on('tooltip', function(msg){
        that.$toast.success(msg);
    });

    socket.emit('chat loading',{chatUserId:chatUserId,nickname:nickname});
  },
  methods:{
    submit(evt) {
      evt.preventDefault();
      if(this.form.input!=''){
        socket.emit('chat message',{chatUserId:chatUserId,nickname:nickname,msg:this.form.input});
        this.form.input='';
      }
    },
    openFullscreenDialog () {
      this.openFullscreen = true;
    },
    closeFullscreenDialog () {
      this.openFullscreen = false;
    },
    EditProfile(){
      this.$prompt('请设置昵称', '提示', {
        validator (value) {
          return {
            valid:value.length>1,
            message: '请设置昵称至少两个字符'
          }
        }
      }).then(({ result, value }) => {
        if (result) {
          nickname=value;
          this.nickname=value;
          socket.emit('nickname',{chatUserId:chatUserId,nickname:value});
          docCookies.setItem('nickname',value,Infinity);
        } 
      });


    }
  }
}
</script>

/views/Chat.vue template 部分

<template>
  <div class="about">
    <mu-appbar style="width: 100%;" color="primary" id="mu-bar">
      <mu-button icon slot="left" @click="openFullscreenDialog">
        <mu-icon value="menu"></mu-icon>
      </mu-button>

      <span v-if="onlines" @click="openFullscreenDialog">聊天室 ({{onlines.length}}) 人在线</span>
    
      <mu-button flat slot="right" @click="EditProfile">设置</mu-button>
    </mu-appbar>


    <mu-dialog width="360" transition="slide-top" fullscreen :open.sync="openFullscreen">
    <mu-appbar color="primary" title="Onlines">
      <mu-button slot="left" icon @click="closeFullscreenDialog">
        <mu-icon value="close"></mu-icon>
      </mu-button>
      <mu-button slot="right" flat  @click="closeFullscreenDialog">
        关闭
      </mu-button>
    </mu-appbar>
    <div class="onlines">
      <mu-list>
        <mu-list-item avatar button :ripple="false" v-for="u in onlines" :key="u.chatUserId">
          <mu-list-item-action>
            <mu-avatar>
              <img src="/assets/images/me.jpg">
            </mu-avatar>
          </mu-list-item-action>
          <mu-list-item-title>{{ u.nickname }}</mu-list-item-title>
          <mu-list-item-action>
            <mu-icon value="chat_bubble"></mu-icon>
          </mu-list-item-action>
        </mu-list-item>
      </mu-list>
    </div>
  </mu-dialog>



    <ul id="messages">
        <li v-for="(item,index) in messages" :class="item.chatUserId==chatUserId?'me':'you'" :key="index">
            <img src="/assets/images/me.jpg" class="avatar" />
            <span class="nickname" v-text="item.nickname"></span>
            <p class="wrapper" v-html="item.msg"></p>
             <div class="clearfix"></div>
        </li>
    </ul>
    

    <mu-form :model="form" class="mu-demo-form" @submit="submit">      
      <mu-text-field multi-line :rows="3" v-model="form.input"></mu-text-field>
      <mu-button color="primary" type="submit">提交</mu-button>
    </mu-form>

  </div>
</template>

main.js 入口

import Vue from 'vue'
import Chat from './views/Chat.vue'
import MuseUI from 'muse-ui';
import 'muse-ui/dist/muse-ui.css';

Vue.use(MuseUI);

Vue.config.productionTip = false

new Vue({
  render: h => h(Chat)
}).$mount('#app')

index.html 文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>Chat</title>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">
    <link rel="stylesheet" href="https://cdn.bootcss.com/material-design-icons/3.0.1/iconfont/material-icons.css">
    <base target="_blank" />
  </head>
  <body>
    <noscript>
      <strong>We're sorry but hello-world doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

转载于:https://my.oschina.net/wcj2020/blog/1932576

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值